Should i use multiple GameSystemManager to control order of my systems ? (Zay-ES)

Hi

Supose i have bunch of groups of systems , ex: group1 includes AbstractGameSystems A, B and group2 includes C, D, E, … and so on.
The order of running of these groups are important. for example g2 will run under the control of g1 and so on.

My question is should i use multiple GameSystemManager to seperate these groups ? because each GameSystemManager needs a GameLoop to run it so is it safe to have multiple GameLoops ? or can i control all GameSystemManagers with one GameLoop ?

I don’t know why you need multiple GameSystemManagers. It will run your systems in the order that you added them.

A GameLoop is a separate thread. I imagine you really don’t want multiple ones of those complicating your life for sure.

Even if you were worried about GameSystemManager’s execution order then you’d just make a composite game system that ran things in the order you wanted.

To manage life cycle of systems separately. So i can initialize() , start(), stop(), terminate() systems inside each GameSystemManagers separately .
For example if i add all systems to one GameSystemManagers then by starting GameSystemManager it will call start() on All systems attached to it which i do not want.

Yes, There is this CompositeAppState which extends JME AppState i need to do similar approach for AbstractGameSystem.

For my current issue i fixed it with technique of pending. with no need to create multiple GameSystemManager.

I wanted to redesign GameEntities to get entity types and their components from server dynamically.
Note that GameEntities is used in both server side and client side to create entities.

/**
 * All available game entity types are registered here. Entity types created by
 * server and returned by ES.
 *
 * @author Ali-RS <ali_codmw@yahoo.com>
 */
public class GameEntities extends AbstractGameSystem {

    static Logger log = LoggerFactory.getLogger(GameEntities.class);

    private EntityData ed;
    private EntitySet entities;

    private Map<String, EntityId> entityMap = new ConcurrentHashMap<>();
    private ConcurrentLinkedQueue<PendingEntity> pendingEntities = new ConcurrentLinkedQueue<>();

    @Override
    protected void initialize() {
        log.info("onInitialize()");
        this.ed = getSystem(EntityData.class);
        if (ed == null) {
            throw new RuntimeException("GameEntities requires an EntityData object.");
        }
        entities = ed.getEntities(TypeDefinition.class);
    }

    @Override
    public void start() {
        log.info("start()");

    }

    @Override
    public void update(SimTime time) {
        if (entities.applyChanges()) {
            log.info("applyChanges()");
            
            // Make sure the added entities have been registered with their names.
            addEntityType(entities.getAddedEntities());
            removeEntityType(entities.getRemovedEntities());

            // Fire off any pending entity. 
            firePendingEntities();
        }
    }

    @Override
    public void stop() {
        log.info("stop()");
        entities.release();
    }

    @Override
    protected void terminate() {

    }

    private void addEntityType(Set<Entity> addedEntities) {
        for (Entity e : addedEntities) {
            String name = ed.getStrings().getString(e.get(TypeDefinition.class).getType());
            log.info("Define entity :" + name);
            entityMap.put(name, e.getId());
        }
    }

    private void removeEntityType(Set<Entity> removedEntities) {
        for (Entity e : removedEntities) {
            String name = ed.getStrings().getString(e.get(TypeDefinition.class).getType());
            entityMap.remove(name);
        }
    }

    /**
     * Use this to create game entities with possibility to overwrite default
     * components.
     *
     * @param name
     * @param overwriteComponents
     * @return
     */
    public EntityId createEntity(String name, EntityComponent... overwriteComponents) {
        EntityId newEntity = ed.createEntity();
        
        if (!entities.isEmpty()) {
            addComponents(name, newEntity, overwriteComponents);
        } else {
            // Handling pending entity to be created after reciving entity types. Handling done in a 
            // thread-consistent way   
            pendingEntities.add(new PendingEntity(name, newEntity, overwriteComponents));
            log.info("Pending entity with name: \"" + name + "\" .");
        }
        return newEntity;
    }

