Breaking down the networking mechanism
Moderator: InsideQC Admins
15 posts
• Page 1 of 1
Breaking down the networking mechanism
The networking aspect of NetQuake and of Quakeworld is bit difficult for me to breakdown. Any good books, web links, internet reference materials or docs to disassemble this stuff.
I get what is happening with all other parts of the Quake engine (memory, rendering, model loaders, video, input, largely understand the sound/audio), but I'm trying to get a foothold into breaking down the network code --- sockets, datagrams, packets, reliable vs. unreliable messages --- even the stuff like sign-on buffer and the handshake.
I get what is happening with all other parts of the Quake engine (memory, rendering, model loaders, video, input, largely understand the sound/audio), but I'm trying to get a foothold into breaking down the network code --- sockets, datagrams, packets, reliable vs. unreliable messages --- even the stuff like sign-on buffer and the handshake.
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
the 'netchan' is the chunk of code which ensures delivery of reliable or unreliable datagrams to the other end. qw merges unreliable and reliable datagrams into a single udp packet, while nq (even dp7) sends both separately. this is handled by the net_*.c
Socket support is handled internally by the netchan supposedly, net_win/net_wins/net_udp handle this.
net_dgram is the datagram code... as opposed to the magic single-other-person serial/modem code, which is completely different and deserves to die.
the engine itself cares only about reliable and unreliable chunks of svc/clc messages/sizebufs. the demo code is basically an alternative to the netchan.
so input goes into an unreliable sizebuf, it gets sent to the netchan and the netchan forwards it into the dgram code. the dgram code adds sequence and ident info and submits it to the socket code, the socket code sends it to the operating system.
on the server, the engine polls the netchan which polls the datagram which polls the sockets. the packet is checked to make sure its for quake, its sequence is checked to count packetloss and ignore duplicates or packets out of sequence, and its dumped into net_message which is then parsed for its clc payloads via MSG_Read*().
its the dgram code which contains all the bugs really, as its the dgram code which contains the connection process (which is evil and blocks because it is not able to poll iteratively), and the code responsible for separate sockets for each client (nq was written a bit like ftp, with relilable modem/serial links - unreliable just means drop it if you have old unreliable to send and thus it would be really really easy to use tcp instead of udp, with NAT/firewall all the issues that go with it - see ftp nat/firewall rules).
The dgram code also contains the server polling and replies. While these are not 'buggy'
Quakeworld has rewritten netchan for a very good reason.
single sockets fixes nat, and simplifies waiting for activity
combined reliables+unreliables reduces packet rates
challenges on connections means you can't flood servers from spoofed ip addresses.
Quakeworld has no explicit 'dgram' layer as everything is a datagram.
connectionless packets are mostly human-readable.
DP has an lhnet.c which basically provides an nq or qw netchan equivelent. I'm not sure on code compatibility, but it does provide network compatibility if configured that way, as well as far superior server queries.
signon buffers are created each frame and are just blocks of svcs like baselines, static sounds, static ents, initial lightstyles. map stuff that is sent to all. prebuilt and broadcast to all. each one should be kept smaller than a udp packet or reliable packet.
the connection handshake is basically that the client sends a 'gimme a connection' packet to the server. the server directly replies with 'this is the ip:port I will use for you, send your stuff there'. The server then begins to send the serverinfo svc to the source of the connection request from the socket at the address:port it gave. Note that it'll retry multiple times, and send keepalives.
backbuffers are specific to quakeworld, and are 'extra storage' for reliable messages which are longer than the max length. They are flushed in order, and this prevents breaking due to routers with broken udp fragmentation, and without large reliables stalling datagram messages on slow links. They are limited to the max reliable packet size of 1024 bytes, but you get lots at a time, so the total reliable data can be much larger, just with no large individual svcs.
any of that help?
Socket support is handled internally by the netchan supposedly, net_win/net_wins/net_udp handle this.
net_dgram is the datagram code... as opposed to the magic single-other-person serial/modem code, which is completely different and deserves to die.
the engine itself cares only about reliable and unreliable chunks of svc/clc messages/sizebufs. the demo code is basically an alternative to the netchan.
so input goes into an unreliable sizebuf, it gets sent to the netchan and the netchan forwards it into the dgram code. the dgram code adds sequence and ident info and submits it to the socket code, the socket code sends it to the operating system.
on the server, the engine polls the netchan which polls the datagram which polls the sockets. the packet is checked to make sure its for quake, its sequence is checked to count packetloss and ignore duplicates or packets out of sequence, and its dumped into net_message which is then parsed for its clc payloads via MSG_Read*().
its the dgram code which contains all the bugs really, as its the dgram code which contains the connection process (which is evil and blocks because it is not able to poll iteratively), and the code responsible for separate sockets for each client (nq was written a bit like ftp, with relilable modem/serial links - unreliable just means drop it if you have old unreliable to send and thus it would be really really easy to use tcp instead of udp, with NAT/firewall all the issues that go with it - see ftp nat/firewall rules).
The dgram code also contains the server polling and replies. While these are not 'buggy'
Quakeworld has rewritten netchan for a very good reason.
single sockets fixes nat, and simplifies waiting for activity
combined reliables+unreliables reduces packet rates
challenges on connections means you can't flood servers from spoofed ip addresses.
Quakeworld has no explicit 'dgram' layer as everything is a datagram.
connectionless packets are mostly human-readable.
DP has an lhnet.c which basically provides an nq or qw netchan equivelent. I'm not sure on code compatibility, but it does provide network compatibility if configured that way, as well as far superior server queries.
signon buffers are created each frame and are just blocks of svcs like baselines, static sounds, static ents, initial lightstyles. map stuff that is sent to all. prebuilt and broadcast to all. each one should be kept smaller than a udp packet or reliable packet.
the connection handshake is basically that the client sends a 'gimme a connection' packet to the server. the server directly replies with 'this is the ip:port I will use for you, send your stuff there'. The server then begins to send the serverinfo svc to the source of the connection request from the socket at the address:port it gave. Note that it'll retry multiple times, and send keepalives.
backbuffers are specific to quakeworld, and are 'extra storage' for reliable messages which are longer than the max length. They are flushed in order, and this prevents breaking due to routers with broken udp fragmentation, and without large reliables stalling datagram messages on slow links. They are limited to the max reliable packet size of 1024 bytes, but you get lots at a time, so the total reliable data can be much larger, just with no large individual svcs.
any of that help?
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
Yeah, it is a good start and probably enough information for me to start dissecting. In the past, I've tried to implement lhnet but failed ... but I have more experience now so maybe I'll get greater traction.
And I guess much of the rest is taking lhnet.c and Quakeworld's "net_*.c" files and looking through them.
And I guess much of the rest is taking lhnet.c and Quakeworld's "net_*.c" files and looking through them.
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Well, some of this is starting to sink in.
Of all the weird things ... 8 days ago ... I was looking through a pile of books I bought a few years back and was looking to sort of ones to trash because they were obsolete ...
This one book I was going to throw out opened up to a page on Winsock programming. Obviously I didn't throw it out and immediately decided against throwing any of them out.
Now .. WTF?
Are there even odds for that happening?
Of all the weird things ... 8 days ago ... I was looking through a pile of books I bought a few years back and was looking to sort of ones to trash because they were obsolete ...
This one book I was going to throw out opened up to a page on Winsock programming. Obviously I didn't throw it out and immediately decided against throwing any of them out.
Now .. WTF?
Are there even odds for that happening?
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
- Code: Select all
void PrintDNS (char *host_addr)
{
struct hostent *remoteHost;
struct in_addr addr = { 0 };
Con_Printf("Calling gethostbyaddr with %s\n", host_addr);
addr.s_addr = inet_addr(host_addr);
if (addr.s_addr == INADDR_NONE) {
Con_Printf("The IPv4 address entered must be a legal address\n");
return;
}
remoteHost = pgethostbyaddr ((char *) &addr, sizeof(struct in_addr), AF_INET);
if (remoteHost)
{
//Q_strncpy (name, (char *)remoteHost->h_name, NET_NAMELEN - 1);
Con_Printf ("DNS for ip address is %s\n", (char *)remoteHost->h_name);
}
else
{
Con_Printf ("Lookup failed ...\n");
}
}
Fun!
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Not 100% related to exactly this thread per se ...
In the past, reading more recent DarkPlaces code [last 6 years versions +/-] was a bit challenging [but rewarding].
More recently I've found DarkPlaces code to far more easy to read and understand. I guess there is a certain amount of general "around the engine knowledge" you have to absorb and after you start to get some depth in understanding those areas, you pretty much know what is going on, you just want to see the fine details.
I think it is very fortunate to have so many engine topics with people such as Spike and MH and others digging down into the technical guts of the engine and some of it sinks in later even if it takes a couple of years to understand some of these conversations.
In the past, reading more recent DarkPlaces code [last 6 years versions +/-] was a bit challenging [but rewarding].
More recently I've found DarkPlaces code to far more easy to read and understand. I guess there is a certain amount of general "around the engine knowledge" you have to absorb and after you start to get some depth in understanding those areas, you pretty much know what is going on, you just want to see the fine details.
I think it is very fortunate to have so many engine topics with people such as Spike and MH and others digging down into the technical guts of the engine and some of it sinks in later even if it takes a couple of years to understand some of these conversations.
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
learn the engine code using spacial memory instead of just remembering individual lines. Its more important to know what links to what rather than what each individual line does.
Knowing the important points of interest, and how they interact is far more important than knowing and understanding each line. Its not something that can just be told to you on a forum, at least not reliably in a realisitic timescale.
Knowing the important points of interest, and how they interact is far more important than knowing and understanding each line. Its not something that can just be told to you on a forum, at least not reliably in a realisitic timescale.
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
Spike wrote:learn the engine code using spacial memory instead of just remembering individual lines. Its more important to know what links to what rather than what each individual line does.
Knowing the important points of interest, and how they interact is far more important than knowing and understanding each line. Its not something that can just be told to you on a forum, at least not reliably in a realisitic timescale.
I'll second this. The structure of the code is such that it's perfectly possible to make one tiny seemingly innocuous change that can have repercussions all over the place, which can be a mildly unpleasant surprise.
Also learning how the code interacts with the underlying hardware, drivers and OS subsystems is critical to getting a good result. No application lives in isolation, and these are all examples of code that you didn't write, that you probably have no control over (and would be unwise to change even if you did) and that can have a major impact on your program.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
We knew the words, we knew the score, we knew what we were fighting for
-

