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
and will appreciate you help me to chose the correct way. 