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