Created
July 12, 2012 16:51
-
-
Save pekkavaa/3099276 to your computer and use it in GitHub Desktop.
Sending an entity from SSQC to CSQC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
< 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