Jump to content
  • Advertisement
Sign in to follow this  
Angus Hollands

Closed-Entity Systems and state replication

This topic is 2130 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey everyone!
I'm rewriting my multiplayer system for a first person shooter. Reasons for doing so include moving closer to OO design patters (or at least trying not to mix with other patterns) and also to make it cleaner to use. First of all, I'm using Python and not the usual C++.

 

So, my first question involves entity systems. I have found few resources that relate them to Python, but they seem to be simply isolated update methods for the "entity". The pattern involves avoiding interaction (direct) between components, and using messages if necessary. 

Assuming that my understanding is correct, can I ask if anyone has successfully implemented an entity system with networking for a First Person Shooter? 

 

Following from this, here are my questions before they are extended:

  1. I don't like the way in which I must add endless methods to a GameObject entity to simply send its state. How would anyone recommend enabling serialisation of an object? At the moment I scrape data and pack it into bytes, but I am open to the idea of networked attributes (properties / fields). Be aware that there is a far more implied meaning of private variables than other language; for the most part they don't exist, simply name-mangled. 
  2. How would one allow for object serialisation? In terms of collecting any data serialised? At present, I have a manager class that calls the to_bytes method of each entity (GameObject) and in turn returns a sum of the bytes. It seems a lengthy step, especially when unserialising is a similar process.

 

Here are my qualms about it:

At present, I use an inheritance model. This is fine, save for the following points:

  1. Adding any new behaviour essentially involves rewriting the inherited method
  2. Serialising the object still requires a dedicated method for doing so

Now, my point about serialisation is almost avoidable at present. I could add an inspection layer that deduces the attributes of the object that should be replicated, but then I am most likely better to use an entity system for the isolation it provides with the components (avoiding cluttering the Object class directly) (see here for a good example (in my opinion) of a networked ES (although for generic game models, and non-authoritative I believe) ). 

 

Overall, I think I have too many questions than I can easily define on paper. So perhaps let me try and summarise (sort of!)

 

  1. What are the main methods for state replication? Is it advantageous to use RPC updates that invoke state changes, or just use a full state / delta encoded state system instead
  2. Are there any good resources other than I've mentioned on EntitySystems? Can anyone offer any insight into alternative yet relatively tolerant uses of OO that could work as a solution?
  3. What is the most common architecture for dealing with game state? E.g Object heirarchy, object manager, ownership of update methods, serialise methods etc.

Thank you all if you've got this far!!

Angus.

Share this post


Link to post
Share on other sites
Advertisement
As far as serialization, my suggestion would be to do something with reflection and some form of annotation. For example, in C# you can tag a specific member or property as needing serialization, and then automatically serialize an object graph via reflection. I know you can do similar things in Ruby with some name-based filtering, and I imagine Python is capable of the same. This saves you writing a lot of custom serialization logic for every single component class.

Share this post


