I've once read about the networkcode in doom/quake. The basic idea is to have a really large state object for each entity and to transfer the whole object. The real trick is, to only send the binary delta of the object,regardless of the object setup. E.g you have a object like
struct MyObject {
char name[50];
int health;
float x;
float y;
float z;
...
}
Instead of creating special movement, health packets , you only send the whole MyObject as binary delta, something like this:
MyObject* obj = getPlayerEntity();
byte* lastSendData = getLastSendData();
byte* currentData = (byte*)obj;
byte* deltaData = getDeltaBuffer();
for(int i=0;i<sizeof(MyObject);i++) {
deltaData[i] = lastSendData ^ currentData; // XOR
}
// simple RLE(run-lentgh-encoding) compression
byte* compressedData = encodeRLE(deltaData);
// send delta
sendPacket(compressedData);
The idea is, that the delta of a large object state will most likely contains a lot of 0 bits (not changing very often), which can be compressed by RLE quite fast and effective.
The decoding would look like this:
byte* compressedData = receivePacket();
byte* deltaData = decodeRLE(compressedData);
MyObject* obj = getPlayerEntity();
byte* currentData = (byte*)obj;
for(int i=0;i<sizeof(MyObject);i++) {
currentData [i] = deltaData ^ currentData; // XOR
}
To construct your object like this, it would be best to have a fix length object, but other delta compressions or hybrid solutions would work too.
PS:
If you dont want to change all your objects, it doesnt hurt to create a intermediate object:
MyComplexeDynamicObject* obj = ...
PlainDataObjectRepresentation plainObj;
plainObj.transformFrom(obj);
.... encoding and sending ...
.... receiving and decoding...
PlainDataObjectRepresentation plainObj = ... received and decoded;
MyComplexeDynamicObject* obj = ...
plainObj.transformTo(obj);