]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - netconn.c
5dcc8de14a589a2e12945e1aaab9b16b783fab98
[xonotic/darkplaces.git] / netconn.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 */
22
23 #include "quakedef.h"
24 #include "lhnet.h"
25
26 #define MASTER_PORT 27950
27
28 cvar_t sv_public = {0, "sv_public", "0"};
29 static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "180"};
30
31 static cvar_t sv_masters [] =
32 {
33         {CVAR_SAVE, "sv_master1", ""},
34         {CVAR_SAVE, "sv_master2", ""},
35         {CVAR_SAVE, "sv_master3", ""},
36         {CVAR_SAVE, "sv_master4", ""},
37         {0, "sv_masterextra1", "198.88.152.4"},
38         {0, "sv_masterextra2", "68.102.242.12"},
39         {0, NULL, NULL}
40 };
41
42 static double nextheartbeattime = 0;
43
44 sizebuf_t net_message;
45
46 cvar_t net_messagetimeout = {0, "net_messagetimeout","300"};
47 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED"};
48 cvar_t developer_networking = {0, "developer_networking", "0"};
49
50 /* statistic counters */
51 static int packetsSent = 0;
52 static int packetsReSent = 0;
53 static int packetsReceived = 0;
54 static int receivedDuplicateCount = 0;
55 static int droppedDatagrams = 0;
56
57 static int unreliableMessagesSent = 0;
58 static int unreliableMessagesReceived = 0;
59 static int reliableMessagesSent = 0;
60 static int reliableMessagesReceived = 0;
61
62 int hostCacheCount = 0;
63 hostcache_t hostcache[HOSTCACHESIZE];
64
65 static qbyte sendbuffer[NET_MAXMESSAGE];
66 static qbyte readbuffer[NET_MAXMESSAGE];
67
68 int cl_numsockets;
69 lhnetsocket_t *cl_sockets[16];
70 int sv_numsockets;
71 lhnetsocket_t *sv_sockets[16];
72
73 netconn_t *netconn_list = NULL;
74 mempool_t *netconn_mempool = NULL;
75
76 cvar_t cl_netport = {0, "cl_port", "0"};
77 cvar_t cl_netaddress = {0, "cl_netaddress", "0.0.0.0"};
78 cvar_t cl_netaddress_ipv6 = {0, "cl_netaddress_ipv6", "[0:0:0:0:0:0:0:0]:0"};
79
80 cvar_t sv_netport = {0, "port", "26000"};
81 cvar_t sv_netaddress = {0, "sv_netaddress", "0.0.0.0"};
82 cvar_t sv_netaddress_ipv6 = {0, "sv_netaddress_ipv6", "[0:0:0:0:0:0:0:0]:26000"};
83
84 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
85 {
86         int length = LHNET_Read(mysocket, data, maxlength, peeraddress);
87         if (developer_networking.integer && length != 0)
88         {
89                 char addressstring[128], addressstring2[128];
90                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
91                 if (length > 0)
92                 {
93                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
94                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", mysocket, addressstring, data, maxlength, peeraddress, length, addressstring2);
95                         Com_HexDumpToConsole(data, length);
96                 }
97                 else
98                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", mysocket, addressstring, data, maxlength, peeraddress, length);
99         }
100         return length;
101 }
102
103 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
104 {
105         int ret = LHNET_Write(mysocket, data, length, peeraddress);
106         if (developer_networking.integer && ret != 0)
107         {
108                 char addressstring[128], addressstring2[128];
109                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
110                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
111                 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", mysocket, addressstring, data, length, peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
112                 Com_HexDumpToConsole(data, length);
113         }
114         return ret;
115 }
116
117 int NetConn_SendReliableMessage(netconn_t *conn, sizebuf_t *data)
118 {
119         unsigned int packetLen;
120         unsigned int dataLen;
121         unsigned int eom;
122         unsigned int *header;
123
124 //#ifdef DEBUG
125         if (data->cursize == 0)
126                 Sys_Error("Datagram_SendMessage: zero length message\n");
127
128         if (data->cursize > NET_MAXMESSAGE)
129                 Sys_Error("Datagram_SendMessage: message too big %u\n", data->cursize);
130
131         if (conn->canSend == false)
132                 Sys_Error("SendMessage: called with canSend == false\n");
133 //#endif
134
135         memcpy(conn->sendMessage, data->data, data->cursize);
136         conn->sendMessageLength = data->cursize;
137
138         if (conn->sendMessageLength <= MAX_DATAGRAM)
139         {
140                 dataLen = conn->sendMessageLength;
141                 eom = NETFLAG_EOM;
142         }
143         else
144         {
145                 dataLen = MAX_DATAGRAM;
146                 eom = 0;
147         }
148
149         packetLen = NET_HEADERSIZE + dataLen;
150
151         header = (void *)sendbuffer;
152         header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
153         header[1] = BigLong(conn->sendSequence);
154         memcpy(sendbuffer + 8, conn->sendMessage, dataLen);
155
156         conn->sendSequence++;
157         conn->canSend = false;
158
159         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
160                 return -1;
161
162         conn->lastSendTime = realtime;
163         packetsSent++;
164         reliableMessagesSent++;
165         return 1;
166 }
167
168 static void NetConn_SendMessageNext(netconn_t *conn)
169 {
170         unsigned int packetLen;
171         unsigned int dataLen;
172         unsigned int eom;
173         unsigned int *header;
174
175         if (conn->sendMessageLength && !conn->canSend && conn->sendNext)
176         {
177                 if (conn->sendMessageLength <= MAX_DATAGRAM)
178                 {
179                         dataLen = conn->sendMessageLength;
180                         eom = NETFLAG_EOM;
181                 }
182                 else
183                 {
184                         dataLen = MAX_DATAGRAM;
185                         eom = 0;
186                 }
187
188                 packetLen = NET_HEADERSIZE + dataLen;
189
190                 header = (void *)sendbuffer;
191                 header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
192                 header[1] = BigLong(conn->sendSequence);
193                 memcpy(sendbuffer + 8, conn->sendMessage, dataLen);
194
195                 conn->sendSequence++;
196                 conn->sendNext = false;
197
198                 if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
199                         return;
200
201                 conn->lastSendTime = realtime;
202                 packetsSent++;
203         }
204 }
205
206 static void NetConn_ReSendMessage(netconn_t *conn)
207 {
208         unsigned int packetLen;
209         unsigned int dataLen;
210         unsigned int eom;
211         unsigned int *header;
212
213         if (conn->sendMessageLength && !conn->canSend && (realtime - conn->lastSendTime) > 1.0)
214         {
215                 if (conn->sendMessageLength <= MAX_DATAGRAM)
216                 {
217                         dataLen = conn->sendMessageLength;
218                         eom = NETFLAG_EOM;
219                 }
220                 else
221                 {
222                         dataLen = MAX_DATAGRAM;
223                         eom = 0;
224                 }
225
226                 packetLen = NET_HEADERSIZE + dataLen;
227
228                 header = (void *)sendbuffer;
229                 header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
230                 header[1] = BigLong(conn->sendSequence - 1);
231                 memcpy(sendbuffer + 8, conn->sendMessage, dataLen);
232
233                 conn->sendNext = false;
234
235                 if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
236                         return;
237
238                 conn->lastSendTime = realtime;
239                 packetsReSent++;
240         }
241 }
242
243 qboolean NetConn_CanSendMessage(netconn_t *conn)
244 {
245         return conn->canSend;
246 }
247
248 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data)
249 {
250         int packetLen;
251         int *header;
252
253 #ifdef DEBUG
254         if (data->cursize == 0)
255                 Sys_Error("Datagram_SendUnreliableMessage: zero length message\n");
256
257         if (data->cursize > MAX_DATAGRAM)
258                 Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
259 #endif
260
261         packetLen = NET_HEADERSIZE + data->cursize;
262
263         header = (void *)sendbuffer;
264         header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE);
265         header[1] = BigLong(conn->unreliableSendSequence);
266         memcpy(sendbuffer + 8, data->data, data->cursize);
267
268         conn->unreliableSendSequence++;
269
270         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
271                 return -1;
272
273         packetsSent++;
274         unreliableMessagesSent++;
275         return 1;
276 }
277
278 void NetConn_CloseClientPorts(void)
279 {
280         for (;cl_numsockets > 0;cl_numsockets--)
281                 if (cl_sockets[cl_numsockets - 1])
282                         LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
283 }
284
285 void NetConn_OpenClientPorts(void)
286 {
287         int port;
288         lhnetaddress_t address;
289         NetConn_CloseClientPorts();
290         port = bound(0, cl_netport.integer, 65535);
291         if (cl_netport.integer != port)
292                 Cvar_SetValueQuick(&cl_netport, port);
293         LHNETADDRESS_FromString(&address, "local", port);
294         cl_sockets[cl_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
295         LHNETADDRESS_FromString(&address, cl_netaddress.string, port);
296         cl_sockets[cl_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
297         LHNETADDRESS_FromString(&address, cl_netaddress_ipv6.string, port);
298         cl_sockets[cl_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
299 }
300
301 void NetConn_CloseServerPorts(void)
302 {
303         for (;sv_numsockets > 0;sv_numsockets--)
304                 if (sv_sockets[sv_numsockets - 1])
305                         LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
306 }
307
308 void NetConn_OpenServerPorts(int opennetports)
309 {
310         int port;
311         lhnetaddress_t address;
312         NetConn_CloseServerPorts();
313         port = bound(0, sv_netport.integer, 65535);
314         if (port == 0)
315                 port = 26000;
316         if (sv_netport.integer != port)
317                 Cvar_SetValueQuick(&sv_netport, port);
318         LHNETADDRESS_FromString(&address, "local", port);
319         sv_sockets[sv_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
320         if (opennetports)
321         {
322                 LHNETADDRESS_FromString(&address, sv_netaddress.string, port);
323                 sv_sockets[sv_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
324                 LHNETADDRESS_FromString(&address, sv_netaddress_ipv6.string, port);
325                 sv_sockets[sv_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
326         }
327 }
328
329 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
330 {
331         int i, a = LHNETADDRESS_GetAddressType(address);
332         for (i = 0;i < cl_numsockets;i++)
333                 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
334                         return cl_sockets[i];
335         return NULL;
336 }
337
338 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
339 {
340         int i, a = LHNETADDRESS_GetAddressType(address);
341         for (i = 0;i < sv_numsockets;i++)
342                 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
343                         return sv_sockets[i];
344         return NULL;
345 }
346
347 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
348 {
349         netconn_t *conn;
350         conn = Mem_Alloc(netconn_mempool, sizeof(*conn));
351         conn->mysocket = mysocket;
352         conn->peeraddress = *peeraddress;
353         conn->canSend = true;
354         conn->connecttime = realtime;
355         conn->lastMessageTime = realtime;
356         LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
357         conn->next = netconn_list;
358         netconn_list = conn;
359         return conn;
360 }
361
362 void NetConn_Close(netconn_t *conn)
363 {
364         netconn_t *c;
365         // remove connection from list
366         if (conn == netconn_list)
367                 netconn_list = conn->next;
368         else
369         {
370                 for (c = netconn_list;c;c = c->next)
371                 {
372                         if (c->next == conn)
373                         {
374                                 c->next = conn->next;
375                                 break;
376                         }
377                 }
378                 // not found in list, we'll avoid crashing here...
379                 if (!c)
380                         return;
381         }
382         // free connection
383         Mem_Free(conn);
384 }
385
386 static int clientport = -1;
387 static int clientport2 = -1;
388 static int hostport = -1;
389 static void NetConn_UpdateServerStuff(void)
390 {
391         if (clientport2 != cl_netport.integer)
392         {
393                 clientport2 = cl_netport.integer;
394                 if (cls.state == ca_connected)
395                         Con_Printf("Changing \"cl_port\" will not take effect until you reconnect.\n");
396         }
397         if (cls.state != ca_dedicated)
398         {
399                 if (cls.state == ca_disconnected && clientport != cl_netport.integer)
400                 {
401                         clientport = cl_netport.integer;
402                         NetConn_CloseClientPorts();
403                 }
404                 if (cl_numsockets == 0)
405                         NetConn_OpenClientPorts();
406         }
407
408         if (hostport != sv_netport.integer)
409         {
410                 hostport = sv_netport.integer;
411                 if (sv.active)
412                         Con_Printf("Changing \"port\" will not take effect until \"map\" command is executed.\n");
413         }
414 }
415
416 int NetConn_ReceivedMessage(netconn_t *conn, qbyte *data, int length)
417 {
418         unsigned int count;
419         unsigned int flags;
420         unsigned int sequence;
421
422         if (length < 8)
423                 return 0;
424
425         length = BigLong(((int *)data)[0]);
426         flags = length & ~NETFLAG_LENGTH_MASK;
427         length &= NETFLAG_LENGTH_MASK;
428         // control packets were already handled
429         if (!(flags & NETFLAG_CTL))
430         {
431                 sequence = BigLong(((int *)data)[1]);
432                 packetsReceived++;
433                 data += 8;
434                 length -= 8;
435                 if (flags & NETFLAG_UNRELIABLE)
436                 {
437                         if (sequence >= conn->unreliableReceiveSequence)
438                         {
439                                 if (sequence > conn->unreliableReceiveSequence)
440                                 {
441                                         count = sequence - conn->unreliableReceiveSequence;
442                                         droppedDatagrams += count;
443                                         Con_DPrintf("Dropped %u datagram(s)\n", count);
444                                 }
445                                 conn->unreliableReceiveSequence = sequence + 1;
446                                 conn->lastMessageTime = realtime;
447                                 unreliableMessagesReceived++;
448                                 SZ_Clear(&net_message);
449                                 SZ_Write(&net_message, data, length);
450                                 MSG_BeginReading();
451                                 return 2;
452                         }
453                         else
454                                 Con_DPrintf("Got a stale datagram\n");
455                         return 1;
456                 }
457                 else if (flags & NETFLAG_ACK)
458                 {
459                         if (sequence == (conn->sendSequence - 1))
460                         {
461                                 if (sequence == conn->ackSequence)
462                                 {
463                                         conn->ackSequence++;
464                                         if (conn->ackSequence != conn->sendSequence)
465                                                 Con_DPrintf("ack sequencing error\n");
466                                         conn->lastMessageTime = realtime;
467                                         conn->sendMessageLength -= MAX_DATAGRAM;
468                                         if (conn->sendMessageLength > 0)
469                                         {
470                                                 memcpy(conn->sendMessage, conn->sendMessage+MAX_DATAGRAM, conn->sendMessageLength);
471                                                 conn->sendNext = true;
472                                                 NetConn_SendMessageNext(conn);
473                                         }
474                                         else
475                                         {
476                                                 conn->sendMessageLength = 0;
477                                                 conn->canSend = true;
478                                         }
479                                 }
480                                 else
481                                         Con_DPrintf("Duplicate ACK received\n");
482                         }
483                         else
484                                 Con_DPrintf("Stale ACK received\n");
485                         return 1;
486                 }
487                 else if (flags & NETFLAG_DATA)
488                 {
489                         unsigned int temppacket[2];
490                         temppacket[0] = BigLong(8 | NETFLAG_ACK);
491                         temppacket[1] = BigLong(sequence);
492                         NetConn_Write(conn->mysocket, (qbyte *)temppacket, 8, &conn->peeraddress);
493                         if (sequence == conn->receiveSequence)
494                         {
495                                 conn->lastMessageTime = realtime;
496                                 conn->receiveSequence++;
497                                 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
498                                 conn->receiveMessageLength += length;
499                                 if (flags & NETFLAG_EOM)
500                                 {
501                                         reliableMessagesReceived++;
502                                         SZ_Clear(&net_message);
503                                         SZ_Write(&net_message, conn->receiveMessage, conn->receiveMessageLength);
504                                         conn->receiveMessageLength = 0;
505                                         MSG_BeginReading();
506                                         return 2;
507                                 }
508                         }
509                         else
510                                 receivedDuplicateCount++;
511                         return 1;
512                 }
513         }
514         return 0;
515 }
516
517 static struct
518 {
519         double senttime;
520         lhnetaddress_t peeraddress;
521 }
522 pingcache[HOSTCACHESIZE];
523
524 int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress)
525 {
526         int ret, c, control;
527         lhnetaddress_t svaddress;
528         const char *s;
529         char *string, addressstring2[128], cname[128], ipstring[32];
530
531         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
532         {
533                 data += 4;
534                 length -= 4;
535                 string = (char *)data;
536                 if (developer.integer)
537                 {
538                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
539                         Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
540                         Com_HexDumpToConsole(data, length);
541                 }
542                 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
543                 {
544                         int i, j, c, n, users, maxusers;
545                         char game[32], mod[32], map[32], name[128];
546                         double pingtime;
547                         hostcache_t temp;
548                         string += 13;
549                         // hostcache only uses text addresses
550                         LHNETADDRESS_ToString(peeraddress, cname, sizeof(cname), true);
551                         // search the cache for this server
552                         for (n = 0; n < hostCacheCount; n++)
553                                 if (!strcmp(cname, hostcache[n].cname))
554                                         break;
555                         // add it or update it
556                         if (n == hostCacheCount)
557                         {
558                                 // if cache is full replace highest ping server (the list is
559                                 // kept sorted so this is always the last, and if this server
560                                 // is good it will be sorted into an early part of the list)
561                                 if (hostCacheCount >= HOSTCACHESIZE)
562                                         n = hostCacheCount - 1;
563                                 else
564                                         hostCacheCount++;
565                         }
566                         if ((s = SearchInfostring(string, "gamename"     )) != NULL) strncpy(game, s, sizeof(game) - 1);else game[0] = 0;
567                         if ((s = SearchInfostring(string, "modname"      )) != NULL) strncpy(mod , s, sizeof(mod ) - 1);else mod[0] = 0;
568                         if ((s = SearchInfostring(string, "mapname"      )) != NULL) strncpy(map , s, sizeof(map ) - 1);else map[0] = 0;
569                         if ((s = SearchInfostring(string, "hostname"     )) != NULL) strncpy(name, s, sizeof(name) - 1);else name[0] = 0;
570                         if ((s = SearchInfostring(string, "protocol"     )) != NULL) c = atoi(s);else c = -1;
571                         if ((s = SearchInfostring(string, "clients"      )) != NULL) users = atoi(s);else users = 0;
572                         if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) maxusers = atoi(s);else maxusers = 0;
573                         pingtime = 9999;
574                         for (i = 0;i < HOSTCACHESIZE;i++)
575                                 if (!LHNETADDRESS_Compare(peeraddress, &pingcache[i].peeraddress))
576                                         pingtime = (int)(realtime - pingcache[i].senttime);
577                         pingtime = bound(0, pingtime, 9999);
578                         memset(&hostcache[n], 0, sizeof(hostcache[n]));
579                         // store the data the engine cares about (address and ping)
580                         strcpy(hostcache[n].cname, cname);
581                         hostcache[n].ping = pingtime;
582                         // build description strings for the things users care about
583                         snprintf(hostcache[n].line1, sizeof(hostcache[n].line1), "%5d%c%3u/%3u %-65.65s", (int)pingtime, c != NET_PROTOCOL_VERSION ? '*' : ' ', users, maxusers, name);
584                         snprintf(hostcache[n].line2, sizeof(hostcache[n].line2), "%-21.21s %-19.19s %-17.17s %-20.20s", cname, game, mod, map);
585                         // if ping is especially high, display it as such
586                         if (pingtime >= 300)
587                         {
588                                 // orange numbers (lower block)
589                                 for (i = 0;i < 5;i++)
590                                         if (hostcache[n].line1[i] != ' ')
591                                                 hostcache[n].line1[i] += 128;
592                         }
593                         else if (pingtime >= 200)
594                         {
595                                 // yellow numbers (in upper block)
596                                 for (i = 0;i < 5;i++)
597                                         if (hostcache[n].line1[i] != ' ')
598                                                 hostcache[n].line1[i] -= 30;
599                         }
600                         // if not in the slist menu we should print the server to console
601                         if (m_state != m_slist)
602                                 Con_Printf("%s\n%s\n", hostcache[n].line1, hostcache[n].line2);
603                         // and finally, re-sort the list
604                         for (i = 0;i < hostCacheCount;i++)
605                         {
606                                 for (j = i + 1;j < hostCacheCount;j++)
607                                 {
608                                         //if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
609                                         if (hostcache[i].ping > hostcache[j].ping)
610                                         {
611                                                 memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
612                                                 memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
613                                                 memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
614                                         }
615                                 }
616                         }
617                         return true;
618                 }
619                 if (!strncmp(string, "getserversResponse\\", 19) && hostCacheCount < HOSTCACHESIZE)
620                 {
621                         int i, best;
622                         double besttime;
623                         // Extract the IP addresses
624                         data += 18;
625                         length -= 18;
626                         while (length >= 7 && data[0] == '\\' && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF) && data[5] * 256 + data[6] != 0)
627                         {
628                                 sprintf(ipstring, "%u.%u.%u.%u:%u", data[1], data[2], data[3], data[4], (data[5] << 8) | data[6]);
629                                 if (developer.integer)
630                                         Con_Printf("Requesting info from server %s\n", ipstring);
631                                 LHNETADDRESS_FromString(&svaddress, ipstring, 0);
632                                 NetConn_Write(mysocket, "\377\377\377\377getinfo", 11, &svaddress);
633                                 // replace oldest or matching entry in ping cache
634                                 // we scan this later when getting a reply to see how long it took
635                                 besttime = realtime;
636                                 best = 0;
637                                 for (i = 0;i < HOSTCACHESIZE;i++)
638                                 {
639                                         if (!LHNETADDRESS_Compare(&svaddress, &pingcache[i].peeraddress))
640                                         {
641                                                 best = i;
642                                                 break;
643                                         }
644                                         if (besttime > pingcache[i].senttime)
645                                         {
646                                                 besttime = pingcache[i].senttime;
647                                                 best = i;
648                                                 // if ping cache isn't full yet we can skip out early
649                                                 if (!besttime)
650                                                         break;
651                                         }
652                                 }
653                                 pingcache[best].peeraddress = svaddress;
654                                 pingcache[best].senttime = realtime;
655                                 // move on to next address in packet
656                                 data += 7;
657                                 length -= 7;
658                         }
659                         return true;
660                 }
661                 /*
662                 if (!strncmp(string, "ping", 4))
663                 {
664                         if (developer.integer)
665                                 Con_Printf("Received ping from %s, sending ack\n", UDP_AddrToString(readaddr));
666                         NetConn_Write(mysocket, "\377\377\377\377ack", 7, peeraddress);
667                         return true;
668                 }
669                 if (!strncmp(string, "ack", 3))
670                         return true;
671                 */
672                 // we may not have liked the packet, but it was a command packet, so
673                 // we're done processing this packet now
674                 return true;
675         }
676         if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
677         {
678                 c = data[4];
679                 data += 5;
680                 length -= 5;
681                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
682                 switch (c)
683                 {
684                 case CCREP_ACCEPT:
685                         if (developer.integer)
686                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
687                         if (cls.connect_trying)
688                         {
689                                 Con_Printf("Connection accepted to %s\n", addressstring2);
690                                 key_dest = key_game;
691                                 m_state = m_none;
692                                 cls.netcon = NetConn_Open(mysocket, peeraddress);
693                                 cls.connect_trying = false;
694                                 cls.demonum = -1;                       // not in the demo loop now
695                                 cls.state = ca_connected;
696                                 cls.signon = 0;                         // need all the signon messages before playing
697                                 CL_ClearState();
698                                 Host_Reconnect_f();
699                         }
700                         break;
701                 case CCREP_REJECT:
702                         if (developer.integer)
703                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
704                         Con_Printf("%s\n", data);
705                         strncpy(m_return_reason, data, sizeof(m_return_reason) - 1);
706                         break;
707 #if 0
708                 case CCREP_SERVER_INFO:
709                         if (developer.integer)
710                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
711                         if (cls.state != ca_dedicated)
712                         {
713                                 // LordHavoc: because the UDP driver reports 0.0.0.0:26000 as the address
714                                 // string we just ignore it and keep the real address
715                                 MSG_ReadString();
716                                 // hostcache only uses text addresses
717                                 cname = UDP_AddrToString(readaddr);
718                                 // search the cache for this server
719                                 for (n = 0; n < hostCacheCount; n++)
720                                         if (!strcmp(cname, hostcache[n].cname))
721                                                 break;
722                                 // add it
723                                 if (n == hostCacheCount && hostCacheCount < HOSTCACHESIZE)
724                                 {
725                                         hostCacheCount++;
726                                         memset(&hostcache[n], 0, sizeof(hostcache[n]));
727                                         strcpy(hostcache[n].name, MSG_ReadString());
728                                         strcpy(hostcache[n].map, MSG_ReadString());
729                                         hostcache[n].users = MSG_ReadByte();
730                                         hostcache[n].maxusers = MSG_ReadByte();
731                                         c = MSG_ReadByte();
732                                         if (c != NET_PROTOCOL_VERSION)
733                                         {
734                                                 strncpy(hostcache[n].cname, hostcache[n].name, sizeof(hostcache[n].cname) - 1);
735                                                 hostcache[n].cname[sizeof(hostcache[n].cname) - 1] = 0;
736                                                 strcpy(hostcache[n].name, "*");
737                                                 strncat(hostcache[n].name, hostcache[n].cname, sizeof(hostcache[n].name) - 1);
738                                                 hostcache[n].name[sizeof(hostcache[n].name) - 1] = 0;
739                                         }
740                                         strcpy(hostcache[n].cname, cname);
741                                 }
742                         }
743                         break;
744                 case CCREP_PLAYER_INFO:
745                         // we got a CCREP_PLAYER_INFO??
746                         //if (developer.integer)
747                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
748                         break;
749                 case CCREP_RULE_INFO:
750                         // we got a CCREP_RULE_INFO??
751                         //if (developer.integer)
752                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
753                         break;
754 #endif
755                 default:
756                         break;
757                 }
758                 // we may not have liked the packet, but it was a valid control
759                 // packet, so we're done processing this packet now
760                 return true;
761         }
762         ret = 0;
763         if (length >= (int)NET_HEADERSIZE && cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress) && (ret = NetConn_ReceivedMessage(cls.netcon, data, length)) == 2)
764                 CL_ParseServerMessage();
765         return ret;
766 }
767
768 void NetConn_ClientFrame(void)
769 {
770         int i, length;
771         lhnetaddress_t peeraddress;
772         netconn_t *conn;
773         NetConn_UpdateServerStuff();
774         if (cls.connect_trying && cls.connect_nextsendtime < realtime)
775         {
776                 if (cls.connect_remainingtries == 0)
777                 {
778                         cls.connect_trying = false;
779                         Host_Error("Connect failed\n");
780                         return;
781                 }
782                 if (cls.connect_nextsendtime)
783                         Con_Printf("Still trying...\n");
784                 else
785                         Con_Printf("Trying...\n");
786                 cls.connect_nextsendtime = realtime + 1;
787                 cls.connect_remainingtries--;
788                 SZ_Clear(&net_message);
789                 // save space for the header, filled in later
790                 MSG_WriteLong(&net_message, 0);
791                 MSG_WriteByte(&net_message, CCREQ_CONNECT);
792                 MSG_WriteString(&net_message, "QUAKE");
793                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
794                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
795                 NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address);
796                 SZ_Clear(&net_message);
797         }
798         for (i = 0;i < cl_numsockets;i++)
799                 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
800                         NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
801         if (cls.netcon && realtime > cls.netcon->lastMessageTime + net_messagetimeout.value)
802         {
803                 Con_Printf("Connection timed out\n");
804                 CL_Disconnect();
805         }
806         for (conn = netconn_list;conn;conn = conn->next)
807                 NetConn_ReSendMessage(conn);
808 }
809
810 extern void SV_ConnectClient(int clientnum, netconn_t *netconnection);
811 int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress)
812 {
813         int i, c, n, ret, clientnum, control, responselength;
814         client_t *client;
815         netconn_t *conn;
816         char *string, response[512], addressstring2[128];
817
818         if (sv.active)
819         {
820                 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
821                 {
822                         data += 4;
823                         length -= 4;
824                         string = (char *)data;
825
826                         if (developer.integer)
827                         {
828                                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
829                                 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
830                                 Com_HexDumpToConsole(data, length);
831                         }
832
833                         if (length >= 7 && !memcmp(string, "getinfo", 7))
834                         {
835                                 const char *challenge = NULL;
836                                 // If there was a challenge in the getinfo message
837                                 if (length > 8 && string[7] == ' ')
838                                         challenge = string + 8;
839                                 for (i = 0, n = 0;i < svs.maxclients;i++)
840                                         if (svs.clients[i].active)
841                                                 n++;
842                                 responselength = snprintf(response, sizeof(response), "\377\377\377\377infoResponse\x0A"
843                                                         "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
844                                                         "\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d%s%s",
845                                                         gamename, com_modname, svs.maxclients, n,
846                                                         sv.name, hostname.string, NET_PROTOCOL_VERSION, challenge ? "\\challenge\\" : "", challenge ? challenge : "");
847                                 // does it fit in the buffer?
848                                 if (responselength < (int)sizeof(response))
849                                 {
850                                         if (developer.integer)
851                                                 Con_Printf("Sending reply to master %s - %s\n", addressstring2, response);
852                                         NetConn_Write(mysocket, response, responselength, peeraddress);
853                                 }
854                                 return true;
855                         }
856                         /*
857                         if (!strncmp(string, "ping", 4))
858                         {
859                                 if (developer.integer)
860                                         Con_Printf("Received ping from %s, sending ack\n", UDP_AddrToString(readaddr));
861                                 NetConn_Write(mysocket, "\377\377\377\377ack", 7, peeraddress);
862                                 return true;
863                         }
864                         if (!strncmp(string, "ack", 3))
865                                 return true;
866                         */
867                         // we may not have liked the packet, but it was a command packet, so
868                         // we're done processing this packet now
869                         return true;
870                 }
871                 if (length >= 5)
872                 {
873                         control = BigLong(*((int *)data));
874                         if ((control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
875                         {
876                                 c = data[4];
877                                 data += 5;
878                                 length -= 5;
879                                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
880 #if 1
881                                 switch (c)
882                                 {
883                                 case CCREQ_CONNECT:
884                                         //if (developer.integer)
885                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
886                                         if (length >= (int)strlen("QUAKE") + 1 + 1)
887                                         {
888                                                 if (memcmp(data, "QUAKE", strlen("QUAKE") + 1) != 0 || (int)data[strlen("QUAKE") + 1] != NET_PROTOCOL_VERSION)
889                                                 {
890                                                         //if (developer.integer)
891                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
892 #if 0
893                                                         NetConn_Write(mysocket, "\377\377\377\377reject Incompatible version.", 32, peeraddress);
894 #else
895                                                         SZ_Clear(&net_message);
896                                                         // save space for the header, filled in later
897                                                         MSG_WriteLong(&net_message, 0);
898                                                         MSG_WriteByte(&net_message, CCREP_REJECT);
899                                                         MSG_WriteString(&net_message, "Incompatible version.\n");
900                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
901                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
902                                                         SZ_Clear(&net_message);
903 #endif
904                                                 }
905                                                 else
906                                                 {
907                                                         // see if this client is already connected
908                                                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
909                                                                 if (client->active && (ret = LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress)) == 0)//>= 0)
910                                                                         break;
911                                                         if (clientnum < svs.maxclients)
912                                                         {
913                                                                 // is this a duplicate connection request?
914                                                                 if (ret == 0 && realtime - client->netconnection->connecttime < 2.0)
915                                                                 {
916                                                                         //if (developer.integer)
917                                                                                 Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
918                                                                         // yes, so send a duplicate reply
919 #if 0
920                                                                         NetConn_Write(mysocket, "\377\377\377\377accept", 10, peeraddress);
921 #else
922                                                                         SZ_Clear(&net_message);
923                                                                         // save space for the header, filled in later
924                                                                         MSG_WriteLong(&net_message, 0);
925                                                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
926                                                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
927                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
928                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
929                                                                         SZ_Clear(&net_message);
930 #endif
931                                                                 }
932                                                                 else
933                                                                 {
934                                                                         //if (developer.integer)
935                                                                                 Con_Printf("Datagram_ParseConnectionless: removing crashed/disconnected client #%i \"%s\" (address %s).\n", clientnum, client->name, client->netconnection->address);
936                                                                         // it's somebody coming back from a
937                                                                         // crash/disconnect so kill old connection
938                                                                         // asap and let them retry to get in
939                                                                         client->deadsocket = true;
940                                                                 }
941                                                         }
942                                                         else
943                                                         {
944                                                                 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
945                                                                         if (!client->active)
946                                                                                 break;
947                                                                 if (clientnum < svs.maxclients && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
948                                                                 {
949                                                                         // connect to the client
950                                                                         // everything is allocated, just fill in the details
951                                                                         strcpy(conn->address, addressstring2);
952                                                                         //if (developer.integer)
953                                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
954 #if 0
955                                                                         NetConn_Write(mysocket, "\377\377\377\377accept", 10, peeraddress);
956 #else
957                                                                         // send back the info about the server connection
958                                                                         SZ_Clear(&net_message);
959                                                                         // save space for the header, filled in later
960                                                                         MSG_WriteLong(&net_message, 0);
961                                                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
962                                                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
963                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
964                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
965                                                                         SZ_Clear(&net_message);
966 #endif
967                                                                         // now set up the client struct
968                                                                         SV_ConnectClient(clientnum, conn);
969                                                                         NetConn_Heartbeat(1);
970                                                                 }
971                                                                 else
972                                                                 {
973                                                                         //if (developer.integer)
974                                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
975                                                                         // no room; try to let player know
976 #if 0
977                                                                         NetConn_Write(mysocket, "\377\377\377\377reject Server is full.", 26, peeraddress);
978 #else
979                                                                         SZ_Clear(&net_message);
980                                                                         // save space for the header, filled in later
981                                                                         MSG_WriteLong(&net_message, 0);
982                                                                         MSG_WriteByte(&net_message, CCREP_REJECT);
983                                                                         MSG_WriteString(&net_message, "Server is full.\n");
984                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
985                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
986                                                                         SZ_Clear(&net_message);
987 #endif
988                                                                 }
989                                                         }
990                                                 }
991                                         }
992                                         break;
993 #if 0
994                                 case CCREQ_SERVER_INFO:
995                                         if (developer.integer)
996                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
997                                         if (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
998                                         {
999                                                 if (developer.integer)
1000                                                         Con_Printf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
1001                                                 SZ_Clear(&net_message);
1002                                                 // save space for the header, filled in later
1003                                                 MSG_WriteLong(&net_message, 0);
1004                                                 MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
1005                                                 UDP_GetSocketAddr(UDP_acceptSock, &newaddr);
1006                                                 MSG_WriteString(&net_message, UDP_AddrToString(&newaddr));
1007                                                 MSG_WriteString(&net_message, hostname.string);
1008                                                 MSG_WriteString(&net_message, sv.name);
1009                                                 MSG_WriteByte(&net_message, net_activeconnections);
1010                                                 MSG_WriteByte(&net_message, svs.maxclients);
1011                                                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
1012                                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1013                                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1014                                                 SZ_Clear(&net_message);
1015                                         }
1016                                         break;
1017                                 case CCREQ_PLAYER_INFO:
1018                                         if (developer.integer)
1019                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
1020                                         if (sv.active)
1021                                         {
1022                                                 int playerNumber, activeNumber, clientNumber;
1023                                                 client_t *client;
1024
1025                                                 playerNumber = MSG_ReadByte();
1026                                                 activeNumber = -1;
1027                                                 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
1028                                                         if (client->active && ++activeNumber == playerNumber)
1029                                                                 break;
1030                                                 if (clientNumber != svs.maxclients)
1031                                                 {
1032                                                         SZ_Clear(&net_message);
1033                                                         // save space for the header, filled in later
1034                                                         MSG_WriteLong(&net_message, 0);
1035                                                         MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
1036                                                         MSG_WriteByte(&net_message, playerNumber);
1037                                                         MSG_WriteString(&net_message, client->name);
1038                                                         MSG_WriteLong(&net_message, client->colors);
1039                                                         MSG_WriteLong(&net_message, (int)client->edict->v->frags);
1040                                                         MSG_WriteLong(&net_message, (int)(realtime - client->netconnection->connecttime));
1041                                                         MSG_WriteString(&net_message, client->netconnection->address);
1042                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1043                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1044                                                         SZ_Clear(&net_message);
1045                                                 }
1046                                         }
1047                                         break;
1048                                 case CCREQ_RULE_INFO:
1049                                         if (developer.integer)
1050                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
1051                                         if (sv.active)
1052                                         {
1053                                                 char *prevCvarName;
1054                                                 cvar_t *var;
1055
1056                                                 // find the search start location
1057                                                 prevCvarName = MSG_ReadString();
1058                                                 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
1059
1060                                                 // send the response
1061                                                 SZ_Clear(&net_message);
1062                                                 // save space for the header, filled in later
1063                                                 MSG_WriteLong(&net_message, 0);
1064                                                 MSG_WriteByte(&net_message, CCREP_RULE_INFO);
1065                                                 if (var)
1066                                                 {
1067                                                         MSG_WriteString(&net_message, var->name);
1068                                                         MSG_WriteString(&net_message, var->string);
1069                                                 }
1070                                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1071                                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1072                                                 SZ_Clear(&net_message);
1073                                         }
1074                                         break;
1075 #endif
1076                                 default:
1077                                         break;
1078                                 }
1079 #endif
1080                                 // we may not have liked the packet, but it was a valid control
1081                                 // packet, so we're done processing this packet now
1082                                 return true;
1083                         }
1084                 }
1085                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1086                 {
1087                         if (host_client->active && host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
1088                         {
1089                                 sv_player = host_client->edict;
1090                                 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
1091                                         SV_ReadClientMessage();
1092                                 return ret;
1093                         }
1094                 }
1095         }
1096         return 0;
1097 }
1098
1099 void NetConn_ServerFrame(void)
1100 {
1101         int i, length;
1102         lhnetaddress_t peeraddress;
1103         netconn_t *conn;
1104         NetConn_UpdateServerStuff();
1105         for (i = 0;i < sv_numsockets;i++)
1106                 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
1107                         NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
1108         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++)
1109         {
1110                 if (host_client->active && realtime > host_client->netconnection->lastMessageTime + net_messagetimeout.value)
1111                 {
1112                         Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
1113                         sv_player = host_client->edict;
1114                         SV_DropClient(false);
1115                 }
1116         }
1117         for (conn = netconn_list;conn;conn = conn->next)
1118                 NetConn_ReSendMessage(conn);
1119 }
1120
1121 void NetConn_QueryMasters(void)
1122 {
1123         int i;
1124         int masternum;
1125         int requestlen;
1126         lhnetaddress_t masteraddress;
1127         char request[256];
1128
1129         if (hostCacheCount >= HOSTCACHESIZE)
1130                 return;
1131
1132         for (i = 0;i < cl_numsockets;i++)
1133         {
1134                 if (cl_sockets[i])
1135                 {
1136 #if 0
1137                         // search LAN
1138 #if 1
1139                         UDP_Broadcast(UDP_controlSock, "\377\377\377\377getinfo", 11);
1140 #else
1141                         SZ_Clear(&net_message);
1142                         // save space for the header, filled in later
1143                         MSG_WriteLong(&net_message, 0);
1144                         MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
1145                         MSG_WriteString(&net_message, "QUAKE");
1146                         MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
1147                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1148                         UDP_Broadcast(UDP_controlSock, net_message.data, net_message.cursize);
1149                         SZ_Clear(&net_message);
1150 #endif
1151 #endif
1152
1153                         // build the getservers
1154                         requestlen = snprintf(request, sizeof(request), "\377\377\377\377getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
1155
1156                         // search internet
1157                         for (masternum = 0;sv_masters[masternum].name;masternum++)
1158                                 if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, MASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
1159                                         NetConn_Write(cl_sockets[i], request, requestlen, &masteraddress);
1160                 }
1161         }
1162 }
1163
1164 void NetConn_Heartbeat(int priority)
1165 {
1166         lhnetaddress_t masteraddress;
1167         int masternum;
1168         char *request = "\377\377\377\377heartbeat DarkPlaces\x0A";
1169         int requestlen = strlen(request);
1170         lhnetsocket_t *mysocket;
1171
1172         // if it's a state change (client connected), limit next heartbeat to no
1173         // more than 30 sec in the future
1174         if (priority == 1 && nextheartbeattime > realtime + 30.0)
1175                 nextheartbeattime = realtime + 30.0;
1176
1177         // limit heartbeatperiod to 30 to 270 second range,
1178         // lower limit is to avoid abusing master servers with excess traffic,
1179         // upper limit is to avoid timing out on the master server (which uses
1180         // 300 sec timeout)
1181         if (sv_heartbeatperiod.value < 30)
1182                 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
1183         if (sv_heartbeatperiod.value > 270)
1184                 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
1185
1186         // make advertising optional and don't advertise singleplayer games, and
1187         // only send a heartbeat as often as the admin wants
1188         if (sv.active && sv_public.integer && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
1189         {
1190                 nextheartbeattime = realtime + sv_heartbeatperiod.value;
1191                 for (masternum = 0;sv_masters[masternum].name;masternum++)
1192                         if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, MASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
1193                                 NetConn_Write(mysocket, request, requestlen, &masteraddress);
1194         }
1195 }
1196
1197 int NetConn_SendToAll(sizebuf_t *data, double blocktime)
1198 {
1199         int i, count = 0;
1200         qbyte state[MAX_SCOREBOARD];
1201
1202         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1203                 state[i] = 0;
1204
1205         // simultaneously wait for the first CanSendMessage and send the message,
1206         // then wait for a second CanSendMessage (verifying it was received)
1207         blocktime += Sys_DoubleTime();
1208         do
1209         {
1210                 count = 0;
1211                 NetConn_ClientFrame();
1212                 NetConn_ServerFrame();
1213                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1214                 {
1215                         if (host_client->active && state[i] < 2)
1216                         {
1217                                 count++;
1218                                 // need to send to this one
1219                                 if (NetConn_CanSendMessage(host_client->netconnection))
1220                                 {
1221                                         if (state[i] == 0 && NetConn_SendReliableMessage(host_client->netconnection, data) == -1)
1222                                                 state[i] = 2; // connection lost
1223                                         state[i]++;
1224                                 }
1225                         }
1226                 }
1227         }
1228         while (count && Sys_DoubleTime() < blocktime);
1229         return count;
1230 }
1231
1232 static void MaxPlayers_f(void)
1233 {
1234         int n;
1235
1236         if (Cmd_Argc() != 2)
1237         {
1238                 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
1239                 return;
1240         }
1241
1242         if (sv.active)
1243         {
1244                 Con_Printf("maxplayers can not be changed while a server is running.\n");
1245                 return;
1246         }
1247
1248         n = atoi(Cmd_Argv(1));
1249         n = bound(1, n, MAX_SCOREBOARD);
1250         if (svs.maxclients != n)
1251                 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
1252
1253         SV_SetMaxClients(n);
1254 }
1255
1256 static void Net_Heartbeat_f(void)
1257 {
1258         NetConn_Heartbeat(2);
1259 }
1260
1261 void PrintStats(netconn_t *conn)
1262 {
1263         Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, conn->canSend, conn->sendSequence, conn->receiveSequence);
1264 }
1265
1266 void Net_Stats_f(void)
1267 {
1268         netconn_t *conn;
1269         Con_Printf("unreliable messages sent   = %i\n", unreliableMessagesSent);
1270         Con_Printf("unreliable messages recv   = %i\n", unreliableMessagesReceived);
1271         Con_Printf("reliable messages sent     = %i\n", reliableMessagesSent);
1272         Con_Printf("reliable messages received = %i\n", reliableMessagesReceived);
1273         Con_Printf("packetsSent                = %i\n", packetsSent);
1274         Con_Printf("packetsReSent              = %i\n", packetsReSent);
1275         Con_Printf("packetsReceived            = %i\n", packetsReceived);
1276         Con_Printf("receivedDuplicateCount     = %i\n", receivedDuplicateCount);
1277         Con_Printf("droppedDatagrams           = %i\n", droppedDatagrams);
1278         Con_Printf("connections                =\n");
1279         for (conn = netconn_list;conn;conn = conn->next)
1280                 PrintStats(conn);
1281 }
1282
1283 void Net_Slist_f(void)
1284 {
1285         hostCacheCount = 0;
1286         memset(&pingcache, 0, sizeof(pingcache));
1287         if (m_state != m_slist)
1288                 Con_Printf("Sending requests to master servers\n");
1289         NetConn_QueryMasters();
1290         if (m_state != m_slist)
1291                 Con_Printf("Listening for replies...\n");
1292 }
1293
1294 void NetConn_Init(void)
1295 {
1296         int masternum;
1297         netconn_mempool = Mem_AllocPool("Networking");
1298         Cmd_AddCommand("net_stats", Net_Stats_f);
1299         Cmd_AddCommand("net_slist", Net_Slist_f);
1300         Cmd_AddCommand("maxplayers", MaxPlayers_f);
1301         Cmd_AddCommand("heartbeat", Net_Heartbeat_f);
1302         Cvar_RegisterVariable(&net_messagetimeout);
1303         Cvar_RegisterVariable(&hostname);
1304         Cvar_RegisterVariable(&developer_networking);
1305         Cvar_RegisterVariable(&cl_netport);
1306         Cvar_RegisterVariable(&cl_netaddress);
1307         Cvar_RegisterVariable(&cl_netaddress_ipv6);
1308         Cvar_RegisterVariable(&sv_netport);
1309         Cvar_RegisterVariable(&sv_netaddress);
1310         Cvar_RegisterVariable(&sv_netaddress_ipv6);
1311         Cvar_RegisterVariable(&sv_public);
1312         Cvar_RegisterVariable(&sv_heartbeatperiod);
1313         for (masternum = 0;sv_masters[masternum].name;masternum++)
1314                 Cvar_RegisterVariable(&sv_masters[masternum]);
1315         cl_numsockets = 0;
1316         sv_numsockets = 0;
1317         memset(&pingcache, 0, sizeof(pingcache));
1318         SZ_Alloc(&net_message, NET_MAXMESSAGE, "net_message");
1319         LHNET_Init();
1320 }
1321
1322 void NetConn_Shutdown(void)
1323 {
1324         NetConn_CloseClientPorts();
1325         NetConn_CloseServerPorts();
1326         LHNET_Shutdown();
1327 }
1328