Wie implementiere ich eine nie neustartende Testwelt?


23

Bin auf der Suche nach Ideen, wie man folgendes macht: Ich möchte eine einfache "Welt" in Java schreiben. Eine, mit der ich später beginnen und dann neue Objekte hinzufügen könnte, um verschiedene Verhaltensweisen zwischen vorhandenen Objekten zu simulieren / zu beobachten. Der Plan ist dann, neuere Objekte zu codieren, nachdem Sie die alten für eine Weile angesehen haben, und sie dann in die bestehende Welt zu laden / fallen zu lassen. Das Problem ist, dass ich die Welt nach dem Start nicht mehr anhalten oder neu starten möchte. Ich möchte, dass sie ein paar Wochen lang ausgeführt wird, aber ich muss Objekte ablegen und sie wiederholen / umschreiben / löschen / erstellen / mutieren können im Laufe der Zeit, ohne einen Neustart zu benötigen. Die Welt könnte so einfach sein wie ein 100 x 100-Array von X / Y-Standorten, mit einer möglichen GUI mit gekachelten Karten, um die Welt visuell darzustellen. Ich weiß, ich brauche eine Art Ticktimer-Prozess, um Objekte zu überwachen und jedem eine Chance zum Handeln zu geben.

Beispiel: Ich verschlüssele World.java am Montag und lasse es laufen. Dann schreibe ich am Dienstag eine neue Klasse namens Rock.java (die sich nicht bewegt). Ich lade / lasse es dann (irgendwie?) In diese bereits laufende Welt (die es einfach irgendwo zufällig in der Weltmatrix ablegt und sich nie bewegt). Dann erstelle ich am Mittwoch eine neue Klasse mit dem Namen Cat.java und lasse diese in die Welt fallen, wieder zufällig platziert, aber dieses neue Objekt kann sich über einen gewissen Zeitraum um die Welt bewegen. Am Donnerstag schreibe ich dann eine Klasse mit dem Namen Dog. Java, das sich ebenfalls bewegt, aber auf ein anderes Objekt "einwirken" kann, wenn es sich am Nachbarort befindet und umgekehrt.

Hier ist das Ding. Ich weiß nicht, welche Art von Struktur / Design ich benötigen würde, um die tatsächliche Weltklasse zu codieren, um zu wissen, wie zukünftige (und derzeit nicht vorhandene) Objekte erkannt / geladen / verfolgt werden.

Irgendwelche Ideen, wie Sie so etwas mit Java machen würden?


2
Hört sich nach Hot Swapping an . Vielleicht gibt es etwas Literatur, die hilfreich sein kann. Wie auch immer, sehr interessante Frage. +1…
Konrad Rudolph

Antworten:


5

Was Sie im Grunde suchen, ist ein Hot-Plug-System. Sie führen eine Hauptanwendung aus und fügen dann zur Laufzeit Plug-Ins hinzu, die in die Ereignisschleife integriert werden. Überlegen Sie sich zunächst, was Ihre Welt von einer Spieleinheit erwartet. Zum Beispiel (basierend auf Ihrer Beschreibung):

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
}

Natürlich können Sie auch andere Methoden hinzufügen, die Sie für notwendig halten. Notieren Sie den Parameter World mit den beiden relevanten Methoden. Dies ermöglicht Ihrer neuen Entität, die Welt beim Einrichten oder Aktualisieren zu berücksichtigen. In Ihrer Hundeklasse können Sie zum Beispiel die Welt nach allen Katzen in der Nachbarschaft fragen. Als Nächstes erstellen Sie Ihre Welt, die mit dieser Schnittstelle und einem System zum dynamischen Kompilieren und Laden von Java-Code funktioniert. Ein Beispiel dafür finden Sie hier .

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

Rufen Sie diese Methode über die Welt-GUI auf, um neue Entitäten hinzuzufügen. Abhängig von Ihrer World-Implementierung kann eine Entity-Init-Funktion folgendermaßen aussehen:

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
Stichworte zu google: "IoC Container", "Inversion of Control", "Dependency Injection". Hierbei handelt es sich um Techniken für generische Hot-Plug-fähige Systeme, mit denen Komponenten zur Laufzeit erkannt und geladen werden können.
Nevermind