Link to post
Share on other sites
As far as serialization, my suggestion would be to do something with reflection and some form of annotation. For example, in C# you can tag a specific member or property as needing serialization, and then automatically serialize an object graph via reflection. I know you can do similar things in Ruby with some name-based filtering, and I imagine Python is capable of the same. This saves you writing a lot of custom serialization logic for every single component class.
In which case (one that I don't think I had fully developed in concept hence having ignored it until now) how should I ref left the changes on the client? Firstly, the update loop. I would probably use simple aggregation to store the GameObject list, and then read from the data. I presume that I should write a method for serialising to the object after reading it once so that i don't have to introspect there class each time. I guess this would be best if it returned the attribute names and the format struct so both read and write methods could use it. Still this leads me to question the state replication model. Would I dedicate an update RPC for each state component or should I continue to use a fully serialised state/delta encoded state? Also should this serialisation method be replicated throughout the system or just for the entities? If so then perhaps I should create a special class that registers these networked attributes, and find a way of recreating then the other side. Questions questions!

Share this post


Link to post
Share on other sites
Assuming that my understanding is correct, can I ask if anyone has successfully implemented an entity system with networking for a First Person Shooter?

The first people I ever heard to use a component-based system (I won't call them 'entity systems' as I think that's a ridiculous name) were the developers of Thief, which is essentially a first person shooter. Even if you only shoot every few minutes. So, yes. Networking and component-based entities have nothing to do with each other really.

 

I don't like the way in which I must add endless methods to a GameObject entity to simply send its state. How would anyone recommend enabling serialisation of an object?

Why must you add endless methods?

There are various off-the-shelf methods of serialising objects (eg. JSON, Protocol Buffers) but I have generally gone for a hand-rolled approach. One function for each of the types I need to serialise, and complex types can yield up a list of simple types covered by the previous functions.

You could have "networked attributes" but essentially this is the same thing, except you have to make a note of what you're sending each time. Whether this is a pro or a con depends on how often you need to send partial updates.

 

At present, I have a manager class that calls the to_bytes method of each entity (GameObject) and in turn returns a sum of the bytes. It seems a lengthy step, especially when unserialising is a similar process.

There's not really any way of getting around this - serialization literally means creating a series of bytes from your object.

 

What are the main methods for state replication? Is it advantageous to use RPC updates that invoke state changes, or just use a full state / delta encoded state system instead?

Depends on your requirements. It's not unusual for games to do all 3 of those. Personally I prefer to separate the state changes from the RPC calls, however.

 

Are there any good resources other than I've mentioned on EntitySystems? Can anyone offer any insight into alternative yet relatively tolerant uses of OO that could work as a solution?


You're overthinking things. Components are just subobjects. You serialise them the same way you serialise any other subobject.


 

What is the most common architecture for dealing with game state? E.g Object heirarchy, object manager, ownership of update methods, serialise methods etc.


There's no standard. People use whatever suits their game. Shooters might just send specific position and orientation updates on a regular basis, with events to notify of projectile creation and destruction, whereas MMOs might often have to send full state updates as characters enter the visible range. How best to serialise these things will differ from genre to genre because you don't need full serialisation to send 2 vectors.

Share this post


Link to post
Share on other sites
Thank you very much. I've thought a lot, and I'm enjoying the realisations!
Best of luck in the new year, I hope I don't come running back too soon ;)!
---------------------------------------------------------------------------
Nope, looks like I'm back as ever!

So, one of my fundamental reasons for rewriting the system was the fact that It felt like I was writing everything twice, and using shared base classes seemed a little messy.
I had started thinking through a system where there was no discrimination between server and client, and I think that would have required some environment setup to establish specific behaviour like binding to the correct port, and establishing server or client behaviour; at which point there is a discrimination. So, I then thought about it in terms of using the same file with switches; defining a server method and a client method, using the same name but invoking only the one that the game environment is set to. Something like this:
http://www.pasteall.org/38986/python

However, I have yet more code that is copied. (DRY principle is once more violated).
So, after picking an idea from the pdf on meerkat, I thought about this:
http://www.pasteall.org/38988/python

It's the same idea; using environment flags (which I haven't exposed in the examples) to suppress method calls if they are incorrect.

Is this an acceptable solution? Can you see any inconveniences that I've not?

Following from this, In order to ensure that only relevant data is shared between client and server, the entity class must declare attributes as networked in order for them to be sent and received. This is done with a hidden interface; As in Source, if an attribute is changed - attributes will be descriptors[1] - it will be flagged as changed. When the networked values are asked for by the network layer, the entity manager (handler) layer will return the concatenation of two things. Firstly, a bitmask of which attributes it contains, and secondly the value of those attributes (in bytes).

[1]If you don't know what a descriptor is in Python, It is a class instanced as an attribute of the holder class. It has get and set methods that are called when the set and get operators are called. When accessed on the class the descriptor instance is returned, but when accessed on the class instance the behaviour is invoked - hence one can intercept the set method to register a changed flag Edited by Angus Hollands

Share this post


Link to post
Share on other sites
I don't like the way in which I must add endless methods to a GameObject entity to simply send its state.

If you ever find yourself typing a lot, then there's probably a better way to solve the problem.
Find out what's common among the typing, and roll that into a single "thing" that can be re-used.

For example, Python has member decorators (with the "@" method) and also properties (as a form of particular "@" decorators.) You should be able to wrap everything you need from properties into a decorator, and use that for all properties. Then, every field that needs serialization just needs to be declared with its name, type, and decorator, and you can do persistence and marshaling based on that.

Share this post


Link to post
Share on other sites
However, I have yet more code that is copied. (DRY principle is once more violated).
So, after picking an idea from the pdf on meerkat, I thought about this:
http://www.pasteall.org/38988/python

It's the same idea; using environment flags (which I haven't exposed in the examples) to suppress method calls if they are incorrect.

Is this an acceptable solution? Can you see any inconveniences that I've not?
In my experience this is a bad idea. Imagine that file is 10x the size. When you look at shoot() you don't know if shoot_sounds() is going to do anything or not without having to inspect that too. That's going to hurt maintainability and debugging later.

I personally would prefer either a clear separation of client and server functionality or clearly labelled functionality inline. eg.
if self.is_client(self.shoot_sounds()). Explicit is better than implicit.

