RFC state management

Started by
5 comments, last by Antheus 15 years, 9 months ago
Lazy summer days gives time for lots of dreaming about that MMO-like game I want to create some day. Here's what I'm thinking for state management between server and client in such a game. If you have the time, please read thru and poke holes in my theories. ----- * Each object is defined using a data definition format. * Generated objects keep a timestamp for every property, which is updated every time the property changes. * Each connection keeps a list of known objects and which timestamp they are updated to. * When an object is up for examination by a connection (which happens a different intervals depending on distance and importance) the timestamp of every property is compared to the last updated timestamp for that object; creating a delta packet of all properties that has changed since the last update. References to IGameObjects are followed when creating delta packets. * Every property in the object can be marked with ServerOnly, ClientOnly, SCLocal or SCWorld. ServerOnly and ClientOnly means they only exist on that end. SCLocal means the property is only transferred to the client (via delta packets) if it is the current player, or an object held by the current player. SCWorld means the property is always transferred to the client, also for other objects than self (like other players, things on the ground). For example; a players inventory or Strength stat is marked as SCLocal, but a players Name and visual appearance is tagged as SCWorld. * The data definition format also specifies which properties, and how, to serialize to disk. ------ Is anything of this sensible? I'm especially worried about how the SCLocal/SCWorld thing will work out in practice.
Advertisement
Keep per-property time stamp doesn't accomplish much. Server states will either be replicated for each time-step, meaning everything that has changed shares the same time stamp.

As for client, they either have the property already, or they don't. There's not much gained from time stamps either.

Each time a property value changes, you can either mark it as dirty, or write a change log. This change log can be sequential, outlining all operations as they occured, or you store the delta state. This later approach allows merging of operations. If health gets changed 5 times during the client update, only the last value will be sent.

With streaming change log, you simply send all the operations that happened server side.

For roll-back, you can maintain multiple copies of full state, based on server time.

Quote:* Every property in the object can be marked with ServerOnly, ClientOnly, SCLocal or SCWorld. ServerOnly and ClientOnly means they only exist on that end.
SCLocal means the property is only transferred to the client (via delta packets) if it is the current player, or an object held by the current player.
SCWorld means the property is always transferred to the client, also for other objects than self (like other players, things on the ground).


Too many different types. Imagine the mess if just one property is improperly marked.

One concept useful for such fine-grained replication are object views. They can be implemented in many ways, and if one insists on using OO, this is a somewhat convenient way to replicate objects.

Another way is through subscriptions, where each interested party can subscribe to any object and any property. So combat logic subscribes to every entity's health property.

This however always struck me as somewhat heavy-weight. Multiverse, for example, allows for this type of replication.

Quote:* The data definition format also specifies which properties, and how, to serialize to disk


I'll just point out this, which could be considered optimal pattern for this type of tasks.

Depending on language, reflection can also be used.
Quote:Original post by Antheus
Keep per-property time stamp doesn't accomplish much...
-snip-
Each time a property value changes, you can either mark it as dirty
-snip-

Actually, the timestamps was my idea for marking them dirty... just with the added possibility to distinguish when they were changed last, so different players could get different delta packets. The values themselves wouldn't actually be deltas, just the current value, but what values (properties) needs updating, and what values that already are up to date for a player, could be derived by comparing timestamps. At least, that was the idea :-)

Quote:
... or write a change log. This change log can be sequential, outlining all operations as they occured, or you store the delta state.

Yes, I was thinking of keeping a changelog for lists/arrays which would be too large to send just the latest value (the entire array) each time.

Quote:
One concept useful for such fine-grained replication are object views. They can be implemented in many ways, and if one insists on using OO, this is a somewhat convenient way to replicate objects.


Excellent! I've been googling for info on this but articles seems scarce.

Quote:
I'll just point out this, which could be considered optimal pattern for this type of tasks.

Depending on language, reflection can also be used.


Thanks, I'll have a look. I'm using c#.

Quote:Too many different types. Imagine the mess if just one property is improperly marked.


In reality, you will note pretty quickly if something is improperly marked, and it won't make it off the developer's machine into source control.

Quote:subscription


HLA uses a public/subscribe model. It even allows different hosts to publish different data pieces for the same entity. Unfortunately, it turns out that this is very high overhead for real-time systems, with not enough gain to justify the overhead. Typically, the client and server both know what the interesting parts of an object are, and a single "I'm interested in this object" should suffice -- and, in turn, because the server knows what objects there are, and the client doesn't, it's more efficient to let the server decide what the client should see.
enum Bool { True, False, FileNotFound };
Quote:Original post by Antheus
One concept useful for such fine-grained replication are object views.


Having read thru this article (once) it seems they are suggesting using an incremental counter for each property to use when comparing if state has changed. This was my initial thinking too; but it requires the Object View to keep a record of the changenumber of every property it tracks. If a timestamp (or, as an optimization, a server-tick-stamp) is used, only the timestamp when the state for the entire object was last updated needs to be recorded.

My data definition would be the definition for the Object View; what properties to pass on to the client (and optionally, how).

I couldn't find anywhere how they deal with the different handling of objects depending on the viewer perspective; ie. distributing "wooden chest" should differ depending on whether you're holding it or it's on the ground; or better still... distributing a player object differs a whole lot depending on whether you ARE that player or not. I might need to read the article more thoroughly tho.
Quote:Original post by Antheus
Too many different types. Imagine the mess if just one property is improperly marked.


If a property is improperly marked, so that it doesn't distribute to the client, it won't be available as a property on the object in the client version of the source file. I'll mark generated classes in the source files as partial, so that the implementation of handling the data can be done in the same class (but different file). Each property will be generated with a PropertyChanged event so the implementation can react to changes in the data.
If a property is improperly marked, so that it distributes to the client unnecessarily... bandwidth will be wasted. Hopefully intellisense will alert any developer of a strange property they did not expect to find.
Quote:Original post by hplus0603
Quote:Too many different types. Imagine the mess if just one property is improperly marked.


In reality, you will note pretty quickly if something is improperly marked, and it won't make it off the developer's machine into source control.


The concern with data-driven replication to me is more connected with why, not how.

Since primary purpose of such fine-grained replication is likely to be conservation of bandwidth, one would always want to keep only relevant subset replicated.

And this minimal set will be determined by algorithms that operate on data. So determining replication behavior on per-property basis seems the other way around.

Even when using views, each view can be determined at compile-time. At the same time, one can go further than that, and make this knowledge implicit, removing the need for complex replication schemes. Expressing interest into entity X implies that position, orientation, scale, health, ...., will be replicated.

Another thing however are priorities. Those do make sense in context of interfaces, perhaps even at property level. But even there, range or other factors will be determined on per-client basis. And here going too low-level isn't necessary - position and orientation will each need to be updated atomically. But I believe that even in this case, these were defined on coarser scale.

In context of most games, the client is pretty fixed in functionality, few support flexible scripting or modifications, compared to various virtual worlds such as SL, which need to give access to everything.

Even with intra-cluster communication, individual interfaces will likely be well defined, perhaps even more than the client side, since it's possible to operate at higher QOS.

Even with tuple systems, each interested party issues a query, while values simply 'are', and aren't too concerned with how or by whom they are replicated - with exception of load balancing, which is determined separately.

This topic is closed to new replies.

Advertisement