    private void addComponents(String name, EntityId newEntity, EntityComponent... overwriteComponents) {
        if (entityMap.containsKey(name)) {
            log.debug("Create entity :" + name);
            Entity e = entities.getEntity(entityMap.get(name));

            TypeDefinition type = e.get(TypeDefinition.class);

            HashSet<EntityComponent> components = new HashSet<>(Arrays.asList(e.getComponents()));
            //Swith TypeDefinition component with it's equivalent ObjectTyppe component
            //so this entity to be known to all game systems.
            components.remove(type);
            components.add(new ObjectTyppe(type.getType()));

            //Find and add default components to entity which are not overwritten with overwriteComponents.
            if (overwriteComponents != null) {
                boolean overwrite;
                for (EntityComponent c : components) {
                    overwrite = false;
                    for (EntityComponent oc : overwriteComponents) {
                        if (c.getClass() == oc.getClass()) {
                            overwrite = true;
                            break;
                        }
                    }
                    if (!overwrite) {
                        ed.setComponents(newEntity, c);
                    }
                }

                //Overwrite components on entity
                ed.setComponents(newEntity, overwriteComponents);
            } else {
                for (EntityComponent c : components) {
                    ed.setComponents(newEntity, c);
                }
            }
        }else{
            log.error("No entity with name = \"" + name + "\" found.");
        }
    }

    private void firePendingEntities() {

        if (!pendingEntities.isEmpty()) {
            PendingEntity entity = null;
            while ((entity = pendingEntities.poll()) != null) {
                addComponents(entity.name, entity.entityId, entity.overwriteComponents);
            }
        }
    }

    private class PendingEntity {

        String name;
        EntityComponent[] overwriteComponents;
        EntityId entityId;

        public PendingEntity(String name, EntityId entityId, EntityComponent... overwriteComponents) {
            this.name = name;
            this.overwriteComponents = overwriteComponents;
            this.entityId = entityId;
        }
    }

}

but the problem was, BasicEnvironment was starting before i receive entity types in GameEntities and i was getting
log.error("No entity with name = \"" + name + "\" found.");
I added a pending mode to GameEntities to pend entity creation until it receives entity types and their components (which are defined by server dynamically).

/**
 *  Creates a bunch of base entities in the environment.
 *
 *  @author    Paul Speed
 */
public class BasicEnvironment extends AbstractGameSystem {

    private EntityData ed;
    private GameEntities gameEntities;
    
    @Override
    protected void initialize() {
        this.ed = getSystem(EntityData.class);
        if( ed == null ) {
            throw new RuntimeException("BasicEnvironment system requires an EntityData object.");
        }
        this.gameEntities = getSystem(GameEntities.class);
        if( gameEntities == null ) {
            throw new RuntimeException("BasicEnvironment system requires GameEntities system.");
        }
    }
    
    @Override
    protected void terminate() {
    }

    @Override
    public void start() {
    
        // Create some built in objects
        double spacing = 256;
        double offset = -2 * spacing + spacing * 0.5; 
        for( int x = 0; x < 4; x++ ) {
            for( int y = 0; y < 4; y++ ) {
                for( int z = 0; z < 4; z++ ) {
                    Vec3d pos = new Vec3d(offset + x * spacing, offset + y * spacing, offset + z * spacing);
                    //GameEntities.createGravSphere(pos, 10, ed);
                    EntityId entityId = gameEntities.createEntity(ObjectTypes.GRAV_SPHERE);
                    ed.setComponent(entityId, new Position(pos, new Quatd().fromAngles(-Math.PI * 0.5, 0, 0)));
                }
            }
        }
    }
    
    @Override
    public void stop() {
        // For now at least, we won't be reciprocal, ie: we won't remove
        // all of the stuff we added.
    }
    
}

Pending seems to be not very elegant way to handle this and for this reason i wanted to handle life cycle of GameEntities in an other GameSystemManager before starting other systems.

Or i also can edit systems like BasicEnvironment which are creating entity at their start() to not start by GameSystemManager instead use EventBus to announce them when they should start.
Actually there are lots of ways to do this i need to think clear to chose the elegant way :slight_smile: and will appreciate you help me to chose the correct way. :slight_smile:

People seem to want to spend a lot of time creating systems to create the entities used to create the systems to create the entities to create the systems to create the entities. It’s so often a huge waste of time and frustration to avoid some very straight forward code.

So I don’t know what the requirements are for why you need to be able to define new entities at runtime… because that’s what you’ve setup. You have kind of an odd mix of “I want to create new entity types at runtime but I also want to create a bunch of entities when I start up.”

In your case, your GameEntities class should be setting up the bootstrap entity types (the ones already stored before the game starts) in initialize() or start(). Then they would be available to the systems that start after.

edit: like in your case you could get away with calling update(null) from your start() method. Personally, I’d probably split the contents of update into a protected method that both start() and initialize() call… and pass it a Set because right now your are actually not even detecting entities that already existed before you created your entity set.

You’ll note that my EntityContainer class does this sort of load right during start().

Yes I am setting up the bootstrap entity types in TNWEntitiesSetup (this is a game specific class is out of core package and is inside app package) and should run on server side. Then GameEntities system in both server side and client side will detect entity types based on TypeDefinition component.

/**
 * Utility methods for creating the common game entities used by 
 * my game (The Night Watchman).  In cases where a game entity may have multiple
 * specific componnets or dependencies used to create it, it can be
 * more convenient to have a centralized factory method.  Especially
 * if those objects are widely used.  For entities with only a few
 * components or that are created by one system and only consumed by
 * one other, then this is not necessarily true.
 * All entities definitions will be synced with GameEntities at both
 * client side and server side.
 *
 * @author Ali-RS <ali_codmw@yahoo.com>
 */
public class TNWEntitiesSetup extends AbstractGameSystem {
    //TODO : Defines object types based on game level.

    static Logger log = LoggerFactory.getLogger(TNWEntitiesSetup.class);
    
    private EntityData ed;

    @Override
    protected void initialize() {
        log.info("onInitialize()");
        this.ed = getSystem(EntityData.class);
        if (ed == null) {
            throw new RuntimeException("TNWEntitiesSetup requires an EntityData object.");
        }
        
    }

    @Override
    protected void terminate() {

    }

    @Override
    public void update(SimTime time) {
        super.update(time);
        
    }

    @Override
    public void start() {
        super.start(); 
        defineGameEntities(ed);
    }

    
    private void defineGameEntities(EntityData ed) {
        
        //Scene Templates
        EntityId MagoTribeMetaData = ed.createEntity();
        ed.setComponents(MagoTribeMetaData, TypeDefinition.create(ObjectTypes.MagoTribeMetaData, ed));
    
        
        //Terrains
        EntityId MagoTribe = ed.createEntity();
        Terrain MagoTribe_terrain = Terrain.create(
                "MagoTribe_HM",
                "MagoTribe_DM",
                "MagoTribe_AM", ed);
        PhysicsDriverInfo MagoTribe_driver = new PhysicsDriverInfo(
                PhysicsDriverFactory.CONTROL_RIGID_BODY,
                PhysicsDriverFactory.COLLISION_SHAPE_HEIGHTFIELD);
        ed.setComponents(MagoTribe, TypeDefinition.create(ObjectTypes.MAGO_TRIBE, ed), MagoTribe_terrain, MagoTribe_driver);
        log.info("MagoTribe : "+ MagoTribe.toString());
        
        //Characters
        EntityId Ship = ed.createEntity();
        ed.setComponents(Ship, TypeDefinition.create(ObjectTypes.SHIP, ed), new MassProperties(1/50.0), new SphereShape(3, new Vec3d()));
                
        EntityId GravSphere = ed.createEntity();
        ed.setComponents(GravSphere, 
                TypeDefinition.create(ObjectTypes.GRAV_SPHERE, ed), 
                new SphereShape(10, new Vec3d()));
//        
//        EntityId Sadam = ed.createEntity();
//        TypeDefinition Type_Sadam = TypeDefinition.create(ObjectTypes.SADAM, ed);
//        ed.setComponents(Sadam, Type_Sadam);
//
//        EntityId SadamServent = ed.createEntity();
//        TypeDefinition Type_SadamServent = TypeDefinition.create(ObjectTypes.SADAM_SERVENT, ed);
//
//        EntityId King = ed.createEntity();
//        TypeDefinition Type_King = TypeDefinition.create(ObjectTypes.KING, ed);
//
//        EntityId JoSangHeon = ed.createEntity();
//        TypeDefinition Type_JoSangHeon = TypeDefinition.create(ObjectTypes.JO_SANG_HEON, ed);
//
//        EntityId Wolgan = ed.createEntity();
//        TypeDefinition Type_Wolgan = TypeDefinition.create(ObjectTypes.WOLGAN, ed);
//
//        EntityId Doha = ed.createEntity();
//        TypeDefinition Type_Doha = TypeDefinition.create(ObjectTypes.DOHA, ed);
//
//        EntityId Guard = ed.createEntity();
//        TypeDefinition Type_Guard = TypeDefinition.create(ObjectTypes.GUARD, ed);
//
//        EntityId StoneMonster = ed.createEntity();
//        TypeDefinition Type_StoneMonster = TypeDefinition.create(ObjectTypes.STONE_MONSTER, ed);
//
//        EntityId KangMooSeok = ed.createEntity();
//        TypeDefinition Type_KangMooSeok = TypeDefinition.create(ObjectTypes.KANG_MOO_SEOK, ed);
//
//        EntityId Doha_Child = ed.createEntity();
//        TypeDefinition Type_Child = TypeDefinition.create(ObjectTypes.DIHA_CHILD, ed);
//
//        EntityId Shaman = ed.createEntity();
//        TypeDefinition Type_Shaman = TypeDefinition.create(ObjectTypes.SHAMAN, ed);
//
//        EntityId Jaime = ed.createEntity();
//        TypeDefinition Type_Jaime = TypeDefinition.create(ObjectTypes.JAIME, ed);
//        
//
//        //Animals
//        EntityId Horse_Brown = ed.createEntity();
//        TypeDefinition Type_Horse_Brown = TypeDefinition.create(ObjectTypes.HORSE_BROWN, ed);

    }

}

