Skip to content

Instantly share code, notes, and snippets.

@seece
Created July 12, 2012 16:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seece/3099276 to your computer and use it in GitHub Desktop.
Save seece/3099276 to your computer and use it in GitHub Desktop.
Sending an entity from SSQC to CSQC
< cce> is there any documentation on how to send an entity from the server to the csqc?
< cce> i can send it but couldn't figure out how to recieve it on the client side
<@LordHavoc> I'm not sure if there is actual documentation :P
<@LordHavoc> there is a lot of sample code
<@LordHavoc> SendEntity basically begins with a WriteByte to MSG_ENTITY containing an entity type
<@LordHavoc> the types are your own constants
<@LordHavoc> CSQC_Ent_Update is called on the csqc and the first thing it must do is ReadByte() to get that entity type, then
a switch or if else to figure out which one it is
<@LordHavoc> once it has figured out what entity type it is, it must issue Read calls similar to the Write calls that the
SendEntity function wrote
<@LordHavoc> and it can store these into properties on self
< cce> ok i think i got it
<@LordHavoc> it may be starting with an entity that is already clear or it may be updating an existing one
<@LordHavoc> so it's worth checking if you've already initialized this entity
<@LordHavoc> and also checking if the type changed
<@LordHavoc> because it is possible (rare, and this is technically an error but we haven't found a fix for it yet) for the
type to change on an entity
<@LordHavoc> where the server reuses an entity slot before the client gets a remove message
< cce> the entities are "invisible" on the client side by default, right?
<@LordHavoc> yeah
< cce> and you need to call spawn() by hand
<@LordHavoc> no
<@LordHavoc> CSQC_Ent_Update is called after CSQC_Ent_Spawn
<@LordHavoc> you can put special logic in CSQC_Ent_Spawn if you want to do special logic before calling spawn()
<@LordHavoc> but in general you just get the CSQC_Ent_Update and it is either an existing entity (previously updated) or fresh
result from spawn() called by the engine
<@LordHavoc> cce: also note that CSQC_Ent_Update takes a float parameter which I call isnew
<@LordHavoc> cce: it indicates that self is a fresh entity from spawn()
<@LordHavoc> cce: this doesn't mean the type matches, but does mean it's a blank entity
<@LordHavoc> so if is a blank entity, you can be sure the entity is new and needs to be
initialized
<@LordHavoc> if it is not a blank entity, you want to check if the first byte you read (the
type) differs from a field on the entity that you stored the last time you
parsed it
<@LordHavoc> and set isnew if the type differs
< cce> should i just use a field with a flag to mark an entity already initialized?
<@LordHavoc> so that you run your init code
<@LordHavoc> well I'm saying the flag exists as isnew
<@LordHavoc> it's just kind of a convention that you'll want to do:
<@LordHavoc> etype = ReadByte();
<@LordHavoc> if (etype != self.enttype) isnew = TRUE;
<@LordHavoc> self.enttype = etype;
<@LordHavoc> if (etype == ENT_PLAYER)
<@LordHavoc> {
<@LordHavoc> parse stuff
<@LordHavoc> }
<@LordHavoc> else if (etype == ENT_ROCKET)
<@LordHavoc> {
<@LordHavoc> parse stuff
<@LordHavoc> }
<@LordHavoc> and so on
<@LordHavoc> isnew only serves as a hint that spawn was called right before this, of course
if you check etype then you already get that flagged as I doubt you use an ENT_
value that is 0 :)
<@LordHavoc> so how you choose to track that is entirely up to you
<@LordHavoc> by convention you will often have entities with multiple properties you want to
conditionally network
<@LordHavoc> for this reason, SendEntity takes a flags value
<@LordHavoc> when you do self.SendFlags = self.SendFlags | PLAYER_MOVED | PLAYER_INVENTORY;
<@LordHavoc> the engine locks that away at the end of the frame into a per-client networking
database
<@LordHavoc> and your SendEntity function will be called with PLAYER_MOVED | PLAYER_INVENTORY
for each client who can see this player entity
<@LordHavoc> it may be called much much later (for instance if someone can not see you at the
time, but then they come into the room later, or encounter you somewhere else
later)
<@LordHavoc> so SendFlags only is a "something interesting changed on this entity, let people
know!" thing
<@LordHavoc> in SendEntity you can WriteByte that flags and then check for flags in it to
decide which writes to do
<@LordHavoc> so you can have some writes conditional on PLAYER_MOVED
<@LordHavoc> and some on PLAYER_INVENTORY
<@LordHavoc> the CSQC_Ent_Update code will need to read that flags byte and do the same
conditional logic on the read calls
<@LordHavoc> so that it does not misparse
<@LordHavoc> if a new player connects to the game, their database starts out with 16777215 as
flags on all entities
<@LordHavoc> so be aware that SendEntity can be called that way with a huge flags value
<@LordHavoc> so you may want to mask it before writing
<@LordHavoc> to just what makes sense
<@LordHavoc> so that it does not misparse
<@LordHavoc> if a new player connects to the game, their database starts out with 16777215 as
flags on all entities
<@LordHavoc> so be aware that SendEntity can be called that way with a huge flags value
<@LordHavoc> so you may want to mask it before writing
<@LordHavoc> to just what makes sense
<@LordHavoc> or you can check for that value and know that this entity has never been sent
before
<@LordHavoc> good luck with it :)
<@LordHavoc> I need to go to work
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment