Two things come to mind that could be used to optimise the bandwidth usage. First is a non-breaking change, the second is a breaking change and would require a protocol version update.
Disclaimer: It's possible that other engines may have done either or both of these before. If so, shout out about what you did and how you did it; sharing the information will help formulate a standard and make it easier for everyone.
Idea 1 - Origin and Angles Updates
These are stored as floats internally on both the server side and client side. However, for transmission, Origin is demoted to a short and angles to a byte. When comparing against the basline to determine if an update is required, demote the values and do the comparison with the demoted values.
Angles would look something like this:
Code: Select all
// ugh - the U_ANGLE bits aren't consecutive!
int U_ANGLE_BITS[] = {U_ANGLE1, U_ANGLE2, U_ANGLE3};
// check angles
for (i = 0; i < 3; i++)
{
// the angles are decomposed into bytes for transmission, so here we check the byte values that will
// be actually transmitted!!!
if (((int) ent->v.angles[i] * 256 / 360) & 255 != ((int) ent->baseline.angles[i] * 256 / 360) & 255)
bits |= U_ANGLE_BITS[i];
}
E.g. an angles change from 120 to 120.5 would cause an update, but both would arrive at the client as 120.
I'm not sure how often this actually happens in the real world, but it seems a sensible thing to do anyway.
The nice thing about this change is that it can be made without breaking compatibility.
Idea 2 - Baseline Comparison
This is a breaking change.
On signon, Quake sends a baseline to the client, and also stores the baseline on the server. Each server-side update, the baseline is compared with the current value, and if it's changed, it's transmitted. On the client side, if an update is read it's used, otherwise the baseline is used.
One obvious failing of this is that the baseline relates to when the entity is spawned. In the case of dead bodies, certain doors and plats, pickup boxes (spawn above the floor then drop to it) and certain others, there will always be an update sent, even though the entity state has not changed since the last update.
The proposed change is to compare with the previous state instead of with the initial baseline. This involves storing any state changes back to the baseline on both the server side and client side, so that if no change is detected, the baseline will be correct for the previous state.
It's required on the client side as well as on the server side, so that if a server side update is not sent, the client side will fall back to a correct previous state rather than to an initial spawn state.
Sample implementation for server side:
Code: Select all
if (ent->baseline.frame != ent->v.frame)
{
bits |= U_FRAME;
ent->baseline.frame = ent->v.frame;
}
Code: Select all
if (bits & U_FRAME)
{
ent->frame = MSG_ReadByte ();
ent->baseline.frame = ent->frame;
}
else
ent->frame = ent->baseline.frame;
So it's over to you lot for discussion!