But now my problem is how should i retrieve all components from entity in GameEntities ?
Calling entity.getComponents() only return TypeDefinition component because GameEntities only listens to entities with TypeDefinition

@Override
    protected void initialize() {
        log.info("onInitialize()");
        this.ed = getSystem(EntityData.class);
        if (ed == null) {
            throw new RuntimeException("GameEntities requires an EntityData object.");
        }
        entities = ed.getEntities(TypeDefinition.class);
    }

I also tried to use
ed.getEntity(entity.getId()).getComponents();
to retrieve all components on this entity but no success.

Can i retrieve all components some how without knowing their class types ?

No. a) it’s unknowable. b) it’s 99% of the time a sign of a design problem.

Question: why do you need to be able to define types at runtime?

Note: your GameEntities class is broken in a way because it won’t add entities that already existed before the entity set was created. Those will exist in the set already and applyChanges() will return false.

1 Like

Anyway, presuming you really do need all of the extra complexity to define new types after the game has been running for hours and want to keep your current approach…

To fix all of your problems, rip the pending stuff out of GameEntities and call:
entities.applyChanges();
addEntityType(entities)

…inside your start() method. That will make sure that the set is as up-to-date as it can be and also add any existing entities that existed before the set was created.

Then system ordering takes care of everything else. You could also gut this class by using EntityContainer to do the management for you and then GameEntities is mostly just a thin wrapper.

If you decide that being able to create new types while the game has been running is not a requirement then you can simplify a lot of this further. (Removing 90% of what you’ve shown me.)

1 Like

Ah, Okay. Really thanks for making this clear. I will rethink on design.[quote=“pspeed, post:7, topic:37493”]
Question: why do you need to be able to define types at runtime?
[/quote]
Actually it is not creating at runtime. I just wanted to client get types from server when connecting after that in the game no one can create an entity with new type.

Ah yes i had forgot about this. So my current design is a total fail.

Okay i will not have runtime type creation. But is it Okay if i want to get types from server once it connects to server?

Curious: why does the client need to know the types?

And even so, that’s a way easier problem to solve.

1 Like

Just an odd smell I noticed rereading your code… without knowing what you are doing with it, it seems odd to create a terrain entity. Even in Mythruna I don’t do this and that’s about as dynamic of terrain as you can get.

Ooooooh **** I am a total dumb :chimpanzee_facepalm: Really sorry…
You are right. The client really do not need to know. Client will only create an empty entity with an ObjectType then server will do the rest of the work by applying needed components. :sweat_smile:

