Hey everyone. Here is an “idea” thread for the redesign of my multiplayer work. The system that I have currently implemented works, and works overall well, but I wish to make it easier to work with. Here lies the basis for replicateable entities:
All entities work on the following system:
- Attributes. Entities have networked attributes (consider them public) which can be read by other objects. These attributes must have a declared attribute format, and they take the type from the default value. The attribute formats are Continuous and Discrete; referring to the correction methods that can be used upon them. If discrete, they must be overwritten, else they can be modified by a delta between true value and stored value.
- Input. All entities must have an input factor. In cases such as user input, this must be abstracted behind an interface so that the same entity class can be used on the server and the client.
- Actions. Actions are driven by input, and they should only have access to themselves and networked attributes of other entities (besides global attributes like game tick)
- Output. All entities must have interface attributes, publically declared as networked. I expect to use descriptors with class-declared variables.
- Timelines. An idea similar to the meerkat implementation of networking, all networked attributes of an entity are stored on the time line every frame. When discriminating between server and client, the client will run ahead of the server (for the same fixed point in time) thus it must apply correction to its simulation. This is the discriminating factor between the server and client.
In order to avoid having shared codebases or separated code bases, I intend to use the same file. I will flag methods as server-only or client-only meaning that if certain methods should not be invoked on the client they will not be (and vice versa). This will take place using an environment (game) variable / flag to suppress method calls if they called on the wrong environment:
Source code example
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 the Source engine, if an attribute is changed - attributes will be descriptors - 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).
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