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