it means that entity 4 got mis-read. There's 7 bytes of data in the message, but the csqc read only 1 of those (which had the value 0).
and that this entity 4's classname is 'indicator' on the server.
Or more specifically, it means that your CSQC_Ent_Update function is fucked and doesn't deal with errors properly, and in addition you didn't bother to update it to support your 'ENT_HMMSG' entity.
You will NEED that addition, because that's your real problem here - you're sending data that you're then not bothering to read.
Like I said (in my bracketed note), your SendFlags field is kinda screwy but its NOT the cause of the error message. Its just a second/minor thing that's wrong with your code.
The engine's deltas works by tracking flags rather than data. You are the one that decides which flag means what data (and vice versa) and you are the one responsible for letting the engine know when that data has actually changed (via the flags that you decide refer to that data). The server tracks ONLY the flags - it copies the flags into a pending[player][entity] 2d array type thing and sets the SendFlags field to 0. You can then set one of the flags again to say that the data has changed once more. So the internal pending flags are set according to SendFlags, as well as when an entity first enters a player's pvs (all pending flags get set), as well as when the server thinks that a packet has been lost (flags whose data was in the dropped packet are automatically re-flagged into the pending data). In a sense, the flags for each entity are pretty much just a bool as to whether the ent needs to get resent or not. However, the full set of flags are passed to the SendEntity function as an argument, allowing the ssqc to see exactly what changed.
Also remember that SendFlags is a float, which means you only have about 24 usable flags.
So, if you 'assign' origin to flag 0x1, then whenever you (significantly) change .origin then you also do .SendFlags |= 0x1; This lets the engine know that there's new data that needs to be sent. It then calls self.SendEntity(player, PendingFlags), and in that function you can test what needs to be sent. So:
Code: Select all
float(entity player, float PendingFlags) =
{
WriteByte(MSG_ENTITY, ENTITY_TYPE);
WriteByte(MSG_ENTITY, PendingFlags&0x7);
if(PendingFlags&0x1)
{
WriteCoord(MSG_ENTITY, self.origin_x);
WriteCoord(MSG_ENTITY, self.origin_y);
WriteCoord(MSG_ENTITY, self.origin_z);
}
if(PendingFlags&0x2)
WriteShort(MSG_ENTITY, self.modelindex);
if(PendingFlags&0x4)
WriteShort(MSG_ENTITY, self.frame);
};
so when it first appears all the flags are automatically flagged. If your code then goes and changes the .frame (and sets SendFlags |= 0x4) then the resulting update will only be 4 bytes, instead of the whole lot.
When its just origin then there's no reduction possible anyway, so you can treat SendFlags as just a boolean. But when you've a whole load of fields all individually changing or more importantly NOT changing, then it pays to put some of those probably-unchanging things behind a specific bitflag so that you don't end up networking the entire thing every time.
your csqc would then have something like:
Code: Select all
void(float isnew) CSQC_Ent_Update =
{
float etype = readbyte();
switch(etype)
{
case ENTITY_TYPE:
float eflags = readbyte();
if (eflags & 1)
{
vector norg;
norg_x = readcoord();
norg_y = readcoord();
norg_z = readcoord();
setorigin(self, norg);
}
if (eflags & 0x2)
setmodelindex(self, readshort()); //sets modelindex and model, which can be handy.
if (eflags & 0x4)
self.frame = readshort();
//if you're paranoid you can check for (eflags&~7)
return; //we're done here. probably you'll want fancy logic and interpolation and etc
default:
error("Unhandled CSQC entity\n"); //you can change it to a print, but if you're not using sv_csqcdebug 1 doing so would just confuse people over the real cause.
return;
}
};
void() CSQC_Ent_Remove =
{ //the entity in question left the player's pvs, and will no longer be tracked...
remove(self);
};
Note that the csqc's self entity is an entity within the csqc vm, not the servers - its a completely separate entity. the client will set .entnum for you, and reuse the same csqc ent in the callbacks, but once it leaves the player's pvs then the entity will get disconnected and become identical to any other csqc ent. usually you'll want to just remove the ent, but you might have some cleanup to do, or you might want to do fancy stuff like assuming that the server removed it (at least if the server used PVSF_NOREMOVE) and trigger some clientside death+fadeout effect (in which case you would remove the entity only once its alpha value dropped to 0, or whatever).
Yes, figuring out when .origin has changed so that you know when to update SendFlags is a pain. With monsters you can just add a SendFlags|=1 after each movetogoal. With projectiles you should network the start time and calculate how far it has travelled since (which despammifies nails really well). Grenades are just messy though, you can resort to .customphysics if needed, but its generally better to network it as some fancy sort of parabola (you can get the same parabola on the client with customphysics+tracelines, or even thinks if you're stuck with DP).
Yes, this stuff is all messy and annoying, so really you should only bother doing it if you're actually adding something (like unnetworked trajectories, or more complex animations). If you're just trying to clone what the engine normally does then frankly you're just wasting your time.
yeah, this post was long winded, but it looked like you needed a proper explanation.