1
\$\begingroup\$

My game server uses entities containing components to represent everything in the game world. This has worked great so far but I've run into a problem now that I'm allowing clients to connect to the server.

Basically the server needs to tell the connecting client about all the existing entities in the game, Since the player, the monsters, the trees etc. are all instantiated from the same Entity class I currently have no way of telling the client what each entity actually is.

What would be the best way of sending the entities to the client so that the client knows what components to add to each? The only thing I can think of is having a string "name" on the entity and then sending packets like this to the client...

{

     'action': 'create-entity',
     'data': {
         'entity-type': 'dragon',
         'x': 300,
         'y': 40,
     }
}

So now the client would know to call the "createDragonEntity" function to create the correct entity.

Any better suggestions?

\$\endgroup\$
  • 1
    \$\begingroup\$ What problems do you forsee that prompted you to ask this question? This seems like the best approach if there are a limited number of different possible entities (which is pretty much any game). On the other hand you might want to send a list of components if there is an unlimited number of user-created entities (that would be a weird use case, but just putting that out there). \$\endgroup\$ – jhocking Mar 12 '14 at 17:07
  • \$\begingroup\$ You're unable to retrieve the components of a specific entity? Why not just send the entity with all of its data? \$\endgroup\$ – MichaelHouse Mar 12 '14 at 17:08
  • \$\begingroup\$ I suppose I could send details of the components too, it seems wrong to be sending that kind of data over the wire though. I don't really see any issues cropping up from sending a "name" but it does seem like a bit of a hack :S \$\endgroup\$ – michael Mar 12 '14 at 17:12
  • 1
    \$\begingroup\$ As long as the data in the components doesn't change, then there's no problem sending just the name. What happens when you have a damaged entity or it's currently attacking some other entity? Just sending the name won't convey that state. \$\endgroup\$ – MichaelHouse Mar 12 '14 at 17:14
  • \$\begingroup\$ I hadn't considered that! \$\endgroup\$ – michael Mar 12 '14 at 17:14
1
\$\begingroup\$

The problem you encountered is that you seem to have no clear way of serialising/unserialising your entities.

You can keep your create<EntityType>() functions to create initial versions of your entities, but you should be able to serialise the entities. And unserialising them should result in the same result as what was there before the serialisation process.

When you write an entity (to file or network stream) the resulting data should allow you to reverse the process and unserialise just as easily:

Serialized entity:

{
    'id':38,
    'transform':{'x': 300, 'y': 40, 'angle':0, scale:1},
    'movement':{'id':'walk', 'maxspeed': 30, 'turnrate': 180},
    'movement':{'id':'flight', 'maxspeed': 180, 'turnrate': 20},
    'weapon':{'id':'claws', 'dmg':[{'slash':300}], 'rate':0.25},
    'weapon':{'id':'firebreath', 'dmg':[{'fire':30}], 'rate':5, 'remaining':58, 'rr':2},
    'visual':{'model':'dragon_02', 'mainColor':'FF4921'},
    'dragonScript'{'anger':12.333333,},
    'health':{'hp':2004, 'hr':3},
    'intent':{script:[
        {'id':'move', 'movement':'walk', 'data':[1,0]},
        {'id':'fire', 'weapon':'firebreath'}]}
}

Here we can clearly rebuild the entity with an id, and all the components needed for the game to resume/start.

There are certain components you will want to avoid serialising (i.e. all components related to local behaviours like input or data which should be recomputed from the existing data).

But in the end a simple function to reload all components should do the trick:

Ex:

unserializeEntity(pojo) {
    var entity = null;
    var cmps = [];
    //Each key could be used to code the type of component...
    for(var componentType in pojo) {
        var cmp = createComponent(componentType, pojo[componentType]);
        if(cmp) {
            cmps.push(cmp);
        }
    }
    if (0 < cmps.length) {
        entity = new Entity(pogo.id);
        entity.addComponents(cmps);
    }
    return entity;
 }



//Once all entities are unserialised simply add them to your engine
for(...) { engine.add(entities[i]); }

// once all entities have been unserialised and added you can wake them up 
//so they can recompute all the missing data and fetch dependencies
for(...) { entities[i].awake(); }

If this approach doesn't allow you to retrieve all the behaviours and data needed for your entities then you might have to rethink your design if you want to be able to pass arbitrary entities across the network.


Edit: To avoid sending too much data try to compress it. For example using http compression if you send the data to a browser.

Otherwise you can use any other compression library available to you. lz4 is a good one, it has a browser implementation as well as a native one for node.

Using the above dragon entity I get the following result after using lz4:

data.length:1200 compressed.length:444

Quite good and it also saves space when writing to files or database.

\$\endgroup\$
  • \$\begingroup\$ @michael try compressing the data you send. have a look at my edit. \$\endgroup\$ – Coyote Mar 13 '14 at 12:30

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.