initialize the various server properties when parsing an infoResponse incase some...
[divverent/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                                 unreliableMessagesReceived++;
447                                 SZ_Clear(&net_message);
448                                 SZ_Write(&net_message, data, length);
449                                 MSG_BeginReading();
450                                 return 2;
451                         }
452                         else
453                                 Con_DPrintf("Got a stale datagram\n");
454                         return 1;
455                 }
456                 else if (flags & NETFLAG_ACK)
457                 {
458                         if (sequence == (conn->sendSequence - 1))
459                         {
460                                 if (sequence == conn->ackSequence)
461                                 {
462                                         conn->ackSequence++;
463                                         if (conn->ackSequence != conn->sendSequence)
464                                                 Con_DPrintf("ack sequencing error\n");
465                                         conn->sendMessageLength -= MAX_DATAGRAM;
466                                         if (conn->sendMessageLength > 0)
467                                         {
468                                                 memcpy(conn->sendMessage, conn->sendMessage+MAX_DATAGRAM, conn->sendMessageLength);
469                                                 conn->sendNext = true;
470                                                 NetConn_SendMessageNext(conn);
471                                         }
472                                         else
473                                         {
474                                                 conn->sendMessageLength = 0;
475                                                 conn->canSend = true;
476                                         }
477                                 }
478                                 else
479                                         Con_DPrintf("Duplicate ACK received\n");
480                         }
481                         else
482                                 Con_DPrintf("Stale ACK received\n");
483                         return 1;
484                 }
485                 else if (flags & NETFLAG_DATA)
486                 {
487                         unsigned int temppacket[2];
488                         temppacket[0] = BigLong(8 | NETFLAG_ACK);
489                         temppacket[1] = BigLong(sequence);
490                         NetConn_Write(conn->mysocket, (qbyte *)temppacket, 8, &conn->peeraddress);
491                         if (sequence == conn->receiveSequence)
492                         {
493                                 conn->receiveSequence++;
494                                 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
495                                 conn->receiveMessageLength += length;
496                                 if (flags & NETFLAG_EOM)
497                                 {
498                                         reliableMessagesReceived++;
499                                         SZ_Clear(&net_message);
500                                         SZ_Write(&net_message, conn->receiveMessage, conn->receiveMessageLength);
501                                         conn->receiveMessageLength = 0;
502                                         MSG_BeginReading();
503                                         return 2;
504                                 }
505                         }
506                         else
507                                 receivedDuplicateCount++;
508                         return 1;
509                 }
510         }
511         return 0;
512 }
513
514 static struct
515 {
516         double senttime;
517         lhnetaddress_t peeraddress;
518 }
519 pingcache[HOSTCACHESIZE];
520
521 int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress)
522 {
523         int ret, c, control;
524         lhnetaddress_t svaddress;
525         const char *s;
526         char *string, addressstring2[128], cname[128], ipstring[32];
527
528         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
529         {
530                 data += 4;
531                 length -= 4;
532                 string = (char *)data;
533                 if (developer.integer)
534                 {
535                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
536                         Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
537                         Com_HexDumpToConsole(data, length);
538                 }
539                 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
540                 {
541                         int i, j, c, n, users, maxusers;
542                         char game[32], mod[32], map[32], name[128];
543                         double pingtime;
544                         hostcache_t temp;
545                         string += 13;
546                         // hostcache only uses text addresses
547                         LHNETADDRESS_ToString(peeraddress, cname, sizeof(cname), true);
548                         // search the cache for this server
549                         for (n = 0; n < hostCacheCount; n++)
550                                 if (!strcmp(cname, hostcache[n].cname))
551                                         break;
552                         // add it or update it
553                         if (n == hostCacheCount)
554                         {
555                                 // if cache is full replace highest ping server (the list is
556                                 // kept sorted so this is always the last, and if this server
557                                 // is good it will be sorted into an early part of the list)
558                                 if (hostCacheCount >= HOSTCACHESIZE)
559                                         n = hostCacheCount - 1;
560                                 else
561                                         hostCacheCount++;
562                         }
563                         if ((s = SearchInfostring(string, "gamename"     )) != NULL) strncpy(game, s, sizeof(game) - 1);else game[0] = 0;
564                         if ((s = SearchInfostring(string, "modname"      )) != NULL) strncpy(mod , s, sizeof(mod ) - 1);else mod[0] = 0;
565                         if ((s = SearchInfostring(string, "mapname"      )) != NULL) strncpy(map , s, sizeof(map ) - 1);else map[0] = 0;
566                         if ((s = SearchInfostring(string, "hostname"     )) != NULL) strncpy(name, s, sizeof(name) - 1);else name[0] = 0;
567                         if ((s = SearchInfostring(string, "protocol"     )) != NULL) c = atoi(s);else c = -1;
568                         if ((s = SearchInfostring(string, "clients"      )) != NULL) users = atoi(s);else users = 0;
569                         if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) maxusers = atoi(s);else maxusers = 0;
570                         pingtime = 9999;
571                         for (i = 0;i < HOSTCACHESIZE;i++)
572                                 if (!LHNETADDRESS_Compare(peeraddress, &pingcache[i].peeraddress))
573                                         pingtime = (int)(realtime - pingcache[i].senttime);
574                         pingtime = bound(0, pingtime, 9999);
575                         memset(&hostcache[n], 0, sizeof(hostcache[n]));
576                         // store the data the engine cares about (address and ping)
577                         strcpy(hostcache[n].cname, cname);
578                         hostcache[n].ping = pingtime;
579                         // build description strings for the things users care about
580                         snprintf(hostcache[n].line1, sizeof(hostcache[n].line1), "%5d%c%3u/%3u %-65.65s", (int)pingtime, c != NET_PROTOCOL_VERSION ? '*' : ' ', users, maxusers, name);
581                         snprintf(hostcache[n].line2, sizeof(hostcache[n].line2), "%-21.21s %-19.19s %-17.17s %-20.20s", cname, game, mod, map);
582                         // if ping is especially high, display it as such
583                         if (pingtime >= 300)
584                         {
585                                 // orange numbers (lower block)
586                                 for (i = 0;i < 5;i++)
587                                         if (hostcache[n].line1[i] != ' ')
588                                                 hostcache[n].line1[i] += 128;
589                         }
590                         else if (pingtime >= 200)
591                         {
592                                 // yellow numbers (in upper block)
593                                 for (i = 0;i < 5;i++)
594                                         if (hostcache[n].line1[i] != ' ')
595                                                 hostcache[n].line1[i] -= 30;
596                         }
597                         // if not in the slist menu we should print the server to console
598                         if (m_state != m_slist)
599                                 Con_Printf("%s\n%s\n", hostcache[n].line1, hostcache[n].line2);
600                         // and finally, re-sort the list
601                         for (i = 0;i < hostCacheCount;i++)
602                         {
603                                 for (j = i + 1;j < hostCacheCount;j++)
604                                 {
605                                         //if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
606                                         if (hostcache[i].ping > hostcache[j].ping)
607                                         {
608                                                 memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
609                                                 memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
610                                                 memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
611                                         }
612                                 }
613                         }
614                         return true;
615                 }
616                 if (!strncmp(string, "getserversResponse\\", 19) && hostCacheCount < HOSTCACHESIZE)
617                 {
618                         int i, best;
619                         double besttime;
620                         // Extract the IP addresses
621                         data += 18;
622                         length -= 18;
623                         while (length >= 7 && data[0] == '\\' && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF) && data[5] * 256 + data[6] != 0)
624                         {
625                                 sprintf(ipstring, "%u.%u.%u.%u:%u", data[1], data[2], data[3], data[4], (data[5] << 8) | data[6]);
626                                 if (developer.integer)
627                                         Con_Printf("Requesting info from server %s\n", ipstring);
628                                 LHNETADDRESS_FromString(&svaddress, ipstring, 0);
629                                 NetConn_Write(mysocket, "\377\377\377\377getinfo", 11, &svaddress);
630                                 // replace oldest or matching entry in ping cache
631                                 // we scan this later when getting a reply to see how long it took
632                                 besttime = realtime;
633                                 best = 0;
634                                 for (i = 0;i < HOSTCACHESIZE;i++)
635                                 {
636                                         if (!LHNETADDRESS_Compare(&svaddress, &pingcache[i].peeraddress))
637                                         {
638                                                 best = i;
639                                                 break;
640                                         }
641                                         if (besttime > pingcache[i].senttime)
642                                         {
643                                                 besttime = pingcache[i].senttime;
644                                                 best = i;
645                                                 // if ping cache isn't full yet we can skip out early
646                                                 if (!besttime)
647                                                         break;
648                                         }
649                                 }
650                                 pingcache[best].peeraddress = svaddress;
651                                 pingcache[best].senttime = realtime;
652                                 // move on to next address in packet
653                                 data += 7;
654                                 length -= 7;
655                         }
656                         return true;
657                 }
658                 /*
659                 if (!strncmp(string, "ping", 4))
660                 {
661                         if (developer.integer)
662                                 Con_Printf("Received ping from %s, sending ack\n", UDP_AddrToString(readaddr));
663                         NetConn_Write(mysocket, "\377\377\377\377ack", 7, peeraddress);
664                         return true;
665                 }
666                 if (!strncmp(string, "ack", 3))
667                         return true;
668                 */
669                 // we may not have liked the packet, but it was a command packet, so
670                 // we're done processing this packet now
671                 return true;
672         }
673         if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
674         {
675                 c = data[4];
676                 data += 5;
677                 length -= 5;
678                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
679                 switch (c)
680                 {
681                 case CCREP_ACCEPT:
682                         if (developer.integer)
683                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
684                         if (cls.connect_trying)
685                         {
686                                 Con_Printf("Connection accepted to %s\n", addressstring2);
687                                 key_dest = key_game;
688                                 m_state = m_none;
689                                 cls.netcon = NetConn_Open(mysocket, peeraddress);
690                                 cls.connect_trying = false;
691                                 cls.demonum = -1;                       // not in the demo loop now
692                                 cls.state = ca_connected;
693                                 cls.signon = 0;                         // need all the signon messages before playing
694                                 CL_ClearState();
695                                 Host_Reconnect_f();
696                         }
697                         break;
698                 case CCREP_REJECT:
699                         if (developer.integer)
700                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
701                         Con_Printf("%s\n", data);
702                         strncpy(m_return_reason, data, sizeof(m_return_reason) - 1);
703                         break;
704 #if 0
705                 case CCREP_SERVER_INFO:
706                         if (developer.integer)
707                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
708                         if (cls.state != ca_dedicated)
709                         {
710                                 // LordHavoc: because the UDP driver reports 0.0.0.0:26000 as the address
711                                 // string we just ignore it and keep the real address
712                                 MSG_ReadString();
713                                 // hostcache only uses text addresses
714                                 cname = UDP_AddrToString(readaddr);
715                                 // search the cache for this server
716                                 for (n = 0; n < hostCacheCount; n++)
717                                         if (!strcmp(cname, hostcache[n].cname))
718                                                 break;
719                                 // add it
720                                 if (n == hostCacheCount && hostCacheCount < HOSTCACHESIZE)
721                                 {
722                                         hostCacheCount++;
723                                         memset(&hostcache[n], 0, sizeof(hostcache[n]));
724                                         strcpy(hostcache[n].name, MSG_ReadString());
725                                         strcpy(hostcache[n].map, MSG_ReadString());
726                                         hostcache[n].users = MSG_ReadByte();
727                                         hostcache[n].maxusers = MSG_ReadByte();
728                                         c = MSG_ReadByte();
729                                         if (c != NET_PROTOCOL_VERSION)
730                                         {
731                                                 strncpy(hostcache[n].cname, hostcache[n].name, sizeof(hostcache[n].cname) - 1);
732                                                 hostcache[n].cname[sizeof(hostcache[n].cname) - 1] = 0;
733                                                 strcpy(hostcache[n].name, "*");
734                                                 strncat(hostcache[n].name, hostcache[n].cname, sizeof(hostcache[n].name) - 1);
735                                                 hostcache[n].name[sizeof(hostcache[n].name) - 1] = 0;
736                                         }
737                                         strcpy(hostcache[n].cname, cname);
738                                 }
739                         }
740                         break;
741                 case CCREP_PLAYER_INFO:
742                         // we got a CCREP_PLAYER_INFO??
743                         //if (developer.integer)
744                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
745                         break;
746                 case CCREP_RULE_INFO:
747                         // we got a CCREP_RULE_INFO??
748                         //if (developer.integer)
749                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
750                         break;
751 #endif
752                 default:
753                         break;
754                 }
755                 // we may not have liked the packet, but it was a valid control
756                 // packet, so we're done processing this packet now
757                 return true;
758         }
759         ret = 0;
760         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)
761                 CL_ParseServerMessage();
762         return ret;
763 }
764
765 void NetConn_ClientFrame(void)
766 {
767         int i, length;
768         lhnetaddress_t peeraddress;
769         netconn_t *conn;
770         NetConn_UpdateServerStuff();
771         if (cls.connect_trying && cls.connect_nextsendtime < realtime)
772         {
773                 if (cls.connect_remainingtries == 0)
774                 {
775                         cls.connect_trying = false;
776                         Host_Error("Connect failed\n");
777                         return;
778                 }
779                 if (cls.connect_nextsendtime)
780                         Con_Printf("Still trying...\n");
781                 else
782                         Con_Printf("Trying...\n");
783                 cls.connect_nextsendtime = realtime + 1;
784                 cls.connect_remainingtries--;
785                 SZ_Clear(&net_message);
786                 // save space for the header, filled in later
787                 MSG_WriteLong(&net_message, 0);
788                 MSG_WriteByte(&net_message, CCREQ_CONNECT);
789                 MSG_WriteString(&net_message, "QUAKE");
790                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
791                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
792                 NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address);
793                 SZ_Clear(&net_message);
794         }
795         for (i = 0;i < cl_numsockets;i++)
796                 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
797                         NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
798         if (cls.netcon && realtime > cls.netcon->lastMessageTime + net_messagetimeout.value)
799         {
800                 Con_Printf("Connection timed out\n");
801                 CL_Disconnect();
802         }
803         for (conn = netconn_list;conn;conn = conn->next)
804                 NetConn_ReSendMessage(conn);
805 }
806
807 extern void SV_ConnectClient(int clientnum, netconn_t *netconnection);
808 int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress)
809 {
810         int i, c, n, ret, clientnum, control, responselength;
811         client_t *client;
812         netconn_t *conn;
813         char *string, response[512], addressstring2[128];
814
815         if (sv.active)
816         {
817                 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
818                 {
819                         data += 4;
820                         length -= 4;
821                         string = (char *)data;
822
823                         if (developer.integer)
824                         {
825                                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
826                                 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
827                                 Com_HexDumpToConsole(data, length);
828                         }
829
830                         if (length >= 7 && !memcmp(string, "getinfo", 7))
831                         {
832                                 const char *challenge = NULL;
833                                 // If there was a challenge in the getinfo message
834                                 if (length > 8 && string[7] == ' ')
835                                         challenge = string + 8;
836                                 for (i = 0, n = 0;i < svs.maxclients;i++)
837                                         if (svs.clients[i].active)
838                                                 n++;
839                                 responselength = snprintf(response, sizeof(response), "\377\377\377\377infoResponse\x0A"
840                                                         "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
841                                                         "\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d%s%s",
842                                                         gamename, com_modname, svs.maxclients, n,
843                                                         sv.name, hostname.string, NET_PROTOCOL_VERSION, challenge ? "\\challenge\\" : "", challenge ? challenge : "");
844                                 // does it fit in the buffer?
845                                 if (responselength < (int)sizeof(response))
846                                 {
847                                         if (developer.integer)
848                                                 Con_Printf("Sending reply to master %s - %s\n", addressstring2, response);
849                                         NetConn_Write(mysocket, response, responselength, peeraddress);
850                                 }
851                                 return true;
852                         }
853                         /*
854                         if (!strncmp(string, "ping", 4))
855                         {
856                                 if (developer.integer)
857                                         Con_Printf("Received ping from %s, sending ack\n", UDP_AddrToString(readaddr));
858                                 NetConn_Write(mysocket, "\377\377\377\377ack", 7, peeraddress);
859                                 return true;
860                         }
861                         if (!strncmp(string, "ack", 3))
862                                 return true;
863                         */
864                         // we may not have liked the packet, but it was a command packet, so
865                         // we're done processing this packet now
866                         return true;
867                 }
868                 if (length >= 5)
869                 {
870                         control = BigLong(*((int *)data));
871                         if ((control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
872                         {
873                                 c = data[4];
874                                 data += 5;
875                                 length -= 5;
876                                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
877 #if 1
878                                 switch (c)
879                                 {
880                                 case CCREQ_CONNECT:
881                                         //if (developer.integer)
882                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
883                                         if (length >= (int)strlen("QUAKE") + 1 + 1)
884                                         {
885                                                 if (memcmp(data, "QUAKE", strlen("QUAKE") + 1) != 0 || (int)data[strlen("QUAKE") + 1] != NET_PROTOCOL_VERSION)
886                                                 {
887                                                         //if (developer.integer)
888                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
889 #if 0
890                                                         NetConn_Write(mysocket, "\377\377\377\377reject Incompatible version.", 32, peeraddress);
891 #else
892                                                         SZ_Clear(&net_message);
893                                                         // save space for the header, filled in later
894                                                         MSG_WriteLong(&net_message, 0);
895                                                         MSG_WriteByte(&net_message, CCREP_REJECT);
896                                                         MSG_WriteString(&net_message, "Incompatible version.\n");
897                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
898                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
899                                                         SZ_Clear(&net_message);
900 #endif
901                                                 }
902                                                 else
903                                                 {
904                                                         // see if this client is already connected
905                                                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
906                                                                 if (client->active && (ret = LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress)) == 0)//>= 0)
907                                                                         break;
908                                                         if (clientnum < svs.maxclients)
909                                                         {
910                                                                 // is this a duplicate connection request?
911                                                                 if (ret == 0 && realtime - client->netconnection->connecttime < 2.0)
912                                                                 {
913                                                                         //if (developer.integer)
914                                                                                 Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
915                                                                         // yes, so send a duplicate reply
916 #if 0
917                                                                         NetConn_Write(mysocket, "\377\377\377\377accept", 10, peeraddress);
918 #else
919                                                                         SZ_Clear(&net_message);
920                                                                         // save space for the header, filled in later
921                                                                         MSG_WriteLong(&net_message, 0);
922                                                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
923                                                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
924                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
925                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
926                                                                         SZ_Clear(&net_message);
927 #endif
928                                                                 }
929                                                                 else
930                                                                 {
931                                                                         //if (developer.integer)
932                                                                                 Con_Printf("Datagram_ParseConnectionless: removing crashed/disconnected client #%i \"%s\" (address %s).\n", clientnum, client->name, client->netconnection->address);
933                                                                         // it's somebody coming back from a
934                                                                         // crash/disconnect so kill old connection
935                                                                         // asap and let them retry to get in
936                                                                         client->deadsocket = true;
937                                                                 }
938                                                         }
939                                                         else
940                                                         {
941                                                                 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
942                                                                         if (!client->active)
943                                                                                 break;
944                                                                 if (clientnum < svs.maxclients && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
945                                                                 {
946                                                                         // connect to the client
947                                                                         // everything is allocated, just fill in the details
948                                                                         strcpy(conn->address, addressstring2);
949                                                                         //if (developer.integer)
950                                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
951 #if 0
952                                                                         NetConn_Write(mysocket, "\377\377\377\377accept", 10, peeraddress);
953 #else
954                                                                         // send back the info about the server connection
955                                                                         SZ_Clear(&net_message);
956                                                                         // save space for the header, filled in later
957                                                                         MSG_WriteLong(&net_message, 0);
958                                                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
959                                                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
960                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
961                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
962                                                                         SZ_Clear(&net_message);
963 #endif
964                                                                         // now set up the client struct
965                                                                         SV_ConnectClient(clientnum, conn);
966                                                                         NetConn_Heartbeat(1);
967                                                                 }
968                                                                 else
969                                                                 {
970                                                                         //if (developer.integer)
971                                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
972                                                                         // no room; try to let player know
973 #if 0
974                                                                         NetConn_Write(mysocket, "\377\377\377\377reject Server is full.", 26, peeraddress);
975 #else
976                                                                         SZ_Clear(&net_message);
977                                                                         // save space for the header, filled in later
978                                                                         MSG_WriteLong(&net_message, 0);
979                                                                         MSG_WriteByte(&net_message, CCREP_REJECT);
980                                                                         MSG_WriteString(&net_message, "Server is full.\n");
981                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
982                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
983                                                                         SZ_Clear(&net_message);
984 #endif
985                                                                 }
986                                                         }
987                                                 }
988                                         }
989                                         break;
990 #if 0
991                                 case CCREQ_SERVER_INFO:
992                                         if (developer.integer)
993                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
994                                         if (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
995                                         {
996                                                 if (developer.integer)
997                                                         Con_Printf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
998                                                 SZ_Clear(&net_message);
999                                                 // save space for the header, filled in later
1000                                                 MSG_WriteLong(&net_message, 0);
1001                                                 MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
1002                                                 UDP_GetSocketAddr(UDP_acceptSock, &newaddr);
1003                                                 MSG_WriteString(&net_message, UDP_AddrToString(&newaddr));
1004                                                 MSG_WriteString(&net_message, hostname.string);
1005                                                 MSG_WriteString(&net_message, sv.name);
1006                                                 MSG_WriteByte(&net_message, net_activeconnections);
1007                                                 MSG_WriteByte(&net_message, svs.maxclients);
1008                                                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
1009                                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1010                                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1011                                                 SZ_Clear(&net_message);
1012                                         }
1013                                         break;
1014                                 case CCREQ_PLAYER_INFO:
1015                                         if (developer.integer)
1016                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
1017                                         if (sv.active)
1018                                         {
1019                                                 int playerNumber, activeNumber, clientNumber;
1020                                                 client_t *client;
1021
1022                                                 playerNumber = MSG_ReadByte();
1023                                                 activeNumber = -1;
1024                                                 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
1025                                                         if (client->active && ++activeNumber == playerNumber)
1026                                                                 break;
1027                                                 if (clientNumber != svs.maxclients)
1028                                                 {
1029                                                         SZ_Clear(&net_message);
1030                                                         // save space for the header, filled in later
1031                                                         MSG_WriteLong(&net_message, 0);
1032                                                         MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
1033                                                         MSG_WriteByte(&net_message, playerNumber);
1034                                                         MSG_WriteString(&net_message, client->name);
1035                                                         MSG_WriteLong(&net_message, client->colors);
1036                                                         MSG_WriteLong(&net_message, (int)client->edict->v->frags);
1037                                                         MSG_WriteLong(&net_message, (int)(realtime - client->netconnection->connecttime));
1038                                                         MSG_WriteString(&net_message, client->netconnection->address);
1039                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1040                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1041                                                         SZ_Clear(&net_message);
1042                                                 }
1043                                         }
1044                                         break;
1045                                 case CCREQ_RULE_INFO:
1046                                         if (developer.integer)
1047                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
1048                                         if (sv.active)
1049                                         {
1050                                                 char *prevCvarName;
1051                                                 cvar_t *var;
1052
1053                                                 // find the search start location
1054                                                 prevCvarName = MSG_ReadString();
1055                                                 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
1056
1057                                                 // send the response
1058                                                 SZ_Clear(&net_message);
1059                                                 // save space for the header, filled in later
1060                                                 MSG_WriteLong(&net_message, 0);
1061                                                 MSG_WriteByte(&net_message, CCREP_RULE_INFO);
1062                                                 if (var)
1063                                                 {
1064                                                         MSG_WriteString(&net_message, var->name);
1065                                                         MSG_WriteString(&net_message, var->string);
1066                                                 }
1067                                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1068                                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1069                                                 SZ_Clear(&net_message);
1070                                         }
1071                                         break;
1072 #endif
1073                                 default:
1074                                         break;
1075                                 }
1076 #endif
1077                                 // we may not have liked the packet, but it was a valid control
1078                                 // packet, so we're done processing this packet now
1079                                 return true;
1080                         }
1081                 }
1082                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1083                 {
1084                         if (host_client->active && host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
1085                         {
1086                                 sv_player = host_client->edict;
1087                                 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
1088                                         SV_ReadClientMessage();
1089                                 return ret;
1090                         }
1091                 }
1092         }
1093         return 0;
1094 }
1095
1096 void NetConn_ServerFrame(void)
1097 {
1098         int i, length;
1099         lhnetaddress_t peeraddress;
1100         netconn_t *conn;
1101         NetConn_UpdateServerStuff();
1102         for (i = 0;i < sv_numsockets;i++)
1103                 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
1104                         NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
1105         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++)
1106         {
1107                 if (host_client->active && realtime > host_client->netconnection->lastMessageTime + net_messagetimeout.value)
1108                 {
1109                         Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
1110                         sv_player = host_client->edict;
1111                         SV_DropClient(false);
1112                 }
1113         }
1114         for (conn = netconn_list;conn;conn = conn->next)
1115                 NetConn_ReSendMessage(conn);
1116 }
1117
1118 void NetConn_QueryMasters(void)
1119 {
1120         int i;
1121         int masternum;
1122         int requestlen;
1123         lhnetaddress_t masteraddress;
1124         char request[256];
1125
1126         if (hostCacheCount >= HOSTCACHESIZE)
1127                 return;
1128
1129         for (i = 0;i < cl_numsockets;i++)
1130         {
1131                 if (cl_sockets[i])
1132                 {
1133 #if 0
1134                         // search LAN
1135 #if 1
1136                         UDP_Broadcast(UDP_controlSock, "\377\377\377\377getinfo", 11);
1137 #else
1138                         SZ_Clear(&net_message);
1139                         // save space for the header, filled in later
1140                         MSG_WriteLong(&net_message, 0);
1141                         MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
1142                         MSG_WriteString(&net_message, "QUAKE");
1143                         MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
1144                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1145                         UDP_Broadcast(UDP_controlSock, net_message.data, net_message.cursize);
1146                         SZ_Clear(&net_message);
1147 #endif
1148 #endif
1149
1150                         // build the getservers
1151                         requestlen = snprintf(request, sizeof(request), "\377\377\377\377getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
1152
1153                         // search internet
1154                         for (masternum = 0;sv_masters[masternum].name;masternum++)
1155                                 if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, MASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
1156                                         NetConn_Write(cl_sockets[i], request, requestlen, &masteraddress);
1157                 }
1158         }
1159 }
1160
1161 void NetConn_Heartbeat(int priority)
1162 {
1163         lhnetaddress_t masteraddress;
1164         int masternum;
1165         char *request = "\377\377\377\377heartbeat DarkPlaces\x0A";
1166         int requestlen = strlen(request);
1167         lhnetsocket_t *mysocket;
1168
1169         // if it's a state change (client connected), limit next heartbeat to no
1170         // more than 30 sec in the future
1171         if (priority == 1 && nextheartbeattime > realtime + 30.0)
1172                 nextheartbeattime = realtime + 30.0;
1173
1174         // limit heartbeatperiod to 30 to 270 second range,
1175         // lower limit is to avoid abusing master servers with excess traffic,
1176         // upper limit is to avoid timing out on the master server (which uses
1177         // 300 sec timeout)
1178         if (sv_heartbeatperiod.value < 30)
1179                 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
1180         if (sv_heartbeatperiod.value > 270)
1181                 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
1182
1183         // make advertising optional and don't advertise singleplayer games, and
1184         // only send a heartbeat as often as the admin wants
1185         if (sv.active && sv_public.integer && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
1186         {
1187                 nextheartbeattime = realtime + sv_heartbeatperiod.value;
1188                 for (masternum = 0;sv_masters[masternum].name;masternum++)
1189                         if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, MASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
1190                                 NetConn_Write(mysocket, request, requestlen, &masteraddress);
1191         }
1192 }
1193
1194 int NetConn_SendToAll(sizebuf_t *data, double blocktime)
1195 {
1196         int i, count = 0;
1197         qbyte state[MAX_SCOREBOARD];
1198
1199         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1200                 state[i] = 0;
1201
1202         // simultaneously wait for the first CanSendMessage and send the message,
1203         // then wait for a second CanSendMessage (verifying it was received)
1204         blocktime += Sys_DoubleTime();
1205         do
1206         {
1207                 count = 0;
1208                 NetConn_ClientFrame();
1209                 NetConn_ServerFrame();
1210                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1211                 {
1212                         if (host_client->active && state[i] < 2)
1213                         {
1214                                 count++;
1215                                 // need to send to this one
1216                                 if (NetConn_CanSendMessage(host_client->netconnection))
1217                                 {
1218                                         if (state[i] == 0 && NetConn_SendReliableMessage(host_client->netconnection, data) == -1)
1219                                                 state[i] = 2; // connection lost
1220                                         state[i]++;
1221                                 }
1222                         }
1223                 }
1224         }
1225         while (count && Sys_DoubleTime() < blocktime);
1226         return count;
1227 }
1228
1229 static void MaxPlayers_f(void)
1230 {
1231         int n;
1232
1233         if (Cmd_Argc() != 2)
1234         {
1235                 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
1236                 return;
1237         }
1238
1239         if (sv.active)
1240         {
1241                 Con_Printf("maxplayers can not be changed while a server is running.\n");
1242                 return;
1243         }
1244
1245         n = atoi(Cmd_Argv(1));
1246         n = bound(1, n, MAX_SCOREBOARD);
1247         if (svs.maxclients != n)
1248                 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
1249
1250         SV_SetMaxClients(n);
1251 }
1252
1253 static void Net_Heartbeat_f(void)
1254 {
1255         NetConn_Heartbeat(2);
1256 }
1257
1258 void PrintStats(netconn_t *conn)
1259 {
1260         Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, conn->canSend, conn->sendSequence, conn->receiveSequence);
1261 }
1262
1263 void Net_Stats_f(void)
1264 {
1265         netconn_t *conn;
1266         Con_Printf("unreliable messages sent   = %i\n", unreliableMessagesSent);
1267         Con_Printf("unreliable messages recv   = %i\n", unreliableMessagesReceived);
1268         Con_Printf("reliable messages sent     = %i\n", reliableMessagesSent);
1269         Con_Printf("reliable messages received = %i\n", reliableMessagesReceived);
1270         Con_Printf("packetsSent                = %i\n", packetsSent);
1271         Con_Printf("packetsReSent              = %i\n", packetsReSent);
1272         Con_Printf("packetsReceived            = %i\n", packetsReceived);
1273         Con_Printf("receivedDuplicateCount     = %i\n", receivedDuplicateCount);
1274         Con_Printf("droppedDatagrams           = %i\n", droppedDatagrams);
1275         Con_Printf("connections                =\n");
1276         for (conn = netconn_list;conn;conn = conn->next)
1277                 PrintStats(conn);
1278 }
1279
1280 void Net_Slist_f(void)
1281 {
1282         hostCacheCount = 0;
1283         memset(&pingcache, 0, sizeof(pingcache));
1284         if (m_state != m_slist)
1285                 Con_Printf("Sending requests to master servers\n");
1286         NetConn_QueryMasters();
1287         if (m_state != m_slist)
1288                 Con_Printf("Listening for replies...\n");
1289 }
1290
1291 void NetConn_Init(void)
1292 {
1293         int masternum;
1294         netconn_mempool = Mem_AllocPool("Networking");
1295         Cmd_AddCommand("net_stats", Net_Stats_f);
1296         Cmd_AddCommand("net_slist", Net_Slist_f);
1297         Cmd_AddCommand("maxplayers", MaxPlayers_f);
1298         Cmd_AddCommand("heartbeat", Net_Heartbeat_f);
1299         Cvar_RegisterVariable(&net_messagetimeout);
1300         Cvar_RegisterVariable(&hostname);
1301         Cvar_RegisterVariable(&developer_networking);
1302         Cvar_RegisterVariable(&cl_netport);
1303         Cvar_RegisterVariable(&cl_netaddress);
1304         Cvar_RegisterVariable(&cl_netaddress_ipv6);
1305         Cvar_RegisterVariable(&sv_netport);
1306         Cvar_RegisterVariable(&sv_netaddress);
1307         Cvar_RegisterVariable(&sv_netaddress_ipv6);
1308         Cvar_RegisterVariable(&sv_public);
1309         Cvar_RegisterVariable(&sv_heartbeatperiod);
1310         for (masternum = 0;sv_masters[masternum].name;masternum++)
1311                 Cvar_RegisterVariable(&sv_masters[masternum]);
1312         cl_numsockets = 0;
1313         sv_numsockets = 0;
1314         memset(&pingcache, 0, sizeof(pingcache));
1315         SZ_Alloc(&net_message, NET_MAXMESSAGE, "net_message");
1316         LHNET_Init();
1317 }
1318
1319 void NetConn_Shutdown(void)
1320 {
1321         NetConn_CloseClientPorts();
1322         NetConn_CloseServerPorts();
1323         LHNET_Shutdown();
1324 }
1325