Or, if you don't want conditionals in the code, make it event-driven. Emit a 'shoot' event and clients and servers can decide how to handle the events in a way relevant to them. It's still fairly opaque compared to explicit conditionals, but at least you'd know that all the server/client specific behaviour is encapsulated in the event system.

EDIT: I would love it if this editor didn't completely break the formatting half the time. Edited by Kylotan

Share this post


Link to post
Share on other sites

This does sound like a good idea, except that the inputs are sampled in the same class instance as the event that is triggered resides. Thus it is largely just an abstraction. However, I want to allow for the inputs to be abstracted so that the same interface can be used for the server and the client (the server receives inputs from the client). Because of this, using events would make sense in some regards, because then I could use the same base class for server and client, and modify the event registration in the initialiser for each class instance.

 

Thanks for replying, and I still have a few more questions. You mentioned that I would be able to encapsulate environment specific behaviour in the event system. That assumes that I would define and register the events outside of the entity classes right? I'm not too sure if I want to move entity-related behaviour outside of the entity, thus I think the inheritance model above may work better. 

 

Assuming I do use the observer pattern, would it make sense to use a "global" event handler or a single event (with listeners) per entity? In some ways having per class is easier, and because the inputs would only influence the entity in question, it wouldn't make sense to inform other parts of the system about what was going on. Yet, If I move the entire design towards an observer pattern (to allow for the flexibility of a base class) would it make more sense to have a global event handler; that can register an event by name and add listeners?

I suppose if I were to add a global event manager then other parts of the system could interact with it. 

 

Continuing on, have read about event queues and immediate events. For most of the system it will require immediate responses, others may get away with having a queue. Could you offer any advice as to when a queue is really needed? If i were to add this as part of an event manager I would probably use an optional immediate flag when triggering the event. 

 

I have also decided that I would like to work with a component system. I think that I feel somewhat drawn to having intelligent components rather than the dumb entity system approach which utilises systems. Are there any issues with this? You'd simple pass the entity instance as an argument to each component and it would have access to its fundamental data (position, name).

Here is my idea for using the event system, and I hope that it wouldn't be too hard to use components with this http://www.pasteall.org/39019/python

 

many thanks for your time

Share this post


Link to post
Share on other sites
One way of doing the server/client separation, is to use interfaces for all the services (be it sound, AI, graphics, networking, etc.)
In C++, these are pure abstract base classes. In Python, this uses duck typing.
Then, objects are only written once, and do not know whether they are running server-side or client-side. However, you provide different interfaces depending on whether the code is server- or client-side. For example, a "no-op" sound interface that doesn't play any sound on the server.
This lets you extract a large number of behaviors. For example, for some games, the server runs AI and does pathfinding using information that's not visible to the player, and it can arrange it so that the client-side implementation of those interfaces uses data that's provided by the server, rather than re-running the algorithms which would require sending the secret data to the player.

Share this post


Link to post
Share on other sites
Thanks for replying, and I still have a few more questions. You mentioned that I would be able to encapsulate environment specific behaviour in the event system. That assumes that I would define and register the events outside of the entity classes right? I'm not too sure if I want to move entity-related behaviour outside of the entity, thus I think the inheritance model above may work better.

What is 'inside' and 'outside'? Whether you use inheritance, composition, or some other association, you still have 2 classes.
 
Assuming I do use the observer pattern, would it make sense to use a "global" event handler or a single event (with listeners) per entity?
In my experience you can go a long way just by assigning callbacks to the object. The abstract Entity could have an onShot member which it calls when it gets shot. All you need to do is assign the ClientEntity.onShot function to it.

The Observer pattern is a good alternative in languages like C++ or Java where assigning arbitrary callables to another object is awkward, but in the likes of Python or C# it can be more trouble than it's worth.
 
 
Could you offer any advice as to when a queue is really needed?
I like to use queues because it means I can totally separate the input from the logic. But they're not strictly necessary.
 
 
I have also decided that I would like to work with a component system. I think that I feel somewhat drawn to having intelligent components rather than the dumb entity system approach which utilises systems. Are there any issues with this?
My opinion on entity-based components, whatever variation you prefer, is that they usually cause more trouble than they are worth unless you are at the level where you don't need to ask how best to use them. They're a bit of a tar-pit for people who are eager to do things the most flexible way from day one. But each method is functionally equivalent to all the others, and all the non-component methods as well. People have written whole games in languages that don't even have structures, never mind classes, so everything is possible - it just changes how you do it.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!