Comment implémenter un monde de test sans redémarrage?


23

Je cherche des idées sur la façon de faire ce qui suit: Je veux écrire un simple "monde" en Java. Un que je pourrais commencer et ajouter de nouveaux objets plus tard à une date ultérieure pour simuler / observer différents comportements entre des objets existants. Le plan est alors de coder les nouveaux objets après avoir regardé les anciens pendant un certain temps, puis de les charger / déposer dans le monde existant. Le problème est que je ne veux jamais arrêter ou redémarrer le monde une fois qu'il a démarré, je veux qu'il fonctionne pendant quelques semaines mais j'ai besoin de pouvoir déposer des objets et les refaire / réécrire / supprimer / créer / muter au fil du temps sans avoir besoin d'un redémarrage. Le monde pourrait être aussi simple qu'un tableau 100 x 100 d'emplacements X / Y, avec une possible interface graphique en mosaïque pour représenter visuellement le monde. Je sais que j'ai besoin d'une sorte de processus de minuterie pour surveiller les objets et donner à chacun une «chance d'agir»

Exemple: j'ai codé World.java lundi et je le laisse tourner. Mardi, j'écris une nouvelle classe appelée Rock.java (qui ne bouge pas). Je le charge / le dépose ensuite (d'une manière ou d'une autre?) Dans ce monde déjà en cours d'exécution (qui le dépose simplement quelque part au hasard dans le tableau des mondes et ne se déplace jamais). Mercredi, je crée une nouvelle classe appelée Cat.java et la laisse tomber dans le monde, à nouveau placée au hasard, mais ce nouvel objet peut se déplacer dans le monde (sur une certaine unité de temps), puis jeudi j'écris une classe appelée Dog. java qui se déplace également mais peut «agir» sur un autre objet s'il se trouve à l'emplacement voisin et vice versa.

Voici le truc. Je ne sais pas quel type de structure / conception j'aurais besoin pour coder la classe mondiale réelle pour savoir comment détecter / charger / suivre les objets futurs (et actuellement non existants).

Des idées sur la façon dont vous feriez quelque chose comme ça en utilisant Java?


2
Cela ressemble beaucoup à un échange à chaud . Il existe peut-être de la documentation à ce sujet qui peut être utile. Quoi qu'il en soit, question très intéressante. +1…
Konrad Rudolph

Réponses:


5

Ce que vous cherchez essentiellement, c'est un système enfichable à chaud. Vous exécutez une application principale, puis ajoutez des plug-ins au moment de l'exécution qui s'intègrent dans la boucle d'événements. Commencez par réfléchir à ce que votre monde attend d'une entité de jeu. Par exemple (basé sur votre description):

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
}

Bien sûr, vous pouvez ajouter d'autres méthodes que vous jugez nécessaires. Notez le paramètre World avec les deux méthodes pertinentes. Cela permet à votre nouvelle entité de considérer le monde lors de la configuration ou de la mise à jour. Dans votre classe de chien, par exemple, vous pouvez demander au monde pour tous les chats du quartier. Ensuite, vous créez votre monde qui fonctionne avec cette interface et un système de compilation et de chargement dynamiques de code Java. Un exemple de ceci peut être trouvé ici .

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

Appelez cette méthode depuis l'interface graphique mondiale pour ajouter de nouvelles entités. Selon votre implémentation de World, une fonction d'initialisation d'entité peut ressembler à ceci:

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
Mots-clés à google: "Conteneurs IoC", "Inversion de contrôle", "Injection de dépendance". Ce sont des techniques pour les systèmes génériques enfichables à chaud, qui peuvent découvrir et charger des composants lors de l'exécution.
Nevermind

16

Nous avons fait quelque chose comme ça à Stendhal pour les raids.

Nous n'avions pas pour objectif d'éviter complètement les redémarrages. Les modifications apportées à nos services d'infrastructure de base tels que la communication client / serveur nécessitent donc un redémarrage. Mais l'ajout d'entités, de créatures et de PNJ et la modification d'objets existants fonctionnent. (Oh, et parfois la correction de bugs en direct, la réflexion peut être utilisée pour manipuler même des champs privés).

Puisque nous voulons non seulement un nouvel objet basé sur de nouvelles données (comme un autre habillage), mais que nous voulons ajouter un nouveau comportement, le programme mondial doit être capable de charger de nouveaux fichiers de classe . Nous les appelons des "scripts", mais ce sont de véritables classes Java compilées. Ces classes implémentent l' interface Script.java .

Maria.java est un exemple simple. C'est un nouveau PNJ qui vend des boissons et de la nourriture aux joueurs. Nous pouvons également y définir des objets très complexes .

Une nouvelle classe est chargée de cette façon:

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

Si vous pouvez garantir des noms uniques et ne jamais vouloir décharger les classes, vous avez terminé avec les trucs de bas niveau.

Le déchargement , cependant, semble assez important. Pour y parvenir, vous devez instancier un nouveau chargeur de classe chaque fois que vous souhaitez injecter du nouveau code. Pour que le GC puisse faire son travail une fois la dernière référence à ce code effacée.

Nous avons une commande / unload qui appelle une méthode de déchargement dans notre interface afin que les scripts puissent faire le nettoyage. Le vrai déchargement est effectué automatiquement par le GC.

Nous créons souvent beaucoup d' objets temporaires lors des raids. Et nous voulons qu'ils soient tous supprimés après la fin du raid. Par exemple, le Gnomes Raid, qui génère un certain nombre de gnomes près de l'administrateur invisible, nous utilisons ce code: GnomeRaid.java étend CreateRaid.java .

Le script pourrait accéder directement au monde (comme le premier exemple le montre) et faire son propre nettoyage dans la méthode unload (). Mais les codeurs Java ne sont pas utilisés pour nettoyer, et c'est ennuyeux. Nous avons donc créé un sandbox que les scripts peuvent utiliser. Au déchargement, tous les objets ajoutés au monde via la classe Sandbox sont supprimés.


3

Sauvez le monde (sans jeu de mots) et tous ses états (positions, vitesses, toutes les variables).

  • Fermer le programme.
  • Mettre en œuvre des modifications (assurer la compatibilité descendante avec les sauvegardes).
  • Recompiler le programme.
  • Redémarrez le programme.
  • Charger le monde et l'état.
  • Répéter

C'est une solution générale cependant, je n'ai pas de spécificités Java pour savoir si vous pouvez implémenter dynamiquement des blocs de code et des classes comme vous l'espérez ...

L'option 2 consiste à lier un langage de script qui peut être chargé à la volée.


+1 pour le langage de script. Si vous n'êtes pas lié à Java, essayez également certains des pilotes MUD et mudlibs basés sur LPC.
Martin Sojka

1

Pour Java, vous n'avez besoin que d' une plate-forme OSGi . Avec cela, il est facile de remplacer à chaud les modules ou les applications, et même de faire de la gestion à distance ou des mises à niveau partielles.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.