16

Wir haben so etwas in Stendhal für Razzien gemacht.

Neustarts wollten wir nicht ganz vermeiden. Änderungen an unseren Kerninfrastrukturdiensten wie der Client / Server-Kommunikation erfordern daher einen Neustart. Das Hinzufügen von Entitäten, Kreaturen und NPCs sowie das Ändern vorhandener Objekte funktioniert jedoch. (Oh, und manchmal kann die Reflexion zur Behebung von Live-Fehlern verwendet werden, um sogar private Felder zu manipulieren.)

Da wir nicht nur ein neues Objekt basierend auf neuen Daten (wie ein anderes Skin) wollen, sondern auch neues Verhalten hinzufügen wollen, muss das Weltprogramm in der Lage sein, neue Klassendateien zu laden . Wir nennen sie "Skripte", aber es handelt sich um echte kompilierte Java-Klassen. Diese Klassen implementieren die Script.java- Schnittstelle.

Maria.java ist ein einfaches Beispiel. Sie ist ein neuer NPC, der Getränke und Essen an Spieler verkauft. Wir können dort auch sehr komplexe Objekte definieren .

Eine neue Klasse wird folgendermaßen geladen:

// 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();

Wenn Sie eindeutige Namen sicherstellen können und keine Klassen mehr entladen möchten, sind Sie mit der Arbeit auf niedriger Ebene fertig.

Das Entladen scheint jedoch ziemlich wichtig zu sein. Um dies zu erreichen, müssen Sie immer dann ein neues Klassenladeprogramm instanziieren, wenn Sie neuen Code einfügen möchten. Damit der GC seine Arbeit erledigen kann, nachdem der letzte Verweis auf diesen Code gelöscht wurde.

Wir haben den Befehl / unload , der eine Entlademethode in unserer Schnittstelle aufruft, damit Skripte bereinigt werden können. Das eigentliche Entladen erfolgt automatisch durch den GC.

Bei Überfällen erstellen wir häufig viele temporäre Objekte . Und wir möchten, dass alle von ihnen entfernt werden, nachdem der Überfall beendet ist. Zum Beispiel verwenden wir den Gnome-Raid, der eine Reihe von Gnomen in der Nähe des unsichtbaren Administrators erzeugt, diesen Code: GnomeRaid.java erweitert CreateRaid.java .

Das Skript könnte direkt auf die Welt zugreifen (wie das erste Beispiel zeigt) und seine eigene Bereinigung in der Methode unload () durchführen. Java-Codierer werden jedoch nicht zum Aufräumen verwendet, und es ist ärgerlich. Deshalb haben wir eine Sandbox erstellt , die von Skripten verwendet werden kann. Beim Entladen werden alle Objekte entfernt, die der Welt über die Sandbox-Klasse hinzugefügt wurden.


3

Rette die Welt (kein Wortspiel beabsichtigt) und all ihren Zustand (Positionen, Geschwindigkeiten, alle Variablen).

  • Schließe das Programm.
  • Implementieren Sie Änderungen (Sicherstellen der Abwärtskompatibilität mit Speicherungen).
  • Programm neu kompilieren.
  • Starten Sie das Programm neu.
  • Lade Welt und Staat.
  • Wiederholen

Dies ist jedoch eine allgemeine Lösung. Ich möchte nicht wissen, ob Sie Codeblöcke und -klassen dynamisch implementieren können, wie Sie es sich erhoffen ...

Option 2 besteht darin, eine Skriptsprache zu binden, die im laufenden Betrieb geladen werden kann.


+1 für die Skriptsprache. Wenn Sie nicht an Java gebunden sind, probieren Sie auch einige der LPC-basierten MUD-Treiber und Mudlibs aus.
Martin Sojka

1

Für Java benötigen Sie lediglich eine OSGi-Plattform . Damit ist es einfach, Module oder Anwendungen im laufenden Betrieb auszutauschen und sogar Remote-Management oder Teil-Upgrades durchzuführen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.