RemoteEntityData is read only. So clients won’t be creating entities either. They’ll be calling the server to do that using some more specific game-type thing or whatever.

1 Like

One thing that I think gets us in trouble if we let our OOP prejudices slip in is this idea that a set of constants is inherently bad.

In OOP land, if I had some ObjectType enum or set of constants, I’d be spreading code all over my codebase and it feels wrong.

In an ES, this doesn’t really happen. There is probably only one place in the entire code base that creates objects based on the ObjectType and everyone else just uses it as a handle.

But anyway, I’ve found that even the whole idea of that is only necessary in the most dynamic systems. If you know all of your game object types at compile time then don’t even bother with an ObjectType style component. Just have methods to create the things you want:
createShip()
createMissile()
…that takes whatever parameters and/or components you need.

The whole idea of “ObjectType” doesn’t even make sense in to the ES and is an entirely arbitrary concept that only a human might care about, really. What thing other than the human coder cares that this entity is a ship or a missile? Nothing.

Just something to think about.

1 Like

Hmmm … I was thinking clients can create and add/update component on entities . Just took a look at source code and yes it will throw an exception to my face if i do it on client. It is cool and adds a great security level to client.

Just one thought, what do you think about making it flexible by supporting permission level on entity ? So server can put read/write permission on a specific component for a specific entity, then client can only change that specific component on that specific entity. I am not sure if this is a good requirement or no and if it will break ES design or will cause performance issues or …
Paul what is your idea about this ?
Edit : If you like i can create other thread for this question on forum.

Physic system needs it. You remember my other post to use bullet on server. It is already working :grinning:. The ControlDriver is the bridge between bullet and ES. I have not done with all physic controls types yet just created bare bone for them. I will create a post for this on WIP screen-shots thread soon when finished.

See :
Note that AssetManager is just a stand alone instance to load texture (Loading HeightMap is the only place i need to use assetManager) or collision shapes on server.

/**
 *
 * @author Ali-RS <ali_codmw@yahoo.com>
 */
public class PhysicsDriverFactory extends AbstractGameSystem {

    //Physic Controls
    public static final int CONTROL_RIGID_BODY = 1;
    public static final int CONTROL_CHARACTER = 2;
    public static final int CONTROL_BETTER_CHARACTER = 3;
    public static final int CONTROL_BOMB = 4;
    public static final int CONTROL_GHOST = 5;
    public static final int CONTROL_KINEMATIC_RAGDOLL = 6;
    public static final int CONTROL_PHYSICS_HOVER = 7;
    public static final int CONTROL_VEHICLE = 8;

    //Physic Shapes
    public static final int COLLISION_SHAPE_BOX = 9;
    public static final int COLLISION_SHAPE_CAPSULE = 10;
    public static final int COLLISION_SHAPE_COMPOUND = 11;
    public static final int COLLISION_SHAPE_CONE = 12;
    public static final int COLLISION_SHAPE_CYLINDER = 13;
    public static final int COLLISION_SHAPE_GIMPACT = 14;
    public static final int COLLISION_SHAPE_HEIGHTFIELD = 15;
    public static final int COLLISION_SHAPE_HULL = 16;
    public static final int COLLISION_SHAPE_MESH = 17;
    public static final int COLLISION_SHAPE_PLANE = 18;
    public static final int COLLISION_SHAPE_SIMPLEX = 19;
    public static final int COLLISION_SHAPE_SPHERE = 20;

    static Logger log = LoggerFactory.getLogger(PhysicsDriverFactory.class);

    private EntityData ed;
    private EntitySet entities;
    private AssetManager assetManager;

    @Override
    protected void initialize() {
        log.info("onInitialize()");
        this.ed = getSystem(EntityData.class);
        if (ed == null) {
            throw new RuntimeException("PhysicsDriverFactory requires an EntityData object.");
        }

        //TODO : get assetManager
    }

    @Override
    public void start() {
        log.info("start()");
        entities = ed.getEntities(ObjectTyppe.class, PhysicsDriverInfo.class);
    }

