You're lucky you're using Quake, you pretty much don't have to worry about server/client stuff! Quake is one of the easiest modding platforms because its networking so simple. (UnrealScript, on the other hand...)
All the QC runs on the server. Every 'tick' (see sys_ticrate cvar) the server sends the states of relevant entities to each client (relevant meaning the entities that are in view). The state includes modelindex, frame, angles, origin, effects, etc. The clients are basically dumb and simply render these entities. Very simple!
The WRITEBYTE stuff is for one-off events, stuff that doesn't work in the 'entity' state system.
Code: Select all
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_EXPLOSION);
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
Here you are setting up to send a sequence of data to all the clients in an unreliable manner (since you're using MSG_BROADCAST). You're sending the 'tempentity' message, so for example this sends an 'explosion event' to all the clients. The client parses the message and makes a particle spray and plays r_exp3.wav.
The first argument to Write* functions is the message type. This has to be the same for all Write* calls in a sequence. The second argument to Write* functions is the data.
Message types:
- MSG_ALL sends to all clients in a reliable manner. Used for such things as chat messages.
- MSG_BROADCAST sends to all clients in an unreliable manner, meaning that the packet could get lost. This is more lightweight than reliable messages and is used for less important messages like sounds and tempentity effects.
- MSG_ONE sends to one client (as defined by the QC global msg_entity) in a reliable manner. This is used, for example, by sprint (SVC_PRINT) and centerprint (SVC_CENTERPRINT), which only print to a single client.
When the client reads data from a packet, it first reads a byte and sees which SVC_* constant it lines up with (see
QuakeWiki for a list of all SVC_* constants). Then it parses more data, depending on which SVC_* was used.
For example, an SVC_SETVIEW byte is expected to be followed by an entity (WriteEntity). An SVC_TEMPENTITY is expected to be followed by a TE_* byte. An SVC_TEMPENTITY, followed by a TE_EXPLOSION, is expected to be followed by three coords (WriteCoord). And so on.
Note that on old engines, if you use up an entire packet (about 1400 bytes), either by having too many entities in view of a client, or by sending him too many WriteByte messages, you'll get 'packet overflow' and everything off the end of the packet will be ignored. You can notice this on old engines if you have too many gibs: you won't hear any more sounds, because the SVC_SOUND messages are after all the entities in the packet, and since there are too many entities the SVC_SOUNDs get lost.
Of course, newer engines like DarkPlaces are more intelligent: they give entities priority, and if an entity doesn't fit in a packet, it will give it extra priority to be sent next tick. So with a lot of entities, you get a bit of delay but they all still get sent. The priority system makes it more or less cycle through entities so they each get sent every couple of ticks, rather than only the first couple of entities on the list getting sent every tick.
Man I'm bad at explaining.