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