Reputation: 512
Background Info:
I am beginning to learn about networking for a small demo project that I'm working on. I have a server with a bunch of Ball objects that have a variety of parameters (size, color, velocity, acceleration, etc.). I would like the server to be able to do 2 things
I'm a little overwhelmed at how to approach this, since there is so much to deal with. My idea was to create a class called ClientUpdate that would be an abstract base class for specific update types that I might want to send.
class ClientUpdate
{
protected:
UpdateTypes type;
public:
ClientUpdate(){};
void setType(UpdateTypes t){ type = t; }
virtual void print(ostream& where)const;
friend std::ostream& operator<<(std::ostream& os, const ClientUpdate & obj)
{
obj.print(os);
return os;
}
};
Then for every event that might occur on the server, like when the a ball changes color or changes its state from frozen to not-frozen, I would create a subclass of ClientUpdate to describe the event. The subclasses would have simple variables (strings, integers, booleans) that I would write to the ostream with the print function.
Finally, I would store all of the updates that happen in a certain area of my game (such as a room) in each update cycle, and then for any clients who are subscribed to that area, I would send 1 large byte array of client updates that would have the form UPDATETYPE_DATA_UPDATETYPE_DATA_....etc. The client would parse the input stream and re-create the update class from it (I haven't written this code yet, but I assume it won't be difficult).
I'm using Boost::Asio for the networking code, and I'm following the tutorials here : http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio/?pg=10. I just mention this because I'm pretty sure I want to stick with boost asio, since I'm trying to very comfortable with boost and modern c++ in general.
Question:
(1) The basic question is "is this a reasonable way of approaching my problem?" I feel very confident that I could at least make it work, but as a novice at anything network-related, I'm not sure if I am re-inventing wheels or wasting time when there are simpler ways of doing things. In particular, is it inefficient to gather all of the "update" objects together and send them with 1 large write or should I send the individual updates with separate writes to the socket?
(2) For example, I've read about Boost::Serialize, and it seems to be very similar to what I'm doing. However, I am more interested in updating certain member variables of objects that should be almost the same on both the client and server. Is Boost::serialize good for this, or is it more for sending whole objects? Are there any other libraries that do things similar to what I'm describing?
Upvotes: 1
Views: 175
Reputation: 393583
The trade offs are hard to judge from here.
I can see a few approaches (disclaimer, I didn't try to be exhaustive, just thinking aloud):
every mutation to game state is an "event"; you "journal" events and every once in a while you send a batch of these to the other side. The other side applies them and sends back a checksum verifying that the resulting state matches that on the sending side (at the time of the sending).
alternatively, you treat the whole game state as a "document". Every once in xxx milliseconds, you snapshot the gamestate, and send it to the other party. The other party replaces its gamestate with that from the document. The server could optimize bandwidth by differencing the gamestate to the former (by saving the previously sent snapshot) and sending only the delta.
In that last respect there might be a similarity to the first approach, but there is a fundamental difference: in the first approach, the mutations sent to the other side are exactly the same as they happened on the source system; In the second approach, the 'delta' mutations are synthesized from the effective difference to the last snapshot: they have no relation to the sequence of events that actually lead to the current game state.
Now, the trade-offs are plentiful and depend on such factors as:
Etc. All these questions will make certain approaches more attractive and others less.
One crucial question that you didn't address is: will there be "inputs" on both sides? If so, could there be conflicting inputs? Could there be consequences of changes on one side that lead to a different outcome if the inputs from the other side have been received slightly later?
I won't go into this for now. If you need bi-directional synchronization, you will become very dependent on low latency and frequent updates, so that you can correct divergent gamestates before the difference becomes humanly noticeable and annoying.
I also won't go into how you should send the data, as it depends very much on the chosen approach. If you send full documents, as you've noticed, Boost Serialization would look like a good candidate.
Upvotes: 2