]> icculus.org git repositories - divverent/darkplaces.git/blob - netconn.c
fix wrong cvar name
[divverent/darkplaces.git] / netconn.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 */
22
23 #include "quakedef.h"
24 #include "lhnet.h"
25
26 #define QWMASTER_PORT 27000
27 #define DPMASTER_PORT 27950
28
29 // note this defaults on for dedicated servers, off for listen servers
30 cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect"};
31 static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
32
33 static cvar_t sv_masters [] =
34 {
35         {CVAR_SAVE, "sv_master1", "", "user-chosen master server 1"},
36         {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
37         {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
38         {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
39         {0, "sv_masterextra1", "ghdigital.com", "default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
40         {0, "sv_masterextra2", "dpmaster.deathmask.net", "default master server 2 (admin: Willis)"}, // admin: Willis
41         {0, "sv_masterextra3", "dpmaster.tchr.no", "default master server 3 (admin: tChr)"}, // admin: tChr
42         {0, NULL, NULL, NULL}
43 };
44
45 static cvar_t sv_qwmasters [] =
46 {
47         {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
48         {CVAR_SAVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
49         {CVAR_SAVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
50         {CVAR_SAVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
51         {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
52         {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
53         {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
54         {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
55         {0, "sv_qwmasterextra5", "kubus.rulez.pl:27000", "Poland master server. (admin: unknown)"},
56         {0, NULL, NULL, NULL}
57 };
58
59 static double nextheartbeattime = 0;
60
61 sizebuf_t net_message;
62 static unsigned char net_message_buf[NET_MAXMESSAGE];
63
64 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
65 cvar_t net_connecttimeout = {0, "net_connecttimeout","10", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods)"};
66 cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods)"};
67 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
68 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
69
70 cvar_t cl_netlocalping = {0, "cl_netlocalping","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
71 static cvar_t cl_netpacketloss_send = {0, "cl_netpacketloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
72 static cvar_t cl_netpacketloss_receive = {0, "cl_netpacketloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
73 static cvar_t net_slist_queriespersecond = {0, "net_slist_queriespersecond", "20", "how many server information requests to send per second"};
74 static cvar_t net_slist_queriesperframe = {0, "net_slist_queriesperframe", "4", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
75 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
76 static cvar_t net_slist_pause = {0, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
77 static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
78
79 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible"};
80 static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode"};
81 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
82
83 /* statistic counters */
84 static int packetsSent = 0;
85 static int packetsReSent = 0;
86 static int packetsReceived = 0;
87 static int receivedDuplicateCount = 0;
88 static int droppedDatagrams = 0;
89
90 static int unreliableMessagesSent = 0;
91 static int unreliableMessagesReceived = 0;
92 static int reliableMessagesSent = 0;
93 static int reliableMessagesReceived = 0;
94
95 double masterquerytime = -1000;
96 int masterquerycount = 0;
97 int masterreplycount = 0;
98 int serverquerycount = 0;
99 int serverreplycount = 0;
100
101 // this is only false if there are still servers left to query
102 static qboolean serverlist_querysleep = true;
103 static qboolean serverlist_paused = false;
104 // this is pushed a second or two ahead of realtime whenever a master server
105 // reply is received, to avoid issuing queries while master replies are still
106 // flooding in (which would make a mess of the ping times)
107 static double serverlist_querywaittime = 0;
108
109 static unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
110 static unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
111
112 static int cl_numsockets;
113 static lhnetsocket_t *cl_sockets[16];
114 static int sv_numsockets;
115 static lhnetsocket_t *sv_sockets[16];
116
117 netconn_t *netconn_list = NULL;
118 mempool_t *netconn_mempool = NULL;
119
120 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
121 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
122 cvar_t net_address = {0, "net_address", "0.0.0.0", "network address to open ports on"};
123 //cvar_t net_netaddress_ipv6 = {0, "net_address_ipv6", "[0:0:0:0:0:0:0:0]", "network address to open ipv6 ports on"};
124
125 char net_extresponse[NET_EXTRESPONSE_MAX][1400];
126 int net_extresponse_count = 0;
127 int net_extresponse_last = 0;
128
129 // ServerList interface
130 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
131 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
132
133 serverlist_infofield_t serverlist_sortbyfield;
134 qboolean serverlist_sortdescending;
135
136 int serverlist_viewcount = 0;
137 serverlist_entry_t *serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
138
139 int serverlist_cachecount;
140 serverlist_entry_t serverlist_cache[SERVERLIST_TOTALSIZE];
141
142 qboolean serverlist_consoleoutput;
143
144 // helper function to insert a value into the viewset
145 // spare entries will be removed
146 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
147 {
148     int i;
149         if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
150                 i = serverlist_viewcount++;
151         } else {
152                 i = SERVERLIST_VIEWLISTSIZE - 1;
153         }
154
155         for( ; i > index ; i-- )
156                 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
157
158         serverlist_viewlist[index] = entry;
159 }
160
161 // we suppose serverlist_viewcount to be valid, ie > 0
162 static void _ServerList_ViewList_Helper_Remove( int index )
163 {
164         serverlist_viewcount--;
165         for( ; index < serverlist_viewcount ; index++ )
166                 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
167 }
168
169 // returns true if A should be inserted before B
170 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
171 {
172         int result = 0; // > 0 if for numbers A > B and for text if A < B
173
174         switch( serverlist_sortbyfield ) {
175                 case SLIF_PING:
176                         result = A->info.ping - B->info.ping;
177                         break;
178                 case SLIF_MAXPLAYERS:
179                         result = A->info.maxplayers - B->info.maxplayers;
180                         break;
181                 case SLIF_NUMPLAYERS:
182                         result = A->info.numplayers - B->info.numplayers;
183                         break;
184                 case SLIF_NUMBOTS:
185                         result = A->info.numbots - B->info.numbots;
186                         break;
187                 case SLIF_NUMHUMANS:
188                         result = A->info.numhumans - B->info.numhumans;
189                         break;
190                 case SLIF_FREESLOTS:
191                         result = A->info.freeslots - B->info.freeslots;
192                         break;
193                 case SLIF_PROTOCOL:
194                         result = A->info.protocol - B->info.protocol;
195                         break;
196                 case SLIF_CNAME:
197                         result = strcmp( B->info.cname, A->info.cname );
198                         break;
199                 case SLIF_GAME:
200                         result = strcasecmp( B->info.game, A->info.game );
201                         break;
202                 case SLIF_MAP:
203                         result = strcasecmp( B->info.map, A->info.map );
204                         break;
205                 case SLIF_MOD:
206                         result = strcasecmp( B->info.mod, A->info.mod );
207                         break;
208                 case SLIF_NAME:
209                         result = strcasecmp( B->info.name, A->info.name );
210                         break;
211                 default:
212                         Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
213                         break;
214         }
215
216         if( serverlist_sortdescending )
217                 return result > 0;
218         if (result != 0)
219                 return result < 0;
220         // if the chosen sort key is identical, sort by index
221         // (makes this a stable sort, so that later replies from servers won't
222         //  shuffle the servers around when they have the same ping)
223         return A < B;
224 }
225
226 static qboolean _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
227 {
228         // This should actually be done with some intermediate and end-of-function return
229         switch( op ) {
230                 case SLMO_LESS:
231                         return A < B;
232                 case SLMO_LESSEQUAL:
233                         return A <= B;
234                 case SLMO_EQUAL:
235                         return A == B;
236                 case SLMO_GREATER:
237                         return A > B;
238                 case SLMO_NOTEQUAL:
239                         return A != B;
240                 case SLMO_GREATEREQUAL:
241                 case SLMO_CONTAINS:
242                 case SLMO_NOTCONTAIN:
243                         return A >= B;
244                 default:
245                         Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
246                         return false;
247         }
248 }
249
250 static qboolean _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
251 {
252         int i;
253         char bufferA[ 256 ], bufferB[ 256 ]; // should be more than enough
254         for (i = 0;i < (int)sizeof(bufferA)-1 && A[i];i++)
255                 bufferA[i] = (A[i] >= 'A' && A[i] <= 'Z') ? (A[i] + 'a' - 'A') : A[i];
256         bufferA[i] = 0;
257         for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
258                 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
259         bufferB[i] = 0;
260
261         // Same here, also using an intermediate & final return would be more appropriate
262         // A info B mask
263         switch( op ) {
264                 case SLMO_CONTAINS:
265                         return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
266                 case SLMO_NOTCONTAIN:
267                         return !*bufferB || !strstr( bufferA, bufferB );
268                 case SLMO_LESS:
269                         return strcmp( bufferA, bufferB ) < 0;
270                 case SLMO_LESSEQUAL:
271                         return strcmp( bufferA, bufferB ) <= 0;
272                 case SLMO_EQUAL:
273                         return strcmp( bufferA, bufferB ) == 0;
274                 case SLMO_GREATER:
275                         return strcmp( bufferA, bufferB ) > 0;
276                 case SLMO_NOTEQUAL:
277                         return strcmp( bufferA, bufferB ) != 0;
278                 case SLMO_GREATEREQUAL:
279                         return strcmp( bufferA, bufferB ) >= 0;
280                 default:
281                         Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
282                         return false;
283         }
284 }
285
286 static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
287 {
288         if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
289                 return false;
290         if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
291                 return false;
292         if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
293                 return false;
294         if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
295                 return false;
296         if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
297                 return false;
298         if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
299                 return false;
300         if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
301                 return false;
302         if( *mask->info.cname
303                 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
304                 return false;
305         if( *mask->info.game
306                 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
307                 return false;
308         if( *mask->info.mod
309                 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
310                 return false;
311         if( *mask->info.map
312                 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
313                 return false;
314         if( *mask->info.name
315                 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
316                 return false;
317         return true;
318 }
319
320 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
321 {
322         int start, end, mid;
323
324         // reject incompatible servers
325         if (entry->info.gameversion != gameversion.integer)
326                 return;
327
328         // FIXME: change this to be more readable (...)
329         // now check whether it passes through the masks
330         for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
331                 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
332                         return;
333
334         for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
335                 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
336                         break;
337         if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
338                 return;
339
340         if( !serverlist_viewcount ) {
341                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
342                 return;
343         }
344         // ok, insert it, we just need to find out where exactly:
345
346         // two special cases
347         // check whether to insert it as new first item
348         if( _ServerList_Entry_Compare( entry, serverlist_viewlist[0] ) ) {
349                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
350                 return;
351         } // check whether to insert it as new last item
352         else if( !_ServerList_Entry_Compare( entry, serverlist_viewlist[serverlist_viewcount - 1] ) ) {
353                 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
354                 return;
355         }
356         start = 0;
357         end = serverlist_viewcount - 1;
358         while( end > start + 1 )
359         {
360                 mid = (start + end) / 2;
361                 // test the item that lies in the middle between start and end
362                 if( _ServerList_Entry_Compare( entry, serverlist_viewlist[mid] ) )
363                         // the item has to be in the upper half
364                         end = mid;
365                 else
366                         // the item has to be in the lower half
367                         start = mid;
368         }
369         _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
370 }
371
372 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
373 {
374         int i;
375         for( i = 0; i < serverlist_viewcount; i++ )
376         {
377                 if (serverlist_viewlist[i] == entry)
378                 {
379                         _ServerList_ViewList_Helper_Remove(i);
380                         break;
381                 }
382         }
383 }
384
385 void ServerList_RebuildViewList(void)
386 {
387         int i;
388
389         serverlist_viewcount = 0;
390         for( i = 0 ; i < serverlist_cachecount ; i++ ) {
391                 serverlist_entry_t *entry = &serverlist_cache[i];
392                 // also display entries that are currently being refreshed [11/8/2007 Black]
393                 if( entry->query == SQS_QUERIED || entry->query == SQS_REFRESHING )
394                         ServerList_ViewList_Insert( entry );
395         }
396 }
397
398 void ServerList_ResetMasks(void)
399 {
400         int i;
401
402         memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
403         memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
404         // numbots needs to be compared to -1 to always succeed
405         for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
406                 serverlist_andmasks[i].info.numbots = -1;
407         for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
408                 serverlist_ormasks[i].info.numbots = -1;
409 }
410
411 void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspointer)
412 {
413         int i;
414         int numplayers = 0, maxplayers = 0;
415         for (i = 0;i < serverlist_cachecount;i++)
416         {
417                 if (serverlist_cache[i].query == SQS_QUERIED)
418                 {
419                         numplayers += serverlist_cache[i].info.numhumans;
420                         maxplayers += serverlist_cache[i].info.maxplayers;
421                 }
422         }
423         *numplayerspointer = numplayers;
424         *maxplayerspointer = maxplayers;
425 }
426
427 #if 0
428 static void _ServerList_Test(void)
429 {
430         int i;
431         for( i = 0 ; i < 1024 ; i++ ) {
432                 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
433                 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
434                 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
435                 serverlist_cache[serverlist_cachecount].finished = true;
436                 dpsnprintf( serverlist_cache[serverlist_cachecount].line1, sizeof(serverlist_cache[serverlist_cachecount].info.line1), "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
437                 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
438                 serverlist_cachecount++;
439         }
440 }
441 #endif
442
443 void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryqw, qboolean consoleoutput)
444 {
445         masterquerytime = realtime;
446         masterquerycount = 0;
447         masterreplycount = 0;
448         if( resetcache ) {
449                 serverquerycount = 0;
450                 serverreplycount = 0;
451                 serverlist_cachecount = 0;
452                 serverlist_viewcount = 0;
453         } else {
454                 // refresh all entries
455                 int n;
456                 for( n = 0 ; n < serverlist_cachecount ; n++ ) {
457                         serverlist_entry_t *entry = &serverlist_cache[ n ];
458                         entry->query = SQS_REFRESHING;
459                         entry->querycounter = 0;
460                 }
461         }
462         serverlist_consoleoutput = consoleoutput;
463
464         //_ServerList_Test();
465
466         NetConn_QueryMasters(querydp, queryqw);
467 }
468
469 // rest
470
471 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
472 {
473         int length = LHNET_Read(mysocket, data, maxlength, peeraddress);
474         int i;
475         if (length == 0)
476                 return 0;
477         if (cl_netpacketloss_receive.integer)
478                 for (i = 0;i < cl_numsockets;i++)
479                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_receive.integer)
480                                 return 0;
481         if (developer_networking.integer)
482         {
483                 char addressstring[128], addressstring2[128];
484                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
485                 if (length > 0)
486                 {
487                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
488                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", mysocket, addressstring, data, maxlength, peeraddress, length, addressstring2);
489                         Com_HexDumpToConsole((unsigned char *)data, length);
490                 }
491                 else
492                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", mysocket, addressstring, data, maxlength, peeraddress, length);
493         }
494         return length;
495 }
496
497 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
498 {
499         int ret;
500         int i;
501         if (cl_netpacketloss_send.integer)
502                 for (i = 0;i < cl_numsockets;i++)
503                         if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
504                                 return length;
505         ret = LHNET_Write(mysocket, data, length, peeraddress);
506         if (developer_networking.integer)
507         {
508                 char addressstring[128], addressstring2[128];
509                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
510                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
511                 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", mysocket, addressstring, data, length, peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
512                 Com_HexDumpToConsole((unsigned char *)data, length);
513         }
514         return ret;
515 }
516
517 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
518 {
519         // note this does not include the trailing NULL because we add that in the parser
520         return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
521 }
522
523 qboolean NetConn_CanSend(netconn_t *conn)
524 {
525         conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
526         conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
527         conn->outgoing_reliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
528         conn->outgoing_acksize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
529         if (realtime > conn->cleartime)
530                 return true;
531         else
532         {
533                 conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_CHOKEDPACKET;
534                 return false;
535         }
536 }
537
538 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, qboolean quakesignon_suppressreliables)
539 {
540         int totallen = 0;
541
542         // if this packet was supposedly choked, but we find ourselves sending one
543         // anyway, make sure the size counting starts at zero
544         // (this mostly happens on level changes and disconnects and such)
545         if (conn->outgoing_unreliablesize[conn->outgoing_packetcounter] == NETGRAPH_CHOKEDPACKET)
546                 conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
547
548         if (protocol == PROTOCOL_QUAKEWORLD)
549         {
550                 int packetLen;
551                 qboolean sendreliable;
552
553                 // note that it is ok to send empty messages to the qw server,
554                 // otherwise it won't respond to us at all
555
556                 sendreliable = false;
557                 // if the remote side dropped the last reliable message, resend it
558                 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
559                         sendreliable = true;
560                 // if the reliable transmit buffer is empty, copy the current message out
561                 if (!conn->sendMessageLength && conn->message.cursize)
562                 {
563                         memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
564                         conn->sendMessageLength = conn->message.cursize;
565                         SZ_Clear(&conn->message); // clear the message buffer
566                         conn->qw.reliable_sequence ^= 1;
567                         sendreliable = true;
568                 }
569                 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
570                 *((int *)(sendbuffer + 0)) = LittleLong((unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
571                 // last received unreliable packet number, and last received reliable packet number (0 or 1)
572                 *((int *)(sendbuffer + 4)) = LittleLong((unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
573                 packetLen = 8;
574                 conn->outgoing_unreliable_sequence++;
575                 // client sends qport in every packet
576                 if (conn == cls.netcon)
577                 {
578                         *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
579                         packetLen += 2;
580                         // also update cls.qw_outgoing_sequence
581                         cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
582                 }
583                 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
584                 {
585                         Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
586                         return -1;
587                 }
588
589                 conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += packetLen;
590
591                 // add the reliable message if there is one
592                 if (sendreliable)
593                 {
594                         conn->outgoing_reliablesize[conn->outgoing_packetcounter] += conn->sendMessageLength;
595                         memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
596                         packetLen += conn->sendMessageLength;
597                         conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
598                 }
599
600                 // add the unreliable message if possible
601                 if (packetLen + data->cursize <= 1400)
602                 {
603                         conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += data->cursize;
604                         memcpy(sendbuffer + packetLen, data->data, data->cursize);
605                         packetLen += data->cursize;
606                 }
607
608                 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
609
610                 packetsSent++;
611                 unreliableMessagesSent++;
612
613                 totallen += packetLen + 28;
614         }
615         else
616         {
617                 unsigned int packetLen;
618                 unsigned int dataLen;
619                 unsigned int eom;
620                 unsigned int *header;
621
622                 // if a reliable message fragment has been lost, send it again
623                 if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
624                 {
625                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
626                         {
627                                 dataLen = conn->sendMessageLength;
628                                 eom = NETFLAG_EOM;
629                         }
630                         else
631                         {
632                                 dataLen = MAX_PACKETFRAGMENT;
633                                 eom = 0;
634                         }
635
636                         packetLen = NET_HEADERSIZE + dataLen;
637
638                         header = (unsigned int *)sendbuffer;
639                         header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
640                         header[1] = BigLong(conn->nq.sendSequence - 1);
641                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
642
643                         conn->outgoing_reliablesize[conn->outgoing_packetcounter] += packetLen;
644
645                         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
646                         {
647                                 conn->lastSendTime = realtime;
648                                 packetsReSent++;
649                         }
650
651                         totallen += packetLen + 28;
652                 }
653
654                 // if we have a new reliable message to send, do so
655                 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
656                 {
657                         if (conn->message.cursize > (int)sizeof(conn->sendMessage))
658                         {
659                                 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
660                                 conn->message.overflowed = true;
661                                 return -1;
662                         }
663
664                         if (developer_networking.integer && conn == cls.netcon)
665                         {
666                                 Con_Print("client sending reliable message to server:\n");
667                                 SZ_HexDumpToConsole(&conn->message);
668                         }
669
670                         memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
671                         conn->sendMessageLength = conn->message.cursize;
672                         SZ_Clear(&conn->message);
673
674                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
675                         {
676                                 dataLen = conn->sendMessageLength;
677                                 eom = NETFLAG_EOM;
678                         }
679                         else
680                         {
681                                 dataLen = MAX_PACKETFRAGMENT;
682                                 eom = 0;
683                         }
684
685                         packetLen = NET_HEADERSIZE + dataLen;
686
687                         header = (unsigned int *)sendbuffer;
688                         header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
689                         header[1] = BigLong(conn->nq.sendSequence);
690                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
691
692                         conn->nq.sendSequence++;
693
694                         conn->outgoing_reliablesize[conn->outgoing_packetcounter] += packetLen;
695
696                         NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
697
698                         conn->lastSendTime = realtime;
699                         packetsSent++;
700                         reliableMessagesSent++;
701
702                         totallen += packetLen + 28;
703                 }
704
705                 // if we have an unreliable message to send, do so
706                 if (data->cursize)
707                 {
708                         packetLen = NET_HEADERSIZE + data->cursize;
709
710                         if (packetLen > (int)sizeof(sendbuffer))
711                         {
712                                 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
713                                 return -1;
714                         }
715
716                         header = (unsigned int *)sendbuffer;
717                         header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE);
718                         header[1] = BigLong(conn->outgoing_unreliable_sequence);
719                         memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
720
721                         conn->outgoing_unreliable_sequence++;
722
723                         conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += packetLen;
724
725                         NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
726
727                         packetsSent++;
728                         unreliableMessagesSent++;
729
730                         totallen += packetLen + 28;
731                 }
732         }
733
734         // delay later packets to obey rate limit
735         if (conn->cleartime < realtime - 0.1)
736                 conn->cleartime = realtime - 0.1;
737         conn->cleartime = conn->cleartime + (double)totallen / (double)rate;
738         if (conn->cleartime < realtime)
739                 conn->cleartime = realtime;
740
741         return 0;
742 }
743
744 qboolean NetConn_HaveClientPorts(void)
745 {
746         return !!cl_numsockets;
747 }
748
749 qboolean NetConn_HaveServerPorts(void)
750 {
751         return !!sv_numsockets;
752 }
753
754 void NetConn_CloseClientPorts(void)
755 {
756         for (;cl_numsockets > 0;cl_numsockets--)
757                 if (cl_sockets[cl_numsockets - 1])
758                         LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
759 }
760
761 void NetConn_OpenClientPort(const char *addressstring, int defaultport)
762 {
763         lhnetaddress_t address;
764         lhnetsocket_t *s;
765         char addressstring2[1024];
766         if (LHNETADDRESS_FromString(&address, addressstring, defaultport))
767         {
768                 if ((s = LHNET_OpenSocket_Connectionless(&address)))
769                 {
770                         cl_sockets[cl_numsockets++] = s;
771                         LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
772                         Con_Printf("Client opened a socket on address %s\n", addressstring2);
773                 }
774                 else
775                 {
776                         LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
777                         Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
778                 }
779         }
780         else
781                 Con_Printf("Client unable to parse address %s\n", addressstring);
782 }
783
784 void NetConn_OpenClientPorts(void)
785 {
786         int port;
787         NetConn_CloseClientPorts();
788         port = bound(0, cl_netport.integer, 65535);
789         if (cl_netport.integer != port)
790                 Cvar_SetValueQuick(&cl_netport, port);
791         if(port == 0)
792                 Con_Printf("Client using an automatically assigned port\n");
793         else
794                 Con_Printf("Client using port %i\n", port);
795         NetConn_OpenClientPort("local:2", 0);
796         NetConn_OpenClientPort(net_address.string, port);
797         //NetConn_OpenClientPort(net_address_ipv6.string, port);
798 }
799
800 void NetConn_CloseServerPorts(void)
801 {
802         for (;sv_numsockets > 0;sv_numsockets--)
803                 if (sv_sockets[sv_numsockets - 1])
804                         LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
805 }
806
807 void NetConn_OpenServerPort(const char *addressstring, int defaultport)
808 {
809         lhnetaddress_t address;
810         lhnetsocket_t *s;
811         int port;
812         char addressstring2[1024];
813
814         for (port = defaultport; port <= defaultport + 100; port++)
815         {
816                 if (LHNETADDRESS_FromString(&address, addressstring, port))
817                 {
818                         if ((s = LHNET_OpenSocket_Connectionless(&address)))
819                         {
820                                 sv_sockets[sv_numsockets++] = s;
821                                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
822                                 Con_Printf("Server listening on address %s\n", addressstring2);
823                                 break;
824                         }
825                         else
826                         {
827                                 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
828                                 Con_Printf("Server failed to open socket on address %s\n", addressstring2);
829                         }
830                 }
831                 else
832                 {
833                         Con_Printf("Server unable to parse address %s\n", addressstring);
834                         // if it cant parse one address, it wont be able to parse another for sure
835                         break;
836                 }
837         }
838 }
839
840 void NetConn_OpenServerPorts(int opennetports)
841 {
842         int port;
843         NetConn_CloseServerPorts();
844         NetConn_UpdateSockets();
845         port = bound(0, sv_netport.integer, 65535);
846         if (port == 0)
847                 port = 26000;
848         Con_Printf("Server using port %i\n", port);
849         if (sv_netport.integer != port)
850                 Cvar_SetValueQuick(&sv_netport, port);
851         if (cls.state != ca_dedicated)
852                 NetConn_OpenServerPort("local:1", 0);
853         if (opennetports)
854         {
855                 NetConn_OpenServerPort(net_address.string, port);
856                 //NetConn_OpenServerPort(net_address_ipv6.string, port);
857         }
858         if (sv_numsockets == 0)
859                 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
860 }
861
862 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
863 {
864         int i, a = LHNETADDRESS_GetAddressType(address);
865         for (i = 0;i < cl_numsockets;i++)
866                 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
867                         return cl_sockets[i];
868         return NULL;
869 }
870
871 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
872 {
873         int i, a = LHNETADDRESS_GetAddressType(address);
874         for (i = 0;i < sv_numsockets;i++)
875                 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
876                         return sv_sockets[i];
877         return NULL;
878 }
879
880 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
881 {
882         netconn_t *conn;
883         conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
884         conn->mysocket = mysocket;
885         conn->peeraddress = *peeraddress;
886         conn->lastMessageTime = realtime;
887         conn->message.data = conn->messagedata;
888         conn->message.maxsize = sizeof(conn->messagedata);
889         conn->message.cursize = 0;
890         // LordHavoc: (inspired by ProQuake) use a short connect timeout to
891         // reduce effectiveness of connection request floods
892         conn->timeout = realtime + net_connecttimeout.value;
893         LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
894         conn->next = netconn_list;
895         netconn_list = conn;
896         return conn;
897 }
898
899 void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress);
900 void NetConn_Close(netconn_t *conn)
901 {
902         netconn_t *c;
903         // remove connection from list
904
905         // allow the client to reconnect immediately
906         NetConn_ClearConnectFlood(&(conn->peeraddress));
907
908         if (conn == netconn_list)
909                 netconn_list = conn->next;
910         else
911         {
912                 for (c = netconn_list;c;c = c->next)
913                 {
914                         if (c->next == conn)
915                         {
916                                 c->next = conn->next;
917                                 break;
918                         }
919                 }
920                 // not found in list, we'll avoid crashing here...
921                 if (!c)
922                         return;
923         }
924         // free connection
925         Mem_Free(conn);
926 }
927
928 static int clientport = -1;
929 static int clientport2 = -1;
930 static int hostport = -1;
931 void NetConn_UpdateSockets(void)
932 {
933         if (cls.state != ca_dedicated)
934         {
935                 if (clientport2 != cl_netport.integer)
936                 {
937                         clientport2 = cl_netport.integer;
938                         if (cls.state == ca_connected)
939                                 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
940                 }
941                 if (cls.state == ca_disconnected && clientport != clientport2)
942                 {
943                         clientport = clientport2;
944                         NetConn_CloseClientPorts();
945                 }
946                 if (cl_numsockets == 0)
947                         NetConn_OpenClientPorts();
948         }
949
950         if (hostport != sv_netport.integer)
951         {
952                 hostport = sv_netport.integer;
953                 if (sv.active)
954                         Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
955         }
956 }
957
958 static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length, protocolversion_t protocol, double newtimeout)
959 {
960         int originallength = length;
961         if (length < 8)
962                 return 0;
963
964         if (protocol == PROTOCOL_QUAKEWORLD)
965         {
966                 int sequence, sequence_ack;
967                 int reliable_ack, reliable_message;
968                 int count;
969                 int qport;
970
971                 sequence = LittleLong(*((int *)(data + 0)));
972                 sequence_ack = LittleLong(*((int *)(data + 4)));
973                 data += 8;
974                 length -= 8;
975
976                 if (conn != cls.netcon)
977                 {
978                         // server only
979                         if (length < 2)
980                                 return 0;
981                         // TODO: use qport to identify that this client really is who they say they are?  (and elsewhere in the code to identify the connection without a port match?)
982                         qport = LittleShort(*((int *)(data + 8)));
983                         data += 2;
984                         length -= 2;
985                 }
986
987                 packetsReceived++;
988                 reliable_message = (sequence >> 31) & 1;
989                 reliable_ack = (sequence_ack >> 31) & 1;
990                 sequence &= ~(1<<31);
991                 sequence_ack &= ~(1<<31);
992                 if (sequence <= conn->qw.incoming_sequence)
993                 {
994                         //Con_DPrint("Got a stale datagram\n");
995                         return 0;
996                 }
997                 count = sequence - (conn->qw.incoming_sequence + 1);
998                 if (count > 0)
999                 {
1000                         droppedDatagrams += count;
1001                         //Con_DPrintf("Dropped %u datagram(s)\n", count);
1002                         while (count--)
1003                         {
1004                                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1005                                 conn->incoming_unreliablesize[conn->incoming_packetcounter] = NETGRAPH_LOSTPACKET;
1006                                 conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
1007                                 conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
1008                         }
1009                 }
1010                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1011                 conn->incoming_unreliablesize[conn->incoming_packetcounter] = originallength;
1012                 conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
1013                 conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
1014                 if (reliable_ack == conn->qw.reliable_sequence)
1015                 {
1016                         // received, now we will be able to send another reliable message
1017                         conn->sendMessageLength = 0;
1018                         reliableMessagesReceived++;
1019                 }
1020                 conn->qw.incoming_sequence = sequence;
1021                 if (conn == cls.netcon)
1022                         cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1023                 conn->qw.incoming_acknowledged = sequence_ack;
1024                 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1025                 if (reliable_message)
1026                         conn->qw.incoming_reliable_sequence ^= 1;
1027                 conn->lastMessageTime = realtime;
1028                 conn->timeout = realtime + newtimeout;
1029                 unreliableMessagesReceived++;
1030                 SZ_Clear(&net_message);
1031                 SZ_Write(&net_message, data, length);
1032                 MSG_BeginReading();
1033                 return 2;
1034         }
1035         else
1036         {
1037                 unsigned int count;
1038                 unsigned int flags;
1039                 unsigned int sequence;
1040                 int qlength;
1041
1042                 qlength = (unsigned int)BigLong(((int *)data)[0]);
1043                 flags = qlength & ~NETFLAG_LENGTH_MASK;
1044                 qlength &= NETFLAG_LENGTH_MASK;
1045                 // control packets were already handled
1046                 if (!(flags & NETFLAG_CTL) && qlength == length)
1047                 {
1048                         sequence = BigLong(((int *)data)[1]);
1049                         packetsReceived++;
1050                         data += 8;
1051                         length -= 8;
1052                         if (flags & NETFLAG_UNRELIABLE)
1053                         {
1054                                 if (sequence >= conn->nq.unreliableReceiveSequence)
1055                                 {
1056                                         if (sequence > conn->nq.unreliableReceiveSequence)
1057                                         {
1058                                                 count = sequence - conn->nq.unreliableReceiveSequence;
1059                                                 droppedDatagrams += count;
1060                                                 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1061                                                 while (count--)
1062                                                 {
1063                                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1064                                                         conn->incoming_unreliablesize[conn->incoming_packetcounter] = NETGRAPH_LOSTPACKET;
1065                                                         conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
1066                                                         conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
1067                                                 }
1068                                         }
1069                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1070                                         conn->incoming_unreliablesize[conn->incoming_packetcounter] = originallength;
1071                                         conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
1072                                         conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
1073                                         conn->nq.unreliableReceiveSequence = sequence + 1;
1074                                         conn->lastMessageTime = realtime;
1075                                         conn->timeout = realtime + newtimeout;
1076                                         unreliableMessagesReceived++;
1077                                         if (length > 0)
1078                                         {
1079                                                 SZ_Clear(&net_message);
1080                                                 SZ_Write(&net_message, data, length);
1081                                                 MSG_BeginReading();
1082                                                 return 2;
1083                                         }
1084                                 }
1085                                 //else
1086                                 //      Con_DPrint("Got a stale datagram\n");
1087                                 return 1;
1088                         }
1089                         else if (flags & NETFLAG_ACK)
1090                         {
1091                                 conn->incoming_acksize[conn->incoming_packetcounter] += originallength;
1092                                 if (sequence == (conn->nq.sendSequence - 1))
1093                                 {
1094                                         if (sequence == conn->nq.ackSequence)
1095                                         {
1096                                                 conn->nq.ackSequence++;
1097                                                 if (conn->nq.ackSequence != conn->nq.sendSequence)
1098                                                         Con_DPrint("ack sequencing error\n");
1099                                                 conn->lastMessageTime = realtime;
1100                                                 conn->timeout = realtime + newtimeout;
1101                                                 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1102                                                 {
1103                                                         unsigned int packetLen;
1104                                                         unsigned int dataLen;
1105                                                         unsigned int eom;
1106                                                         unsigned int *header;
1107
1108                                                         conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1109                                                         memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1110
1111                                                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1112                                                         {
1113                                                                 dataLen = conn->sendMessageLength;
1114                                                                 eom = NETFLAG_EOM;
1115                                                         }
1116                                                         else
1117                                                         {
1118                                                                 dataLen = MAX_PACKETFRAGMENT;
1119                                                                 eom = 0;
1120                                                         }
1121
1122                                                         packetLen = NET_HEADERSIZE + dataLen;
1123
1124                                                         header = (unsigned int *)sendbuffer;
1125                                                         header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
1126                                                         header[1] = BigLong(conn->nq.sendSequence);
1127                                                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1128
1129                                                         conn->nq.sendSequence++;
1130
1131                                                         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
1132                                                         {
1133                                                                 conn->lastSendTime = realtime;
1134                                                                 packetsSent++;
1135                                                         }
1136                                                 }
1137                                                 else
1138                                                         conn->sendMessageLength = 0;
1139                                         }
1140                                         //else
1141                                         //      Con_DPrint("Duplicate ACK received\n");
1142                                 }
1143                                 //else
1144                                 //      Con_DPrint("Stale ACK received\n");
1145                                 return 1;
1146                         }
1147                         else if (flags & NETFLAG_DATA)
1148                         {
1149                                 unsigned int temppacket[2];
1150                                 conn->incoming_reliablesize[conn->incoming_packetcounter] += originallength;
1151                                 conn->outgoing_acksize[conn->outgoing_packetcounter] += 8;
1152                                 temppacket[0] = BigLong(8 | NETFLAG_ACK);
1153                                 temppacket[1] = BigLong(sequence);
1154                                 NetConn_Write(conn->mysocket, (unsigned char *)temppacket, 8, &conn->peeraddress);
1155                                 if (sequence == conn->nq.receiveSequence)
1156                                 {
1157                                         conn->lastMessageTime = realtime;
1158                                         conn->timeout = realtime + newtimeout;
1159                                         conn->nq.receiveSequence++;
1160                                         if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1161                                                 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1162                                                 conn->receiveMessageLength += length;
1163                                         } else {
1164                                                 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1165                                                                         "Dropping the message!\n", sequence );
1166                                                 conn->receiveMessageLength = 0;
1167                                                 return 1;
1168                                         }
1169                                         if (flags & NETFLAG_EOM)
1170                                         {
1171                                                 reliableMessagesReceived++;
1172                                                 length = conn->receiveMessageLength;
1173                                                 conn->receiveMessageLength = 0;
1174                                                 if (length > 0)
1175                                                 {
1176                                                         SZ_Clear(&net_message);
1177                                                         SZ_Write(&net_message, conn->receiveMessage, length);
1178                                                         MSG_BeginReading();
1179                                                         return 2;
1180                                                 }
1181                                         }
1182                                 }
1183                                 else
1184                                         receivedDuplicateCount++;
1185                                 return 1;
1186                         }
1187                 }
1188         }
1189         return 0;
1190 }
1191
1192 void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1193 {
1194         cls.connect_trying = false;
1195         M_Update_Return_Reason("");
1196         // the connection request succeeded, stop current connection and set up a new connection
1197         CL_Disconnect();
1198         // if we're connecting to a remote server, shut down any local server
1199         if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
1200                 Host_ShutdownServer ();
1201         // allocate a net connection to keep track of things
1202         cls.netcon = NetConn_Open(mysocket, peeraddress);
1203         Con_Printf("Connection accepted to %s\n", cls.netcon->address);
1204         key_dest = key_game;
1205         m_state = m_none;
1206         cls.demonum = -1;                       // not in the demo loop now
1207         cls.state = ca_connected;
1208         cls.signon = 0;                         // need all the signon messages before playing
1209         cls.protocol = initialprotocol;
1210         // reset move sequence numbering on this new connection
1211         cls.servermovesequence = 0;
1212         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1213                 Cmd_ForwardStringToServer("new");
1214         if (cls.protocol == PROTOCOL_QUAKE)
1215         {
1216                 // write a keepalive (clc_nop) as it seems to greatly improve the
1217                 // chances of connecting to a netquake server
1218                 sizebuf_t msg;
1219                 unsigned char buf[4];
1220                 memset(&msg, 0, sizeof(msg));
1221                 msg.data = buf;
1222                 msg.maxsize = sizeof(buf);
1223                 MSG_WriteChar(&msg, clc_nop);
1224                 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, false);
1225         }
1226 }
1227
1228 int NetConn_IsLocalGame(void)
1229 {
1230         if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1231                 return true;
1232         return false;
1233 }
1234
1235 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
1236 {
1237         int n;
1238         int pingtime;
1239         serverlist_entry_t *entry = NULL;
1240
1241         // search the cache for this server and update it
1242         for (n = 0;n < serverlist_cachecount;n++) {
1243                 entry = &serverlist_cache[ n ];
1244                 if (!strcmp(addressstring, entry->info.cname))
1245                         break;
1246         }
1247
1248         if (n == serverlist_cachecount)
1249         {
1250                 // LAN search doesnt require an answer from the master server so we wont
1251                 // know the ping nor will it be initialized already...
1252
1253                 // find a slot
1254                 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1255                         return -1;
1256
1257                 entry = &serverlist_cache[n];
1258
1259                 memset(entry, 0, sizeof(*entry));
1260                 // store the data the engine cares about (address and ping)
1261                 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1262                 entry->info.ping = 100000;
1263                 entry->querytime = realtime;
1264                 // if not in the slist menu we should print the server to console
1265                 if (serverlist_consoleoutput)
1266                         Con_Printf("querying %s\n", addressstring);
1267                 ++serverlist_cachecount;
1268         }
1269         // if this is the first reply from this server, count it as having replied
1270         pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
1271         pingtime = bound(0, pingtime, 9999);
1272         if (entry->query == SQS_REFRESHING) {
1273                 entry->info.ping = pingtime;
1274                 entry->query = SQS_QUERIED;
1275         } else {
1276                 // convert to unsigned to catch the -1
1277                 // I still dont like this but its better than the old 10000 magic ping number - as in easier to type and read :( [11/8/2007 Black]
1278                 entry->info.ping = min((unsigned) entry->info.ping, (unsigned) pingtime);
1279                 serverreplycount++;
1280         }
1281         
1282         // other server info is updated by the caller
1283         return n;
1284 }
1285
1286 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1287 {
1288         serverlist_entry_t *entry = &serverlist_cache[n];
1289         serverlist_info_t *info = &entry->info;
1290         // update description strings for engine menu and console output
1291         dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
1292         dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game, (info->gameversion != gameversion.integer) ? '1' : '4', info->mod, info->map);
1293         if (entry->query == SQS_QUERIED)
1294         {
1295                 if(!serverlist_paused)
1296                         ServerList_ViewList_Remove(entry);
1297         }
1298         // if not in the slist menu we should print the server to console (if wanted)
1299         else if( serverlist_consoleoutput )
1300                 Con_Printf("%s\n%s\n", serverlist_cache[n].line1, serverlist_cache[n].line2);
1301         // and finally, update the view set
1302         if(!serverlist_paused)
1303                 ServerList_ViewList_Insert( entry );
1304         //      update the entry's state
1305         serverlist_cache[n].query = SQS_QUERIED;
1306 }
1307
1308 // returns true, if it's sensible to continue the processing
1309 static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol, const char *ipstring ) {
1310         int n;
1311         serverlist_entry_t *entry;
1312
1313         //      ignore the rest of the message if the serverlist is full
1314         if( serverlist_cachecount == SERVERLIST_TOTALSIZE )
1315                 return false;
1316         //      also ignore     it      if      we      have already queried    it      (other master server    response)
1317         for( n =        0 ; n   < serverlist_cachecount ; n++   )
1318                 if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
1319                         break;
1320
1321         entry = &serverlist_cache[n];
1322
1323         if( n < serverlist_cachecount ) {
1324                 // the entry has already been queried once or 
1325                 return true;
1326         }
1327
1328         memset(entry, 0, sizeof(entry));
1329         entry->protocol =       protocol;
1330         //      store   the data        the engine cares about (address and     ping)
1331         strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
1332         
1333         // no, then reset the ping right away
1334         entry->info.ping = -1;
1335         // we also want to increase the serverlist_cachecount then
1336         serverlist_cachecount++;
1337         serverquerycount++;
1338
1339         entry->query =  SQS_QUERYING;
1340
1341         return true;
1342 }
1343
1344 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
1345 {
1346         qboolean fromserver;
1347         int ret, c, control;
1348         const char *s;
1349         char *string, addressstring2[128], ipstring[32];
1350         char stringbuf[16384];
1351
1352         // quakeworld ingame packet
1353         fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
1354
1355         // convert the address to a string incase we need it
1356         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1357
1358         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
1359         {
1360                 // received a command string - strip off the packaging and put it
1361                 // into our string buffer with NULL termination
1362                 data += 4;
1363                 length -= 4;
1364                 length = min(length, (int)sizeof(stringbuf) - 1);
1365                 memcpy(stringbuf, data, length);
1366                 stringbuf[length] = 0;
1367                 string = stringbuf;
1368
1369                 if (developer_networking.integer)
1370                 {
1371                         Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
1372                         Com_HexDumpToConsole(data, length);
1373                 }
1374
1375                 if (length > 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
1376                 {
1377                         // darkplaces or quake3
1378                         char protocolnames[1400];
1379                         Protocol_Names(protocolnames, sizeof(protocolnames));
1380                         Con_Printf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
1381                         M_Update_Return_Reason("Got challenge response");
1382                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1383                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1384                         // TODO: add userinfo stuff here instead of using NQ commands?
1385                         NetConn_WriteString(mysocket, va("\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s\\challenge\\%s", protocolnames, string + 10), peeraddress);
1386                         return true;
1387                 }
1388                 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
1389                 {
1390                         // darkplaces or quake3
1391                         M_Update_Return_Reason("Accepted");
1392                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
1393                         return true;
1394                 }
1395                 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
1396                 {
1397                         char rejectreason[32];
1398                         cls.connect_trying = false;
1399                         string += 7;
1400                         length = max(length - 7, (int)sizeof(rejectreason) - 1);
1401                         memcpy(rejectreason, string, length);
1402                         rejectreason[length] = 0;
1403                         M_Update_Return_Reason(rejectreason);
1404                         return true;
1405                 }
1406                 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
1407                 {
1408                         serverlist_info_t *info;
1409                         int n;
1410
1411                         string += 13;
1412                         // search the cache for this server and update it
1413                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1414                         if (n < 0)
1415                                 return true;
1416
1417                         info = &serverlist_cache[n].info;
1418                         info->game[0] = 0;
1419                         info->mod[0]  = 0;
1420                         info->map[0]  = 0;
1421                         info->name[0] = 0;
1422                         info->protocol = -1;
1423                         info->numplayers = 0;
1424                         info->numbots = -1;
1425                         info->maxplayers  = 0;
1426                         info->gameversion = 0;
1427                         if ((s = SearchInfostring(string, "gamename"     )) != NULL) strlcpy(info->game, s, sizeof (info->game));
1428                         if ((s = SearchInfostring(string, "modname"      )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
1429                         if ((s = SearchInfostring(string, "mapname"      )) != NULL) strlcpy(info->map , s, sizeof (info->map ));
1430                         if ((s = SearchInfostring(string, "hostname"     )) != NULL) strlcpy(info->name, s, sizeof (info->name));
1431                         if ((s = SearchInfostring(string, "protocol"     )) != NULL) info->protocol = atoi(s);
1432                         if ((s = SearchInfostring(string, "clients"      )) != NULL) info->numplayers = atoi(s);
1433                         if ((s = SearchInfostring(string, "bots"         )) != NULL) info->numbots = atoi(s);
1434                         if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);
1435                         if ((s = SearchInfostring(string, "gameversion"  )) != NULL) info->gameversion = atoi(s);
1436                         info->numhumans = info->numplayers - max(0, info->numbots);
1437                         info->freeslots = info->maxplayers - info->numplayers;
1438
1439                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1440
1441                         return true;
1442                 }
1443                 if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1444                 {
1445                         // Extract the IP addresses
1446                         data += 18;
1447                         length -= 18;
1448                         masterreplycount++;
1449                         if (serverlist_consoleoutput)
1450                                 Con_Print("received DarkPlaces server list...\n");
1451                         while (length >= 7 && data[0] == '\\' && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF) && data[5] * 256 + data[6] != 0)
1452                         {
1453                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[1], data[2], data[3], data[4], data[5] * 256 + data[6]);
1454                                 if (serverlist_consoleoutput && developer_networking.integer)
1455                                         Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1456                                 
1457                                 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring ) ) {
1458                                         break;
1459                                 }
1460
1461                                 // move on to next address in packet
1462                                 data += 7;
1463                                 length -= 7;
1464                         }
1465                         // begin or resume serverlist queries
1466                         serverlist_querysleep = false;
1467                         serverlist_querywaittime = realtime + 3;
1468                         return true;
1469                 }
1470                 if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
1471                 {
1472                         // Extract the IP addresses
1473                         data += 2;
1474                         length -= 2;
1475                         masterreplycount++;
1476                         if (serverlist_consoleoutput)
1477                                 Con_Printf("received QuakeWorld server list from %s...\n", addressstring2);
1478                         while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
1479                         {
1480                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
1481                                 if (serverlist_consoleoutput && developer_networking.integer)
1482                                         Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
1483                                 
1484                                 if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, ipstring ) ) {
1485                                         break;
1486                                 }
1487
1488                                 // move on to next address in packet
1489                                 data += 6;
1490                                 length -= 6;
1491                         }
1492                         // begin or resume serverlist queries
1493                         serverlist_querysleep = false;
1494                         serverlist_querywaittime = realtime + 3;
1495                         return true;
1496                 }
1497                 if (!strncmp(string, "extResponse ", 12))
1498                 {
1499                         ++net_extresponse_count;
1500                         if(net_extresponse_count > NET_EXTRESPONSE_MAX)
1501                                 net_extresponse_count = NET_EXTRESPONSE_MAX;
1502                         net_extresponse_last = (net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
1503                         dpsnprintf(net_extresponse[net_extresponse_last], sizeof(net_extresponse[net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
1504                         return true;
1505                 }
1506                 if (!strncmp(string, "ping", 4))
1507                 {
1508                         if (developer.integer >= 10)
1509                                 Con_Printf("Received ping from %s, sending ack\n", addressstring2);
1510                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
1511                         return true;
1512                 }
1513                 if (!strncmp(string, "ack", 3))
1514                         return true;
1515                 // QuakeWorld compatibility
1516                 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
1517                 {
1518                         // challenge message
1519                         Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
1520                         M_Update_Return_Reason("Got QuakeWorld challenge response");
1521                         cls.qw_qport = qport.integer;
1522                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1523                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1524                         NetConn_WriteString(mysocket, va("\377\377\377\377connect %i %i %i \"%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo), peeraddress);
1525                         return true;
1526                 }
1527                 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
1528                 {
1529                         // accept message
1530                         M_Update_Return_Reason("QuakeWorld Accepted");
1531                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
1532                         return true;
1533                 }
1534                 if (length > 2 && !memcmp(string, "n\\", 2))
1535                 {
1536                         serverlist_info_t *info;
1537                         int n;
1538
1539                         // qw server status
1540                         if (serverlist_consoleoutput && developer_networking.integer >= 2)
1541                                 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
1542
1543                         string += 1;
1544                         // search the cache for this server and update it
1545                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1546                         if (n < 0)
1547                                 return true;
1548
1549                         info = &serverlist_cache[n].info;
1550                         strlcpy(info->game, "QuakeWorld", sizeof(info->game));;
1551                         if ((s = SearchInfostring(string, "*gamedir"     )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0]  = 0;
1552                         if ((s = SearchInfostring(string, "map"          )) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0]  = 0;
1553                         if ((s = SearchInfostring(string, "hostname"     )) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
1554                         info->protocol = 0;
1555                         info->numplayers = 0; // updated below
1556                         info->numhumans = 0; // updated below
1557                         if ((s = SearchInfostring(string, "maxclients"   )) != NULL) info->maxplayers = atoi(s);else info->maxplayers  = 0;
1558                         if ((s = SearchInfostring(string, "gameversion"  )) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
1559
1560                         // count active players on server
1561                         // (we could gather more info, but we're just after the number)
1562                         s = strchr(string, '\n');
1563                         if (s)
1564                         {
1565                                 s++;
1566                                 while (s < string + length)
1567                                 {
1568                                         for (;s < string + length && *s != '\n';s++)
1569                                                 ;
1570                                         if (s >= string + length)
1571                                                 break;
1572                                         info->numplayers++;
1573                                         info->numhumans++;
1574                                         s++;
1575                                 }
1576                         }
1577
1578                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1579
1580                         return true;
1581                 }
1582                 if (string[0] == 'n')
1583                 {
1584                         // qw print command
1585                         Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
1586                 }
1587                 // we may not have liked the packet, but it was a command packet, so
1588                 // we're done processing this packet now
1589                 return true;
1590         }
1591         // quakeworld ingame packet
1592         if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
1593         {
1594                 ret = 0;
1595                 CL_ParseServerMessage();
1596                 return ret;
1597         }
1598         // netquake control packets, supported for compatibility only
1599         if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
1600         {
1601                 int n;
1602                 serverlist_info_t *info;
1603
1604                 data += 4;
1605                 length -= 4;
1606                 SZ_Clear(&net_message);
1607                 SZ_Write(&net_message, data, length);
1608                 MSG_BeginReading();
1609                 c = MSG_ReadByte();
1610                 switch (c)
1611                 {
1612                 case CCREP_ACCEPT:
1613                         if (developer.integer >= 10)
1614                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
1615                         if (cls.connect_trying)
1616                         {
1617                                 lhnetaddress_t clientportaddress;
1618                                 clientportaddress = *peeraddress;
1619                                 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong());
1620                                 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
1621                                 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
1622                                 M_Update_Return_Reason("Accepted");
1623                                 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
1624                         }
1625                         break;
1626                 case CCREP_REJECT:
1627                         if (developer.integer >= 10)
1628                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
1629                         cls.connect_trying = false;
1630                         M_Update_Return_Reason((char *)MSG_ReadString());
1631                         break;
1632                 case CCREP_SERVER_INFO:
1633                         if (developer.integer >= 10)
1634                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
1635                         // LordHavoc: because the quake server may report weird addresses
1636                         // we just ignore it and keep the real address
1637                         MSG_ReadString();
1638                         // search the cache for this server and update it
1639                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
1640                         if (n < 0)
1641                                 break;
1642
1643                         info = &serverlist_cache[n].info;
1644                         strlcpy(info->game, "Quake", sizeof(info->game));
1645                         strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
1646                         strlcpy(info->name, MSG_ReadString(), sizeof(info->name));
1647                         strlcpy(info->map , MSG_ReadString(), sizeof(info->map));
1648                         info->numplayers = MSG_ReadByte();
1649                         info->maxplayers = MSG_ReadByte();
1650                         info->protocol = MSG_ReadByte();
1651
1652                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
1653
1654                         break;
1655                 case CCREP_PLAYER_INFO:
1656                         // we got a CCREP_PLAYER_INFO??
1657                         //if (developer.integer >= 10)
1658                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
1659                         break;
1660                 case CCREP_RULE_INFO:
1661                         // we got a CCREP_RULE_INFO??
1662                         //if (developer.integer >= 10)
1663                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
1664                         break;
1665                 default:
1666                         break;
1667                 }
1668                 SZ_Clear(&net_message);
1669                 // we may not have liked the packet, but it was a valid control
1670                 // packet, so we're done processing this packet now
1671                 return true;
1672         }
1673         ret = 0;
1674         if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
1675                 CL_ParseServerMessage();
1676         return ret;
1677 }
1678
1679 void NetConn_QueryQueueFrame(void)
1680 {
1681         int index;
1682         int queries;
1683         int maxqueries;
1684         double timeouttime;
1685         static double querycounter = 0;
1686
1687         if(!net_slist_pause.integer && serverlist_paused)
1688                 ServerList_RebuildViewList();
1689         serverlist_paused = net_slist_pause.integer;
1690
1691         if (serverlist_querysleep)
1692                 return;
1693
1694         // apply a cool down time after master server replies,
1695         // to avoid messing up the ping times on the servers
1696         if (serverlist_querywaittime > realtime)
1697                 return;
1698
1699         // each time querycounter reaches 1.0 issue a query
1700         querycounter += cl.realframetime * net_slist_queriespersecond.value;
1701         maxqueries = (int)querycounter;
1702         maxqueries = bound(0, maxqueries, net_slist_queriesperframe.integer);
1703         querycounter -= maxqueries;
1704
1705         if( maxqueries == 0 ) {
1706                 return;
1707         }
1708
1709         //      scan serverlist and issue queries as needed
1710         serverlist_querysleep = true;
1711
1712         timeouttime     = realtime - net_slist_timeout.value;
1713         for( index = 0, queries = 0 ;   index   < serverlist_cachecount &&      queries < maxqueries    ; index++ )
1714         {
1715                 serverlist_entry_t *entry = &serverlist_cache[ index ];
1716                 if( entry->query != SQS_QUERYING && entry->query != SQS_REFRESHING )
1717                 {
1718                         continue;
1719                 }
1720
1721                 serverlist_querysleep   = false;
1722                 if( entry->querycounter !=      0 && entry->querytime > timeouttime     )
1723                 {
1724                         continue;
1725                 }
1726
1727                 if( entry->querycounter !=      (unsigned) net_slist_maxtries.integer )
1728                 {
1729                         lhnetaddress_t  address;
1730                         int socket;
1731
1732                         LHNETADDRESS_FromString(&address, entry->info.cname, 0);
1733                         if      (entry->protocol == PROTOCOL_QUAKEWORLD)
1734                         {
1735                                 for (socket     = 0; socket     < cl_numsockets ;       socket++)
1736                                         NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
1737                         }
1738                         else
1739                         {
1740                                 for (socket     = 0; socket     < cl_numsockets ;       socket++)
1741                                         NetConn_WriteString(cl_sockets[socket], "\377\377\377\377getinfo", &address);
1742                         }
1743
1744                         //      update the entry fields
1745                         entry->querytime = realtime;
1746                         entry->querycounter++;
1747
1748                         // if not in the slist menu we should print the server to console
1749                         if (serverlist_consoleoutput)
1750                                 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
1751
1752                         queries++;
1753                 }
1754                 else
1755                 {
1756                         // have we tried to refresh this server?
1757                         if( entry->query == SQS_REFRESHING ) {
1758                                 // yes, so update the reply count (since its not responding anymore)
1759                                 serverreplycount--;
1760                                 if(!serverlist_paused)
1761                                         ServerList_ViewList_Remove(entry);
1762                         }
1763                         entry->query = SQS_TIMEDOUT;
1764                 }
1765         }
1766 }
1767
1768 void NetConn_ClientFrame(void)
1769 {
1770         int i, length;
1771         lhnetaddress_t peeraddress;
1772         NetConn_UpdateSockets();
1773         if (cls.connect_trying && cls.connect_nextsendtime < realtime)
1774         {
1775                 if (cls.connect_remainingtries == 0)
1776                         M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
1777                 cls.connect_nextsendtime = realtime + 1;
1778                 cls.connect_remainingtries--;
1779                 if (cls.connect_remainingtries <= -10)
1780                 {
1781                         cls.connect_trying = false;
1782                         M_Update_Return_Reason("Connect: Failed");
1783                         return;
1784                 }
1785                 // try challenge first (newer DP server or QW)
1786                 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
1787                 // then try netquake as a fallback (old server, or netquake)
1788                 SZ_Clear(&net_message);
1789                 // save space for the header, filled in later
1790                 MSG_WriteLong(&net_message, 0);
1791                 MSG_WriteByte(&net_message, CCREQ_CONNECT);
1792                 MSG_WriteString(&net_message, "QUAKE");
1793                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
1794                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1795                 NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address);
1796                 SZ_Clear(&net_message);
1797         }
1798         for (i = 0;i < cl_numsockets;i++)
1799                 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
1800                         NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
1801         NetConn_QueryQueueFrame();
1802         if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
1803         {
1804                 Con_Print("Connection timed out\n");
1805                 CL_Disconnect();
1806                 Host_ShutdownServer ();
1807         }
1808 }
1809
1810 #define MAX_CHALLENGES 128
1811 struct challenge_s
1812 {
1813         lhnetaddress_t address;
1814         double time;
1815         char string[12];
1816 }
1817 challenge[MAX_CHALLENGES];
1818
1819 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
1820 {
1821         int i;
1822         char c;
1823         for (i = 0;i < bufferlength - 1;i++)
1824         {
1825                 do
1826                 {
1827                         c = rand () % (127 - 33) + 33;
1828                 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
1829                 buffer[i] = c;
1830         }
1831         buffer[i] = 0;
1832 }
1833
1834 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
1835 {
1836         unsigned int nb_clients = 0, nb_bots = 0, i;
1837         int length;
1838
1839         // How many clients are there?
1840         for (i = 0;i < (unsigned int)svs.maxclients;i++)
1841         {
1842                 if (svs.clients[i].active)
1843                 {
1844                         nb_clients++;
1845                         if (!svs.clients[i].netconnection)
1846                                 nb_bots++;
1847                 }
1848         }
1849
1850         // TODO: we should add more information for the full status string
1851         length = dpsnprintf(out_msg, out_size,
1852                                                 "\377\377\377\377%s\x0A"
1853                                                 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
1854                                                 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
1855                                                 "%s%s"
1856                                                 "%s",
1857                                                 fullstatus ? "statusResponse" : "infoResponse",
1858                                                 gamename, com_modname, gameversion.integer, svs.maxclients,
1859                                                 nb_clients, nb_bots, sv.name, hostname.string, NET_PROTOCOL_VERSION,
1860                                                 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
1861                                                 fullstatus ? "\n" : "");
1862
1863         // Make sure it fits in the buffer
1864         if (length < 0)
1865                 return false;
1866
1867         if (fullstatus)
1868         {
1869                 char *ptr;
1870                 int left;
1871
1872                 ptr = out_msg + length;
1873                 left = (int)out_size - length;
1874
1875                 for (i = 0;i < (unsigned int)svs.maxclients;i++)
1876                 {
1877                         client_t *cl = &svs.clients[i];
1878                         if (cl->active)
1879                         {
1880                                 int nameind, cleanind, pingvalue;
1881                                 char curchar;
1882                                 char cleanname [sizeof(cl->name)];
1883
1884                                 // Remove all characters '"' and '\' in the player name
1885                                 nameind = 0;
1886                                 cleanind = 0;
1887                                 do
1888                                 {
1889                                         curchar = cl->name[nameind++];
1890                                         if (curchar != '"' && curchar != '\\')
1891                                         {
1892                                                 cleanname[cleanind++] = curchar;
1893                                                 if (cleanind == sizeof(cleanname) - 1)
1894                                                         break;
1895                                         }
1896                                 } while (curchar != '\0');
1897
1898                                 pingvalue = (int)(cl->ping * 1000.0f);
1899                                 if(cl->netconnection)
1900                                         pingvalue = bound(1, pingvalue, 9999);
1901                                 else
1902                                         pingvalue = 0;
1903                                 length = dpsnprintf(ptr, left, "%d %d \"%s\"\n",
1904                                                                         cl->frags,
1905                                                                         pingvalue,
1906                                                                         cleanname);
1907                                 if(length < 0)
1908                                         return false;
1909                                 left -= length;
1910                                 ptr += length;
1911                         }
1912                 }
1913         }
1914
1915         return true;
1916 }
1917
1918 static qboolean NetConn_PreventConnectFlood(lhnetaddress_t *peeraddress)
1919 {
1920         int floodslotnum, bestfloodslotnum;
1921         double bestfloodtime;
1922         lhnetaddress_t noportpeeraddress;
1923         // see if this is a connect flood
1924         noportpeeraddress = *peeraddress;
1925         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
1926         bestfloodslotnum = 0;
1927         bestfloodtime = sv.connectfloodaddresses[bestfloodslotnum].lasttime;
1928         for (floodslotnum = 0;floodslotnum < MAX_CONNECTFLOODADDRESSES;floodslotnum++)
1929         {
1930                 if (bestfloodtime >= sv.connectfloodaddresses[floodslotnum].lasttime)
1931                 {
1932                         bestfloodtime = sv.connectfloodaddresses[floodslotnum].lasttime;
1933                         bestfloodslotnum = floodslotnum;
1934                 }
1935                 if (sv.connectfloodaddresses[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &sv.connectfloodaddresses[floodslotnum].address) == 0)
1936                 {
1937                         // this address matches an ongoing flood address
1938                         if (realtime < sv.connectfloodaddresses[floodslotnum].lasttime + net_connectfloodblockingtimeout.value)
1939                         {
1940                                 // renew the ban on this address so it does not expire
1941                                 // until the flood has subsided
1942                                 sv.connectfloodaddresses[floodslotnum].lasttime = realtime;
1943                                 //Con_Printf("Flood detected!\n");
1944                                 return true;
1945                         }
1946                         // the flood appears to have subsided, so allow this
1947                         bestfloodslotnum = floodslotnum; // reuse the same slot
1948                         break;
1949                 }
1950         }
1951         // begin a new timeout on this address
1952         sv.connectfloodaddresses[bestfloodslotnum].address = noportpeeraddress;
1953         sv.connectfloodaddresses[bestfloodslotnum].lasttime = realtime;
1954         //Con_Printf("Flood detection initiated!\n");
1955         return false;
1956 }
1957
1958 void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress)
1959 {
1960         int floodslotnum;
1961         lhnetaddress_t noportpeeraddress;
1962         // see if this is a connect flood
1963         noportpeeraddress = *peeraddress;
1964         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
1965         for (floodslotnum = 0;floodslotnum < MAX_CONNECTFLOODADDRESSES;floodslotnum++)
1966         {
1967                 if (sv.connectfloodaddresses[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &sv.connectfloodaddresses[floodslotnum].address) == 0)
1968                 {
1969                         // this address matches an ongoing flood address
1970                         // remove the ban
1971                         sv.connectfloodaddresses[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
1972                         sv.connectfloodaddresses[floodslotnum].lasttime = 0;
1973                         //Con_Printf("Flood cleared!\n");
1974                 }
1975         }
1976 }
1977
1978 // returns a string describing the user level, or NULL for auth failure
1979 const char *RCon_Authenticate(const char *password, const char *s, const char *endpos)
1980 {
1981         const char *text;
1982         qboolean hasquotes;
1983
1984         if(!strcmp(rcon_password.string, password))
1985                 return "rcon";
1986         
1987         if(strcmp(rcon_restricted_password.string, password))
1988                 return NULL;
1989
1990         for(text = s; text != endpos; ++text)
1991                 if(*text > 0 && (*text < ' ' || *text == ';'))
1992                         return NULL; // block possible exploits against the parser/alias expansion
1993
1994         while(s != endpos)
1995         {
1996                 size_t l = strlen(s);
1997                 if(l)
1998                 {
1999                         hasquotes = (strchr(s, '"') != NULL);
2000                         // sorry, we can't allow these substrings in wildcard expressions,
2001                         // as they can mess with the argument counts
2002                         text = rcon_restricted_commands.string;
2003                         while(COM_ParseToken_Console(&text))
2004                         {
2005                                 // com_token now contains a pattern to check for...
2006                                 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2007                                 {
2008                                         if(!hasquotes)
2009                                                 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2010                                                         goto match;
2011                                 }
2012                                 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2013                                 {
2014                                         if(!strcmp(com_token, s))
2015                                                 goto match;
2016                                 }
2017                                 else // single-arg expression? must match the beginning of the command
2018                                 {
2019                                         if(!strcmp(com_token, s))
2020                                                 goto match;
2021                                         if(!memcmp(va("%s ", com_token), s, strlen(com_token) + 1))
2022                                                 goto match;
2023                                 }
2024                         }
2025                         // if we got here, nothing matched!
2026                         return NULL;
2027                 }
2028 match:
2029                 s += l + 1;
2030         }
2031
2032         return "restricted rcon";
2033 }
2034
2035 extern void SV_SendServerinfo (client_t *client);
2036 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2037 {
2038         int i, ret, clientnum, best;
2039         double besttime;
2040         client_t *client;
2041         char *s, *string, response[1400], addressstring2[128], stringbuf[16384];
2042         qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2043
2044         if (!sv.active)
2045                 return false;
2046
2047         // convert the address to a string incase we need it
2048         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2049
2050         // see if we can identify the sender as a local player
2051         // (this is necessary for rcon to send a reliable reply if the client is
2052         //  actually on the server, not sending remotely)
2053         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2054                 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2055                         break;
2056         if (i == svs.maxclients)
2057                 host_client = NULL;
2058
2059         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2060         {
2061                 // received a command string - strip off the packaging and put it
2062                 // into our string buffer with NULL termination
2063                 data += 4;
2064                 length -= 4;
2065                 length = min(length, (int)sizeof(stringbuf) - 1);
2066                 memcpy(stringbuf, data, length);
2067                 stringbuf[length] = 0;
2068                 string = stringbuf;
2069
2070                 if (developer.integer >= 10)
2071                 {
2072                         Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2073                         Com_HexDumpToConsole(data, length);
2074                 }
2075
2076                 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -2))
2077                 {
2078                         for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
2079                         {
2080                                 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
2081                                         break;
2082                                 if (besttime > challenge[i].time)
2083                                         besttime = challenge[best = i].time;
2084                         }
2085                         // if we did not find an exact match, choose the oldest and
2086                         // update address and string
2087                         if (i == MAX_CHALLENGES)
2088                         {
2089                                 i = best;
2090                                 challenge[i].address = *peeraddress;
2091                                 NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
2092                         }
2093                         challenge[i].time = realtime;
2094                         // send the challenge
2095                         NetConn_WriteString(mysocket, va("\377\377\377\377challenge %s", challenge[i].string), peeraddress);
2096                         return true;
2097                 }
2098                 if (length > 8 && !memcmp(string, "connect\\", 8) && (islocal || sv_public.integer > -2))
2099                 {
2100                         string += 7;
2101                         length -= 7;
2102
2103                         if (!(s = SearchInfostring(string, "challenge")))
2104                                 return true;
2105                         // validate the challenge
2106                         for (i = 0;i < MAX_CHALLENGES;i++)
2107                                 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
2108                                         break;
2109                         // if the challenge is not recognized, drop the packet
2110                         if (i == MAX_CHALLENGES)
2111                                 return true;
2112
2113                         // check engine protocol
2114                         if (strcmp(SearchInfostring(string, "protocol"), "darkplaces 3"))
2115                         {
2116                                 if (developer.integer >= 10)
2117                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
2118                                 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
2119                                 return true;
2120                         }
2121
2122                         // see if this is a duplicate connection request or a disconnected
2123                         // client who is rejoining to the same client slot
2124                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2125                         {
2126                                 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
2127                                 {
2128                                         // this is a known client...
2129                                         if (client->spawned)
2130                                         {
2131                                                 // client crashed and is coming back,
2132                                                 // keep their stuff intact
2133                                                 if (developer.integer >= 10)
2134                                                         Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
2135                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2136                                                 SV_VM_Begin();
2137                                                 SV_SendServerinfo(client);
2138                                                 SV_VM_End();
2139                                         }
2140                                         else
2141                                         {
2142                                                 // client is still trying to connect,
2143                                                 // so we send a duplicate reply
2144                                                 if (developer.integer >= 10)
2145                                                         Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
2146                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2147                                         }
2148                                         return true;
2149                                 }
2150                         }
2151
2152                         if (NetConn_PreventConnectFlood(peeraddress))
2153                                 return true;
2154
2155                         // find an empty client slot for this new client
2156                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2157                         {
2158                                 netconn_t *conn;
2159                                 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
2160                                 {
2161                                         // allocated connection
2162                                         if (developer.integer >= 10)
2163                                                 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
2164                                         NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
2165                                         // now set up the client
2166                                         SV_VM_Begin();
2167                                         SV_ConnectClient(clientnum, conn);
2168                                         SV_VM_End();
2169                                         NetConn_Heartbeat(1);
2170                                         return true;
2171                                 }
2172                         }
2173
2174                         // no empty slots found - server is full
2175                         if (developer.integer >= 10)
2176                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
2177                         NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
2178
2179                         return true;
2180                 }
2181                 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
2182                 {
2183                         const char *challenge = NULL;
2184
2185                         // If there was a challenge in the getinfo message
2186                         if (length > 8 && string[7] == ' ')
2187                                 challenge = string + 8;
2188
2189                         if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
2190                         {
2191                                 if (developer.integer >= 10)
2192                                         Con_Printf("Sending reply to master %s - %s\n", addressstring2, response);
2193                                 NetConn_WriteString(mysocket, response, peeraddress);
2194                         }
2195                         return true;
2196                 }
2197                 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
2198                 {
2199                         const char *challenge = NULL;
2200
2201                         // If there was a challenge in the getinfo message
2202                         if (length > 10 && string[9] == ' ')
2203                                 challenge = string + 10;
2204
2205                         if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
2206                         {
2207                                 if (developer.integer >= 10)
2208                                         Con_Printf("Sending reply to client %s - %s\n", addressstring2, response);
2209                                 NetConn_WriteString(mysocket, response, peeraddress);
2210                         }
2211                         return true;
2212                 }
2213                 if (length >= 5 && !memcmp(string, "rcon ", 5))
2214                 {
2215                         int i;
2216                         char *s = string + 5;
2217                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
2218                         char password[64];
2219                         for (i = 0;*s > ' ';s++)
2220                                 if (i < (int)sizeof(password) - 1)
2221                                         password[i++] = *s;
2222                         if(*s <= ' ' && s != endpos) // skip leading ugly space
2223                                 ++s;
2224                         password[i] = 0;
2225                         if (password[0] > ' ')
2226                         {
2227                                 const char *userlevel = RCon_Authenticate(password, s, endpos);
2228                                 if(userlevel)
2229                                 {
2230                                         // looks like a legitimate rcon command with the correct password
2231                                         char *s_ptr = s;
2232                                         Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2233                                         while(s_ptr != endpos)
2234                                         {
2235                                                 size_t l = strlen(s_ptr);
2236                                                 if(l)
2237                                                         Con_Printf(" %s;", s_ptr);
2238                                                 s_ptr += l + 1;
2239                                         }
2240                                         Con_Printf("\n");
2241                                         rcon_redirect = true;
2242                                         rcon_redirect_bufferpos = 0;
2243                                         while(s != endpos)
2244                                         {
2245                                                 size_t l = strlen(s);
2246                                                 if(l)
2247                                                 {
2248                                                         client_t *host_client_save = host_client;
2249                                                         Cmd_ExecuteString(s, src_command);
2250                                                         host_client = host_client_save;
2251                                                         // in case it is a command that changes host_client (like restart)
2252                                                 }
2253                                                 s += l + 1;
2254                                         }
2255                                         rcon_redirect_buffer[rcon_redirect_bufferpos] = 0;
2256                                         rcon_redirect = false;
2257                                         // print resulting text to client
2258                                         // if client is playing, send a reliable reply instead of
2259                                         // a command packet
2260                                         if (host_client)
2261                                         {
2262                                                 // if the netconnection is loop, then this is the
2263                                                 // local player on a listen mode server, and it would
2264                                                 // result in duplicate printing to the console
2265                                                 // (not that the local player should be using rcon
2266                                                 //  when they have the console)
2267                                                 if (host_client->netconnection && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2268                                                         SV_ClientPrintf("%s", rcon_redirect_buffer);
2269                                         }
2270                                         else
2271                                         {
2272                                                 // qw print command
2273                                                 dpsnprintf(response, sizeof(response), "\377\377\377\377n%s", rcon_redirect_buffer);
2274                                                 NetConn_WriteString(mysocket, response, peeraddress);
2275                                         }
2276                                 }
2277                                 else
2278                                 {
2279                                         Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2280                                 }
2281                         }
2282                         return true;
2283                 }
2284                 if (!strncmp(string, "ping", 4))
2285                 {
2286                         if (developer.integer >= 10)
2287                                 Con_Printf("Received ping from %s, sending ack\n", addressstring2);
2288                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2289                         return true;
2290                 }
2291                 if (!strncmp(string, "ack", 3))
2292                         return true;
2293                 // we may not have liked the packet, but it was a command packet, so
2294                 // we're done processing this packet now
2295                 return true;
2296         }
2297         // netquake control packets, supported for compatibility only, and only
2298         // when running game protocols that are normally served via this connection
2299         // protocol
2300         // (this protects more modern protocols against being used for
2301         //  Quake packet flood Denial Of Service attacks)
2302         if (length >= 5 && (i = BigLong(*((int *)data))) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3))
2303         {
2304                 int c;
2305                 int protocolnumber;
2306                 const char *protocolname;
2307                 data += 4;
2308                 length -= 4;
2309                 SZ_Clear(&net_message);
2310                 SZ_Write(&net_message, data, length);
2311                 MSG_BeginReading();
2312                 c = MSG_ReadByte();
2313                 switch (c)
2314                 {
2315                 case CCREQ_CONNECT:
2316                         if (developer.integer >= 10)
2317                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
2318                         if(!islocal && sv_public.integer <= -2)
2319                                 break;
2320
2321                         protocolname = MSG_ReadString();
2322                         protocolnumber = MSG_ReadByte();
2323                         if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
2324                         {
2325                                 if (developer.integer >= 10)
2326                                         Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
2327                                 SZ_Clear(&net_message);
2328                                 // save space for the header, filled in later
2329                                 MSG_WriteLong(&net_message, 0);
2330                                 MSG_WriteByte(&net_message, CCREP_REJECT);
2331                                 MSG_WriteString(&net_message, "Incompatible version.\n");
2332                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2333                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2334                                 SZ_Clear(&net_message);
2335                                 break;
2336                         }
2337
2338                         // see if this connect request comes from a known client
2339                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2340                         {
2341                                 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
2342                                 {
2343                                         // this is either a duplicate connection request
2344                                         // or coming back from a timeout
2345                                         // (if so, keep their stuff intact)
2346
2347                                         // send a reply
2348                                         if (developer.integer >= 10)
2349                                                 Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
2350                                         SZ_Clear(&net_message);
2351                                         // save space for the header, filled in later
2352                                         MSG_WriteLong(&net_message, 0);
2353                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
2354                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
2355                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2356                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2357                                         SZ_Clear(&net_message);
2358
2359                                         // if client is already spawned, re-send the
2360                                         // serverinfo message as they'll need it to play
2361                                         if (client->spawned)
2362                                         {
2363                                                 SV_VM_Begin();
2364                                                 SV_SendServerinfo(client);
2365                                                 SV_VM_End();
2366                                         }
2367                                         return true;
2368                                 }
2369                         }
2370
2371                         // this is a new client, check for connection flood
2372                         if (NetConn_PreventConnectFlood(peeraddress))
2373                                 break;
2374
2375                         // find a slot for the new client
2376                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
2377                         {
2378                                 netconn_t *conn;
2379                                 if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
2380                                 {
2381                                         // connect to the client
2382                                         // everything is allocated, just fill in the details
2383                                         strlcpy (conn->address, addressstring2, sizeof (conn->address));
2384                                         if (developer.integer >= 10)
2385                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
2386                                         // send back the info about the server connection
2387                                         SZ_Clear(&net_message);
2388                                         // save space for the header, filled in later
2389                                         MSG_WriteLong(&net_message, 0);
2390                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
2391                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
2392                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2393                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2394                                         SZ_Clear(&net_message);
2395                                         // now set up the client struct
2396                                         SV_VM_Begin();
2397                                         SV_ConnectClient(clientnum, conn);
2398                                         SV_VM_End();
2399                                         NetConn_Heartbeat(1);
2400                                         return true;
2401                                 }
2402                         }
2403
2404                         if (developer.integer >= 10)
2405                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
2406                         // no room; try to let player know
2407                         SZ_Clear(&net_message);
2408                         // save space for the header, filled in later
2409                         MSG_WriteLong(&net_message, 0);
2410                         MSG_WriteByte(&net_message, CCREP_REJECT);
2411                         MSG_WriteString(&net_message, "Server is full.\n");
2412                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2413                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2414                         SZ_Clear(&net_message);
2415                         break;
2416                 case CCREQ_SERVER_INFO:
2417                         if (developer.integer >= 10)
2418                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
2419                         if(!islocal && sv_public.integer <= -1)
2420                                 break;
2421                         if (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
2422                         {
2423                                 int numclients;
2424                                 char myaddressstring[128];
2425                                 if (developer.integer >= 10)
2426                                         Con_Printf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
2427                                 SZ_Clear(&net_message);
2428                                 // save space for the header, filled in later
2429                                 MSG_WriteLong(&net_message, 0);
2430                                 MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
2431                                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
2432                                 MSG_WriteString(&net_message, myaddressstring);
2433                                 MSG_WriteString(&net_message, hostname.string);
2434                                 MSG_WriteString(&net_message, sv.name);
2435                                 // How many clients are there?
2436                                 for (i = 0, numclients = 0;i < svs.maxclients;i++)
2437                                         if (svs.clients[i].active)
2438                                                 numclients++;
2439                                 MSG_WriteByte(&net_message, numclients);
2440                                 MSG_WriteByte(&net_message, svs.maxclients);
2441                                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
2442                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2443                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2444                                 SZ_Clear(&net_message);
2445                         }
2446                         break;
2447                 case CCREQ_PLAYER_INFO:
2448                         if (developer.integer >= 10)
2449                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
2450                         if(!islocal && sv_public.integer <= -1)
2451                                 break;
2452                         if (sv.active)
2453                         {
2454                                 int playerNumber, activeNumber, clientNumber;
2455                                 client_t *client;
2456
2457                                 playerNumber = MSG_ReadByte();
2458                                 activeNumber = -1;
2459                                 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
2460                                         if (client->active && ++activeNumber == playerNumber)
2461                                                 break;
2462                                 if (clientNumber != svs.maxclients)
2463                                 {
2464                                         SZ_Clear(&net_message);
2465                                         // save space for the header, filled in later
2466                                         MSG_WriteLong(&net_message, 0);
2467                                         MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
2468                                         MSG_WriteByte(&net_message, playerNumber);
2469                                         MSG_WriteString(&net_message, client->name);
2470                                         MSG_WriteLong(&net_message, client->colors);
2471                                         MSG_WriteLong(&net_message, client->frags);
2472                                         MSG_WriteLong(&net_message, (int)(realtime - client->connecttime));
2473                                         MSG_WriteString(&net_message, client->netconnection ? client->netconnection->address : "botclient");
2474                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2475                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2476                                         SZ_Clear(&net_message);
2477                                 }
2478                         }
2479                         break;
2480                 case CCREQ_RULE_INFO:
2481                         if (developer.integer >= 10)
2482                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
2483                         if(!islocal && sv_public.integer <= -1)
2484                                 break;
2485                         if (sv.active)
2486                         {
2487                                 char *prevCvarName;
2488                                 cvar_t *var;
2489
2490                                 // find the search start location
2491                                 prevCvarName = MSG_ReadString();
2492                                 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
2493
2494                                 // send the response
2495                                 SZ_Clear(&net_message);
2496                                 // save space for the header, filled in later
2497                                 MSG_WriteLong(&net_message, 0);
2498                                 MSG_WriteByte(&net_message, CCREP_RULE_INFO);
2499                                 if (var)
2500                                 {
2501                                         MSG_WriteString(&net_message, var->name);
2502                                         MSG_WriteString(&net_message, var->string);
2503                                 }
2504                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2505                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
2506                                 SZ_Clear(&net_message);
2507                         }
2508                         break;
2509                 default:
2510                         break;
2511                 }
2512                 SZ_Clear(&net_message);
2513                 // we may not have liked the packet, but it was a valid control
2514                 // packet, so we're done processing this packet now
2515                 return true;
2516         }
2517         if (host_client)
2518         {
2519                 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->spawned ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
2520                 {
2521                         SV_VM_Begin();
2522                         SV_ReadClientMessage();
2523                         SV_VM_End();
2524                         return ret;
2525                 }
2526         }
2527         return 0;
2528 }
2529
2530 void NetConn_ServerFrame(void)
2531 {
2532         int i, length;
2533         lhnetaddress_t peeraddress;
2534         for (i = 0;i < sv_numsockets;i++)
2535                 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2536                         NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
2537         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2538         {
2539                 // never timeout loopback connections
2540                 if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2541                 {
2542                         Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
2543                         SV_VM_Begin();
2544                         SV_DropClient(false);
2545                         SV_VM_End();
2546                 }
2547         }
2548 }
2549
2550 void NetConn_SleepMicroseconds(int microseconds)
2551 {
2552         LHNET_SleepUntilPacket_Microseconds(microseconds);
2553 }
2554
2555 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
2556 {
2557         int i;
2558         int masternum;
2559         lhnetaddress_t masteraddress;
2560         lhnetaddress_t broadcastaddress;
2561         char request[256];
2562
2563         if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
2564                 return;
2565
2566         // 26000 is the default quake server port, servers on other ports will not
2567         // be found
2568         // note this is IPv4-only, I doubt there are IPv6-only LANs out there
2569         LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
2570
2571         if (querydp)
2572         {
2573                 for (i = 0;i < cl_numsockets;i++)
2574                 {
2575                         if (cl_sockets[i])
2576                         {
2577                                 // search LAN for Quake servers
2578                                 SZ_Clear(&net_message);
2579                                 // save space for the header, filled in later
2580                                 MSG_WriteLong(&net_message, 0);
2581                                 MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
2582                                 MSG_WriteString(&net_message, "QUAKE");
2583                                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
2584                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2585                                 NetConn_Write(cl_sockets[i], net_message.data, net_message.cursize, &broadcastaddress);
2586                                 SZ_Clear(&net_message);
2587
2588                                 // search LAN for DarkPlaces servers
2589                                 NetConn_WriteString(cl_sockets[i], "\377\377\377\377getinfo", &broadcastaddress);
2590
2591                                 // build the getservers message to send to the dpmaster master servers
2592                                 dpsnprintf(request, sizeof(request), "\377\377\377\377getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
2593
2594                                 // search internet
2595                                 for (masternum = 0;sv_masters[masternum].name;masternum++)
2596                                 {
2597                                         if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
2598                                         {
2599                                                 masterquerycount++;
2600                                                 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
2601                                         }
2602                                 }
2603                         }
2604                 }
2605         }
2606
2607         // only query QuakeWorld servers when the user wants to
2608         if (queryqw)
2609         {
2610                 for (i = 0;i < cl_numsockets;i++)
2611                 {
2612                         if (cl_sockets[i])
2613                         {
2614                                 // search LAN for QuakeWorld servers
2615                                 NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
2616
2617                                 // build the getservers message to send to the qwmaster master servers
2618                                 // note this has no -1 prefix, and the trailing nul byte is sent
2619                                 dpsnprintf(request, sizeof(request), "c\n");
2620
2621                                 // search internet
2622                                 for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
2623                                 {
2624                                         if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
2625                                         {
2626                                                 if (m_state != m_slist)
2627                                                 {
2628                                                         char lookupstring[128];
2629                                                         LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
2630                                                         Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
2631                                                 }
2632                                                 masterquerycount++;
2633                                                 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
2634                                         }
2635                                 }
2636                         }
2637                 }
2638         }
2639         if (!masterquerycount)
2640         {
2641                 Con_Print("Unable to query master servers, no suitable network sockets active.\n");
2642                 M_Update_Return_Reason("No network");
2643         }
2644 }
2645
2646 void NetConn_Heartbeat(int priority)
2647 {
2648         lhnetaddress_t masteraddress;
2649         int masternum;
2650         lhnetsocket_t *mysocket;
2651
2652         // if it's a state change (client connected), limit next heartbeat to no
2653         // more than 30 sec in the future
2654         if (priority == 1 && nextheartbeattime > realtime + 30.0)
2655                 nextheartbeattime = realtime + 30.0;
2656
2657         // limit heartbeatperiod to 30 to 270 second range,
2658         // lower limit is to avoid abusing master servers with excess traffic,
2659         // upper limit is to avoid timing out on the master server (which uses
2660         // 300 sec timeout)
2661         if (sv_heartbeatperiod.value < 30)
2662                 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
2663         if (sv_heartbeatperiod.value > 270)
2664                 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
2665
2666         // make advertising optional and don't advertise singleplayer games, and
2667         // only send a heartbeat as often as the admin wants
2668         if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
2669         {
2670                 nextheartbeattime = realtime + sv_heartbeatperiod.value;
2671                 for (masternum = 0;sv_masters[masternum].name;masternum++)
2672                         if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
2673                                 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
2674         }
2675 }
2676
2677 static void Net_Heartbeat_f(void)
2678 {
2679         if (sv.active)
2680                 NetConn_Heartbeat(2);
2681         else
2682                 Con_Print("No server running, can not heartbeat to master server.\n");
2683 }
2684
2685 void PrintStats(netconn_t *conn)
2686 {
2687         if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
2688                 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
2689         else
2690                 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
2691 }
2692
2693 void Net_Stats_f(void)
2694 {
2695         netconn_t *conn;
2696         Con_Printf("unreliable messages sent   = %i\n", unreliableMessagesSent);
2697         Con_Printf("unreliable messages recv   = %i\n", unreliableMessagesReceived);
2698         Con_Printf("reliable messages sent     = %i\n", reliableMessagesSent);
2699         Con_Printf("reliable messages received = %i\n", reliableMessagesReceived);
2700         Con_Printf("packetsSent                = %i\n", packetsSent);
2701         Con_Printf("packetsReSent              = %i\n", packetsReSent);
2702         Con_Printf("packetsReceived            = %i\n", packetsReceived);
2703         Con_Printf("receivedDuplicateCount     = %i\n", receivedDuplicateCount);
2704         Con_Printf("droppedDatagrams           = %i\n", droppedDatagrams);
2705         Con_Print("connections                =\n");
2706         for (conn = netconn_list;conn;conn = conn->next)
2707                 PrintStats(conn);
2708 }
2709
2710 void Net_Refresh_f(void)
2711 {
2712         if (m_state != m_slist) {
2713                 Con_Print("Sending new requests to master servers\n");
2714                 ServerList_QueryList(false, true, false, true);
2715                 Con_Print("Listening for replies...\n");
2716         } else
2717                 ServerList_QueryList(false, true, false, false);
2718 }
2719
2720 void Net_Slist_f(void)
2721 {
2722         ServerList_ResetMasks();
2723         serverlist_sortbyfield = SLIF_PING;
2724         serverlist_sortdescending = false;
2725     if (m_state != m_slist) {
2726                 Con_Print("Sending requests to master servers\n");
2727                 ServerList_QueryList(true, true, false, true);
2728                 Con_Print("Listening for replies...\n");
2729         } else
2730                 ServerList_QueryList(true, true, false, false);
2731 }
2732
2733 void Net_SlistQW_f(void)
2734 {
2735         ServerList_ResetMasks();
2736         serverlist_sortbyfield = SLIF_PING;
2737         serverlist_sortdescending = false;
2738     if (m_state != m_slist) {
2739                 Con_Print("Sending requests to master servers\n");
2740                 ServerList_QueryList(true, false, true, true);
2741                 serverlist_consoleoutput = true;
2742                 Con_Print("Listening for replies...\n");
2743         } else
2744                 ServerList_QueryList(true, false, true, false);
2745 }
2746
2747 void NetConn_Init(void)
2748 {
2749         int i;
2750         lhnetaddress_t tempaddress;
2751         netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
2752         Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
2753         Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
2754         Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
2755         Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
2756         Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
2757         Cvar_RegisterVariable(&rcon_restricted_password);
2758         Cvar_RegisterVariable(&rcon_restricted_commands);
2759         Cvar_RegisterVariable(&net_slist_queriespersecond);
2760         Cvar_RegisterVariable(&net_slist_queriesperframe);
2761         Cvar_RegisterVariable(&net_slist_timeout);
2762         Cvar_RegisterVariable(&net_slist_maxtries);
2763         Cvar_RegisterVariable(&net_slist_pause);
2764         Cvar_RegisterVariable(&net_messagetimeout);
2765         Cvar_RegisterVariable(&net_connecttimeout);
2766         Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
2767         Cvar_RegisterVariable(&cl_netlocalping);
2768         Cvar_RegisterVariable(&cl_netpacketloss_send);
2769         Cvar_RegisterVariable(&cl_netpacketloss_receive);
2770         Cvar_RegisterVariable(&hostname);
2771         Cvar_RegisterVariable(&developer_networking);
2772         Cvar_RegisterVariable(&cl_netport);
2773         Cvar_RegisterVariable(&sv_netport);
2774         Cvar_RegisterVariable(&net_address);
2775         //Cvar_RegisterVariable(&net_address_ipv6);
2776         Cvar_RegisterVariable(&sv_public);
2777         Cvar_RegisterVariable(&sv_heartbeatperiod);
2778         for (i = 0;sv_masters[i].name;i++)
2779                 Cvar_RegisterVariable(&sv_masters[i]);
2780         Cvar_RegisterVariable(&gameversion);
2781 // 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.
2782         if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
2783         {
2784                 if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
2785                 {
2786                         Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
2787                         Cvar_SetQuick(&net_address, com_argv[i + 1]);
2788                 }
2789                 else
2790                         Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
2791         }
2792 // 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
2793         if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
2794         {
2795                 i = atoi(com_argv[i + 1]);
2796                 if (i >= 0 && i < 65536)
2797                 {
2798                         Con_Printf("-port option used, setting port cvar to %i\n", i);
2799                         Cvar_SetValueQuick(&sv_netport, i);
2800                 }
2801                 else
2802                         Con_Printf("-port option used, but %i is not a valid port number\n", i);
2803         }
2804         cl_numsockets = 0;
2805         sv_numsockets = 0;
2806         net_message.data = net_message_buf;
2807         net_message.maxsize = sizeof(net_message_buf);
2808         net_message.cursize = 0;
2809         LHNET_Init();
2810 }
2811
2812 void NetConn_Shutdown(void)
2813 {
2814         NetConn_CloseClientPorts();
2815         NetConn_CloseServerPorts();
2816         LHNET_Shutdown();
2817 }
2818