0

I'm working on a text-based game as a practice project.

I already managed to write an engine that displays information and handles user input. It draws the games content from custom Scene classes. More precisely, I have a Scene superclass and create child-classes, like EvilDungeon extends Scene, for the actual playable levels.

All scenes are located in a "Scenes"-folder, but I want to be able to make game expansions later on by dropping additional .class files of new scenes into said folder.

My plan was to use loadClass when the game is started to add them to a Scene-array, but it requires the class names which I can't know, since there can a any random combination of scenes in the Scenes-folder.

How can I load all scenes in the folder without knowing which scenes exactly are present? Can I retrieve the class name somehow?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • Can't your game depend on the source of those Scene subclasses? Instead of using reflections to load .class at runtime you could recompile the project with new Scene subclasses. – Deloo Sep 30 '22 at 10:42

1 Answers1

1

My plan was to use loadClass when the game is started to add them to a Scene-array, but it requires the class names which I can't know, since there can a any random combination of scenes in the Scenes-folder.

That's what Service Provider Interfaces are for. Just define an interface for your plugins and use ServiceLoader to get an instance of each plug-in.

The plug-in needs to provide a text-file in /META-INF/services named after the interface and containing the class which implements that interface (both names must be fully qualified). You'll find the details in the documentation of ServiceLoader.

This gives you the ability to add plugins to the class-path. E.g. you can run your application using something like java -cp myapp.jar;plugins/* my.app.Main, then all the plugin-Jars in folder plugin will be added to the class-path and are available to your application (for more information on the asterisk, see this answer).

If you want to load plug-ins at runtime, you can use a URLClassLoader, e. g. read this question or this tutorial on how to use the URLClassLoader.

Mihe
  • 2,270
  • 2
  • 4
  • 14
  • SPI is intended for such extendibility. You can list the available scenes. Make an interface (needed for SPI), Scene, and rename your old scene class something like AbstractScene or BaseScene or DefaultScene. – Joop Eggen Sep 30 '22 at 11:16
  • Thanks for your answer! It was very helpful! But there's still a part I don't understand: How do I create the instance of my scenes? If I turn my `Scene` superclass into an interface or abstract class I can't create a `new` object from them, right? Now I'd do something like this: (with `defaultScene` being the interface) `list allScenes = new arraylist; ServiceLoader loader = ServiceLoader.load(defaultScene.class); for (defaultScene myScene : loader) { allscenes.add(myScene); }` Would this still run the constructor of the individual scenes? – SquishySquid Oct 06 '22 at 15:06
  • The ServiceLoader creates instances of the implementations, therefore each implementation of the Service needs to provide a public no-args constructor. Of course, you can define a Service which is able to create instances of the Scene in question. – Mihe Oct 06 '22 at 16:29
  • Oh, and of course the no-args constructor will be used. – Mihe Oct 06 '22 at 16:36