problems with a very simple function :)

Discuss CSQC related programming.
Post Reply
Nahuel
Posts: 495
Joined: Wed Jan 12, 2011 8:42 pm
Location: mar del plata

problems with a very simple function :)

Post by Nahuel »

I'm trying to spawn an entity above some enemies to know certain things about them. This entity would be carryed to csqc so that there i could freely use it, with stuff like drawstring and drawpic, etc...
So basically, in my first attempt I was trying to take this entity to csqc with the most basic information, the origin.

Well, apparently I'm forgetting something important.
I did write this function to call in some enemies: (ssqc part)

Code: Select all

void()ind_update =
{
setorigin (self, self.owner.origin + '0 0 64');
self.nextthink = time + 0.1;
self.think = ind_update;
}

float() Send_Indicator =
{
	WriteByte(MSG_ENTITY, ENT_HMMSG);
	WriteCoord(MSG_ENTITY, self.origin_x);
	WriteCoord(MSG_ENTITY, self.origin_y);
	WriteCoord(MSG_ENTITY, self.origin_z);
	return TRUE;
};

void(entity self) indicator =
{	
	local	entity indicator;
	indicator = spawn ();
	indicator.owner = self;
	indicator.classname = "indicator";
	setmodel (indicator, "progs/indicator.mdl"); // test model
	setorigin (indicator, self.origin + '0 0 64');
	indicator.nextthink = time;
	indicator.think = ind_update;
	indicator.SendEntity = Send_Indicator;
	indicator.SendFlags = self.SendFlags  + 1;
};
ENT_HMMSG is defined and shared (ssqc and csqc) all the stuff compiles perfectly, but when i call the "indicator" function every time i get "Host_error: CL_ParseSeverMessage: Illegible server message"
AFAIK the message is broken from server, i do not have problems in the csqc part (is not defined already) Any clue about this problem? Did you see the problem in this ssqc part?

Thank you :)
hi, I am nahuel, I love quake and qc.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: problems with a very simple function :)

Post by Spike »

firstly:
(note that this isn't your immediate problem, but will be important later)
you're not using SendFlags properly. The field provides a set of flags that the engine uses to track what parts of the entity need to be resent.
If you want to resend everything, then you can just set it to -1 (integer maths trick which is the set of all bits). Usually you'll just |= into it though. any adds or subtracts or multiplies or divides or even bit-clears should be considered a bug. copying the value from another entity is also a wtf.
the engine will periodically clear the field back to 0 - so anything that cares about the actual value is thus bugged (the bits get stored into a per-client state tracker, and are passed back to your SendEntity function so that you know its safe to send smaller updates - the first call will have all bits set or so).

secondly:
'Illegible server message' means something got sent that the client didn't understand for one reason or another - the reads in the client didn't match the writes in the server.
FTE has an sv_csqcdebug cvar - this will cause FTE to prefix entity updates and cgame packets with a size value. If the reads do not match the writes then the client will rewind/skip back to the correct place and print a warning whenever it does so. This can be used to tell you where in your code those reads don't match the writes.
So set that cvar to let FTE detect what went wrong.
However it looks like you're debugging in DP, so you're screwed as there's no DP equivalent. My advice to you is to debug it in FTE instead (if only briefly).
Nahuel
Posts: 495
Joined: Wed Jan 12, 2011 8:42 pm
Location: mar del plata

Re: problems with a very simple function :)

Post by Nahuel »

thank youspike, with fte and the debug cvar I get

Code: Select all

csqc underread entity 4. Size 7, read 1, first byte is 0 (0)
server classname "indicator"
I do not understand it. so i guess my problem is in the send flags?
Sorry i do not understand it at all.
Ty
hi, I am nahuel, I love quake and qc.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: problems with a very simple function :)

Post by Spike »

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.
Nahuel
Posts: 495
Joined: Wed Jan 12, 2011 8:42 pm
Location: mar del plata

Re: problems with a very simple function :)

Post by Nahuel »

Thank you spike, i managed to make it work by following your advice. But I have a problem, I want to send a custom float that is also variable (as origin) it is. In this case it is the health of the enemy (which is the owner of "indicator").
i have updated the sendflags from ssqc with the + = 1 as I have "understand" your suggestions,
so in this new case i am using: origin + health, so what which sendflags should I use in this case? It is not clear that sendflags should i use for custom floats

Thank you :)
hi, I am nahuel, I love quake and qc.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: problems with a very simple function :)

Post by Spike »

dude, use |= for SendFlags, NOT +=.
If you're ignoring the flags argument in your SendEntity function, then you might use =1; or possibly =-1; instead of |=1; but still not +=1; insisting on using += is just a wtf that shows that you didn't understand anything of what I wrote.

in case it wasn't clear, the engine doesn't have a clue what data is actually associated with any specific flag. It doesn't care at all. That's for you to decide. They are ALL 'custom'.
Post Reply