I don't understand the whole server/client thing.

Discuss programming in the QuakeC language.
Post Reply
Nash
Posts: 95
Joined: Fri Oct 19, 2007 5:56 pm
Location: Kuala Lumpur, Malaysia
Contact:

I don't understand the whole server/client thing.

Post by Nash »

To be honest, I really don't get the concept at all. A lot of stuff I'm doing is basically "because everyone else is doing it that way". I don't fully understand the things I code in my QC.

For example, all that WRITEBYTE stuff. What's that all about... ? Would the game not work if they weren't there?
daemon
Posts: 81
Joined: Wed Nov 07, 2007 11:10 pm
Contact:

Post by daemon »

I don't exactly know what the WHOLE purpose of WRITEBYTE is, but I know it's one way of sending information to just one client. For example, a you can make a sound play for just one player, like a menu sound or something. There are lots of other purposes for it though, i just don't know what they are.

The main point for me to use CSQC is to be able to draw text onto the screen w/o having to send strings over the network every second or so. But there are lots of other goodies like being able to draw images to the screen w/o doing something hacky like attaching them to the player's view and disabling gunbob.

I haven't seen it done yet, but you could probably also make player movement clientside, and then instead of you seeing yourself lag before you move, only the other players would see you lagging, but no one would really be aware of it unless the lag got really bad and you started taking damage while it looks to you like you're not getting hit.
-daemon [ daemonforge.org ]
Sajt
Posts: 1215
Joined: Sat Oct 16, 2004 3:39 am

Post by Sajt »

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.
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
daemon
Posts: 81
Joined: Wed Nov 07, 2007 11:10 pm
Contact:

Post by daemon »

Sajt wrote: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.
So when you use sprint() or centerprint() it automatically uses MSG_ONE? I assume so, just curious.
-daemon [ daemonforge.org ]
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

daemon wrote:
Sajt wrote: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.
So when you use sprint() or centerprint() it automatically uses MSG_ONE? I assume so, just curious.
Nope, that's a simplification.
sprint and centerprint are _like_ MSG_ONE (sent to one client and reliable) but in reality the code in those builtins goes through directly without ever thinking about MSG_ONE. although as far as qc is concerned, there is no difference.

packet overflow happens after 1000 bytes in unmodified quake. 1400 in quakeworld (but quakeworld limits the number of ents anyway, so 1400 isn't really reachable).

To reiterate what sajt said:

In basic unmodified quake, the server is the entire game with the clients as nothing more than input gatherers and renderers. but they do need other information, like what to draw where, hence centerprints, sprints, and all the WriteByte calls.
To simplify the engine design, quake is both a client and a server, even when playing single player. Demos are simply a recording of the data sent from the server to the client.

MSG_BROADCAST is seen by all, and its unreliable (meaning its meant to be seen by all players, but it might never actually arrive, effectivly getting ignored). MSG_ONE is seen only by the player named in msg_entity. MSG_ALL is like MSG_BROADCAST, except that you know they'll actually get it.

Be careful when using WriteByte. Try and get the exact sequence correct, and avoid spamming too much in any one frame. Old servers (and a decent handful of new servers too, certainly in quakeworld) will barf if your writebyte data overflows a buffer, which will be seen on the client as illegible server messages. If you get one of these messages, then it was 90% chance of something to do with writebytes. Darkplaces provides many builtins which are not only slightly easier on the eye, but also ensure that the buffers are correctly flushed and will not overflow awkwardly.

CSQC complicates matters by putting seperate gamecode on the client, which can be interpreted however you want. with csqc, the state-based form of ssqc is highly weakened. It can still be written as state based, but the csqc is able to impose additional state that would not otherwise be there, but it requires additional mapping on the server, which can be hidden away in a seperate qc file somewhere. CSQC gives additional control, particuarly over the 2d parts of the screen, but it also allows prediction and things too.

daemon: regarding player movement in csqc, I did get this working at least with fte and qw protocols/physics. FTE at least is capable of doing it as perfectly as normal qw prediction, although I do admit that the qc code required to do so is larger than is really desirable, due to my own design decisions regarding how ssqc/csqc interact - read: requires ssqc changes too. But this is a seperate topic which I really ought to elaborate on some other time in a different topic.
Seven
Posts: 301
Joined: Sat Oct 06, 2007 8:49 pm
Location: Germany

Re: I don't understand the whole server/client thing.

Post by Seven »

Hello,

First of all I am sorry for bumping this old thread, but I have some questions.

Sajt and Spike did an excellent job in explaining the syntax behind "WriteByte", "MSG_BROADCAST" and others.
Thank you very much.

Where I got lost is the more seldom "WriteByte" codes in QuakeC 1.08/1.09:
Example:

We all know now what this means:

Code: Select all

   WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
	WriteByte (MSG_BROADCAST, TE_EXPLOSION);
	WriteCoord (MSG_BROADCAST, self.origin_x);
	WriteCoord (MSG_BROADCAST, self.origin_y);
	WriteCoord (MSG_BROADCAST, self.origin_z);
But what will this do (taken from Rogue´s "dragon.qc") ?

Code: Select all

	
   WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
	WriteByte (MSG_BROADCAST, TE_EXPLOSION2);
	WriteCoord (MSG_BROADCAST, self.origin_x);
	WriteCoord (MSG_BROADCAST, self.origin_y);
	WriteCoord (MSG_BROADCAST, self.origin_z);
     WriteByte (MSG_BROADCAST, 228);  // 247
     WriteByte (MSG_BROADCAST, 5);
There are 3 new things in the code:
- TE_EXPLOSION2
- WriteByte (MSG_BROADCAST, 228); // 247
- WriteByte (MSG_BROADCAST, 5);

I guess the TE_EXPLOSION2 is just an alternative to TE_EXPLOSION.
But I have absolutley no clue what "228" or "247" or "5" will do.

Could someone please tell me if there is a complete value list for all "WriteByte" values ?
And what these 3 above mentioned things will do ?
Is it a specific color for the particle effect "TE_EXPLOSION2" ?

Thank you very much for your answer.
qbism
Posts: 1236
Joined: Thu Nov 04, 2004 5:51 am
Contact:

Re: I don't understand the whole server/client thing.

Post by qbism »

From engine source code.

Code: Select all

	case TE_EXPLOSION2:				// color mapped explosion
		pos[0] = MSG_ReadCoord ();
		pos[1] = MSG_ReadCoord ();
		pos[2] = MSG_ReadCoord ();
		colorStart = MSG_ReadByte ();
		colorLength = MSG_ReadByte ();
It's creating a temporary entity. The type is a TE_EXPLOSION2. This is going to expect three coordinates (location), a color start palette index (a byte) and how many colors to use from that point.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: I don't understand the whole server/client thing.

Post by Cobalt »

Hi Seven:

You are right, this is a very good thread explaining those message types, thanks for bumping it up again. Ya, that looks same as this in the dp extensions:

void(vector org, float color, float colorlength) te_explosion2 = #427;
Post Reply