Как реализовать тестовый мир без перезагрузки?

23

Ищу идеи о том, как сделать следующее: я хочу написать простой "мир" на Java. Тот, который я мог бы запустить, а затем добавить новые объекты позже, чтобы смоделировать / наблюдать различное поведение между существующими объектами. План состоит в том, чтобы затем закодировать новые объекты после некоторого просмотра старых, а затем загрузить / выбросить их в существующий мир. Проблема в том, что я не хочу останавливать или перезапускать мир после его запуска, я хочу, чтобы он работал в течение нескольких недель, но мне нужна возможность бросать объекты и повторять / переписывать / удалять / создавать / изменять их со временем без перезагрузки. Мир может быть таким простым, как массив X / Y 100 × 100, с возможным графическим интерфейсом мозаичной карты для визуального представления мира. Я знаю, что мне нужен какой-то тиктимерный процесс, чтобы контролировать объекты и дать каждому «шанс действовать»

Пример: я кодирую World.java в понедельник и оставляю его работающим. Затем во вторник я пишу новый класс под названием Rock.java (который не двигается). Затем я загружаю / удаляю его (каким-то образом?) В этот уже работающий мир (который просто сбрасывает его в случайном порядке в массиве мира и никогда не перемещается) Затем в среду я создаю новый класс с именем Cat.java и помещаю его в мир, снова размещенный случайным образом, но этот новый объект может перемещаться по миру (в течение некоторой единицы времени), затем в четверг я пишу класс с именем Dog. Java, который также перемещается, но может «воздействовать» на другой объект, если он находится в соседнем месте, и наоборот.

Вот вещь Я не знаю, какую структуру / дизайн мне нужно было бы кодировать для реального мирового класса, чтобы знать, как обнаруживать / загружать / отслеживать будущие (и в настоящее время несуществующие) объекты.

Любые идеи о том, как вы могли бы сделать что-то подобное с помощью Java?

d33j
источник
2
Звучит как горячая замена . Возможно, есть некоторая литература по этому вопросу, которая может быть полезной. Во всяком случае, очень интересный вопрос. +1…
Конрад Рудольф

Ответы:

5

То, что вы в основном ищете, это система с возможностью горячей замены. Вы запускаете основное приложение, а затем добавляете плагины во время выполнения, которые интегрируются в цикл обработки событий. Во-первых, начните думать о том, чего ожидает ваш мир от игровой сущности. Например (на основе вашего описания):

interface Entity {
   void init(World world);
   // Called when loaded for the first time in the world and adds itself
   // to the world (correct position in the array, scheduler for updates)

   void update(World world);
   // Called when scheduler fires (allows the entity to think about its
   // next move)

   Image getImage();
   // Called by the GUI when it is time to draw the entity on the screen
}

Конечно, вы можете добавить другие методы, которые вы считаете необходимыми. Обратите внимание на параметр World с помощью двух соответствующих методов. Это позволяет вашей новой сущности учитывать мир при настройке или обновлении. Например, на уроке «Собака» вы можете попросить мир всех кошек по соседству. Затем вы создаете свой мир, который работает с этим интерфейсом и системой для динамической компиляции и загрузки кода Java. Пример этого можно найти здесь .

void injectEntity(String filename, World world) {
    // Load the class as described in the mentioned link
    Entity e = (Entity) loadClass(filename);
    e.init(world);
}

Вызовите этот метод из World GUI, чтобы добавить новые объекты. В зависимости от вашей реализации World, функция Entity init может выглядеть так:

void init(World world) {
   // Register entity with world for easy access
   world.register(this, "RockImpl A");

   // Place the entity on the world map
   Point initialLocation = Point(10,5);
   world.place(this, initialLocation);

   // Schedule its update method for every 5 seconds
   world.schedule(this, 5);
}
призрак
источник
1
Ключевые слова для Google: «контейнеры IoC», «инверсия управления», «внедрение зависимостей». Это методы для общих систем с «горячей» заменой, которые могут обнаруживать и загружать компоненты во время выполнения.
Nevermind
16

Мы сделали что-то подобное в Стендале для рейдов.

Мы не стремились полностью избежать перезапусков. Таким образом, изменения в наших основных инфраструктурных сервисах, таких как связь клиент / сервер, требуют перезапуска. Но добавление сущностей, существ и NPC, а также изменение существующих объектов работает. (Да, и иногда исправление ошибок, отражение может использоваться, чтобы управлять даже частными полями).

Поскольку мы не только хотим новый объект, основанный на новых данных (например, другой скин), но и хотим добавить новое поведение, всемирная программа должна иметь возможность загружать новые файлы классов . Мы называем их «скриптами», но это настоящие скомпилированные классы Java. Эти классы реализуют интерфейс Script.java .

Maria.java - простой пример. Она новый NPC, который продает напитки и еду игрокам. Мы также можем определить очень сложные объекты .

Новый класс загружается следующим образом:

// create a new class loader, with the script folder as classpath.
final File file = new File("./data/script");
final ClassLoader loader = new URLClassLoader(new URL[]{file.toURI().toURL()});

// load class through new loader
final Class< ? > aClass = loader.loadClass(classname);
script = (Script) aClass.newInstance();

Если вы можете гарантировать уникальные имена и никогда не хотите выгружать классы, вы закончили работу с вещами низкого уровня.

разгрузочный , однако, кажется довольно важной. Чтобы достичь этого, вы должны создавать новый загрузчик классов всякий раз, когда хотите внедрить новый код. Чтобы GC мог выполнять свою работу после очистки последней ссылки на этот код.

У нас есть команда / unload, которая вызывает метод unload в нашем интерфейсе, чтобы скрипты могли выполнять очистку. Реальная разгрузка выполняется автоматически GC.

Мы часто создаем много временных объектов во время рейдов. И мы хотим, чтобы все они были удалены после окончания рейда. Например, Рейд Гномов, который порождает несколько гномов возле невидимого администратора, мы используем этот код: GnomeRaid.java extends CreateRaid.java .

Сценарий может напрямую обращаться к миру (как показано в первом примере) и выполнять собственную очистку в методе unload (). Но Java-кодеры не используются для очистки, и это раздражает. Поэтому мы создали песочницу, которую могут использовать скрипты. При выгрузке удаляются все объекты, добавленные в мир через класс Sandbox.

Хендрик Бруммерманн
источник
3

Спасите мир (без каламбура) и все его состояние (позиции, скорости, все переменные).

  • Закройте программу.
  • Внедрить изменения (обеспечение обратной совместимости с сохранениями).
  • Перекомпилируй программу.
  • Перезапустите программу.
  • Загрузите мир и состояние.
  • Повторение

Это общее решение, хотя я не знаю специфику Java, чтобы узнать, можете ли вы динамически реализовывать блоки кода и классы, как вы надеетесь ...

Вариант 2 - привязать язык сценариев, который можно загрузить на лету.

deceleratedcaviar
источник
+1 за язык сценариев. Если вы не связаны с Java, попробуйте некоторые из MUD-драйверов на основе LPC и грязные библиотеки.
Мартин Сойка
1

Для Java все, что вам нужно, это платформа OSGi . При этом тривиально заменять модули или приложения с возможностью горячей замены и даже выполнять удаленное управление или частичные обновления.


источник