    @Override
    public void update(SimTime time) {
        if (entities.applyChanges()) {
            createPhysicsControlDriver(entities.getAddedEntities());
            updatePhysicsControlDriver(entities.getAddedEntities());
            removePhysicsControlDriver(entities.getRemovedEntities());
        }
    }

    @Override
    public void stop() {
        log.info("stop()");
        entities.release();
    }

    @Override
    protected void terminate() {
        assetManager = null;
    }

    private void createPhysicsControlDriver(Set<Entity> addedEntities) {
        for (Entity e : addedEntities) {
            PhysicsDriverInfo driverInfo = e.get(PhysicsDriverInfo.class);
            PhysicsControlDriver driver = null;
            switch (driverInfo.getPhysicsShape()) {
                
                case COLLISION_SHAPE_HEIGHTFIELD:
                    TerrainQuad terrainQuad = TerrainCreator.createNonMatTerrainQuad(assetManager, ed, e);
                    
                    switch (driverInfo.getPhysicsControl()) {
                        case CONTROL_RIGID_BODY:
                            driver = new RigidBodyDriver(new RigidBodyControl(new HeightfieldCollisionShape(terrainQuad.getHeightMap(), terrainQuad.getLocalScale()), 0));
                            break;                       
                    }                    
                    break;
                case COLLISION_SHAPE_BOX:
                    break;
                case COLLISION_SHAPE_CAPSULE:
                    break;
                case COLLISION_SHAPE_COMPOUND:
                    break;
                case COLLISION_SHAPE_CONE:
                    break;
                case COLLISION_SHAPE_CYLINDER:
                    break;
                case COLLISION_SHAPE_GIMPACT:
                    break;
                case COLLISION_SHAPE_HULL:
                    break;
                case COLLISION_SHAPE_MESH:
                    break;
                case COLLISION_SHAPE_PLANE:
                    break;
                case COLLISION_SHAPE_SPHERE:
                    break;
                
            }

            if (driver != null) {
                getSystem(PhysicsSystem.class).setControlDriver(e.getId(), driver);
            } else {
                throw new UnsupportedOperationException("Unsuported Physics Driver !");
            }

        }

    }

    private void updatePhysicsControlDriver(Set<Entity> updatedEntities) {
        createPhysicsControlDriver(updatedEntities);
    }

    private void removePhysicsControlDriver(Set<Entity> removedEntities) {
        for (Entity e : removedEntities) {
            getSystem(PhysicsSystem.class).setControlDriver(e.getId(), null);
        }
    }

}

And extension of ControlDriver for bullet physic :

public interface PhysicsControlDriver extends ControlDriver {
    /**
     * Add your physic objects to physicSpace provided by this method.
     * @param physicsSpace 
     */
    public void addToPhysicSpace(PhysicsSpace physicsSpace);
    
    /**
     * Remove physic objects from physicSpace.
     * @param physicsSpace 
     */
    public void removeFromPhysicSpace(PhysicsSpace physicsSpace);
    
    /**
     * Update physics state with your physic components.
     * Note, this method is supposed to work with EntitySystem 
     * structure using Zay-ES.
     * @param entity 
     */
    public void updatePhysicalProperties (Entity entity);
}

You can ask on another thread if you want but you will open up lots of problems if you let the player shove any random component value into an entity. Just let the server do it. You would add so much validation and security code (to the ES itself) just to avoid creating another method. It’s a waste.

There is really no way to provide accurate security except to hack it right into the ES networking code. And you’d have to add the ability to do all kinds of filtering then handle the errors gracefully on the client code and so on.

…versus just adding another RMI method to your client-server code that is a game-level concept of an action versus a ‘shove this raw value into this abstract object’ kind of action.

1 Like

Well, physics the way you’ve written yours seems to need it. You could have also expanded your physics system to pull static terrain data from some ‘world’ object and bypassed the ES for that.

Whether that world object exists on both client and server or has a remoting component depends on how you’d want your game to work. (Data is static in the client or streams over the network.)

1 Like

Maybe take a look at what i’ve done for alchemist : the pipelines. I’m not very proud of that code ^^ but it might be inspiring. Note that it works only if your components are immutales : this way, data change is managed only by EntityData which is thread-safe.

1 Like

Thanks.