mh - Posts: 2292
- Joined: Sat Jan 12, 2008 1:38 am
mh wrote:I'll second this. The structure of the code is such that it's perfectly possible to make one tiny seemingly innocuous change that can have repercussions all over the place, which can be a mildly unpleasant surprise.
The network code starts to become much easier after you start getting into it. It really becomes satisfying to finally get a grasp of what is going on there. I guess a lot of patience pays off. 2 years ago I tried to graft lhnet into GLQuake and failed and I really was quite clueless as to what exactly was going on at that time.
Ironically, if I could pick one thing that in the whole engine that is rough to pick up on, it is the progs intepreter stuffs. That piece of the engine in some ways stands out as being a bit more sophisticated than the rest. I almost want to look back at the changes in Spike's CSQC prototype, to try to get a clearer picture into at least some of it.
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
a large part of the changes for csqc were purely in getting a VM which can cope with multiple instances of the VM simultaneously. Another large part of the changes is to get the VM able to cope with separate global/field defs (ie: different crcs) between instances.
And changing builtins and logic to cope with the differences.
That's the theory anyway.
In reality, it would have been much easier to implement if the added globals/fields were just glued onto the end of the existing ones, then most of the code could have been shared with barely any modifications, just a function call whenever context switching between server and client VMs. Would be able to easily reuse half the builtins instead of rewriting them so some could be reused safely. Still, it helps to know 'this is server' 'this is client' 'this is whatever you want' 'this is menu'.
I'm still wondering if the RMQ project will ever support csqc, and if so, to what level of compatibility with existing implementations.
And changing builtins and logic to cope with the differences.
That's the theory anyway.
In reality, it would have been much easier to implement if the added globals/fields were just glued onto the end of the existing ones, then most of the code could have been shared with barely any modifications, just a function call whenever context switching between server and client VMs. Would be able to easily reuse half the builtins instead of rewriting them so some could be reused safely. Still, it helps to know 'this is server' 'this is client' 'this is whatever you want' 'this is menu'.
I'm still wondering if the RMQ project will ever support csqc, and if so, to what level of compatibility with existing implementations.
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
If I had more time, I'd think about coding the Quake HUD in CSQC. The sbar.c is mighty ugly.
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Spike wrote:I'm still wondering if the RMQ project will ever support csqc, and if so, to what level of compatibility with existing implementations.
As soon as I can get a globalvars struct, an entvars struct, a list of #defines and a progheader CRC for CSQC I'll let you know.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
We knew the words, we knew the score, we knew what we were fighting for
-

