I'm continuing my experimentation with entity component design by making a multiplayer space/trading/combat game with python and panda3d. My component system is a simple version of java's artimis. The entity id is stored in a simple list with it's position as its id. Components are stored in a second list of dictionaries with the position as the entity id. Components are then mapped in a {Class: instance} dictionary.

The client will only know about entities near it's local space. Entities to be networked will have a ClientComponent which tracks what clients that object is broadcasting to. The server and client sends updates with the entity id of the object being acted on, then the action type, followed by the relevant data and yes, the server will check if it is a valid action by the client. The client needs to have a local copy of the relevant entities and components but I am running into issues on how to set this up.

  1. My initial reaction is to just use the entity id's from the server on a client. The client will probably need its own entities for elements like the UI which can interfere with a future entity from a server. I could reserve the first 64-128ish entities for client local use only, but that seems very awkward
  2. Next though was to have the client generate the entity ids locally and have a map of client vs server entity ids. The problem here is that some components will have an id of what entity owns it, which can make the mapping system fall apart and hard to debug.

Here are my two questions. Is there a better way to set up the network protocol where I wont run into this issue. If not, what is a decent solution for the client to know the server's entity ids.


2 Answers 2


This is a rough description of how an MMO that I worked on chose how to generate IDs:

For any object that will exist on both the client and server, allow the server to generate the ID, these are global IDs. Any time that a client needs to know about that object it should be serialized down to them along with any other data about that object.

For objects that will not need to be networked, there are two categories of IDs: Local, and Static. Some objects that are not networked are still known by both the client and server, for example, a tree in the world that was placed in a level editor, these objects can have Static IDs, generated however you wish, maybe based on the order they are placed in the map. And then there are objects that are created on the fly for server-only or client-only use, and are not networked, these can have unique Local IDs.

So you have:

  • Global - Networked objects
  • Static - Non-networked objects that are known by both client and server.
  • Local - Non-networked objects that are known only by client or server, but not both.

It's probably a good idea to generate each type of ID in a different and unique way so that just by looking at an ID you can determine which type it is. For example, if all of your IDs are unsigned 32-bits, that gives you a range from 0 to 4,294,967,295. You could allow a few million or so values for local IDs, as you're not likely to generate a that many objects locally before restarting your client or server. Then split the remaining 4 billion or so between global and static IDs. If you have distinct ranges for your IDs you will know what is global, static, or local just by the value.

  • \$\begingroup\$ I am a little confused for example I have entity35 (on the server), and clientX request information on it. would I generate an entity3654915 to give to that client, and then how would they be mapped to stop duplication? \$\endgroup\$
    – gardian06
    May 24, 2012 at 5:30
  • \$\begingroup\$ How is the client requesting information for an entity it doesn't have an ID for? As a player comes into range of an entity, the server sends a packet to the client for it, let's say that is entity35. In that packet it tells the client that the global ID is 35. Whenever the client wants to send messages to that object on the server, or retrieve information about it, it just references it by using the ID (35). Also, the client makes sure to give the client version of that entity the same ID (35). Whenever the server sends future updates to that entity, the client can update the matching entity. \$\endgroup\$
    – Nic Foster
    May 24, 2012 at 5:40
  • \$\begingroup\$ then wouldn't it be beneficial to have any simply possess all 3 ids even if they may be the null id (0) \$\endgroup\$
    – gardian06
    May 24, 2012 at 6:39
  • \$\begingroup\$ @gardian06: I'm sorry I don't understand the question. \$\endgroup\$
    – Nic Foster
    May 24, 2012 at 18:33
  • \$\begingroup\$ allow all entities (regardless of where it was created) to have a reference number of each type, and then if it doesn't need that value set it to 0. \$\endgroup\$
    – gardian06
    May 24, 2012 at 19:24

I've always just had IDs created by the server, and sent to the client, but that works because the client doesn't have any numbered entities specific to itself in my case.

If I did want to have some client-only entities, I would probably consider storing them in a separate structure to the server-managed ones, so that ID clashes wouldn't matter.

But, if you are trying to have just one set of entities, where each one knows nothing about whether it is controlled by the server or the client, then simply partition the identifier space. You can just reserve a small set of numbers, as you say, but the easiest and probably safest way is to move from using a single number as an ID to having a small class containing 2 values - one is the machine identifier (local or server) and one is the unique identifier generated by that machine. Each machine has a counter and just allocates new entity IDs sequentially from that counter.

You can't safely use a position in a list as an ID anyway. Removing items from that list will change the position, and there will come a time when you want to remove items. You're using Python, so you should use a dict for this, mapping unique values to each item, letting you remove items without affecting the positions of others.

  • 1
    \$\begingroup\$ I was thinking along similar lines. Artimis does it by placing the entity in a third "deleted" list, which createEntity() checks first. I do have some worries about the performance compared to lists, but optimize later right :) \$\endgroup\$
    – croxis
    May 26, 2012 at 1:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .