-Added a new hostcache interface:
[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 // FIXME: resolve DNS on masters whenever their value changes and cache it (to avoid major delays in active servers when they heartbeat)
32 static cvar_t sv_masters [] =
33 {
34         {CVAR_SAVE, "sv_master1", ""},
35         {CVAR_SAVE, "sv_master2", ""},
36         {CVAR_SAVE, "sv_master3", ""},
37         {CVAR_SAVE, "sv_master4", ""},
38         {0, "sv_masterextra1", "69.59.212.88"}, // ghdigital.com
39         {0, "sv_masterextra2", "66.169.205.13"}, // dpmaster.deathmask.net
40         {0, NULL, NULL}
41 };
42
43 static double nextheartbeattime = 0;
44
45 sizebuf_t net_message;
46
47 cvar_t net_messagetimeout = {0, "net_messagetimeout","300"};
48 cvar_t net_messagerejointimeout = {0, "net_messagerejointimeout","10"};
49 cvar_t net_connecttimeout = {0, "net_connecttimeout","10"};
50 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED"};
51 cvar_t developer_networking = {0, "developer_networking", "0"};
52
53 cvar_t cl_netlocalping = {0, "cl_netlocalping","0"};
54 static cvar_t cl_netpacketloss = {0, "cl_netpacketloss","0"};
55
56
57 /* statistic counters */
58 static int packetsSent = 0;
59 static int packetsReSent = 0;
60 static int packetsReceived = 0;
61 static int receivedDuplicateCount = 0;
62 static int droppedDatagrams = 0;
63
64 static int unreliableMessagesSent = 0;
65 static int unreliableMessagesReceived = 0;
66 static int reliableMessagesSent = 0;
67 static int reliableMessagesReceived = 0;
68
69 double masterquerytime = -1000;
70 int masterquerycount = 0;
71 int masterreplycount = 0;
72 int serverquerycount = 0;
73 int serverreplycount = 0;
74
75 static qbyte sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
76 static qbyte readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
77
78 int cl_numsockets;
79 lhnetsocket_t *cl_sockets[16];
80 int sv_numsockets;
81 lhnetsocket_t *sv_sockets[16];
82
83 netconn_t *netconn_list = NULL;
84 mempool_t *netconn_mempool = NULL;
85
86 cvar_t cl_netport = {0, "cl_port", "0"};
87 cvar_t sv_netport = {0, "port", "26000"};
88 cvar_t net_address = {0, "net_address", "0.0.0.0"};
89 //cvar_t net_netaddress_ipv6 = {0, "net_address_ipv6", "[0:0:0:0:0:0:0:0]"};
90
91 // HostCache interface
92 hostcache_mask_t                hostcache_currentmask;
93 hostcache_infofield_t   hostcache_sortbyfield;
94 qboolean                                hostcache_sortdescending;
95
96 int                     hostcache_viewcount = 0;
97 hostcache_t     *hostcache_viewset[HOSTCACHE_VIEWCACHESIZE];
98
99 int                     hostcache_cachecount;
100 hostcache_t hostcache_cache[HOSTCACHE_TOTALSIZE];
101
102 qboolean        hostcache_consoleoutput;
103
104 // helper function to insert a value into the viewset
105 // spare entries will be removed
106 static void _HostCache_VS_Insert( int index, hostcache_t *entry )
107 {
108     int start;
109         for( start = index, index = hostcache_viewcount; index > start; index-- )
110                 hostcache_viewset[index] = hostcache_viewset[index - 1];
111         if( ++hostcache_viewcount > HOSTCACHE_VIEWCACHESIZE )
112                 hostcache_viewcount = HOSTCACHE_VIEWCACHESIZE;
113         hostcache_viewset[start] = entry;
114 }
115
116 // we suppose hostcache_viewcount to be valid, ie > 0
117 static void _HostCache_VS_Remove( int index )
118 {
119         for( --hostcache_viewcount; index < hostcache_viewcount; index++ )
120                 hostcache_viewset[index] = hostcache_viewset[index + 1];
121 }
122
123 // returns true if A should be inserted before B
124 static qboolean _HostCache_SortTest( hostcache_t *A, hostcache_t *B )
125 {
126         int result; // > 0 if for numbers A > B and for text if A < B 
127
128         if( hostcache_sortbyfield == HCIF_PING )
129                 result = A->info.ping - B->info.ping;
130         else if( hostcache_sortbyfield == HCIF_MAXPLAYERS )
131                 result = A->info.maxplayers - B->info.maxplayers;
132         else if( hostcache_sortbyfield == HCIF_NUMPLAYERS )
133                 result = A->info.numplayers - B->info.numplayers;
134         else if( hostcache_sortbyfield == HCIF_PROTOCOL )
135                 result = A->info.protocol - B->info.protocol;
136         else if( hostcache_sortbyfield == HCIF_CNAME )
137                 result = strcmp( B->info.cname, A->info.cname );
138         else if( hostcache_sortbyfield == HCIF_GAME )
139                 result = strcmp( B->info.game, A->info.game );
140         else if( hostcache_sortbyfield == HCIF_MAP )
141                 result = strcmp( B->info.map, A->info.map );
142         else if( hostcache_sortbyfield == HCIF_MOD )
143                 result = strcmp( B->info.mod, A->info.mod );
144         else if( hostcache_sortbyfield == HCIF_NAME )
145                 result = strcmp( B->info.name, A->info.name );
146         
147         if( result > 0 && hostcache_sortdescending)
148                 return true;
149         return false;
150 }
151
152 static qboolean _hc_testint( int A, hostcache_infofield_t op, int B )
153 {
154         int diff;
155
156         diff = A - B;
157         switch( hostcache_currentmask.pingtest ) {
158                         case HCMO_GREATER:
159                                 if( !diff )
160                                         return false;
161                         case HCMO_GREATEREQUAL:
162                                 if( diff < 0 )
163                                         return false;
164                                 break;
165                         case HCMO_EQUAL:
166                                 if( diff )
167                                         return false;
168                                 break;
169                         case HCMO_LESS:
170                                 if( !diff )
171                                         return false;
172                         case HCMO_LESSEQUAL:
173                                 if( diff > 0 )
174                                         return false;
175                                 break;
176         }
177         return true;
178 }
179
180 static qboolean _HostCache_TestMask( hostcache_info_t *info )
181 {
182         if( !_hc_testint( info->ping, hostcache_currentmask.pingtest, hostcache_currentmask.info.ping ) )
183                 return false;
184         if( !_hc_testint( info->maxplayers, hostcache_currentmask.maxplayerstest, hostcache_currentmask.info.maxplayers ) )
185                 return false;
186         if( !_hc_testint( info->numplayers, hostcache_currentmask.numplayerstest, hostcache_currentmask.info.numplayers ) )
187                 return false;
188         if( !_hc_testint( info->protocol, hostcache_currentmask.protocoltest, hostcache_currentmask.info.protocol ))
189                 return false;
190         if( *hostcache_currentmask.info.cname
191                 && !strstr( info->cname, hostcache_currentmask.info.cname ) )
192                 return false;
193         if( *hostcache_currentmask.info.game
194                 && !strstr( info->game, hostcache_currentmask.info.game ) )
195                 return false;
196         if( *hostcache_currentmask.info.mod
197                 && !strstr( info->mod, hostcache_currentmask.info.mod ) )
198                 return false;
199         if( *hostcache_currentmask.info.map
200                 && !strstr( info->map, hostcache_currentmask.info.map ) )
201                 return false;
202         if( *hostcache_currentmask.info.name
203                 && !strstr( info->name, hostcache_currentmask.info.name ) )
204                 return false;
205         return true;
206 }
207
208 static void _HostCache_Insert( hostcache_t *entry )
209 {
210         int start, end, size;
211         if( hostcache_viewcount == HOSTCACHE_VIEWCACHESIZE )
212                 return;
213         // now check whether it passes through mask
214         if( !_HostCache_TestMask( &entry->info ) )
215                 return;
216
217         if( !hostcache_viewcount ) {
218                 _HostCache_VS_Insert( 0, entry );
219                 return;
220         }
221         // ok, insert it, we just need to find out where exactly:
222
223         // two special cases
224         // check whether to insert it as new first item
225         if( _HostCache_SortTest( entry, hostcache_viewset[0] ) ) {
226                 _HostCache_VS_Insert( 0, entry );
227                 return;
228         } // check whether to insert it as new last item
229         else if( !_HostCache_SortTest( entry, hostcache_viewset[hostcache_viewcount - 1] ) ) {
230                 _HostCache_VS_Insert( hostcache_viewcount, entry );
231                 return;
232         }
233         start = 1;
234         end = hostcache_viewcount - 1;
235         while( (size = start - end) > 0 )
236                 // test the item that lies in the middle between start and end
237                 if( _HostCache_SortTest( entry, hostcache_viewset[(start + end) / 2] ) )
238                         // the item has to be in the upper half
239                         end = (start + end) / 2 - 1;
240                 else 
241                         // the item has to be in the lower half
242                         start = (start + end) / 2 + 1;
243         _HostCache_VS_Insert( start + 1, entry );
244 }
245
246 void HostCache_RebuildViewSet(void)
247 {
248         int i;
249         
250         hostcache_viewcount = 0;
251         for( i = 0 ; i < hostcache_cachecount ; i++ )
252                 if( hostcache_cache[i].finished )
253                         _HostCache_Insert( &hostcache_cache[i] );
254 }
255
256 void HostCache_ResetMask(void)
257 {
258         memset( &hostcache_currentmask, 0, sizeof( hostcache_mask_t ) );
259 }
260
261
262 void HostCache_QueryList(void)
263 {
264         masterquerytime = realtime;
265         masterquerycount = 0;
266         masterreplycount = 0;
267         serverquerycount = 0;
268         serverreplycount = 0;
269         hostcache_cachecount = 0;
270         hostcache_viewcount = 0;
271         hostcache_consoleoutput = false;
272         NetConn_QueryMasters();
273 }
274
275 // rest
276
277 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
278 {
279         int length = LHNET_Read(mysocket, data, maxlength, peeraddress);
280         int i;
281         if (cl_netpacketloss.integer)
282                 for (i = 0;i < cl_numsockets;i++)
283                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss.integer)
284                                 return 0;
285         if (developer_networking.integer && length != 0)
286         {
287                 char addressstring[128], addressstring2[128];
288                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
289                 if (length > 0)
290                 {
291                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
292                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", mysocket, addressstring, data, maxlength, peeraddress, length, addressstring2);
293                         Com_HexDumpToConsole(data, length);
294                 }
295                 else
296                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", mysocket, addressstring, data, maxlength, peeraddress, length);
297         }
298         return length;
299 }
300
301 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
302 {
303         int ret;
304         int i;
305         if (cl_netpacketloss.integer)
306                 for (i = 0;i < cl_numsockets;i++)
307                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss.integer)
308                                 return length;
309         ret = LHNET_Write(mysocket, data, length, peeraddress);
310         if (developer_networking.integer)
311         {
312                 char addressstring[128], addressstring2[128];
313                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
314                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
315                 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", mysocket, addressstring, data, length, peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
316                 Com_HexDumpToConsole(data, length);
317         }
318         return ret;
319 }
320
321 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
322 {
323         // note this does not include the trailing NULL because we add that in the parser
324         return NetConn_Write(mysocket, string, strlen(string), peeraddress);
325 }
326
327 int NetConn_SendReliableMessage(netconn_t *conn, sizebuf_t *data)
328 {
329         unsigned int packetLen;
330         unsigned int dataLen;
331         unsigned int eom;
332         unsigned int *header;
333
334 //#ifdef DEBUG
335         if (data->cursize == 0)
336                 Sys_Error("Datagram_SendMessage: zero length message\n");
337
338         if (data->cursize > (int)sizeof(conn->sendMessage))
339                 Sys_Error("Datagram_SendMessage: message too big (%u > %u)\n", data->cursize, sizeof(conn->sendMessage));
340
341         if (conn->canSend == false)
342                 Sys_Error("SendMessage: called with canSend == false\n");
343 //#endif
344
345         memcpy(conn->sendMessage, data->data, data->cursize);
346         conn->sendMessageLength = data->cursize;
347
348         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
349         {
350                 dataLen = conn->sendMessageLength;
351                 eom = NETFLAG_EOM;
352         }
353         else
354         {
355                 dataLen = MAX_PACKETFRAGMENT;
356                 eom = 0;
357         }
358
359         packetLen = NET_HEADERSIZE + dataLen;
360
361         header = (void *)sendbuffer;
362         header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
363         header[1] = BigLong(conn->sendSequence);
364         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
365
366         conn->sendSequence++;
367         conn->canSend = false;
368
369         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
370                 return -1;
371
372         conn->lastSendTime = realtime;
373         packetsSent++;
374         reliableMessagesSent++;
375         return 1;
376 }
377
378 static void NetConn_SendMessageNext(netconn_t *conn)
379 {
380         unsigned int packetLen;
381         unsigned int dataLen;
382         unsigned int eom;
383         unsigned int *header;
384
385         if (conn->sendMessageLength && !conn->canSend && conn->sendNext)
386         {
387                 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
388                 {
389                         dataLen = conn->sendMessageLength;
390                         eom = NETFLAG_EOM;
391                 }
392                 else
393                 {
394                         dataLen = MAX_PACKETFRAGMENT;
395                         eom = 0;
396                 }
397
398                 packetLen = NET_HEADERSIZE + dataLen;
399
400                 header = (void *)sendbuffer;
401                 header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
402                 header[1] = BigLong(conn->sendSequence);
403                 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
404
405                 conn->sendSequence++;
406                 conn->sendNext = false;
407
408                 if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
409                         return;
410
411                 conn->lastSendTime = realtime;
412                 packetsSent++;
413         }
414 }
415
416 static void NetConn_ReSendMessage(netconn_t *conn)
417 {
418         unsigned int packetLen;
419         unsigned int dataLen;
420         unsigned int eom;
421         unsigned int *header;
422
423         if (conn->sendMessageLength && !conn->canSend && (realtime - conn->lastSendTime) > 1.0)
424         {
425                 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
426                 {
427                         dataLen = conn->sendMessageLength;
428                         eom = NETFLAG_EOM;
429                 }
430                 else
431                 {
432                         dataLen = MAX_PACKETFRAGMENT;
433                         eom = 0;
434                 }
435
436                 packetLen = NET_HEADERSIZE + dataLen;
437
438                 header = (void *)sendbuffer;
439                 header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
440                 header[1] = BigLong(conn->sendSequence - 1);
441                 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
442
443                 conn->sendNext = false;
444
445                 if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
446                         return;
447
448                 conn->lastSendTime = realtime;
449                 packetsReSent++;
450         }
451 }
452
453 qboolean NetConn_CanSendMessage(netconn_t *conn)
454 {
455         return conn->canSend;
456 }
457
458 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data)
459 {
460         int packetLen;
461         int *header;
462
463         packetLen = NET_HEADERSIZE + data->cursize;
464
465 //#ifdef DEBUG
466         if (data->cursize == 0)
467                 Sys_Error("Datagram_SendUnreliableMessage: zero length message\n");
468
469         if (packetLen > (int)sizeof(sendbuffer))
470                 Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
471 //#endif
472
473         header = (void *)sendbuffer;
474         header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE);
475         header[1] = BigLong(conn->unreliableSendSequence);
476         memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
477
478         conn->unreliableSendSequence++;
479
480         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
481                 return -1;
482
483         packetsSent++;
484         unreliableMessagesSent++;
485         return 1;
486 }
487
488 void NetConn_CloseClientPorts(void)
489 {
490         for (;cl_numsockets > 0;cl_numsockets--)
491                 if (cl_sockets[cl_numsockets - 1])
492                         LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
493 }
494
495 void NetConn_OpenClientPort(const char *addressstring, int defaultport)
496 {
497         lhnetaddress_t address;
498         lhnetsocket_t *s;
499         char addressstring2[1024];
500         if (LHNETADDRESS_FromString(&address, addressstring, defaultport))
501         {
502                 if ((s = LHNET_OpenSocket_Connectionless(&address)))
503                 {
504                         cl_sockets[cl_numsockets++] = s;
505                         LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
506                         Con_Printf("Client opened a socket on address %s\n", addressstring2);
507                 }
508                 else
509                 {
510                         LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
511                         Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
512                 }
513         }
514         else
515                 Con_Printf("Client unable to parse address %s\n", addressstring);
516 }
517
518 void NetConn_OpenClientPorts(void)
519 {
520         int port;
521         NetConn_CloseClientPorts();
522         port = bound(0, cl_netport.integer, 65535);
523         if (cl_netport.integer != port)
524                 Cvar_SetValueQuick(&cl_netport, port);
525         Con_Printf("Client using port %i\n", port);
526         NetConn_OpenClientPort("local:2", 0);
527         NetConn_OpenClientPort(net_address.string, port);
528         //NetConn_OpenClientPort(net_address_ipv6.string, port);
529 }
530
531 void NetConn_CloseServerPorts(void)
532 {
533         for (;sv_numsockets > 0;sv_numsockets--)
534                 if (sv_sockets[sv_numsockets - 1])
535                         LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
536 }
537
538 void NetConn_OpenServerPort(const char *addressstring, int defaultport)
539 {
540         lhnetaddress_t address;
541         lhnetsocket_t *s;
542         char addressstring2[1024];
543         if (LHNETADDRESS_FromString(&address, addressstring, defaultport))
544         {
545                 if ((s = LHNET_OpenSocket_Connectionless(&address)))
546                 {
547                         sv_sockets[sv_numsockets++] = s;
548                         LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
549                         Con_Printf("Server listening on address %s\n", addressstring2);
550                 }
551                 else
552                 {
553                         LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
554                         Con_Printf("Server failed to open socket on address %s\n", addressstring2);
555                 }
556         }
557         else
558                 Con_Printf("Server unable to parse address %s\n", addressstring);
559 }
560
561 void NetConn_OpenServerPorts(int opennetports)
562 {
563         int port;
564         NetConn_CloseServerPorts();
565         port = bound(0, sv_netport.integer, 65535);
566         if (port == 0)
567                 port = 26000;
568         Con_Printf("Server using port %i\n", port);
569         if (sv_netport.integer != port)
570                 Cvar_SetValueQuick(&sv_netport, port);
571         if (cls.state != ca_dedicated)
572                 NetConn_OpenServerPort("local:1", 0);
573         if (opennetports)
574         {
575                 NetConn_OpenServerPort(net_address.string, port);
576                 //NetConn_OpenServerPort(net_address_ipv6.string, port);
577         }
578         if (sv_numsockets == 0)
579                 Host_Error("NetConn_OpenServerPorts: unable to open any ports!\n");
580 }
581
582 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
583 {
584         int i, a = LHNETADDRESS_GetAddressType(address);
585         for (i = 0;i < cl_numsockets;i++)
586                 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
587                         return cl_sockets[i];
588         return NULL;
589 }
590
591 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
592 {
593         int i, a = LHNETADDRESS_GetAddressType(address);
594         for (i = 0;i < sv_numsockets;i++)
595                 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
596                         return sv_sockets[i];
597         return NULL;
598 }
599
600 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
601 {
602         netconn_t *conn;
603         conn = Mem_Alloc(netconn_mempool, sizeof(*conn));
604         conn->mysocket = mysocket;
605         conn->peeraddress = *peeraddress;
606         conn->canSend = true;
607         conn->lastMessageTime = realtime;
608         // LordHavoc: (inspired by ProQuake) use a short connect timeout to
609         // reduce effectiveness of connection request floods
610         conn->timeout = realtime + net_connecttimeout.value;
611         LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
612         conn->next = netconn_list;
613         netconn_list = conn;
614         return conn;
615 }
616
617 void NetConn_Close(netconn_t *conn)
618 {
619         netconn_t *c;
620         // remove connection from list
621         if (conn == netconn_list)
622                 netconn_list = conn->next;
623         else
624         {
625                 for (c = netconn_list;c;c = c->next)
626                 {
627                         if (c->next == conn)
628                         {
629                                 c->next = conn->next;
630                                 break;
631                         }
632                 }
633                 // not found in list, we'll avoid crashing here...
634                 if (!c)
635                         return;
636         }
637         // free connection
638         Mem_Free(conn);
639 }
640
641 static int clientport = -1;
642 static int clientport2 = -1;
643 static int hostport = -1;
644 static void NetConn_UpdateServerStuff(void)
645 {
646         if (cls.state != ca_dedicated)
647         {
648                 if (clientport2 != cl_netport.integer)
649                 {
650                         clientport2 = cl_netport.integer;
651                         if (cls.state == ca_connected)
652                                 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
653                 }
654                 if (cls.state == ca_disconnected && clientport != clientport2)
655                 {
656                         clientport = clientport2;
657                         NetConn_CloseClientPorts();
658                 }
659                 if (cl_numsockets == 0)
660                         NetConn_OpenClientPorts();
661         }
662
663         if (hostport != sv_netport.integer)
664         {
665                 hostport = sv_netport.integer;
666                 if (sv.active)
667                         Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
668         }
669 }
670
671 int NetConn_ReceivedMessage(netconn_t *conn, qbyte *data, int length)
672 {
673         unsigned int count;
674         unsigned int flags;
675         unsigned int sequence;
676
677         if (length >= 8)
678         {
679                 length = BigLong(((int *)data)[0]);
680                 flags = length & ~NETFLAG_LENGTH_MASK;
681                 length &= NETFLAG_LENGTH_MASK;
682                 // control packets were already handled
683                 if (!(flags & NETFLAG_CTL))
684                 {
685                         sequence = BigLong(((int *)data)[1]);
686                         packetsReceived++;
687                         data += 8;
688                         length -= 8;
689                         if (flags & NETFLAG_UNRELIABLE)
690                         {
691                                 if (sequence >= conn->unreliableReceiveSequence)
692                                 {
693                                         if (sequence > conn->unreliableReceiveSequence)
694                                         {
695                                                 count = sequence - conn->unreliableReceiveSequence;
696                                                 droppedDatagrams += count;
697                                                 Con_DPrintf("Dropped %u datagram(s)\n", count);
698                                         }
699                                         conn->unreliableReceiveSequence = sequence + 1;
700                                         conn->lastMessageTime = realtime;
701                                         conn->timeout = realtime + net_messagetimeout.value;
702                                         unreliableMessagesReceived++;
703                                         if (length > 0)
704                                         {
705                                                 SZ_Clear(&net_message);
706                                                 SZ_Write(&net_message, data, length);
707                                                 MSG_BeginReading();
708                                                 return 2;
709                                         }
710                                 }
711                                 else
712                                         Con_DPrint("Got a stale datagram\n");
713                                 return 1;
714                         }
715                         else if (flags & NETFLAG_ACK)
716                         {
717                                 if (sequence == (conn->sendSequence - 1))
718                                 {
719                                         if (sequence == conn->ackSequence)
720                                         {
721                                                 conn->ackSequence++;
722                                                 if (conn->ackSequence != conn->sendSequence)
723                                                         Con_DPrint("ack sequencing error\n");
724                                                 conn->lastMessageTime = realtime;
725                                                 conn->timeout = realtime + net_messagetimeout.value;
726                                                 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
727                                                 if (conn->sendMessageLength > 0)
728                                                 {
729                                                         memcpy(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
730                                                         conn->sendNext = true;
731                                                         NetConn_SendMessageNext(conn);
732                                                 }
733                                                 else
734                                                 {
735                                                         conn->sendMessageLength = 0;
736                                                         conn->canSend = true;
737                                                 }
738                                         }
739                                         else
740                                                 Con_DPrint("Duplicate ACK received\n");
741                                 }
742                                 else
743                                         Con_DPrint("Stale ACK received\n");
744                                 return 1;
745                         }
746                         else if (flags & NETFLAG_DATA)
747                         {
748                                 unsigned int temppacket[2];
749                                 temppacket[0] = BigLong(8 | NETFLAG_ACK);
750                                 temppacket[1] = BigLong(sequence);
751                                 NetConn_Write(conn->mysocket, (qbyte *)temppacket, 8, &conn->peeraddress);
752                                 if (sequence == conn->receiveSequence)
753                                 {
754                                         conn->lastMessageTime = realtime;
755                                         conn->timeout = realtime + net_messagetimeout.value;
756                                         conn->receiveSequence++;
757                                         memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
758                                         conn->receiveMessageLength += length;
759                                         if (flags & NETFLAG_EOM)
760                                         {
761                                                 reliableMessagesReceived++;
762                                                 length = conn->receiveMessageLength;
763                                                 conn->receiveMessageLength = 0;
764                                                 if (length > 0)
765                                                 {
766                                                         SZ_Clear(&net_message);
767                                                         SZ_Write(&net_message, conn->receiveMessage, length);
768                                                         MSG_BeginReading();
769                                                         return 2;
770                                                 }
771                                         }
772                                 }
773                                 else
774                                         receivedDuplicateCount++;
775                                 return 1;
776                         }
777                 }
778         }
779         return 0;
780 }
781
782 void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
783 {
784         cls.connect_trying = false;
785         M_Update_Return_Reason("");
786         // the connection request succeeded, stop current connection and set up a new connection
787         CL_Disconnect();
788         cls.netcon = NetConn_Open(mysocket, peeraddress);
789         Con_Printf("Connection accepted to %s\n", cls.netcon->address);
790         key_dest = key_game;
791         m_state = m_none;
792         cls.demonum = -1;                       // not in the demo loop now
793         cls.state = ca_connected;
794         cls.signon = 0;                         // need all the signon messages before playing
795         CL_ClearState();
796         SCR_BeginLoadingPlaque();
797 }
798
799 int NetConn_IsLocalGame(void)
800 {
801         if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
802                 return true;
803         return false;
804 }
805
806 int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress)
807 {
808         int ret, c, control;
809         lhnetaddress_t svaddress;
810         const char *s;
811         char *string, addressstring2[128], cname[128], ipstring[32];
812         char stringbuf[16384];
813
814         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
815         {
816                 // received a command string - strip off the packaging and put it
817                 // into our string buffer with NULL termination
818                 data += 4;
819                 length -= 4;
820                 length = min(length, (int)sizeof(stringbuf) - 1);
821                 memcpy(stringbuf, data, length);
822                 stringbuf[length] = 0;
823                 string = stringbuf;
824
825                 if (developer.integer)
826                 {
827                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
828                         Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
829                         Com_HexDumpToConsole(data, length);
830                 }
831
832                 if (length > 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
833                 {
834                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
835                         Con_Printf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
836                         M_Update_Return_Reason("Got challenge response");
837                         NetConn_WriteString(mysocket, va("\377\377\377\377connect\\protocol\\darkplaces 3\\challenge\\%s", string + 10), peeraddress);
838                         return true;
839                 }
840                 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
841                 {
842                         M_Update_Return_Reason("Accepted");
843                         NetConn_ConnectionEstablished(mysocket, peeraddress);
844                         return true;
845                 }
846                 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
847                 {
848                         char rejectreason[32];
849                         cls.connect_trying = false;
850                         string += 7;
851                         length = max(length - 7, (int)sizeof(rejectreason) - 1);
852                         memcpy(rejectreason, string, length);
853                         rejectreason[length] = 0;
854                         M_Update_Return_Reason(rejectreason);
855                         return true;
856                 }
857                 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
858                 {
859                         hostcache_info_t *info;
860                         int i, n;
861                         double pingtime;
862         
863                         string += 13;
864                         // hostcache only uses text addresses
865                         LHNETADDRESS_ToString(peeraddress, cname, sizeof(cname), true);
866                         // search the cache for this server and update it
867                         for( n = 0; n < hostcache_cachecount; n++ ) {
868                                 if( !strcmp( cname, hostcache_cache[n].info.cname ) )
869                                         break;
870                         }
871                         if( n == hostcache_cachecount )
872                                 return true;
873
874                         info = &hostcache_cache[n].info;
875                         if ((s = SearchInfostring(string, "gamename"     )) != NULL) strlcpy(info->game, s, sizeof (info->game));else info->game[0] = 0;
876                         if ((s = SearchInfostring(string, "modname"      )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0]  = 0;
877                         if ((s = SearchInfostring(string, "mapname"      )) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0]  = 0;
878                         if ((s = SearchInfostring(string, "hostname"     )) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
879                         if ((s = SearchInfostring(string, "protocol"     )) != NULL) info->protocol = atoi(s);else info->protocol = -1;
880                         if ((s = SearchInfostring(string, "clients"      )) != NULL) info->numplayers = atoi(s);else info->numplayers = 0;
881                         if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);else info->maxplayers  = 0;
882
883                         if (info->ping == 100000)
884                                         serverreplycount++;
885         
886                         pingtime = (int)((realtime - hostcache_cache[n].querytime) * 1000.0);
887                         pingtime = bound(0, pingtime, 9999);
888                         // update the ping
889                         info->ping = pingtime;
890
891                         // legacy/old stuff move it to the menu ASAP
892                         
893                         // build description strings for the things users care about
894                         snprintf(hostcache_cache[n].line1, sizeof(hostcache_cache[n].line1), "%5d%c%3u/%3u %-65.65s", (int)pingtime, info->protocol != NET_PROTOCOL_VERSION ? '*' : ' ', info->numplayers, info->maxplayers, info->name);
895                         snprintf(hostcache_cache[n].line2, sizeof(hostcache_cache[n].line2), "%-21.21s %-19.19s %-17.17s %-20.20s", info->cname, info->game, info->mod, info->map);
896                         // if ping is especially high, display it as such
897                         if (pingtime >= 300)
898                         {
899                                 // orange numbers (lower block)
900                                 for (i = 0;i < 5;i++)
901                                         if (hostcache_cache[n].line1[i] != ' ')
902                                                 hostcache_cache[n].line1[i] += 128;
903                         }
904                         else if (pingtime >= 200)
905                         {
906                                 // yellow numbers (in upper block)
907                                 for (i = 0;i < 5;i++)
908                                         if (hostcache_cache[n].line1[i] != ' ')
909                                                 hostcache_cache[n].line1[i] -= 30;
910                         }
911                         // if not in the slist menu we should print the server to console
912                         if( hostcache_consoleoutput )
913                                 Con_Printf("%s\n%s\n", hostcache_cache[n].line1, hostcache_cache[n].line2);
914                         // and finally, update the view set
915                         if( hostcache_cache[n].finished ) {
916                 _HostCache_VS_Remove( n );
917                                 _HostCache_Insert( &hostcache_cache[n] );
918                         } else
919                                 _HostCache_Insert( &hostcache_cache[n] );
920                         hostcache_cache[n].finished = true;
921         
922                         return true;
923                 }
924                 if (!strncmp(string, "getserversResponse\\", 19) && hostcache_cachecount < HOSTCACHE_TOTALSIZE)
925                 {
926                         // Extract the IP addresses
927                         data += 18;
928                         length -= 18;
929                         masterreplycount++;
930                         if (hostcache_consoleoutput)
931                                 Con_Print("received server list...\n");
932                         while (length >= 7 && data[0] == '\\' && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF) && data[5] * 256 + data[6] != 0)
933                         {
934                                 serverquerycount++;
935         
936                                 snprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[1], data[2], data[3], data[4], (data[5] << 8) | data[6]);
937                                 if (developer.integer)
938                                         Con_Printf("Requesting info from server %s\n", ipstring);                               
939                                 if( hostcache_cachecount == HOSTCACHE_TOTALSIZE )
940                                                 // ignore the rest of the message since there wont magically disappear entries
941                                                 break;
942                                                 
943                                 LHNETADDRESS_FromString(&svaddress, ipstring, 0);
944                                 NetConn_WriteString(mysocket, "\377\377\377\377getinfo", &svaddress);
945                 
946                                 memset(&hostcache_cache[hostcache_cachecount], 0, sizeof(hostcache_cache[hostcache_cachecount]));
947                                 // store the data the engine cares about (address and ping)
948                                 strlcpy (hostcache_cache[hostcache_cachecount].info.cname, ipstring, sizeof (hostcache_cache[hostcache_cachecount].info.cname));
949                                 hostcache_cache[hostcache_cachecount].info.ping = 100000;
950                                 hostcache_cache[hostcache_cachecount].querytime = realtime;
951                                 // if not in the slist menu we should print the server to console
952                                 if (hostcache_consoleoutput)
953                                         Con_Printf("querying %s\n", ipstring);
954
955                                 ++hostcache_cachecount;
956                                 
957                                 // move on to next address in packet
958                                 data += 7;
959                                 length -= 7;
960                         }
961                         return true;
962                 }
963                 /*
964                 if (!strncmp(string, "ping", 4))
965                 {
966                         if (developer.integer)
967                                 Con_Printf("Received ping from %s, sending ack\n", UDP_AddrToString(readaddr));
968                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
969                         return true;
970                 }
971                 if (!strncmp(string, "ack", 3))
972                         return true;
973                 */
974                 // we may not have liked the packet, but it was a command packet, so
975                 // we're done processing this packet now
976                 return true;
977         }
978         // netquake control packets, supported for compatibility only
979         if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
980         {
981                 c = data[4];
982                 data += 5;
983                 length -= 5;
984                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
985                 switch (c)
986                 {
987                 case CCREP_ACCEPT:
988                         if (developer.integer)
989                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
990                         if (cls.connect_trying)
991                         {
992                                 lhnetaddress_t clientportaddress;
993                                 clientportaddress = *peeraddress;
994                                 if (length >= 4)
995                                 {
996                                         unsigned int port = (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
997                                         data += 4;
998                                         length -= 4;
999                                         LHNETADDRESS_SetPort(&clientportaddress, port);
1000                                 }
1001                                 M_Update_Return_Reason("Accepted");
1002                                 NetConn_ConnectionEstablished(mysocket, &clientportaddress);
1003                         }
1004                         break;
1005                 case CCREP_REJECT:
1006                         if (developer.integer)
1007                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
1008                         cls.connect_trying = false;
1009                         M_Update_Return_Reason(data);
1010                         break;
1011 #if 0
1012                 case CCREP_SERVER_INFO:
1013                         if (developer.integer)
1014                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
1015                         if (cls.state != ca_dedicated)
1016                         {
1017                                 // LordHavoc: because the UDP driver reports 0.0.0.0:26000 as the address
1018                                 // string we just ignore it and keep the real address
1019                                 MSG_ReadString();
1020                                 // hostcache only uses text addresses
1021                                 cname = UDP_AddrToString(readaddr);
1022                                 // search the cache for this server
1023                                 for (n = 0; n < hostCacheCount; n++)
1024                                         if (!strcmp(cname, hostcache[n].cname))
1025                                                 break;
1026                                 // add it
1027                                 if (n == hostCacheCount && hostCacheCount < HOSTCACHESIZE)
1028                                 {
1029                                         hostCacheCount++;
1030                                         memset(&hostcache[n], 0, sizeof(hostcache[n]));
1031                                         strlcpy (hostcache[n].name, MSG_ReadString(), sizeof (hostcache[n].name));
1032                                         strlcpy (hostcache[n].map, MSG_ReadString(), sizeof (hostcache[n].map));
1033                                         hostcache[n].users = MSG_ReadByte();
1034                                         hostcache[n].maxusers = MSG_ReadByte();
1035                                         c = MSG_ReadByte();
1036                                         if (c != NET_PROTOCOL_VERSION)
1037                                         {
1038                                                 strlcpy (hostcache[n].cname, hostcache[n].name, sizeof (hostcache[n].cname));
1039                                                 strcpy(hostcache[n].name, "*");
1040                                                 strlcat (hostcache[n].name, hostcache[n].cname, sizeof(hostcache[n].name));
1041                                         }
1042                                         strlcpy (hostcache[n].cname, cname, sizeof (hostcache[n].cname));
1043                                 }
1044                         }
1045                         break;
1046                 case CCREP_PLAYER_INFO:
1047                         // we got a CCREP_PLAYER_INFO??
1048                         //if (developer.integer)
1049                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
1050                         break;
1051                 case CCREP_RULE_INFO:
1052                         // we got a CCREP_RULE_INFO??
1053                         //if (developer.integer)
1054                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
1055                         break;
1056 #endif
1057                 default:
1058                         break;
1059                 }
1060                 // we may not have liked the packet, but it was a valid control
1061                 // packet, so we're done processing this packet now
1062                 return true;
1063         }
1064         ret = 0;
1065         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)
1066                 CL_ParseServerMessage();
1067         return ret;
1068 }
1069
1070 void NetConn_ClientFrame(void)
1071 {
1072         int i, length;
1073         lhnetaddress_t peeraddress;
1074         netconn_t *conn;
1075         NetConn_UpdateServerStuff();
1076         if (cls.connect_trying && cls.connect_nextsendtime < realtime)
1077         {
1078                 if (cls.connect_remainingtries == 0)
1079                 {
1080                         cls.connect_trying = false;
1081                         M_Update_Return_Reason("Connect: Failed");
1082                         return;
1083                 }
1084                 cls.connect_nextsendtime = realtime + 1;
1085                 cls.connect_remainingtries--;
1086                 // try challenge first (newer server)
1087                 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
1088                 // then try netquake as a fallback (old server, or netquake)
1089                 SZ_Clear(&net_message);
1090                 // save space for the header, filled in later
1091                 MSG_WriteLong(&net_message, 0);
1092                 MSG_WriteByte(&net_message, CCREQ_CONNECT);
1093                 MSG_WriteString(&net_message, "QUAKE");
1094                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
1095                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1096                 NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address);
1097                 SZ_Clear(&net_message);
1098         }
1099         for (i = 0;i < cl_numsockets;i++)
1100                 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
1101                         NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
1102         if (cls.netcon && realtime > cls.netcon->timeout)
1103         {
1104                 Con_Print("Connection timed out\n");
1105                 CL_Disconnect();
1106                 Host_ShutdownServer (false);
1107         }
1108         for (conn = netconn_list;conn;conn = conn->next)
1109                 NetConn_ReSendMessage(conn);
1110 }
1111
1112 #define MAX_CHALLENGES 128
1113 struct
1114 {
1115         lhnetaddress_t address;
1116         double time;
1117         char string[12];
1118 }
1119 challenge[MAX_CHALLENGES];
1120
1121 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
1122 {
1123         int i;
1124         char c;
1125         for (i = 0;i < bufferlength - 1;i++)
1126         {
1127                 do
1128                 {
1129                         c = rand () % (127 - 33) + 33;
1130                 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
1131                 buffer[i] = c;
1132         }
1133         buffer[i] = 0;
1134 }
1135
1136 int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress)
1137 {
1138         int i, n, ret, clientnum, responselength, best;
1139         double besttime;
1140         client_t *client;
1141         netconn_t *conn;
1142         char *s, *string, response[512], addressstring2[128], stringbuf[16384];
1143
1144         if (sv.active)
1145         {
1146                 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1147                 {
1148                         // received a command string - strip off the packaging and put it
1149                         // into our string buffer with NULL termination
1150                         data += 4;
1151                         length -= 4;
1152                         length = min(length, (int)sizeof(stringbuf) - 1);
1153                         memcpy(stringbuf, data, length);
1154                         stringbuf[length] = 0;
1155                         string = stringbuf;
1156
1157                         if (developer.integer)
1158                         {
1159                                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1160                                 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
1161                                 Com_HexDumpToConsole(data, length);
1162                         }
1163
1164                         if (length >= 12 && !memcmp(string, "getchallenge", 12))
1165                         {
1166                                 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
1167                                 {
1168                                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
1169                                                 break;
1170                                         if (besttime > challenge[i].time)
1171                                                 besttime = challenge[best = i].time;
1172                                 }
1173                                 // if we did not find an exact match, choose the oldest and
1174                                 // update address and string
1175                                 if (i == MAX_CHALLENGES)
1176                                 {
1177                                         i = best;
1178                                         challenge[i].address = *peeraddress;
1179                                         NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
1180                                 }
1181                                 challenge[i].time = realtime;
1182                                 // send the challenge
1183                                 NetConn_WriteString(mysocket, va("\377\377\377\377challenge %s", challenge[i].string), peeraddress);
1184                                 return true;
1185                         }
1186                         if (length > 8 && !memcmp(string, "connect\\", 8))
1187                         {
1188                                 string += 7;
1189                                 length -= 7;
1190                                 if ((s = SearchInfostring(string, "challenge")))
1191                                 {
1192                                         // validate the challenge
1193                                         for (i = 0;i < MAX_CHALLENGES;i++)
1194                                                 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
1195                                                         break;
1196                                         if (i < MAX_CHALLENGES)
1197                                         {
1198                                                 // check engine protocol
1199                                                 if (strcmp(SearchInfostring(string, "protocol"), "darkplaces 3"))
1200                                                 {
1201                                                         if (developer.integer)
1202                                                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
1203                                                         NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
1204                                                 }
1205                                                 else
1206                                                 {
1207                                                         // see if this is a duplicate connection request
1208                                                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
1209                                                                 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
1210                                                                         break;
1211                                                         if (clientnum < svs.maxclients)
1212                                                         {
1213                                                                 // duplicate connection request
1214                                                                 if (realtime - client->connecttime < 2.0)
1215                                                                 {
1216                                                                         // client is still trying to connect,
1217                                                                         // so we send a duplicate reply
1218                                                                         if (developer.integer)
1219                                                                                 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
1220                                                                         NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
1221                                                                 }
1222                                                                 // only kick if old connection seems dead
1223                                                                 if (realtime - client->netconnection->lastMessageTime >= net_messagerejointimeout.value)
1224                                                                 {
1225                                                                         // kick off connection and await retry
1226                                                                         client->deadsocket = true;
1227                                                                 }
1228                                                         }
1229                                                         else
1230                                                         {
1231                                                                 // this is a new client, find a slot
1232                                                                 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
1233                                                                         if (!client->active)
1234                                                                                 break;
1235                                                                 if (clientnum < svs.maxclients)
1236                                                                 {
1237                                                                         // prepare the client struct
1238                                                                         if ((conn = NetConn_Open(mysocket, peeraddress)))
1239                                                                         {
1240                                                                                 // allocated connection
1241                                                                                 LHNETADDRESS_ToString(peeraddress, conn->address, sizeof(conn->address), true);
1242                                                                                 if (developer.integer)
1243                                                                                         Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
1244                                                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
1245                                                                                 // now set up the client
1246                                                                                 SV_ConnectClient(clientnum, conn);
1247                                                                                 NetConn_Heartbeat(1);
1248                                                                         }
1249                                                                 }
1250                                                                 else
1251                                                                 {
1252                                                                         // server is full
1253                                                                         if (developer.integer)
1254                                                                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
1255                                                                         NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
1256                                                                 }
1257                                                         }
1258                                                 }
1259                                         }
1260                                 }
1261                                 return true;
1262                         }
1263                         if (length >= 7 && !memcmp(string, "getinfo", 7))
1264                         {
1265                                 const char *challenge = NULL;
1266                                 // If there was a challenge in the getinfo message
1267                                 if (length > 8 && string[7] == ' ')
1268                                         challenge = string + 8;
1269                                 for (i = 0, n = 0;i < svs.maxclients;i++)
1270                                         if (svs.clients[i].active)
1271                                                 n++;
1272                                 responselength = snprintf(response, sizeof(response), "\377\377\377\377infoResponse\x0A"
1273                                                         "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
1274                                                         "\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d%s%s",
1275                                                         gamename, com_modname, svs.maxclients, n,
1276                                                         sv.name, hostname.string, NET_PROTOCOL_VERSION, challenge ? "\\challenge\\" : "", challenge ? challenge : "");
1277                                 // does it fit in the buffer?
1278                                 if (responselength < (int)sizeof(response))
1279                                 {
1280                                         if (developer.integer)
1281                                                 Con_Printf("Sending reply to master %s - %s\n", addressstring2, response);
1282                                         NetConn_WriteString(mysocket, response, peeraddress);
1283                                 }
1284                                 return true;
1285                         }
1286                         /*
1287                         if (!strncmp(string, "ping", 4))
1288                         {
1289                                 if (developer.integer)
1290                                         Con_Printf("Received ping from %s, sending ack\n", UDP_AddrToString(readaddr));
1291                                 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
1292                                 return true;
1293                         }
1294                         if (!strncmp(string, "ack", 3))
1295                                 return true;
1296                         */
1297                         // we may not have liked the packet, but it was a command packet, so
1298                         // we're done processing this packet now
1299                         return true;
1300                 }
1301                 // LordHavoc: disabled netquake control packet support in server
1302 #if 0
1303                 {
1304                         int c, control;
1305                         // netquake control packets, supported for compatibility only
1306                         if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
1307                         {
1308                                 c = data[4];
1309                                 data += 5;
1310                                 length -= 5;
1311                                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1312                                 switch (c)
1313                                 {
1314                                 case CCREQ_CONNECT:
1315                                         //if (developer.integer)
1316                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
1317                                         if (length >= (int)strlen("QUAKE") + 1 + 1)
1318                                         {
1319                                                 if (memcmp(data, "QUAKE", strlen("QUAKE") + 1) != 0 || (int)data[strlen("QUAKE") + 1] != NET_PROTOCOL_VERSION)
1320                                                 {
1321                                                         if (developer.integer)
1322                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
1323                                                         SZ_Clear(&net_message);
1324                                                         // save space for the header, filled in later
1325                                                         MSG_WriteLong(&net_message, 0);
1326                                                         MSG_WriteByte(&net_message, CCREP_REJECT);
1327                                                         MSG_WriteString(&net_message, "Incompatible version.\n");
1328                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1329                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1330                                                         SZ_Clear(&net_message);
1331                                                 }
1332                                                 else
1333                                                 {
1334                                                         // see if this is a duplicate connection request
1335                                                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
1336                                                                 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
1337                                                                         break;
1338                                                         if (clientnum < svs.maxclients)
1339                                                         {
1340                                                                 // duplicate connection request
1341                                                                 if (realtime - client->connecttime < 2.0)
1342                                                                 {
1343                                                                         // client is still trying to connect,
1344                                                                         // so we send a duplicate reply
1345                                                                         if (developer.integer)
1346                                                                                 Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
1347                                                                         SZ_Clear(&net_message);
1348                                                                         // save space for the header, filled in later
1349                                                                         MSG_WriteLong(&net_message, 0);
1350                                                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
1351                                                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
1352                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1353                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1354                                                                         SZ_Clear(&net_message);
1355                                                                 }
1356                                                                 else if (realtime - client->netconnection->lastMessageTime >= net_messagerejointimeout.value)
1357                                                                 {
1358                                                                         // the old client hasn't sent us anything
1359                                                                         // in quite a while, so kick off and let
1360                                                                         // the retry take care of it...
1361                                                                         client->deadsocket = true;
1362                                                                 }
1363                                                         }
1364                                                         else
1365                                                         {
1366                                                                 // this is a new client, find a slot
1367                                                                 for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
1368                                                                         if (!client->active)
1369                                                                                 break;
1370                                                                 if (clientnum < svs.maxclients && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
1371                                                                 {
1372                                                                         // connect to the client
1373                                                                         // everything is allocated, just fill in the details
1374                                                                         strlcpy (conn->address, addressstring2, sizeof (conn->address));
1375                                                                         if (developer.integer)
1376                                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
1377                                                                         // send back the info about the server connection
1378                                                                         SZ_Clear(&net_message);
1379                                                                         // save space for the header, filled in later
1380                                                                         MSG_WriteLong(&net_message, 0);
1381                                                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
1382                                                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
1383                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1384                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1385                                                                         SZ_Clear(&net_message);
1386                                                                         // now set up the client struct
1387                                                                         SV_ConnectClient(clientnum, conn);
1388                                                                         NetConn_Heartbeat(1);
1389                                                                 }
1390                                                                 else
1391                                                                 {
1392                                                                         //if (developer.integer)
1393                                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
1394                                                                         // no room; try to let player know
1395                                                                         SZ_Clear(&net_message);
1396                                                                         // save space for the header, filled in later
1397                                                                         MSG_WriteLong(&net_message, 0);
1398                                                                         MSG_WriteByte(&net_message, CCREP_REJECT);
1399                                                                         MSG_WriteString(&net_message, "Server is full.\n");
1400                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1401                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1402                                                                         SZ_Clear(&net_message);
1403                                                                 }
1404                                                         }
1405                                                 }
1406                                         }
1407                                         break;
1408 #if 0
1409                                 case CCREQ_SERVER_INFO:
1410                                         if (developer.integer)
1411                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
1412                                         if (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
1413                                         {
1414                                                 if (developer.integer)
1415                                                         Con_Printf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
1416                                                 SZ_Clear(&net_message);
1417                                                 // save space for the header, filled in later
1418                                                 MSG_WriteLong(&net_message, 0);
1419                                                 MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
1420                                                 UDP_GetSocketAddr(UDP_acceptSock, &newaddr);
1421                                                 MSG_WriteString(&net_message, UDP_AddrToString(&newaddr));
1422                                                 MSG_WriteString(&net_message, hostname.string);
1423                                                 MSG_WriteString(&net_message, sv.name);
1424                                                 MSG_WriteByte(&net_message, net_activeconnections);
1425                                                 MSG_WriteByte(&net_message, svs.maxclients);
1426                                                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
1427                                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1428                                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1429                                                 SZ_Clear(&net_message);
1430                                         }
1431                                         break;
1432                                 case CCREQ_PLAYER_INFO:
1433                                         if (developer.integer)
1434                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
1435                                         if (sv.active)
1436                                         {
1437                                                 int playerNumber, activeNumber, clientNumber;
1438                                                 client_t *client;
1439
1440                                                 playerNumber = MSG_ReadByte();
1441                                                 activeNumber = -1;
1442                                                 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
1443                                                         if (client->active && ++activeNumber == playerNumber)
1444                                                                 break;
1445                                                 if (clientNumber != svs.maxclients)
1446                                                 {
1447                                                         SZ_Clear(&net_message);
1448                                                         // save space for the header, filled in later
1449                                                         MSG_WriteLong(&net_message, 0);
1450                                                         MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
1451                                                         MSG_WriteByte(&net_message, playerNumber);
1452                                                         MSG_WriteString(&net_message, client->name);
1453                                                         MSG_WriteLong(&net_message, client->colors);
1454                                                         MSG_WriteLong(&net_message, (int)client->edict->v->frags);
1455                                                         MSG_WriteLong(&net_message, (int)(realtime - client->connecttime));
1456                                                         MSG_WriteString(&net_message, client->netconnection ? client->netconnection->address : "botclient");
1457                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1458                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1459                                                         SZ_Clear(&net_message);
1460                                                 }
1461                                         }
1462                                         break;
1463                                 case CCREQ_RULE_INFO:
1464                                         if (developer.integer)
1465                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
1466                                         if (sv.active)
1467                                         {
1468                                                 char *prevCvarName;
1469                                                 cvar_t *var;
1470
1471                                                 // find the search start location
1472                                                 prevCvarName = MSG_ReadString();
1473                                                 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
1474
1475                                                 // send the response
1476                                                 SZ_Clear(&net_message);
1477                                                 // save space for the header, filled in later
1478                                                 MSG_WriteLong(&net_message, 0);
1479                                                 MSG_WriteByte(&net_message, CCREP_RULE_INFO);
1480                                                 if (var)
1481                                                 {
1482                                                         MSG_WriteString(&net_message, var->name);
1483                                                         MSG_WriteString(&net_message, var->string);
1484                                                 }
1485                                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1486                                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1487                                                 SZ_Clear(&net_message);
1488                                         }
1489                                         break;
1490 #endif
1491                                 default:
1492                                         break;
1493                                 }
1494                                 // we may not have liked the packet, but it was a valid control
1495                                 // packet, so we're done processing this packet now
1496                                 return true;
1497                         }
1498                 }
1499 #endif
1500                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1501                 {
1502                         if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
1503                         {
1504                                 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
1505                                         SV_ReadClientMessage();
1506                                 return ret;
1507                         }
1508                 }
1509         }
1510         return 0;
1511 }
1512
1513 void NetConn_ServerFrame(void)
1514 {
1515         int i, length;
1516         lhnetaddress_t peeraddress;
1517         netconn_t *conn;
1518         NetConn_UpdateServerStuff();
1519         for (i = 0;i < sv_numsockets;i++)
1520                 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
1521                         NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
1522         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1523         {
1524                 // never timeout loopback connections
1525                 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
1526                 {
1527                         Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
1528                         SV_DropClient(false);
1529                 }
1530         }
1531         for (conn = netconn_list;conn;conn = conn->next)
1532                 NetConn_ReSendMessage(conn);
1533 }
1534
1535 void NetConn_QueryMasters(void)
1536 {
1537         int i;
1538         int masternum;
1539         lhnetaddress_t masteraddress;
1540         char request[256];
1541
1542         if (hostcache_cachecount >= HOSTCACHE_TOTALSIZE)
1543                 return;
1544
1545         for (i = 0;i < cl_numsockets;i++)
1546         {
1547                 if (cl_sockets[i])
1548                 {
1549 #if 0
1550                         // search LAN
1551 #if 1
1552                         UDP_Broadcast(UDP_controlSock, "\377\377\377\377getinfo", 11);
1553 #else
1554                         SZ_Clear(&net_message);
1555                         // save space for the header, filled in later
1556                         MSG_WriteLong(&net_message, 0);
1557                         MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
1558                         MSG_WriteString(&net_message, "QUAKE");
1559                         MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
1560                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1561                         UDP_Broadcast(UDP_controlSock, net_message.data, net_message.cursize);
1562                         SZ_Clear(&net_message);
1563 #endif
1564 #endif
1565
1566                         // build the getservers
1567                         snprintf(request, sizeof(request), "\377\377\377\377getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
1568
1569                         // search internet
1570                         for (masternum = 0;sv_masters[masternum].name;masternum++)
1571                         {
1572                                 if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, MASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
1573                                 {
1574                                         masterquerycount++;
1575                                         NetConn_WriteString(cl_sockets[i], request, &masteraddress);
1576                                 }
1577                         }
1578                 }
1579         }
1580         if (!masterquerycount)
1581         {
1582                 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
1583                 M_Update_Return_Reason("No network");
1584         }
1585 }
1586
1587 void NetConn_Heartbeat(int priority)
1588 {
1589         lhnetaddress_t masteraddress;
1590         int masternum;
1591         lhnetsocket_t *mysocket;
1592
1593         // if it's a state change (client connected), limit next heartbeat to no
1594         // more than 30 sec in the future
1595         if (priority == 1 && nextheartbeattime > realtime + 30.0)
1596                 nextheartbeattime = realtime + 30.0;
1597
1598         // limit heartbeatperiod to 30 to 270 second range,
1599         // lower limit is to avoid abusing master servers with excess traffic,
1600         // upper limit is to avoid timing out on the master server (which uses
1601         // 300 sec timeout)
1602         if (sv_heartbeatperiod.value < 30)
1603                 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
1604         if (sv_heartbeatperiod.value > 270)
1605                 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
1606
1607         // make advertising optional and don't advertise singleplayer games, and
1608         // only send a heartbeat as often as the admin wants
1609         if (sv.active && sv_public.integer && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
1610         {
1611                 nextheartbeattime = realtime + sv_heartbeatperiod.value;
1612                 for (masternum = 0;sv_masters[masternum].name;masternum++)
1613                         if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, MASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
1614                                 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
1615         }
1616 }
1617
1618 int NetConn_SendToAll(sizebuf_t *data, double blocktime)
1619 {
1620         int i, count = 0;
1621         qbyte sent[MAX_SCOREBOARD];
1622
1623         memset(sent, 0, sizeof(sent));
1624
1625         // simultaneously wait for the first CanSendMessage and send the message,
1626         // then wait for a second CanSendMessage (verifying it was received), or
1627         // the client drops and is no longer counted
1628         // the loop aborts when either it runs out of clients to send to, or a
1629         // timeout expires
1630         blocktime += Sys_DoubleTime();
1631         do
1632         {
1633                 count = 0;
1634                 NetConn_ClientFrame();
1635                 NetConn_ServerFrame();
1636                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1637                 {
1638                         if (host_client->netconnection)
1639                         {
1640                                 if (NetConn_CanSendMessage(host_client->netconnection))
1641                                 {
1642                                         if (!sent[i])
1643                                                 NetConn_SendReliableMessage(host_client->netconnection, data);
1644                                         sent[i] = true;
1645                                 }
1646                                 if (!NetConn_CanSendMessage(host_client->netconnection))
1647                                         count++;
1648                         }
1649                 }
1650         }
1651         while (count && Sys_DoubleTime() < blocktime);
1652         return count;
1653 }
1654
1655 static void Net_Heartbeat_f(void)
1656 {
1657         if (sv.active)
1658                 NetConn_Heartbeat(2);
1659         else
1660                 Con_Print("No server running, can not heartbeat to master server.\n");
1661 }
1662
1663 void PrintStats(netconn_t *conn)
1664 {
1665         Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, conn->canSend, conn->sendSequence, conn->receiveSequence);
1666 }
1667
1668 void Net_Stats_f(void)
1669 {
1670         netconn_t *conn;
1671         Con_Printf("unreliable messages sent   = %i\n", unreliableMessagesSent);
1672         Con_Printf("unreliable messages recv   = %i\n", unreliableMessagesReceived);
1673         Con_Printf("reliable messages sent     = %i\n", reliableMessagesSent);
1674         Con_Printf("reliable messages received = %i\n", reliableMessagesReceived);
1675         Con_Printf("packetsSent                = %i\n", packetsSent);
1676         Con_Printf("packetsReSent              = %i\n", packetsReSent);
1677         Con_Printf("packetsReceived            = %i\n", packetsReceived);
1678         Con_Printf("receivedDuplicateCount     = %i\n", receivedDuplicateCount);
1679         Con_Printf("droppedDatagrams           = %i\n", droppedDatagrams);
1680         Con_Print("connections                =\n");
1681         for (conn = netconn_list;conn;conn = conn->next)
1682                 PrintStats(conn);
1683 }
1684
1685 void Net_Slist_f(void)
1686 {
1687         HostCache_ResetMask();
1688         hostcache_sortbyfield = HCIF_PING;
1689         hostcache_sortdescending = false;
1690     if (m_state != m_slist) {
1691                 Con_Print("Sending requests to master servers\n");
1692                 HostCache_QueryList();
1693                 hostcache_consoleoutput = true;
1694                 Con_Print("Listening for replies...\n");
1695         } else
1696                 HostCache_QueryList();
1697 }
1698
1699 void NetConn_Init(void)
1700 {
1701         int i;
1702         lhnetaddress_t tempaddress;
1703         netconn_mempool = Mem_AllocPool("Networking", 0, NULL);
1704         Cmd_AddCommand("net_stats", Net_Stats_f);
1705         Cmd_AddCommand("net_slist", Net_Slist_f);
1706         Cmd_AddCommand("heartbeat", Net_Heartbeat_f);
1707         Cvar_RegisterVariable(&net_messagetimeout);
1708         Cvar_RegisterVariable(&net_messagerejointimeout);
1709         Cvar_RegisterVariable(&net_connecttimeout);
1710         Cvar_RegisterVariable(&cl_netlocalping);
1711         Cvar_RegisterVariable(&cl_netpacketloss);
1712         Cvar_RegisterVariable(&hostname);
1713         Cvar_RegisterVariable(&developer_networking);
1714         Cvar_RegisterVariable(&cl_netport);
1715         Cvar_RegisterVariable(&sv_netport);
1716         Cvar_RegisterVariable(&net_address);
1717         //Cvar_RegisterVariable(&net_address_ipv6);
1718         Cvar_RegisterVariable(&sv_public);
1719         Cvar_RegisterVariable(&sv_heartbeatperiod);
1720         for (i = 0;sv_masters[i].name;i++)
1721                 Cvar_RegisterVariable(&sv_masters[i]);
1722 // COMMANDLINEOPTION: Server: -ip <ipaddress> sets the ip address of this machine for purposes of networking (default 0.0.0.0 also known as INADDR_ANY), use only if you have multiple network adapters and need to choose one specifically.
1723         if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
1724         {
1725                 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
1726                 {
1727                         Con_Printf("-ip option used, setting net_address to \"%s\"\n");
1728                         Cvar_SetQuick(&net_address, com_argv[i + 1]);
1729                 }
1730                 else
1731                         Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
1732         }
1733 // COMMANDLINEOPTION: Server: -port <portnumber> sets the port to use for a server (default 26000, the same port as QUAKE itself), useful if you host multiple servers on your machine
1734         if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
1735         {
1736                 i = atoi(com_argv[i + 1]);
1737                 if (i >= 0 && i < 65536)
1738                 {
1739                         Con_Printf("-port option used, setting port cvar to %i\n", i);
1740                         Cvar_SetValueQuick(&sv_netport, i);
1741                 }
1742                 else
1743                         Con_Printf("-port option used, but %i is not a valid port number\n", i);
1744         }
1745         cl_numsockets = 0;
1746         sv_numsockets = 0;
1747         SZ_Alloc(&net_message, NET_MAXMESSAGE, "net_message");
1748         LHNET_Init();
1749 }
1750
1751 void NetConn_Shutdown(void)
1752 {
1753         NetConn_CloseClientPorts();
1754         NetConn_CloseServerPorts();
1755         LHNET_Shutdown();
1756 }
1757