]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - netconn.c
optimized surface rendering to surface->groupmesh->data_* array pointers directly...
[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
48 cvar_t net_messagetimeout = {0, "net_messagetimeout","300"};
49 cvar_t net_messagerejointimeout = {0, "net_messagerejointimeout","10"};
50 cvar_t net_connecttimeout = {0, "net_connecttimeout","10"};
51 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED"};
52 cvar_t developer_networking = {0, "developer_networking", "0"};
53
54 cvar_t cl_netlocalping = {0, "cl_netlocalping","0"};
55 static cvar_t cl_netpacketloss = {0, "cl_netpacketloss","0"};
56
57
58 /* statistic counters */
59 static int packetsSent = 0;
60 static int packetsReSent = 0;
61 static int packetsReceived = 0;
62 static int receivedDuplicateCount = 0;
63 static int droppedDatagrams = 0;
64
65 static int unreliableMessagesSent = 0;
66 static int unreliableMessagesReceived = 0;
67 static int reliableMessagesSent = 0;
68 static int reliableMessagesReceived = 0;
69
70 double masterquerytime = -1000;
71 int masterquerycount = 0;
72 int masterreplycount = 0;
73 int serverquerycount = 0;
74 int serverreplycount = 0;
75
76 static qbyte sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
77 static qbyte readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
78
79 int cl_numsockets;
80 lhnetsocket_t *cl_sockets[16];
81 int sv_numsockets;
82 lhnetsocket_t *sv_sockets[16];
83
84 netconn_t *netconn_list = NULL;
85 mempool_t *netconn_mempool = NULL;
86
87 cvar_t cl_netport = {0, "cl_port", "0"};
88 cvar_t sv_netport = {0, "port", "26000"};
89 cvar_t net_address = {0, "net_address", "0.0.0.0"};
90 //cvar_t net_netaddress_ipv6 = {0, "net_address_ipv6", "[0:0:0:0:0:0:0:0]"};
91
92 // ServerList interface
93 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
94 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
95
96 serverlist_infofield_t serverlist_sortbyfield;
97 qboolean serverlist_sortdescending;
98
99 int serverlist_viewcount = 0;
100 serverlist_entry_t *serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
101
102 int serverlist_cachecount;
103 serverlist_entry_t serverlist_cache[SERVERLIST_TOTALSIZE];
104
105 qboolean serverlist_consoleoutput;
106
107 // helper function to insert a value into the viewset
108 // spare entries will be removed
109 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
110 {
111     int i;
112         if( serverlist_viewcount == SERVERLIST_VIEWLISTSIZE )
113                 return;
114
115         for( i = serverlist_viewcount ; i > index ; i-- )
116                 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
117
118         serverlist_viewlist[index] = entry;
119         serverlist_viewcount++;
120 }
121
122 // we suppose serverlist_viewcount to be valid, ie > 0
123 static void _ServerList_ViewList_Helper_Remove( int index )
124 {
125         serverlist_viewcount--;
126         for( ; index < serverlist_viewcount ; index++ )
127                 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
128 }
129
130 // returns true if A should be inserted before B
131 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
132 {
133         int result = 0; // > 0 if for numbers A > B and for text if A < B
134
135         switch( serverlist_sortbyfield ) {
136                 case SLIF_PING:
137                         result = A->info.ping - B->info.ping;
138                         break;
139                 case SLIF_MAXPLAYERS:
140                         result = A->info.maxplayers - B->info.maxplayers;
141                         break;
142                 case SLIF_NUMPLAYERS:
143                         result = A->info.numplayers - B->info.numplayers;
144                         break;
145                 case SLIF_PROTOCOL:
146                         result = A->info.protocol - B->info.protocol;
147                         break;
148                 case SLIF_CNAME:
149                         result = strcmp( B->info.cname, A->info.cname );
150                         break;
151                 case SLIF_GAME:
152                         result = strcmp( B->info.game, A->info.game );
153                         break;
154                 case SLIF_MAP:
155                         result = strcmp( B->info.map, A->info.map );
156                         break;
157                 case SLIF_MOD:
158                         result = strcmp( B->info.mod, A->info.mod );
159                         break;
160                 case SLIF_NAME:
161                         result = strcmp( B->info.name, A->info.name );
162                         break;
163                 default:
164                         Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
165                         break;
166         }
167
168         if( serverlist_sortdescending )
169                 return result > 0;
170         return result < 0;
171 }
172
173 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
174 {
175         // This should actually be done with some intermediate and end-of-function return
176         switch( op ) {
177                 case SLMO_LESS:
178                         return A < B;
179                 case SLMO_LESSEQUAL:
180                         return A <= B;
181                 case SLMO_EQUAL:
182                         return A == B;
183                 case SLMO_GREATER:
184                         return A > B;
185                 case SLMO_NOTEQUAL:
186                         return A != B;
187                 case SLMO_GREATEREQUAL:
188                         return A >= B;
189                 default:
190                         Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
191                         return false;
192         }
193 }
194
195 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
196
197         // Same here, also using an intermediate & final return would be more appropriate
198         // A info B mask
199         switch( op ) {
200                 case SLMO_CONTAINS:
201                         return *B && !!strstr( A, B ); // we want a real bool
202                 case SLMO_NOTCONTAIN:
203                         return !*B || !strstr( A, B );
204                 case SLMO_LESS:
205                         return strcmp( A, B ) < 0;
206                 case SLMO_LESSEQUAL:
207                         return strcmp( A, B ) <= 0;
208                 case SLMO_EQUAL:
209                         return strcmp( A, B ) == 0;
210                 case SLMO_GREATER:
211                         return strcmp( A, B ) > 0;
212                 case SLMO_NOTEQUAL:
213                         return strcmp( A, B ) != 0;
214                 case SLMO_GREATEREQUAL:
215                         return strcmp( A, B ) >= 0;
216                 default:
217                         Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
218                         return false;
219         }
220 }
221
222 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
223 {
224         if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
225                 return false;
226         if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
227                 return false;
228         if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
229                 return false;
230         if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
231                 return false;
232         if( *mask->info.cname
233                 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
234                 return false;
235         if( *mask->info.game
236                 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
237                 return false;
238         if( *mask->info.mod
239                 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
240                 return false;
241         if( *mask->info.map
242                 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
243                 return false;
244         if( *mask->info.name
245                 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
246                 return false;
247         return true;
248 }
249
250 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
251 {
252         int start, end, mid;
253
254         if( serverlist_viewcount == SERVERLIST_VIEWLISTSIZE )
255                 return;
256
257         // now check whether it passes through the masks
258         for( start = 0 ; serverlist_andmasks[start].active && start < SERVERLIST_ANDMASKCOUNT ; start++ )
259                 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
260                         return;
261
262         for( start = 0 ; serverlist_ormasks[start].active && start < SERVERLIST_ORMASKCOUNT ; start++ )
263                 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
264                         break;
265         if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
266                 return;
267
268         if( !serverlist_viewcount ) {
269                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
270                 return;
271         }
272         // ok, insert it, we just need to find out where exactly:
273
274         // two special cases
275         // check whether to insert it as new first item
276         if( _ServerList_Entry_Compare( entry, serverlist_viewlist[0] ) ) {
277                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
278                 return;
279         } // check whether to insert it as new last item
280         else if( !_ServerList_Entry_Compare( entry, serverlist_viewlist[serverlist_viewcount - 1] ) ) {
281                 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
282                 return;
283         }
284         start = 0;
285         end = serverlist_viewcount - 1;
286         while( end > start + 1 )
287         {
288                 mid = (start + end) / 2;
289                 // test the item that lies in the middle between start and end
290                 if( _ServerList_Entry_Compare( entry, serverlist_viewlist[mid] ) )
291                         // the item has to be in the upper half
292                         end = mid;
293                 else
294                         // the item has to be in the lower half
295                         start = mid;
296         }
297         _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
298 }
299
300 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
301 {
302         int i;
303         for( i = 0; i < serverlist_viewcount; i++ )
304         {
305                 if (serverlist_viewlist[i] == entry)
306                 {
307                         _ServerList_ViewList_Helper_Remove(i);
308                         break;
309                 }
310         }
311 }
312
313 void ServerList_RebuildViewList(void)
314 {
315         int i;
316
317         serverlist_viewcount = 0;
318         for( i = 0 ; i < serverlist_cachecount ; i++ )
319                 if( serverlist_cache[i].finished )
320                         ServerList_ViewList_Insert( &serverlist_cache[i] );
321 }
322
323 void ServerList_ResetMasks(void)
324 {
325         memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
326         memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
327 }
328
329 #if 0
330 static void _ServerList_Test(void)
331 {
332         int i;
333         for( i = 0 ; i < 1024 ; i++ ) {
334                 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_t ) );
335                 serverlist_cache[serverlist_cachecount].info.ping = rand() % 450 + 250;
336                 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, 128, "Black's ServerList Test %i", i );
337                 serverlist_cache[serverlist_cachecount].finished = true;
338                 sprintf( serverlist_cache[serverlist_cachecount].line1, "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
339                 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
340                 serverlist_cachecount++;
341         }
342 }
343 #endif
344
345 void ServerList_QueryList(void)
346 {
347         masterquerytime = realtime;
348         masterquerycount = 0;
349         masterreplycount = 0;
350         serverquerycount = 0;
351         serverreplycount = 0;
352         serverlist_cachecount = 0;
353         serverlist_viewcount = 0;
354         serverlist_consoleoutput = false;
355         NetConn_QueryMasters();
356
357         //_ServerList_Test();
358 }
359
360 // rest
361
362 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
363 {
364         int length = LHNET_Read(mysocket, data, maxlength, peeraddress);
365         int i;
366         if (length == 0)
367                 return 0;
368         if (cl_netpacketloss.integer)
369                 for (i = 0;i < cl_numsockets;i++)
370                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss.integer)
371                                 return 0;
372         if (developer_networking.integer)
373         {
374                 char addressstring[128], addressstring2[128];
375                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
376                 if (length > 0)
377                 {
378                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
379                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", mysocket, addressstring, data, maxlength, peeraddress, length, addressstring2);
380                         Com_HexDumpToConsole(data, length);
381                 }
382                 else
383                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", mysocket, addressstring, data, maxlength, peeraddress, length);
384         }
385         return length;
386 }
387
388 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
389 {
390         int ret;
391         int i;
392         if (cl_netpacketloss.integer)
393                 for (i = 0;i < cl_numsockets;i++)
394                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss.integer)
395                                 return length;
396         ret = LHNET_Write(mysocket, data, length, peeraddress);
397         if (developer_networking.integer)
398         {
399                 char addressstring[128], addressstring2[128];
400                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
401                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
402                 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", mysocket, addressstring, data, length, peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
403                 Com_HexDumpToConsole(data, length);
404         }
405         return ret;
406 }
407
408 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
409 {
410         // note this does not include the trailing NULL because we add that in the parser
411         return NetConn_Write(mysocket, string, strlen(string), peeraddress);
412 }
413
414 int NetConn_SendReliableMessage(netconn_t *conn, sizebuf_t *data)
415 {
416         unsigned int packetLen;
417         unsigned int dataLen;
418         unsigned int eom;
419         unsigned int *header;
420
421 //#ifdef DEBUG
422         if (data->cursize == 0)
423                 Sys_Error("Datagram_SendMessage: zero length message\n");
424
425         if (data->cursize > (int)sizeof(conn->sendMessage))
426                 Sys_Error("Datagram_SendMessage: message too big (%u > %u)\n", data->cursize, sizeof(conn->sendMessage));
427
428         if (conn->canSend == false)
429                 Sys_Error("SendMessage: called with canSend == false\n");
430 //#endif
431
432         memcpy(conn->sendMessage, data->data, data->cursize);
433         conn->sendMessageLength = data->cursize;
434
435         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
436         {
437                 dataLen = conn->sendMessageLength;
438                 eom = NETFLAG_EOM;
439         }
440         else
441         {
442                 dataLen = MAX_PACKETFRAGMENT;
443                 eom = 0;
444         }
445
446         packetLen = NET_HEADERSIZE + dataLen;
447
448         header = (void *)sendbuffer;
449         header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
450         header[1] = BigLong(conn->sendSequence);
451         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
452
453         conn->sendSequence++;
454         conn->canSend = false;
455
456         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
457                 return -1;
458
459         conn->lastSendTime = realtime;
460         packetsSent++;
461         reliableMessagesSent++;
462         return 1;
463 }
464
465 static void NetConn_SendMessageNext(netconn_t *conn)
466 {
467         unsigned int packetLen;
468         unsigned int dataLen;
469         unsigned int eom;
470         unsigned int *header;
471
472         if (conn->sendMessageLength && !conn->canSend && conn->sendNext)
473         {
474                 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
475                 {
476                         dataLen = conn->sendMessageLength;
477                         eom = NETFLAG_EOM;
478                 }
479                 else
480                 {
481                         dataLen = MAX_PACKETFRAGMENT;
482                         eom = 0;
483                 }
484
485                 packetLen = NET_HEADERSIZE + dataLen;
486
487                 header = (void *)sendbuffer;
488                 header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
489                 header[1] = BigLong(conn->sendSequence);
490                 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
491
492                 conn->sendSequence++;
493                 conn->sendNext = false;
494
495                 if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
496                         return;
497
498                 conn->lastSendTime = realtime;
499                 packetsSent++;
500         }
501 }
502
503 static void NetConn_ReSendMessage(netconn_t *conn)
504 {
505         unsigned int packetLen;
506         unsigned int dataLen;
507         unsigned int eom;
508         unsigned int *header;
509
510         if (conn->sendMessageLength && !conn->canSend && (realtime - conn->lastSendTime) > 1.0)
511         {
512                 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
513                 {
514                         dataLen = conn->sendMessageLength;
515                         eom = NETFLAG_EOM;
516                 }
517                 else
518                 {
519                         dataLen = MAX_PACKETFRAGMENT;
520                         eom = 0;
521                 }
522
523                 packetLen = NET_HEADERSIZE + dataLen;
524
525                 header = (void *)sendbuffer;
526                 header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
527                 header[1] = BigLong(conn->sendSequence - 1);
528                 memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
529
530                 conn->sendNext = false;
531
532                 if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
533                         return;
534
535                 conn->lastSendTime = realtime;
536                 packetsReSent++;
537         }
538 }
539
540 qboolean NetConn_CanSendMessage(netconn_t *conn)
541 {
542         return conn->canSend;
543 }
544
545 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data)
546 {
547         int packetLen;
548         int *header;
549
550         packetLen = NET_HEADERSIZE + data->cursize;
551
552 //#ifdef DEBUG
553         if (data->cursize == 0)
554                 Sys_Error("Datagram_SendUnreliableMessage: zero length message\n");
555
556         if (packetLen > (int)sizeof(sendbuffer))
557                 Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
558 //#endif
559
560         header = (void *)sendbuffer;
561         header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE);
562         header[1] = BigLong(conn->unreliableSendSequence);
563         memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
564
565         conn->unreliableSendSequence++;
566
567         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
568                 return -1;
569
570         packetsSent++;
571         unreliableMessagesSent++;
572         return 1;
573 }
574
575 void NetConn_CloseClientPorts(void)
576 {
577         for (;cl_numsockets > 0;cl_numsockets--)
578                 if (cl_sockets[cl_numsockets - 1])
579                         LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
580 }
581
582 void NetConn_OpenClientPort(const char *addressstring, int defaultport)
583 {
584         lhnetaddress_t address;
585         lhnetsocket_t *s;
586         char addressstring2[1024];
587         if (LHNETADDRESS_FromString(&address, addressstring, defaultport))
588         {
589                 if ((s = LHNET_OpenSocket_Connectionless(&address)))
590                 {
591                         cl_sockets[cl_numsockets++] = s;
592                         LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
593                         Con_Printf("Client opened a socket on address %s\n", addressstring2);
594                 }
595                 else
596                 {
597                         LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
598                         Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
599                 }
600         }
601         else
602                 Con_Printf("Client unable to parse address %s\n", addressstring);
603 }
604
605 void NetConn_OpenClientPorts(void)
606 {
607         int port;
608         NetConn_CloseClientPorts();
609         port = bound(0, cl_netport.integer, 65535);
610         if (cl_netport.integer != port)
611                 Cvar_SetValueQuick(&cl_netport, port);
612         Con_Printf("Client using port %i\n", port);
613         NetConn_OpenClientPort("local:2", 0);
614         NetConn_OpenClientPort(net_address.string, port);
615         //NetConn_OpenClientPort(net_address_ipv6.string, port);
616 }
617
618 void NetConn_CloseServerPorts(void)
619 {
620         for (;sv_numsockets > 0;sv_numsockets--)
621                 if (sv_sockets[sv_numsockets - 1])
622                         LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
623 }
624
625 void NetConn_OpenServerPort(const char *addressstring, int defaultport)
626 {
627         lhnetaddress_t address;
628         lhnetsocket_t *s;
629         char addressstring2[1024];
630         if (LHNETADDRESS_FromString(&address, addressstring, defaultport))
631         {
632                 if ((s = LHNET_OpenSocket_Connectionless(&address)))
633                 {
634                         sv_sockets[sv_numsockets++] = s;
635                         LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
636                         Con_Printf("Server listening on address %s\n", addressstring2);
637                 }
638                 else
639                 {
640                         LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
641                         Con_Printf("Server failed to open socket on address %s\n", addressstring2);
642                 }
643         }
644         else
645                 Con_Printf("Server unable to parse address %s\n", addressstring);
646 }
647
648 void NetConn_OpenServerPorts(int opennetports)
649 {
650         int port;
651         NetConn_CloseServerPorts();
652         port = bound(0, sv_netport.integer, 65535);
653         if (port == 0)
654                 port = 26000;
655         Con_Printf("Server using port %i\n", port);
656         if (sv_netport.integer != port)
657                 Cvar_SetValueQuick(&sv_netport, port);
658         if (cls.state != ca_dedicated)
659                 NetConn_OpenServerPort("local:1", 0);
660         if (opennetports)
661         {
662                 NetConn_OpenServerPort(net_address.string, port);
663                 //NetConn_OpenServerPort(net_address_ipv6.string, port);
664         }
665         if (sv_numsockets == 0)
666                 Host_Error("NetConn_OpenServerPorts: unable to open any ports!\n");
667 }
668
669 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
670 {
671         int i, a = LHNETADDRESS_GetAddressType(address);
672         for (i = 0;i < cl_numsockets;i++)
673                 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
674                         return cl_sockets[i];
675         return NULL;
676 }
677
678 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
679 {
680         int i, a = LHNETADDRESS_GetAddressType(address);
681         for (i = 0;i < sv_numsockets;i++)
682                 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
683                         return sv_sockets[i];
684         return NULL;
685 }
686
687 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
688 {
689         netconn_t *conn;
690         conn = Mem_Alloc(netconn_mempool, sizeof(*conn));
691         conn->mysocket = mysocket;
692         conn->peeraddress = *peeraddress;
693         conn->canSend = true;
694         conn->lastMessageTime = realtime;
695         // LordHavoc: (inspired by ProQuake) use a short connect timeout to
696         // reduce effectiveness of connection request floods
697         conn->timeout = realtime + net_connecttimeout.value;
698         LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
699         conn->next = netconn_list;
700         netconn_list = conn;
701         return conn;
702 }
703
704 void NetConn_Close(netconn_t *conn)
705 {
706         netconn_t *c;
707         // remove connection from list
708         if (conn == netconn_list)
709                 netconn_list = conn->next;
710         else
711         {
712                 for (c = netconn_list;c;c = c->next)
713                 {
714                         if (c->next == conn)
715                         {
716                                 c->next = conn->next;
717                                 break;
718                         }
719                 }
720                 // not found in list, we'll avoid crashing here...
721                 if (!c)
722                         return;
723         }
724         // free connection
725         Mem_Free(conn);
726 }
727
728 static int clientport = -1;
729 static int clientport2 = -1;
730 static int hostport = -1;
731 static void NetConn_UpdateServerStuff(void)
732 {
733         if (cls.state != ca_dedicated)
734         {
735                 if (clientport2 != cl_netport.integer)
736                 {
737                         clientport2 = cl_netport.integer;
738                         if (cls.state == ca_connected)
739                                 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
740                 }
741                 if (cls.state == ca_disconnected && clientport != clientport2)
742                 {
743                         clientport = clientport2;
744                         NetConn_CloseClientPorts();
745                 }
746                 if (cl_numsockets == 0)
747                         NetConn_OpenClientPorts();
748         }
749
750         if (hostport != sv_netport.integer)
751         {
752                 hostport = sv_netport.integer;
753                 if (sv.active)
754                         Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
755         }
756 }
757
758 int NetConn_ReceivedMessage(netconn_t *conn, qbyte *data, int length)
759 {
760         unsigned int count;
761         unsigned int flags;
762         unsigned int sequence;
763
764         if (length >= 8)
765         {
766                 length = BigLong(((int *)data)[0]);
767                 flags = length & ~NETFLAG_LENGTH_MASK;
768                 length &= NETFLAG_LENGTH_MASK;
769                 // control packets were already handled
770                 if (!(flags & NETFLAG_CTL))
771                 {
772                         sequence = BigLong(((int *)data)[1]);
773                         packetsReceived++;
774                         data += 8;
775                         length -= 8;
776                         if (flags & NETFLAG_UNRELIABLE)
777                         {
778                                 if (sequence >= conn->unreliableReceiveSequence)
779                                 {
780                                         if (sequence > conn->unreliableReceiveSequence)
781                                         {
782                                                 count = sequence - conn->unreliableReceiveSequence;
783                                                 droppedDatagrams += count;
784                                                 Con_DPrintf("Dropped %u datagram(s)\n", count);
785                                         }
786                                         conn->unreliableReceiveSequence = sequence + 1;
787                                         conn->lastMessageTime = realtime;
788                                         conn->timeout = realtime + net_messagetimeout.value;
789                                         unreliableMessagesReceived++;
790                                         if (length > 0)
791                                         {
792                                                 SZ_Clear(&net_message);
793                                                 SZ_Write(&net_message, data, length);
794                                                 MSG_BeginReading();
795                                                 return 2;
796                                         }
797                                 }
798                                 else
799                                         Con_DPrint("Got a stale datagram\n");
800                                 return 1;
801                         }
802                         else if (flags & NETFLAG_ACK)
803                         {
804                                 if (sequence == (conn->sendSequence - 1))
805                                 {
806                                         if (sequence == conn->ackSequence)
807                                         {
808                                                 conn->ackSequence++;
809                                                 if (conn->ackSequence != conn->sendSequence)
810                                                         Con_DPrint("ack sequencing error\n");
811                                                 conn->lastMessageTime = realtime;
812                                                 conn->timeout = realtime + net_messagetimeout.value;
813                                                 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
814                                                 if (conn->sendMessageLength > 0)
815                                                 {
816                                                         memcpy(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
817                                                         conn->sendNext = true;
818                                                         NetConn_SendMessageNext(conn);
819                                                 }
820                                                 else
821                                                 {
822                                                         conn->sendMessageLength = 0;
823                                                         conn->canSend = true;
824                                                 }
825                                         }
826                                         else
827                                                 Con_DPrint("Duplicate ACK received\n");
828                                 }
829                                 else
830                                         Con_DPrint("Stale ACK received\n");
831                                 return 1;
832                         }
833                         else if (flags & NETFLAG_DATA)
834                         {
835                                 unsigned int temppacket[2];
836                                 temppacket[0] = BigLong(8 | NETFLAG_ACK);
837                                 temppacket[1] = BigLong(sequence);
838                                 NetConn_Write(conn->mysocket, (qbyte *)temppacket, 8, &conn->peeraddress);
839                                 if (sequence == conn->receiveSequence)
840                                 {
841                                         conn->lastMessageTime = realtime;
842                                         conn->timeout = realtime + net_messagetimeout.value;
843                                         conn->receiveSequence++;
844                                         memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
845                                         conn->receiveMessageLength += length;
846                                         if (flags & NETFLAG_EOM)
847                                         {
848                                                 reliableMessagesReceived++;
849                                                 length = conn->receiveMessageLength;
850                                                 conn->receiveMessageLength = 0;
851                                                 if (length > 0)
852                                                 {
853                                                         SZ_Clear(&net_message);
854                                                         SZ_Write(&net_message, conn->receiveMessage, length);
855                                                         MSG_BeginReading();
856                                                         return 2;
857                                                 }
858                                         }
859                                 }
860                                 else
861                                         receivedDuplicateCount++;
862                                 return 1;
863                         }
864                 }
865         }
866         return 0;
867 }
868
869 void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
870 {
871         cls.connect_trying = false;
872         M_Update_Return_Reason("");
873         // the connection request succeeded, stop current connection and set up a new connection
874         CL_Disconnect();
875         cls.netcon = NetConn_Open(mysocket, peeraddress);
876         Con_Printf("Connection accepted to %s\n", cls.netcon->address);
877         key_dest = key_game;
878         m_state = m_none;
879         cls.demonum = -1;                       // not in the demo loop now
880         cls.state = ca_connected;
881         cls.signon = 0;                         // need all the signon messages before playing
882         CL_ClearState();
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("Networking", 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         SZ_Alloc(&net_message, NET_MAXMESSAGE, "net_message");
1842         LHNET_Init();
1843 }
1844
1845 void NetConn_Shutdown(void)
1846 {
1847         NetConn_CloseClientPorts();
1848         NetConn_CloseServerPorts();
1849         LHNET_Shutdown();
1850 }
1851