如何实现永不重启的测试世界?


23

我正在寻找有关如何执行以下操作的想法:我想用Java编写一个简单的“世界”。我可以先启动一个,然后在以后再添加新对象,以模拟/观察现有对象之间的不同行为。然后计划是在观看旧对象一段时间之后,对较新的对象进行编码,然后将其加载/放置到现有环境中。问题是,我不想在启动世界后就停止或重新启动它,我希望它运行几周,但我确实需要能够放入对象并重做/重写/删除/创建/更改它们的功能。随着时间的流逝,无需重启。这个世界可能像X / Y位置的100 x 100数组一样简单,并且可以使用平铺的图形用户界面直观地表示世界。我知道我需要某种滴答计时器流程来监视对象并为每个对象提供“行动的机会”

示例:我在星期一对World.java进行了编码,并使其运行。然后在星期二,我编写了一个名为Rock.java的新类(不会移动)。然后,我将它加载(或以某种方式放置)到这个已经运行的世界中(这只是将它随机放置在world数组中,并且永远不会移动)。然后在星期三,我创建一个名为Cat.java的新类,并将其放到世界中,再随机放置,但是这个新对象可以在世界范围内移动(在某个时间单位内),然后在星期四,我编写一个名为Dog的类。也可以四处移动的java,但如果它在邻居位置,则可以“作用于”另一个对象,反之亦然。

就是这个 我不知道我需要对实际的世界类进行编码才能知道如何检测/加载/跟踪未来(以及当前不存在)的对象。

关于如何使用Java进行此类操作的任何想法?


2
听起来很像热插拔。也许有些文献对此有所帮助。无论如何,这是一个非常有趣的问题。+1…
Konrad Rudolph

Answers:


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容器”,“控制反转”,“依赖项注入”。这些是通用热插拔系统的技术,可以在运行时发现和加载组件。
没关系

16

我们在Stendhal进行了类似的突袭。

我们的目标不是完全避免重新启动。因此,对我们的核心基础架构服务(例如客户端/服务器通信)的更改确实需要重新启动。但是添加实体,生物和NPC以及修改现有对象确实可以。(哦,有时是实时的错误修复,反射甚至可以用于处理私有字段)。

由于我们不仅希望基于新数据的新对象(例如另一种皮肤),而且希望添加新的行为,因此world程序需要能够加载新的类文件。我们称它们为“脚本”,但它们是真正的编译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延伸CreateRaid.java

该脚本可以直接访问世界(如第一个示例所示),并在unload()方法中进行自己的清理。但是Java编码人员不习惯清理,这很烦人。因此,我们创建了脚本可以使用的沙箱。卸载时,将删除通过Sandbox类添加到世界的所有对象。


3

拯救世界(无双关语)及其所有状态(位置,速度,所有变量)。

  • 关闭程序。
  • 实施更改(确保与保存向后兼容)。
  • 重新编译程序。
  • 重新启动程序。
  • 加载世界和状态。
  • 重复

不过,这是一个通用的解决方案,我不知道需要使用Java来动态地实现代码块和类,所以我没有Java的详细信息。

选项2是绑定可以动态加载的脚本语言。


+1为脚本语言。如果您不希望使用Java,也可以尝试一些基于LPC的MUD驱动程序和Mulibs。
Martin Sojka

1

对于Java,您只需要一个OSGi平台。有了它,热交换模块或应用程序甚至进行远程管理或部分升级就变得微不足道了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.