mh - Posts: 2292
- Joined: Sat Jan 12, 2008 1:38 am
mh wrote:As soon as I can get a globalvars struct, an entvars struct, a list of #defines and a progheader CRC for CSQC I'll let you know. :evil: :evil: :evil:
PMed you the contents of progdefs.h that FTE and DP both individually expect (fteqcc still writes it out if you use -progdefs argument).
Really going off topic. :s
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
Back to an earlier post, thanks Spike for the comprehensive descriptive overview of, in particular, the Quakeworld protocol and the DP comments. It is helping me understand what I seeing in the code more quickly.
In the past, I mostly was for the most part only able to dissect others engine work to explain how it worked or summarize the changes but I'm now on the verge of being able to make my own contributions and --- well --- also being able to dissect and explain more advanced modifications.
In the past, I mostly was for the most part only able to dissect others engine work to explain how it worked or summarize the changes but I'm now on the verge of being able to make my own contributions and --- well --- also being able to dissect and explain more advanced modifications.
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Burying this here. I had what I consider a cleaner and more concise run-through, but can no longer find the link:
This is the beginning point where a connection is being established.
This is the beginning point where a connection is being established.
8.6.2 Establish a game session
Those messages are sent to and from the PUBLIC UDP PORT of the server (which is 26000 by default).
Client sends a Connect message,
with game name QUAKE.
Server sends an Accept message,
containing the identifier of a PERSONAL UDP PORT for the client.
Client now expects server message coming from the PERSONAL UDP PORT
Warning: after it replied with an Accept message, the game server will only use a PERSONAL UDP PORT to communicate with a given client. The server PUBLIC UDP PORT is not used anymore to talk to that client. It can only be used by clients that are not connected yet.
8.6.3 Prepare the client for the game (precache)
Server sends a big message that contains:
- the server banner "VERSION 1.01 SERVER (21456 CRC)"
- a server info message (code 0x0B) with map name, and precached models and sounds
- an indication that client should move to PRESPAWN state.
Client sends a No Operation (code 0x01)
Client sends a console command (code 0x04) "prespawn", indicating prespawn is finished.
Server sends a big message that contains order to spawn static sounds and static entities
- For all the static entities described in the .BSP file,
One "Spawn Static entity" order, and possibly a Static Sound order.
Client sends a player information message:
- console order "name PLAYER\n" (where PLAYER is the player name)
- console order "color 0 0\n" (shirt and pants colors)
- console order "spawn " to indicate the client is ready to play.
Server sends a big message:
- For each possible player in the game (including those not connected):
a set of orders UPDATE NAME, UPDATE FRAGS, UPDATE COLORS
- For each of the 64 light style:
a SET LIGHT STYLE message.
- an UPDATE STATE message for total and found monsters and secrets
- a SET ANGLE order, to orient the player's view
- a CLIENT DATA order, fix the status bar display
- an order to move to Start 3d Rendering state.
- a last update NAMe, FRAGS, and COLORS, for the client.
Client sends a console message "begin".
8.6.4 Run the game with the client
Every 50 milliseconds,
Server sends an update message, that contains:
- an indication of the game time
- a CLIENT DATA order, to fix the status bar display (mostly useless)
- an UPDATE ENTITY order for each entity possibly in sight
if the entity has not changed, only minimal informations will be sent.
Each time the player moves,
Client sends an update message containing:
- a movement order.
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
15 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest