5

I am working on a game framework that would allow users to use multiple mods/plugins to add additional features to the game.

For the remainder of the post, I'll use the following definitions:

mod: code that requires changes to both the client and the server. For example, adding a new packet to the protocol.

plugin: code that changes existing functionality without requiring a change on the other side. For example, adding a remote management interface to the server, or allows the client more fine-grained controls over sound and audio quality.

This question relates to both of these aspects, however I'll be focusing on mods as they seem to be giving me the biggest trouble.

Let's say I provide a default player class called Player that models a Player's character. Let's say, for this particular server, the server operator wants to include 2 mods, Foo and Bar, both of which are written by different people.

Both Foo and Bar need to add additional functionality to the Player class. Foo needs a method doFoo, and Bar needs a method doBar. Both actual implementations are included somehow by the mod developers (probably in a different class).

One solution I've considered is using Reflection, and modifying the class at runtime, extending specific mod interfaces. Something like

public interface FooPlayer {
    void doFoo();
}

public interface BarPlayer {
    void doBar();
}

public class FooPlayerImplementation implements FooPlayer {
    public void doFoo() {
        // ...
    }
}

public class BarPlayerImplementation implements BarPlayer {
    public void doBar() {
        // ...
    }
}

public class FooLoader {

    public void onLoad() {
        SomeManager.findPlayerClass()
                   .implementInterface(FooPlayer.class, FooPlayerImplementation.class);
    }
}

public class BarLoader {
    public void onLoad() {
        SomeManager.findPlayerClass()
                   .implementInterface(BarPlayer.class, BarPlayerImplementation.class);
    }
}

Where at runtime, it would inject the Player class with the implementations from Foo and Bar, and make it implement FooPlayer and BarPlayer. But then we have issues where two different mods add the same method signature in their own interface (which could be resolved by prepending the class name to the method, but then we're going down a very dark rabbit hole).

Another thing that I had thought about briefly was just going event-driven to an insane degree, so that developers didn't need to do something like this, but that opens an equally hair can of worms, as well as not providing the amount of control a developer might need.

But I'd REALLY like to avoid reflection if at all possible.

I've seen AspectJ and aspect-oriented programming, and it looks like it may work, but I don't have any experience to tell.

The questions I have are:

  1. Is there a way to get this sort of functionality in an OOP way?
  2. Is there a way to do this with the standard Java SE library?
  3. Could AOP accomplish this?

Considerations: If any of these are wrong or misguided, please let me know.

  1. I don't think that inheritance will work because there could be multiple mods that want to extend the functionality of Player, and all Players would be subject to all of the mods. There couldn't be a FooPlayer and a FooBarPlayer at the same time. If n mods extend the Player functionality, than the Player must have n components.
  2. The decorator pattern won't really work I think because manual intervention would be required by the operator (who may or may not know anything about programming) to weave the implementations together.

Something like

public class FooBarStaminaMagickaPlayer implements Player, Foo, Bar, Stamina, Magicka
  1. If possible, I would like to avoid having each mod developer have a mapping within their code to keep track of things.

For example, something like

public class StaminaMapper {
    private final Map<Player, Stamina> playerStaminaMap;
}
  • Shouldn't all players in a game have the same set of mods enabled? – COME FROM May 9 '16 at 10:26
  • @COMEFROM I've updated the OP a bit – Zymus May 9 '16 at 19:20
  • I think having mods as subclasses of the Player class conflicts with your goal. Perhaps you could provide a PlayerExtension class (or interface) for mods. – COME FROM May 10 '16 at 6:29
  • @COMEFROM none of the code in the OP suggests subclassing the player. That was the reason i updated the op in the first place. How would a PlayerExtension class help? – Zymus May 10 '16 at 6:40
  • Sorry, I didn't read the question through after the edit. I guess my suggestion falls into the category "not giving developers the amount of control they might need". – COME FROM May 10 '16 at 6:57
1

There is one other solution. It may slow down code (not as bad as reflection though) .

Everything has an interface. The classes will only do function composition: pipe returns to parameters to returns, etc. All prameters and instance variables are interfaces.

A mod or plugin is just a package of implementions for interfaces. Make one mod called defaults. Put this in its own package. That way, the rest is API code.

Mod loading has to be done. Now a container can inject classes in, and look for mod folders.

There can be a list of all mods in a file, in priority order. With a conflict, the first mod listed will be the one used in the container.

I recomand a dependency injection framework for this. I would also recomand using gaurd clauses. with messages in the exeptions for parameter valid ranges. Just because glitches, and exploits would be made.

  • One problem with this is that, for conflicts, I can't just use a pick the first strategy. The operators intend on having all functionality within the mods usable. It needs to be a merge. – Zymus May 10 '16 at 20:15
  • Blending can be a class that implements a interface. It will also implement the interface. Then average the results from the list for each method and return that. The class can be called mod merging. – Akash May 11 '16 at 10:40
1

You are most likely feeling like all of your options will take you down a "very dark rabbit hole" because you are already in a rabbit hole, and the proper solution is to step back out of the hole and consider a new direction.

Here is a link on how many games are developed which might address your concerns indirectly but more fundamentally.

https://en.wikipedia.org/wiki/Entity_component_system

This quote I think addresses your exact roadblock:

An ECS follows the Composition over inheritance principle that allows greater flexibility in defining entities where every object in a game's scene is an entity (e.g. enemies, bullets, vehicles, etc.). Every Entity consists of one or more components which add additional behavior or functionality. Therefore the behavior of an entity can be changed at runtime by adding or removing components. This eliminates the ambiguity problems of deep and wide inheritance hierarchies that are difficult to understand, maintain and extend.

and

http://www.gamedev.net/page/resources/_/technical/game-programming/understanding-component-entity-systems-r3013


Ok this is my attempt to give you an example of how to solve the problem using ECS. This can be greatly improved upon. The purpose is to show off the design, not the details of how to make it work optimally.

abstract class Component
{
    public string ComponentType { get; } // implements some identifier which modders can generate/register for new components
}

class Position : Component { }
class Velocity : Component { }
class Health : Component { }

abstract class Entity
{
    public List<Component> Components;
    public abstract T GetComponentOfType<T>();
    public abstract Component GetComponentById(string Id);
}

class MovementSystem
{
    public static void Execute(Position p, Velocity v)
    {
        // do stuff with p and v
    }
}


// When it comes time to add new components, all you do is add the modder's new component, system, registration, and any other mechanism for incrementing the game loop.

class Stamina : Component { }

class StaminaSystem
{
    void Execute(Position p, Velocity v, Stamina s)
    {
        // do stuff with p and v and s
    }
}

class StaminaSystemLoop
{
    bool IsEntityEquipedWithCorrectComponentsForThisSystem(Entity e);

    void Increment(Entity e)
    {
         StaminaSystem.Execute(e.GetComponentOfType<Position>(), e.GetComponentOfType<Velocity>(), e.GetComponentOfType<Stamina>());
    }
}



class GameLoop
{
    void Increment(List<Entity> gameEntities)
    {
        foreach(var e in gameEntities)
        {
             // Detect if entity is a match for any given system.
             // Execute the system with the components using GetComponentOfType.
             //
             // The installation process for the mod will need to add the detection logic and the execution logic.
             // So this is where you have to get creative about it. E.g.:
             if(StaminaSystemLoop.IsEntityEquipedWithCorrectComponentsForThisSystem(e))
                  StaminaSystemLoop.Increment(e);
        }
    }
}
  • If Player had a component Health, how then would I deal with a mod that wants to add a Stamina component, and another mod that wants to add a Magicka component in the calling code? I'd like to avoid something like (Stamina) player.getComponent("stamina"); – Zymus May 10 '16 at 14:46
  • That's a good question and one I do not have an immediate answer for. It seems to me if you want that kind of freedom for your modders, you would have to allow modders not only the capability of mixing/matching predefined components to create entirely new entities, but the capability to create entirely new components and therefore new systems to handle those components. – Price Jones May 10 '16 at 15:50
  • At which point I would suggest inheritance as a possibility. It seems to me it would be fairly easy in ECS to extend player to contain a new Stamina component, and extend the Attack system to reduce stamina and extend the move system to reduce stamina recovery at one rate while extending the idle system to set stamina recovery at a new rate. – Price Jones May 10 '16 at 15:57
  • But then we get to the same problem that I mentioned here, where we have two different mods that want to extend the Player, a StaminaPlayer that has a Stamina component, and a MagickaPlayer that has a Magicka component. Because all players have to have all of the same components, I can't have them separate. Stamina and Magicka are just examples I felt would illustrate the idea succinctly; in reality, players in the game would have neither. – Zymus May 10 '16 at 16:02
  • Maybe I don't understand clearly the requirements. If modder A creates mod A which includes the Stamina component and the extensions to the systems which handle Stamina logic, and modder B creates mod B which includes the Magicka component and the extensions to the systems which handle Magicka logic, do I understand correctly that that server operator will install both and that users will have to choose which or none they wish to use? If so, the ECS design handles this, since the player objects which are not StaminaPlayers could be ignored when run through the extended systems. – Price Jones May 10 '16 at 16:29
0

I will only respond to your first question since I don't have enough experience to pronounce myself on AOP.

My favourite way to extend a complete (understand non-abstract) existing piece of code is to use the decorator pattern.

Let's say this is what you defined as a minimal working player for your framework. The most important point is that every public method in PlayerImpl must be inherited from Player (see below why):

public interface Player {
    void doAction();
}

public final class PlayerImpl implements Player {
    @Override
    public void doAction() {
        //do action
    }
}

It is then possible for mods developers to safely (without altering the existing) add behaviour to a player using the decorator pattern:

public interface Foo {
    void foo();
}

public final class FooPlayer implements Player, Foo {
    private final Player origin;

    public FooPlayer(Player origin) {
        this.origin = origin;
    }

    @Override
    public void doAction() {
        origin.doAction(); //or define totally new behaviour when needed (typically for a plugin)
    }

    @Override
    public void foo() {
        // put special mod behaviour
    }
}

And then in usage, instances of FooPlayer are always usable by your framework (because they inherit from Player) and the mod developer can also use the specific behaviour in its code when needed:

public static void main(String[] args) {
    FooPlayer p1 = new FooPlayer(MyGame.provideNewPlayer());
    MyGame.registerNewPlayer(p1); //still compatible with what you provide altough holding a reference of FooPlayer
    p1.doAction();
    p1.foo();
}
  • This results in the same issue we were in before I edited the question I think. Where there's a mod Foo and a mod Bar and both of them make different changes to the Player class. – Zymus May 10 '16 at 13:29
  • @Zymus The PlayerImpl class is immutable and isn't modified neither by Foo nor by Bar. Both can make different changes in two different classes (no conflict). – Spotted May 10 '16 at 13:35
  • Bit would the game engine not have 3x the Players registered then? Because there'd be a new object for each mod, I believe i would also have to have some sort of synchronizer when saving them for example. – Zymus May 10 '16 at 13:42
  • If a mod adds new behaviour, there's no way (except maybe reflection) to "teach" it to your existing framework. It means that the mod has to manage every aspect of the behaviour it adds. Example: you can design your framework to be able to save instances of Player, but the specifics of a FooPlayer's instance must be saved by the mod because your framework can't guess what the mod added (except, as said, by reflection). – Spotted May 11 '16 at 5:44
0

I don't think any form of inheritance is going to work reliably with this. As you've identified, you're going to have conflicts, and you'll be creating a brittle/fragile set of classes. If you change your base class (for whatever reason) then all your clients will have to change too.

AOP may work for you, but I think it'll be non-intuitive, and I don't think it'll be particularly resilient in the face of code changes.

My preferred solution in this sort of scenario is to follow your observer idea. Clients A and B can both register listeners and then run code upon changes registered by your server class. You'll probably find that you have to issue events for most (all?) state changes, and perhaps make other entities in your system available (via a supported API) for your clients. As it stands, it sounds like your requirements are pretty vague, and I would do something simple for the moment and see what transpires.

-1

For blending, make a class called FooBlender. Where Foo is the interface. It will have a list off Foo. For each method call, get the list of what the other classes return. Then return the average or blend of that list.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.