Initial Bandwidth Optimizations (mmo prototype)
Amit Patel (http://www.redblobgames.com/) suggested a super compact format for entity movements:
id : 16 bits
delta-x : 8 bits
delta-y : 8 bits
That's a total of 32 bits of binary data, which is ~85% smaller than the JSON string I currently use in this situation:
{"x":1213,"y":1001,"id":999}
The binary format isn't the only optimization, the notion of sending a change in position as opposed to a full position (because it is a smaller number) also shrinks down the data needs. This sort of optimization may be overkill for infrequently sent pieces of data, but I estimate that movement data is by a significant margin the most frequent piece of data. Attacking is a very distant second. That said, with my grandiose goals I'll probably still optimize any predictable repetitive message that I can find.
For the actual sending of binary data, I'm going to use https://github.com/einaros/ws. I also don't know anything about binary nor bit math, but here's my v1 client (browser HTML5) binary reader:
function readBinaryMovement(arrayBuffer) { var dataView = new DataView(arrayBuffer) return { id: dataView.getUint16(0,true), x: dataView.getUint8(2, true), y: dataView.getUint8(3, true) }
I'm hard-pressed to estimate the gain that would come from optimizing the movement message. It is certainly a large gain, but it is also a gain that applies more in crowded situations than others.I don't know how big the TCP/IP protocol itself is, but I wager that it makes 32 bits seem insignificant. So in a scenario with 1-2 players, the difference between a JSON and binary movement message is hard to notice. However with 20 players onscreen, the JSON approach adds about 600 bytes. The binary approach could describe 20 players in 80+1 bytes. This is such a huge gain and it occurs precisely in the area of concern for an mmo. This will likely present the option to maintain a higher network rate in much more crowded scenarios than previously estimated, or to simply increase the number of entities.
All of the above has been logistically tested in a standalone prototype, but isn't integrated into the game yet.
Another improvement that is being tested is a serverside cache of the clientstate. Whenever the server sends information to a client (e.g. entity 203 moved to 28, 39) the server now additionally stores this entity's information in the cache for the given client. On the following network update, the server will only send information to the client that isn't already in the client's cache. This has effectively reduced redundant network messages to zero, and also drops the bandwidth usage to zero if nothing is moving onscreen. However it comes at a cost that I haven't fully tested, and there are other ways to accomplish similar goals. My uncertainty about this simple but powerful feature is in fact why I went ahead and implemented it... I just have to get a feel for it.
One of the costs of the serverside client cache is memory. The server now has a representation of the client state for every single client... if 100 players were all to stand together on the same screen then the server would have a representation of 10,000 players cached. Not only that, but this representation of 10,000 would linger for some time even after the players broke apart. The delta between the current state of an entity and the client cache is made by enumerating through the properties of the cached 'proxyEntity' and the the current entity -- this could get intensive. Also every message from the server is pretty unique as it contains only the name of the property that changed (if its part of the list of network properties), and the new value, which adds a small overhead to network messages. I like it so far, but as a whole it may be prohibitive, and I may remove it. Here's an example of where it does a good job: if an NPC were to move offscreen from the player (while remaining in the cache) and then theoretically change its gear and move back onscreen, the cache delta would flag the differences and nothing other than these would need sent across the network. Without data like this, the server would end up sending a full update about any entity that left the client's view and returned.









