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