]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - netconn.c
upgraded network protocol to DPPROTOCOL_VERSION4 - this means partial entity updates...
[xonotic/darkplaces.git] / netconn.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Forest Hale
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 */
22
23 #include "quakedef.h"
24 #include "lhnet.h"
25
26 #define MASTER_PORT 27950
27
28 cvar_t sv_maxplayers = {0, "maxplayers", "8"};
29 cvar_t sv_public = {0, "sv_public", "0"};
30 static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "180"};
31
32 static cvar_t sv_masters [] =
33 {
34         {CVAR_SAVE, "sv_master1", ""},
35         {CVAR_SAVE, "sv_master2", ""},
36         {CVAR_SAVE, "sv_master3", ""},
37         {CVAR_SAVE, "sv_master4", ""},
38         {0, "sv_masterextra1", "198.88.152.4"},
39         {0, "sv_masterextra2", "68.102.242.12"},
40         {0, NULL, NULL}
41 };
42
43 static double nextheartbeattime = 0;
44
45 sizebuf_t net_message;
46
47 cvar_t net_messagetimeout = {0, "net_messagetimeout","300"};
48 cvar_t net_messagerejointimeout = {0, "net_messagerejointimeout","10"};
49 cvar_t net_connecttimeout = {0, "net_connecttimeout","10"};
50 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED"};
51 cvar_t developer_networking = {0, "developer_networking", "0"};
52
53 /* statistic counters */
54 static int packetsSent = 0;
55 static int packetsReSent = 0;
56 static int packetsReceived = 0;
57 static int receivedDuplicateCount = 0;
58 static int droppedDatagrams = 0;
59
60 static int unreliableMessagesSent = 0;
61 static int unreliableMessagesReceived = 0;
62 static int reliableMessagesSent = 0;
63 static int reliableMessagesReceived = 0;
64
65 int hostCacheCount = 0;
66 hostcache_t hostcache[HOSTCACHESIZE];
67
68 static qbyte sendbuffer[NET_MAXMESSAGE];
69 static qbyte readbuffer[NET_MAXMESSAGE];
70
71 int cl_numsockets;
72 lhnetsocket_t *cl_sockets[16];
73 int sv_numsockets;
74 lhnetsocket_t *sv_sockets[16];
75
76 netconn_t *netconn_list = NULL;
77 mempool_t *netconn_mempool = NULL;
78
79 cvar_t cl_netport = {0, "cl_port", "0"};
80 cvar_t cl_netaddress = {0, "cl_netaddress", "0.0.0.0"};
81 cvar_t cl_netaddress_ipv6 = {0, "cl_netaddress_ipv6", "[0:0:0:0:0:0:0:0]:0"};
82
83 cvar_t sv_netport = {0, "port", "26000"};
84 cvar_t sv_netaddress = {0, "sv_netaddress", "0.0.0.0"};
85 cvar_t sv_netaddress_ipv6 = {0, "sv_netaddress_ipv6", "[0:0:0:0:0:0:0:0]:26000"};
86
87 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
88 {
89         int length = LHNET_Read(mysocket, data, maxlength, peeraddress);
90         if (developer_networking.integer && length != 0)
91         {
92                 char addressstring[128], addressstring2[128];
93                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
94                 if (length > 0)
95                 {
96                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
97                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", mysocket, addressstring, data, maxlength, peeraddress, length, addressstring2);
98                         Com_HexDumpToConsole(data, length);
99                 }
100                 else
101                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", mysocket, addressstring, data, maxlength, peeraddress, length);
102         }
103         return length;
104 }
105
106 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
107 {
108         int ret = LHNET_Write(mysocket, data, length, peeraddress);
109         if (developer_networking.integer)
110         {
111                 char addressstring[128], addressstring2[128];
112                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
113                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
114                 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", mysocket, addressstring, data, length, peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
115                 Com_HexDumpToConsole(data, length);
116         }
117         return ret;
118 }
119
120 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
121 {
122         // note this does not include the trailing NULL because we add that in the parser
123         return NetConn_Write(mysocket, string, strlen(string), peeraddress);
124 }
125
126 int NetConn_SendReliableMessage(netconn_t *conn, sizebuf_t *data)
127 {
128         unsigned int packetLen;
129         unsigned int dataLen;
130         unsigned int eom;
131         unsigned int *header;
132
133 //#ifdef DEBUG
134         if (data->cursize == 0)
135                 Sys_Error("Datagram_SendMessage: zero length message\n");
136
137         if (data->cursize > NET_MAXMESSAGE)
138                 Sys_Error("Datagram_SendMessage: message too big %u\n", data->cursize);
139
140         if (conn->canSend == false)
141                 Sys_Error("SendMessage: called with canSend == false\n");
142 //#endif
143
144         memcpy(conn->sendMessage, data->data, data->cursize);
145         conn->sendMessageLength = data->cursize;
146
147         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
148         {
149                 dataLen = conn->sendMessageLength;
150                 eom = NETFLAG_EOM;
151         }
152         else
153         {
154                 dataLen = MAX_PACKETFRAGMENT;
155                 eom = 0;
156         }
157
158         packetLen = NET_HEADERSIZE + dataLen;
159
160         header = (void *)sendbuffer;
161         header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
162         header[1] = BigLong(conn->sendSequence);
163         memcpy(sendbuffer + 8, conn->sendMessage, dataLen);
164
165         conn->sendSequence++;
166         conn->canSend = false;
167
168         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
169                 return -1;
170
171         conn->lastSendTime = realtime;
172         packetsSent++;
173         reliableMessagesSent++;
174         return 1;
175 }
176
177 static void NetConn_SendMessageNext(netconn_t *conn)
178 {
179         unsigned int packetLen;
180         unsigned int dataLen;
181         unsigned int eom;
182         unsigned int *header;
183
184         if (conn->sendMessageLength && !conn->canSend && conn->sendNext)
185         {
186                 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
187                 {
188                         dataLen = conn->sendMessageLength;
189                         eom = NETFLAG_EOM;
190                 }
191                 else
192                 {
193                         dataLen = MAX_PACKETFRAGMENT;
194                         eom = 0;
195                 }
196
197                 packetLen = NET_HEADERSIZE + dataLen;
198
199                 header = (void *)sendbuffer;
200                 header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
201                 header[1] = BigLong(conn->sendSequence);
202                 memcpy(sendbuffer + 8, conn->sendMessage, dataLen);
203
204                 conn->sendSequence++;
205                 conn->sendNext = false;
206
207                 if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
208                         return;
209
210                 conn->lastSendTime = realtime;
211                 packetsSent++;
212         }
213 }
214
215 static void NetConn_ReSendMessage(netconn_t *conn)
216 {
217         unsigned int packetLen;
218         unsigned int dataLen;
219         unsigned int eom;
220         unsigned int *header;
221
222         if (conn->sendMessageLength && !conn->canSend && (realtime - conn->lastSendTime) > 1.0)
223         {
224                 if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
225                 {
226                         dataLen = conn->sendMessageLength;
227                         eom = NETFLAG_EOM;
228                 }
229                 else
230                 {
231                         dataLen = MAX_PACKETFRAGMENT;
232                         eom = 0;
233                 }
234
235                 packetLen = NET_HEADERSIZE + dataLen;
236
237                 header = (void *)sendbuffer;
238                 header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
239                 header[1] = BigLong(conn->sendSequence - 1);
240                 memcpy(sendbuffer + 8, conn->sendMessage, dataLen);
241
242                 conn->sendNext = false;
243
244                 if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
245                         return;
246
247                 conn->lastSendTime = realtime;
248                 packetsReSent++;
249         }
250 }
251
252 qboolean NetConn_CanSendMessage(netconn_t *conn)
253 {
254         return conn->canSend;
255 }
256
257 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data)
258 {
259         int packetLen;
260         int *header;
261
262 #ifdef DEBUG
263         if (data->cursize == 0)
264                 Sys_Error("Datagram_SendUnreliableMessage: zero length message\n");
265
266         if (data->cursize > MAX_DATAGRAM)
267                 Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
268 #endif
269
270         packetLen = NET_HEADERSIZE + data->cursize;
271
272         header = (void *)sendbuffer;
273         header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE);
274         header[1] = BigLong(conn->unreliableSendSequence);
275         memcpy(sendbuffer + 8, data->data, data->cursize);
276
277         conn->unreliableSendSequence++;
278
279         if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) != (int)packetLen)
280                 return -1;
281
282         packetsSent++;
283         unreliableMessagesSent++;
284         return 1;
285 }
286
287 void NetConn_CloseClientPorts(void)
288 {
289         for (;cl_numsockets > 0;cl_numsockets--)
290                 if (cl_sockets[cl_numsockets - 1])
291                         LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
292 }
293
294 void NetConn_OpenClientPorts(void)
295 {
296         int port;
297         lhnetaddress_t address;
298         NetConn_CloseClientPorts();
299         port = bound(0, cl_netport.integer, 65535);
300         if (cl_netport.integer != port)
301                 Cvar_SetValueQuick(&cl_netport, port);
302         LHNETADDRESS_FromString(&address, "local", port);
303         cl_sockets[cl_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
304         LHNETADDRESS_FromString(&address, cl_netaddress.string, port);
305         cl_sockets[cl_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
306         LHNETADDRESS_FromString(&address, cl_netaddress_ipv6.string, port);
307         cl_sockets[cl_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
308 }
309
310 void NetConn_CloseServerPorts(void)
311 {
312         for (;sv_numsockets > 0;sv_numsockets--)
313                 if (sv_sockets[sv_numsockets - 1])
314                         LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
315 }
316
317 void NetConn_OpenServerPorts(int opennetports)
318 {
319         int port;
320         lhnetaddress_t address;
321         NetConn_CloseServerPorts();
322         port = bound(0, sv_netport.integer, 65535);
323         if (port == 0)
324                 port = 26000;
325         if (sv_netport.integer != port)
326                 Cvar_SetValueQuick(&sv_netport, port);
327         LHNETADDRESS_FromString(&address, "local", port);
328         sv_sockets[sv_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
329         if (opennetports)
330         {
331                 LHNETADDRESS_FromString(&address, sv_netaddress.string, port);
332                 sv_sockets[sv_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
333                 LHNETADDRESS_FromString(&address, sv_netaddress_ipv6.string, port);
334                 sv_sockets[sv_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
335         }
336 }
337
338 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
339 {
340         int i, a = LHNETADDRESS_GetAddressType(address);
341         for (i = 0;i < cl_numsockets;i++)
342                 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
343                         return cl_sockets[i];
344         return NULL;
345 }
346
347 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
348 {
349         int i, a = LHNETADDRESS_GetAddressType(address);
350         for (i = 0;i < sv_numsockets;i++)
351                 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
352                         return sv_sockets[i];
353         return NULL;
354 }
355
356 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
357 {
358         netconn_t *conn;
359         conn = Mem_Alloc(netconn_mempool, sizeof(*conn));
360         conn->mysocket = mysocket;
361         conn->peeraddress = *peeraddress;
362         conn->canSend = true;
363         conn->connecttime = realtime;
364         conn->lastMessageTime = realtime;
365         // LordHavoc: (inspired by ProQuake) use a short connect timeout to
366         // reduce effectiveness of connection request floods
367         conn->timeout = realtime + net_connecttimeout.value;
368         LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
369         conn->next = netconn_list;
370         netconn_list = conn;
371         return conn;
372 }
373
374 void NetConn_Close(netconn_t *conn)
375 {
376         netconn_t *c;
377         // remove connection from list
378         if (conn == netconn_list)
379                 netconn_list = conn->next;
380         else
381         {
382                 for (c = netconn_list;c;c = c->next)
383                 {
384                         if (c->next == conn)
385                         {
386                                 c->next = conn->next;
387                                 break;
388                         }
389                 }
390                 // not found in list, we'll avoid crashing here...
391                 if (!c)
392                         return;
393         }
394         // free connection
395         Mem_Free(conn);
396 }
397
398 static int clientport = -1;
399 static int clientport2 = -1;
400 static int hostport = -1;
401 static void NetConn_UpdateServerStuff(void)
402 {
403         if (clientport2 != cl_netport.integer)
404         {
405                 clientport2 = cl_netport.integer;
406                 if (cls.state == ca_connected)
407                         Con_Printf("Changing \"cl_port\" will not take effect until you reconnect.\n");
408         }
409         if (cls.state != ca_dedicated)
410         {
411                 if (cls.state == ca_disconnected && clientport != cl_netport.integer)
412                 {
413                         clientport = cl_netport.integer;
414                         NetConn_CloseClientPorts();
415                 }
416                 if (cl_numsockets == 0)
417                         NetConn_OpenClientPorts();
418         }
419
420         if (hostport != sv_netport.integer)
421         {
422                 hostport = sv_netport.integer;
423                 if (sv.active)
424                         Con_Printf("Changing \"port\" will not take effect until \"map\" command is executed.\n");
425         }
426 }
427
428 int NetConn_ReceivedMessage(netconn_t *conn, qbyte *data, int length)
429 {
430         unsigned int count;
431         unsigned int flags;
432         unsigned int sequence;
433
434         if (length >= 8)
435         {
436                 length = BigLong(((int *)data)[0]);
437                 flags = length & ~NETFLAG_LENGTH_MASK;
438                 length &= NETFLAG_LENGTH_MASK;
439                 // control packets were already handled
440                 if (!(flags & NETFLAG_CTL))
441                 {
442                         sequence = BigLong(((int *)data)[1]);
443                         packetsReceived++;
444                         data += 8;
445                         length -= 8;
446                         if (flags & NETFLAG_UNRELIABLE)
447                         {
448                                 if (sequence >= conn->unreliableReceiveSequence)
449                                 {
450                                         if (sequence > conn->unreliableReceiveSequence)
451                                         {
452                                                 count = sequence - conn->unreliableReceiveSequence;
453                                                 droppedDatagrams += count;
454                                                 Con_DPrintf("Dropped %u datagram(s)\n", count);
455                                         }
456                                         conn->unreliableReceiveSequence = sequence + 1;
457                                         conn->lastMessageTime = realtime;
458                                         conn->timeout = realtime + net_messagetimeout.value;
459                                         unreliableMessagesReceived++;
460                                         if (length > 0)
461                                         {
462                                                 SZ_Clear(&net_message);
463                                                 SZ_Write(&net_message, data, length);
464                                                 MSG_BeginReading();
465                                                 return 2;
466                                         }
467                                 }
468                                 else
469                                         Con_DPrintf("Got a stale datagram\n");
470                                 return 1;
471                         }
472                         else if (flags & NETFLAG_ACK)
473                         {
474                                 if (sequence == (conn->sendSequence - 1))
475                                 {
476                                         if (sequence == conn->ackSequence)
477                                         {
478                                                 conn->ackSequence++;
479                                                 if (conn->ackSequence != conn->sendSequence)
480                                                         Con_DPrintf("ack sequencing error\n");
481                                                 conn->lastMessageTime = realtime;
482                                                 conn->timeout = realtime + net_messagetimeout.value;
483                                                 conn->sendMessageLength -= MAX_PACKETFRAGMENT;
484                                                 if (conn->sendMessageLength > 0)
485                                                 {
486                                                         memcpy(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
487                                                         conn->sendNext = true;
488                                                         NetConn_SendMessageNext(conn);
489                                                 }
490                                                 else
491                                                 {
492                                                         conn->sendMessageLength = 0;
493                                                         conn->canSend = true;
494                                                 }
495                                         }
496                                         else
497                                                 Con_DPrintf("Duplicate ACK received\n");
498                                 }
499                                 else
500                                         Con_DPrintf("Stale ACK received\n");
501                                 return 1;
502                         }
503                         else if (flags & NETFLAG_DATA)
504                         {
505                                 unsigned int temppacket[2];
506                                 temppacket[0] = BigLong(8 | NETFLAG_ACK);
507                                 temppacket[1] = BigLong(sequence);
508                                 NetConn_Write(conn->mysocket, (qbyte *)temppacket, 8, &conn->peeraddress);
509                                 if (sequence == conn->receiveSequence)
510                                 {
511                                         conn->lastMessageTime = realtime;
512                                         conn->timeout = realtime + net_messagetimeout.value;
513                                         conn->receiveSequence++;
514                                         memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
515                                         conn->receiveMessageLength += length;
516                                         if (flags & NETFLAG_EOM)
517                                         {
518                                                 reliableMessagesReceived++;
519                                                 length = conn->receiveMessageLength;
520                                                 conn->receiveMessageLength = 0;
521                                                 if (length > 0)
522                                                 {
523                                                         SZ_Clear(&net_message);
524                                                         SZ_Write(&net_message, conn->receiveMessage, length);
525                                                         MSG_BeginReading();
526                                                         return 2;
527                                                 }
528                                         }
529                                 }
530                                 else
531                                         receivedDuplicateCount++;
532                                 return 1;
533                         }
534                 }
535         }
536         return 0;
537 }
538
539 void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
540 {
541         cls.netcon = NetConn_Open(mysocket, peeraddress);
542         Con_Printf("Connection accepted to %s\n", cls.netcon->address);
543         key_dest = key_game;
544         m_state = m_none;
545         cls.connect_trying = false;
546         cls.demonum = -1;                       // not in the demo loop now
547         cls.state = ca_connected;
548         cls.signon = 0;                         // need all the signon messages before playing
549         CL_ClearState();
550         Host_Reconnect_f();
551 }
552
553 int NetConn_IsLocalGame(void)
554 {
555         int i;
556         if (cls.state == ca_connected && sv.active/* && LHNETADDRESS_GetAddressType(cl.netcon->peeraddress) == LHNETADDRESSTYPE_LOOP*/)
557         {
558                 // make sure there are no other connected clients
559                 for (i = 1;i < MAX_SCOREBOARD;i++)
560                         if (svs.connectedclients[i])
561                                 return false;
562                 return true;
563         }
564         return false;
565 }
566
567 static struct
568 {
569         double senttime;
570         lhnetaddress_t peeraddress;
571 }
572 pingcache[HOSTCACHESIZE];
573
574 int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress)
575 {
576         int ret, c, control;
577         lhnetaddress_t svaddress;
578         const char *s;
579         char *string, addressstring2[128], cname[128], ipstring[32];
580         char stringbuf[16384];
581
582         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
583         {
584                 // received a command string - strip off the packaging and put it
585                 // into our string buffer with NULL termination
586                 data += 4;
587                 length -= 4;
588                 length = min(length, (int)sizeof(stringbuf) - 1);
589                 memcpy(stringbuf, data, length);
590                 stringbuf[length] = 0;
591                 string = stringbuf;
592
593                 if (developer.integer)
594                 {
595                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
596                         Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
597                         Com_HexDumpToConsole(data, length);
598                 }
599
600                 if (length > 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
601                 {
602                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
603                         Con_Printf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
604                         NetConn_WriteString(mysocket, va("\377\377\377\377connect\\protocol\\darkplaces 3\\challenge\\%s", string + 10), peeraddress);
605                         return true;
606                 }
607                 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
608                 {
609                         NetConn_ConnectionEstablished(mysocket, peeraddress);
610                         return true;
611                 }
612                 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
613                 {
614                         cls.connect_trying = false;
615                         string += 7;
616                         length = max(length - 7, (int)sizeof(m_return_reason) - 1);
617                         memcpy(m_return_reason, string, length);
618                         m_return_reason[length] = 0;
619                         Con_Printf("%s\n", m_return_reason);
620                         return true;
621                 }
622                 if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
623                 {
624                         int i, j, c, n, users, maxusers;
625                         char game[32], mod[32], map[32], name[128];
626                         double pingtime;
627                         hostcache_t temp;
628                         string += 13;
629                         // hostcache only uses text addresses
630                         LHNETADDRESS_ToString(peeraddress, cname, sizeof(cname), true);
631                         // search the cache for this server
632                         for (n = 0; n < hostCacheCount; n++)
633                                 if (!strcmp(cname, hostcache[n].cname))
634                                         break;
635                         // add it or update it
636                         if (n == hostCacheCount)
637                         {
638                                 // if cache is full replace highest ping server (the list is
639                                 // kept sorted so this is always the last, and if this server
640                                 // is good it will be sorted into an early part of the list)
641                                 if (hostCacheCount >= HOSTCACHESIZE)
642                                         n = hostCacheCount - 1;
643                                 else
644                                         hostCacheCount++;
645                         }
646                         if ((s = SearchInfostring(string, "gamename"     )) != NULL) strncpy(game, s, sizeof(game) - 1);else game[0] = 0;
647                         if ((s = SearchInfostring(string, "modname"      )) != NULL) strncpy(mod , s, sizeof(mod ) - 1);else mod[0] = 0;
648                         if ((s = SearchInfostring(string, "mapname"      )) != NULL) strncpy(map , s, sizeof(map ) - 1);else map[0] = 0;
649                         if ((s = SearchInfostring(string, "hostname"     )) != NULL) strncpy(name, s, sizeof(name) - 1);else name[0] = 0;
650                         if ((s = SearchInfostring(string, "protocol"     )) != NULL) c = atoi(s);else c = -1;
651                         if ((s = SearchInfostring(string, "clients"      )) != NULL) users = atoi(s);else users = 0;
652                         if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) maxusers = atoi(s);else maxusers = 0;
653                         pingtime = 9999;
654                         for (i = 0;i < HOSTCACHESIZE;i++)
655                                 if (!LHNETADDRESS_Compare(peeraddress, &pingcache[i].peeraddress))
656                                         pingtime = (int)(realtime - pingcache[i].senttime);
657                         pingtime = bound(0, pingtime, 9999);
658                         memset(&hostcache[n], 0, sizeof(hostcache[n]));
659                         // store the data the engine cares about (address and ping)
660                         strcpy(hostcache[n].cname, cname);
661                         hostcache[n].ping = pingtime;
662                         // build description strings for the things users care about
663                         snprintf(hostcache[n].line1, sizeof(hostcache[n].line1), "%5d%c%3u/%3u %-65.65s", (int)pingtime, c != NET_PROTOCOL_VERSION ? '*' : ' ', users, maxusers, name);
664                         snprintf(hostcache[n].line2, sizeof(hostcache[n].line2), "%-21.21s %-19.19s %-17.17s %-20.20s", cname, game, mod, map);
665                         // if ping is especially high, display it as such
666                         if (pingtime >= 300)
667                         {
668                                 // orange numbers (lower block)
669                                 for (i = 0;i < 5;i++)
670                                         if (hostcache[n].line1[i] != ' ')
671                                                 hostcache[n].line1[i] += 128;
672                         }
673                         else if (pingtime >= 200)
674                         {
675                                 // yellow numbers (in upper block)
676                                 for (i = 0;i < 5;i++)
677                                         if (hostcache[n].line1[i] != ' ')
678                                                 hostcache[n].line1[i] -= 30;
679                         }
680                         // if not in the slist menu we should print the server to console
681                         if (m_state != m_slist)
682                                 Con_Printf("%s\n%s\n", hostcache[n].line1, hostcache[n].line2);
683                         // and finally, re-sort the list
684                         for (i = 0;i < hostCacheCount;i++)
685                         {
686                                 for (j = i + 1;j < hostCacheCount;j++)
687                                 {
688                                         //if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
689                                         if (hostcache[i].ping > hostcache[j].ping)
690                                         {
691                                                 memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
692                                                 memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
693                                                 memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
694                                         }
695                                 }
696                         }
697                         return true;
698                 }
699                 if (!strncmp(string, "getserversResponse\\", 19) && hostCacheCount < HOSTCACHESIZE)
700                 {
701                         int i, best;
702                         double besttime;
703                         // Extract the IP addresses
704                         data += 18;
705                         length -= 18;
706                         while (length >= 7 && data[0] == '\\' && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF) && data[5] * 256 + data[6] != 0)
707                         {
708                                 sprintf(ipstring, "%u.%u.%u.%u:%u", data[1], data[2], data[3], data[4], (data[5] << 8) | data[6]);
709                                 if (developer.integer)
710                                         Con_Printf("Requesting info from server %s\n", ipstring);
711                                 LHNETADDRESS_FromString(&svaddress, ipstring, 0);
712                                 NetConn_WriteString(mysocket, "\377\377\377\377getinfo", &svaddress);
713                                 // replace oldest or matching entry in ping cache
714                                 // we scan this later when getting a reply to see how long it took
715                                 besttime = realtime;
716                                 best = 0;
717                                 for (i = 0;i < HOSTCACHESIZE;i++)
718                                 {
719                                         if (!LHNETADDRESS_Compare(&svaddress, &pingcache[i].peeraddress))
720                                         {
721                                                 best = i;
722                                                 break;
723                                         }
724                                         if (besttime > pingcache[i].senttime)
725                                         {
726                                                 besttime = pingcache[i].senttime;
727                                                 best = i;
728                                                 // if ping cache isn't full yet we can skip out early
729                                                 if (!besttime)
730                                                         break;
731                                         }
732                                 }
733                                 pingcache[best].peeraddress = svaddress;
734                                 pingcache[best].senttime = realtime;
735                                 // move on to next address in packet
736                                 data += 7;
737                                 length -= 7;
738                         }
739                         return true;
740                 }
741                 /*
742                 if (!strncmp(string, "ping", 4))
743                 {
744                         if (developer.integer)
745                                 Con_Printf("Received ping from %s, sending ack\n", UDP_AddrToString(readaddr));
746                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
747                         return true;
748                 }
749                 if (!strncmp(string, "ack", 3))
750                         return true;
751                 */
752                 // we may not have liked the packet, but it was a command packet, so
753                 // we're done processing this packet now
754                 return true;
755         }
756         // netquake control packets, supported for compatibility only
757         if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
758         {
759                 c = data[4];
760                 data += 5;
761                 length -= 5;
762                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
763                 switch (c)
764                 {
765                 case CCREP_ACCEPT:
766                         if (developer.integer)
767                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
768                         if (cls.connect_trying)
769                         {
770                                 lhnetaddress_t clientportaddress;
771                                 clientportaddress = *peeraddress;
772                                 if (length >= 4)
773                                 {
774                                         unsigned int port = (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
775                                         data += 4;
776                                         length -= 4;
777                                         LHNETADDRESS_SetPort(&clientportaddress, port);
778                                 }
779                                 NetConn_ConnectionEstablished(mysocket, &clientportaddress);
780                         }
781                         break;
782                 case CCREP_REJECT:
783                         if (developer.integer)
784                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
785                         Con_Printf("%s\n", data);
786                         strncpy(m_return_reason, data, sizeof(m_return_reason) - 1);
787                         break;
788 #if 0
789                 case CCREP_SERVER_INFO:
790                         if (developer.integer)
791                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
792                         if (cls.state != ca_dedicated)
793                         {
794                                 // LordHavoc: because the UDP driver reports 0.0.0.0:26000 as the address
795                                 // string we just ignore it and keep the real address
796                                 MSG_ReadString();
797                                 // hostcache only uses text addresses
798                                 cname = UDP_AddrToString(readaddr);
799                                 // search the cache for this server
800                                 for (n = 0; n < hostCacheCount; n++)
801                                         if (!strcmp(cname, hostcache[n].cname))
802                                                 break;
803                                 // add it
804                                 if (n == hostCacheCount && hostCacheCount < HOSTCACHESIZE)
805                                 {
806                                         hostCacheCount++;
807                                         memset(&hostcache[n], 0, sizeof(hostcache[n]));
808                                         strcpy(hostcache[n].name, MSG_ReadString());
809                                         strcpy(hostcache[n].map, MSG_ReadString());
810                                         hostcache[n].users = MSG_ReadByte();
811                                         hostcache[n].maxusers = MSG_ReadByte();
812                                         c = MSG_ReadByte();
813                                         if (c != NET_PROTOCOL_VERSION)
814                                         {
815                                                 strncpy(hostcache[n].cname, hostcache[n].name, sizeof(hostcache[n].cname) - 1);
816                                                 hostcache[n].cname[sizeof(hostcache[n].cname) - 1] = 0;
817                                                 strcpy(hostcache[n].name, "*");
818                                                 strncat(hostcache[n].name, hostcache[n].cname, sizeof(hostcache[n].name) - 1);
819                                                 hostcache[n].name[sizeof(hostcache[n].name) - 1] = 0;
820                                         }
821                                         strcpy(hostcache[n].cname, cname);
822                                 }
823                         }
824                         break;
825                 case CCREP_PLAYER_INFO:
826                         // we got a CCREP_PLAYER_INFO??
827                         //if (developer.integer)
828                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
829                         break;
830                 case CCREP_RULE_INFO:
831                         // we got a CCREP_RULE_INFO??
832                         //if (developer.integer)
833                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
834                         break;
835 #endif
836                 default:
837                         break;
838                 }
839                 // we may not have liked the packet, but it was a valid control
840                 // packet, so we're done processing this packet now
841                 return true;
842         }
843         ret = 0;
844         if (length >= (int)NET_HEADERSIZE && cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress) && (ret = NetConn_ReceivedMessage(cls.netcon, data, length)) == 2)
845                 CL_ParseServerMessage();
846         return ret;
847 }
848
849 void NetConn_ClientFrame(void)
850 {
851         int i, length;
852         lhnetaddress_t peeraddress;
853         netconn_t *conn;
854         NetConn_UpdateServerStuff();
855         if (cls.connect_trying && cls.connect_nextsendtime < realtime)
856         {
857                 if (cls.connect_remainingtries == 0)
858                 {
859                         cls.connect_trying = false;
860                         Host_Error("Connect failed\n");
861                         return;
862                 }
863                 if (cls.connect_nextsendtime)
864                         Con_Printf("Still trying...\n");
865                 else
866                         Con_Printf("Trying...\n");
867                 cls.connect_nextsendtime = realtime + 1;
868                 cls.connect_remainingtries--;
869                 // try challenge first (newer server)
870                 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
871                 // then try netquake as a fallback (old server, or netquake)
872                 SZ_Clear(&net_message);
873                 // save space for the header, filled in later
874                 MSG_WriteLong(&net_message, 0);
875                 MSG_WriteByte(&net_message, CCREQ_CONNECT);
876                 MSG_WriteString(&net_message, "QUAKE");
877                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
878                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
879                 NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address);
880                 SZ_Clear(&net_message);
881         }
882         for (i = 0;i < cl_numsockets;i++)
883                 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
884                         NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
885         if (cls.netcon && realtime > cls.netcon->timeout)
886         {
887                 Con_Printf("Connection timed out\n");
888                 CL_Disconnect();
889         }
890         for (conn = netconn_list;conn;conn = conn->next)
891                 NetConn_ReSendMessage(conn);
892 }
893
894 #define MAX_CHALLENGES 128
895 struct
896 {
897         lhnetaddress_t address;
898         double time;
899         char string[12];
900 }
901 challenge[MAX_CHALLENGES];
902
903 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
904 {
905         int i;
906         char c;
907         for (i = 0;i < bufferlength - 1;i++)
908         {
909                 do
910                 {
911                         c = rand () % (127 - 33) + 33;
912                 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
913                 buffer[i] = c;
914         }
915         buffer[i] = 0;
916 }
917
918 extern void SV_ConnectClient(int clientnum, netconn_t *netconnection);
919 int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress)
920 {
921         int i, n, ret, clientnum, responselength, best;
922         double besttime;
923         client_t *client;
924         netconn_t *conn;
925         char *s, *string, response[512], addressstring2[128], stringbuf[16384];
926
927         if (sv.active)
928         {
929                 if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
930                 {
931                         // received a command string - strip off the packaging and put it
932                         // into our string buffer with NULL termination
933                         data += 4;
934                         length -= 4;
935                         length = min(length, (int)sizeof(stringbuf) - 1);
936                         memcpy(stringbuf, data, length);
937                         stringbuf[length] = 0;
938                         string = stringbuf;
939
940                         if (developer.integer)
941                         {
942                                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
943                                 Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
944                                 Com_HexDumpToConsole(data, length);
945                         }
946
947                         if (length >= 12 && !memcmp(string, "getchallenge", 12))
948                         {
949                                 for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
950                                 {
951                                         if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
952                                                 break;
953                                         if (besttime > challenge[i].time)
954                                                 besttime = challenge[best = i].time;
955                                 }
956                                 // if we did not find an exact match, choose the oldest and
957                                 // update address and string
958                                 if (i == MAX_CHALLENGES)
959                                 {
960                                         i = best;
961                                         challenge[i].address = *peeraddress;
962                                         NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
963                                 }
964                                 challenge[i].time = realtime;
965                                 // send the challenge
966                                 NetConn_WriteString(mysocket, va("\377\377\377\377challenge %s", challenge[i].string), peeraddress);
967                                 return true;
968                         }
969                         if (length > 8 && !memcmp(string, "connect\\", 8))
970                         {
971                                 string += 7;
972                                 length -= 7;
973                                 if ((s = SearchInfostring(string, "challenge")))
974                                 {
975                                         // validate the challenge
976                                         for (i = 0;i < MAX_CHALLENGES;i++)
977                                                 if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
978                                                         break;
979                                         if (i < MAX_CHALLENGES)
980                                         {
981                                                 // check engine protocol
982                                                 if (strcmp(SearchInfostring(string, "protocol"), "darkplaces 3"))
983                                                 {
984                                                         if (developer.integer)
985                                                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
986                                                         NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
987                                                 }
988                                                 else
989                                                 {
990                                                         // see if this is a duplicate connection request
991                                                         for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++)
992                                                                 if ((client = svs.connectedclients[clientnum]) && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
993                                                                         break;
994                                                         if (clientnum < MAX_SCOREBOARD)
995                                                         {
996                                                                 // duplicate connection request
997                                                                 if (realtime - client->netconnection->connecttime < 2.0)
998                                                                 {
999                                                                         // client is still trying to connect,
1000                                                                         // so we send a duplicate reply
1001                                                                         if (developer.integer)
1002                                                                                 Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
1003                                                                         NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
1004                                                                 }
1005                                                                 // only kick if old connection seems dead
1006                                                                 if (realtime - client->netconnection->lastMessageTime >= net_messagerejointimeout.value)
1007                                                                 {
1008                                                                         // kick off connection and await retry
1009                                                                         client->deadsocket = true;
1010                                                                 }
1011                                                         }
1012                                                         else
1013                                                         {
1014                                                                 // this is a new client, find a slot
1015                                                                 for (clientcount = 0, clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++)
1016                                                                         if (svs.connectedclients[clientnum])
1017                                                                                 clientcount++;
1018                                                                 for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++)
1019                                                                         if (!svs.connectedclients[clientnum])
1020                                                                                 break;
1021                                                                 if (clientcount < max(1, sv_maxplayers.integer) && clientnum < MAX_SCOREBOARD)
1022                                                                 {
1023                                                                         // allocate and prepare the client struct
1024                                                                         if ((client = Mem_Alloc(sv_clients_mempool, sizeof(client_t))))
1025                                                                         {
1026                                                                                 if ((client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_clients_mempool)))
1027                                                                                 {
1028                                                                                         if ((conn = NetConn_Open(mysocket, peeraddress)))
1029                                                                                         {
1030                                                                                                 // allocated connection
1031                                                                                                 LHNETADDRESS_ToString(peeraddress, conn->address, sizeof(conn->address), true);
1032                                                                                                 if (developer.integer)
1033                                                                                                         Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
1034                                                                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
1035                                                                                                 // now set up the client struct
1036                                                                                                 svs.connectedclients[clientnum] = client;
1037                                                                                                 SV_ConnectClient(clientnum, conn);
1038                                                                                                 NetConn_Heartbeat(1);
1039                                                                                         }
1040                                                                                         else
1041                                                                                         {
1042                                                                                                 EntityFrame4_FreeDatabase(client->entitydatabase4);
1043                                                                                                 Mem_Free(client);
1044                                                                                         }
1045                                                                                 }
1046                                                                                 else
1047                                                                                         Mem_Free(client);
1048                                                                         }
1049                                                                 }
1050                                                                 else
1051                                                                 {
1052                                                                         // server is full
1053                                                                         if (developer.integer)
1054                                                                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
1055                                                                         NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
1056                                                                 }
1057                                                         }
1058                                                 }
1059                                         }
1060                                 }
1061                                 return true;
1062                         }
1063                         if (length >= 7 && !memcmp(string, "getinfo", 7))
1064                         {
1065                                 const char *challenge = NULL;
1066                                 // If there was a challenge in the getinfo message
1067                                 if (length > 8 && string[7] == ' ')
1068                                         challenge = string + 8;
1069                                 for (i = 0, n = 0;i < MAX_SCOREBOARD;i++)
1070                                         if (svs.connectedclients[i])
1071                                                 n++;
1072                                 responselength = snprintf(response, sizeof(response), "\377\377\377\377infoResponse\x0A"
1073                                                         "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
1074                                                         "\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d%s%s",
1075                                                         gamename, com_modname, min(sv_maxplayers.integer, MAX_SCOREBOARD), n,
1076                                                         sv.name, hostname.string, NET_PROTOCOL_VERSION, challenge ? "\\challenge\\" : "", challenge ? challenge : "");
1077                                 // does it fit in the buffer?
1078                                 if (responselength < (int)sizeof(response))
1079                                 {
1080                                         if (developer.integer)
1081                                                 Con_Printf("Sending reply to master %s - %s\n", addressstring2, response);
1082                                         NetConn_WriteString(mysocket, response, peeraddress);
1083                                 }
1084                                 return true;
1085                         }
1086                         /*
1087                         if (!strncmp(string, "ping", 4))
1088                         {
1089                                 if (developer.integer)
1090                                         Con_Printf("Received ping from %s, sending ack\n", UDP_AddrToString(readaddr));
1091                                 NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
1092                                 return true;
1093                         }
1094                         if (!strncmp(string, "ack", 3))
1095                                 return true;
1096                         */
1097                         // we may not have liked the packet, but it was a command packet, so
1098                         // we're done processing this packet now
1099                         return true;
1100                 }
1101                 // LordHavoc: disabled netquake control packet support in server
1102 #if 0
1103                 {
1104                         int c, control;
1105                         // netquake control packets, supported for compatibility only
1106                         if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
1107                         {
1108                                 c = data[4];
1109                                 data += 5;
1110                                 length -= 5;
1111                                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
1112                                 switch (c)
1113                                 {
1114                                 case CCREQ_CONNECT:
1115                                         //if (developer.integer)
1116                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
1117                                         if (length >= (int)strlen("QUAKE") + 1 + 1)
1118                                         {
1119                                                 if (memcmp(data, "QUAKE", strlen("QUAKE") + 1) != 0 || (int)data[strlen("QUAKE") + 1] != NET_PROTOCOL_VERSION)
1120                                                 {
1121                                                         if (developer.integer)
1122                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
1123                                                         SZ_Clear(&net_message);
1124                                                         // save space for the header, filled in later
1125                                                         MSG_WriteLong(&net_message, 0);
1126                                                         MSG_WriteByte(&net_message, CCREP_REJECT);
1127                                                         MSG_WriteString(&net_message, "Incompatible version.\n");
1128                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1129                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1130                                                         SZ_Clear(&net_message);
1131                                                 }
1132                                                 else
1133                                                 {
1134                                                         // see if this is a duplicate connection request
1135                                                         for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++)
1136                                                                 if ((client = svs.connectedclients[clientnum]) && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
1137                                                                         break;
1138                                                         if (clientnum < MAX_SCOREBOARD)
1139                                                         {
1140                                                                 // duplicate connection request
1141                                                                 if (realtime - client->netconnection->connecttime < 2.0)
1142                                                                 {
1143                                                                         // client is still trying to connect,
1144                                                                         // so we send a duplicate reply
1145                                                                         if (developer.integer)
1146                                                                                 Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
1147                                                                         SZ_Clear(&net_message);
1148                                                                         // save space for the header, filled in later
1149                                                                         MSG_WriteLong(&net_message, 0);
1150                                                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
1151                                                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
1152                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1153                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1154                                                                         SZ_Clear(&net_message);
1155                                                                 }
1156                                                                 else if (realtime - client->netconnection->lastMessageTime >= net_messagerejointimeout.value)
1157                                                                 {
1158                                                                         // the old client hasn't sent us anything
1159                                                                         // in quite a while, so kick off and let
1160                                                                         // the retry take care of it...
1161                                                                         client->deadsocket = true;
1162                                                                 }
1163                                                         }
1164                                                         else
1165                                                         {
1166                                                                 // this is a new client, find a slot
1167                                                                 for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++)
1168                                                                         if (!(client = svs.connectedclients[clientnum]))
1169                                                                                 break;
1170                                                                 // WARNING: this is broken code
1171                                                                 if (clientnum < MAX_SCOREBOARD && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
1172                                                                 {
1173                                                                         // connect to the client
1174                                                                         // everything is allocated, just fill in the details
1175                                                                         strcpy(conn->address, addressstring2);
1176                                                                         if (developer.integer)
1177                                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
1178                                                                         // send back the info about the server connection
1179                                                                         SZ_Clear(&net_message);
1180                                                                         // save space for the header, filled in later
1181                                                                         MSG_WriteLong(&net_message, 0);
1182                                                                         MSG_WriteByte(&net_message, CCREP_ACCEPT);
1183                                                                         MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
1184                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1185                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1186                                                                         SZ_Clear(&net_message);
1187                                                                         // now set up the client struct
1188                                                                         SV_ConnectClient(clientnum, conn);
1189                                                                         NetConn_Heartbeat(1);
1190                                                                 }
1191                                                                 else
1192                                                                 {
1193                                                                         //if (developer.integer)
1194                                                                                 Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
1195                                                                         // no room; try to let player know
1196                                                                         SZ_Clear(&net_message);
1197                                                                         // save space for the header, filled in later
1198                                                                         MSG_WriteLong(&net_message, 0);
1199                                                                         MSG_WriteByte(&net_message, CCREP_REJECT);
1200                                                                         MSG_WriteString(&net_message, "Server is full.\n");
1201                                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1202                                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1203                                                                         SZ_Clear(&net_message);
1204                                                                 }
1205                                                         }
1206                                                 }
1207                                         }
1208                                         break;
1209 #if 0
1210                                 case CCREQ_SERVER_INFO:
1211                                         if (developer.integer)
1212                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
1213                                         if (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
1214                                         {
1215                                                 if (developer.integer)
1216                                                         Con_Printf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
1217                                                 SZ_Clear(&net_message);
1218                                                 // save space for the header, filled in later
1219                                                 MSG_WriteLong(&net_message, 0);
1220                                                 MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
1221                                                 UDP_GetSocketAddr(UDP_acceptSock, &newaddr);
1222                                                 MSG_WriteString(&net_message, UDP_AddrToString(&newaddr));
1223                                                 MSG_WriteString(&net_message, hostname.string);
1224                                                 MSG_WriteString(&net_message, sv.name);
1225                                                 MSG_WriteByte(&net_message, net_activeconnections);
1226                                                 MSG_WriteByte(&net_message, min(sv_maxplayers.integer, MAX_SCOREBOARD));
1227                                                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
1228                                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1229                                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1230                                                 SZ_Clear(&net_message);
1231                                         }
1232                                         break;
1233                                 case CCREQ_PLAYER_INFO:
1234                                         if (developer.integer)
1235                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
1236                                         if (sv.active)
1237                                         {
1238                                                 int playerNumber, activeNumber, clientNumber;
1239                                                 client_t *client;
1240
1241                                                 playerNumber = MSG_ReadByte();
1242                                                 activeNumber = -1;
1243                                                 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
1244                                                         if (client->active && ++activeNumber == playerNumber)
1245                                                                 break;
1246                                                 if (clientNumber != svs.maxclients)
1247                                                 {
1248                                                         SZ_Clear(&net_message);
1249                                                         // save space for the header, filled in later
1250                                                         MSG_WriteLong(&net_message, 0);
1251                                                         MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
1252                                                         MSG_WriteByte(&net_message, playerNumber);
1253                                                         MSG_WriteString(&net_message, client->name);
1254                                                         MSG_WriteLong(&net_message, client->colors);
1255                                                         MSG_WriteLong(&net_message, (int)client->edict->v->frags);
1256                                                         MSG_WriteLong(&net_message, (int)(realtime - client->netconnection->connecttime));
1257                                                         MSG_WriteString(&net_message, client->netconnection->address);
1258                                                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1259                                                         NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1260                                                         SZ_Clear(&net_message);
1261                                                 }
1262                                         }
1263                                         break;
1264                                 case CCREQ_RULE_INFO:
1265                                         if (developer.integer)
1266                                                 Con_Printf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
1267                                         if (sv.active)
1268                                         {
1269                                                 char *prevCvarName;
1270                                                 cvar_t *var;
1271
1272                                                 // find the search start location
1273                                                 prevCvarName = MSG_ReadString();
1274                                                 var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
1275
1276                                                 // send the response
1277                                                 SZ_Clear(&net_message);
1278                                                 // save space for the header, filled in later
1279                                                 MSG_WriteLong(&net_message, 0);
1280                                                 MSG_WriteByte(&net_message, CCREP_RULE_INFO);
1281                                                 if (var)
1282                                                 {
1283                                                         MSG_WriteString(&net_message, var->name);
1284                                                         MSG_WriteString(&net_message, var->string);
1285                                                 }
1286                                                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1287                                                 NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
1288                                                 SZ_Clear(&net_message);
1289                                         }
1290                                         break;
1291 #endif
1292                                 default:
1293                                         break;
1294                                 }
1295                                 // we may not have liked the packet, but it was a valid control
1296                                 // packet, so we're done processing this packet now
1297                                 return true;
1298                         }
1299                 }
1300 #endif
1301                 for (i = 0;i < MAX_SCOREBOARD;i++)
1302                 {
1303                         if ((host_client = svs.connectedclients[i]))
1304                         {
1305                                 if (host_client->netconnection)
1306                                 {
1307                                         if (host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
1308                                         {
1309                                                 sv_player = host_client->edict;
1310                                                 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
1311                                                         SV_ReadClientMessage();
1312                                                 return ret;
1313                                         }
1314                                 }
1315                                 else
1316                                 {
1317                                         Con_Printf("Removing client with no netconnection!\n");
1318                                         SV_DropClient(true);
1319                                 }
1320                         }
1321                 }
1322         }
1323         return 0;
1324 }
1325
1326 void NetConn_ServerFrame(void)
1327 {
1328         int i, length;
1329         lhnetaddress_t peeraddress;
1330         netconn_t *conn;
1331         NetConn_UpdateServerStuff();
1332         for (i = 0;i < sv_numsockets;i++)
1333                 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
1334                         NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
1335         for (i = 0;i < MAX_SCOREBOARD;i++)
1336         {
1337                 if ((host_client = svs.connectedclients[i]) && realtime > host_client->netconnection->timeout)
1338                 {
1339                         Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
1340                         sv_player = host_client->edict;
1341                         SV_DropClient(false);
1342                 }
1343         }
1344         for (conn = netconn_list;conn;conn = conn->next)
1345                 NetConn_ReSendMessage(conn);
1346 }
1347
1348 void NetConn_QueryMasters(void)
1349 {
1350         int i;
1351         int masternum;
1352         lhnetaddress_t masteraddress;
1353         char request[256];
1354
1355         if (hostCacheCount >= HOSTCACHESIZE)
1356                 return;
1357
1358         for (i = 0;i < cl_numsockets;i++)
1359         {
1360                 if (cl_sockets[i])
1361                 {
1362 #if 0
1363                         // search LAN
1364 #if 1
1365                         UDP_Broadcast(UDP_controlSock, "\377\377\377\377getinfo", 11);
1366 #else
1367                         SZ_Clear(&net_message);
1368                         // save space for the header, filled in later
1369                         MSG_WriteLong(&net_message, 0);
1370                         MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
1371                         MSG_WriteString(&net_message, "QUAKE");
1372                         MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
1373                         *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
1374                         UDP_Broadcast(UDP_controlSock, net_message.data, net_message.cursize);
1375                         SZ_Clear(&net_message);
1376 #endif
1377 #endif
1378
1379                         // build the getservers
1380                         snprintf(request, sizeof(request), "\377\377\377\377getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
1381
1382                         // search internet
1383                         for (masternum = 0;sv_masters[masternum].name;masternum++)
1384                                 if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, MASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
1385                                         NetConn_WriteString(cl_sockets[i], request, &masteraddress);
1386                 }
1387         }
1388 }
1389
1390 void NetConn_Heartbeat(int priority)
1391 {
1392         lhnetaddress_t masteraddress;
1393         int masternum;
1394         lhnetsocket_t *mysocket;
1395
1396         // if it's a state change (client connected), limit next heartbeat to no
1397         // more than 30 sec in the future
1398         if (priority == 1 && nextheartbeattime > realtime + 30.0)
1399                 nextheartbeattime = realtime + 30.0;
1400
1401         // limit heartbeatperiod to 30 to 270 second range,
1402         // lower limit is to avoid abusing master servers with excess traffic,
1403         // upper limit is to avoid timing out on the master server (which uses
1404         // 300 sec timeout)
1405         if (sv_heartbeatperiod.value < 30)
1406                 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
1407         if (sv_heartbeatperiod.value > 270)
1408                 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
1409
1410         // make advertising optional and don't advertise singleplayer games, and
1411         // only send a heartbeat as often as the admin wants
1412         if (sv.active && sv_public.integer && (!cl.islocalgame || sv_maxplayers.integer >= 2) && (priority > 1 || realtime > nextheartbeattime))
1413         {
1414                 nextheartbeattime = realtime + sv_heartbeatperiod.value;
1415                 for (masternum = 0;sv_masters[masternum].name;masternum++)
1416                         if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, MASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
1417                                 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
1418         }
1419 }
1420
1421 int NetConn_SendToAll(sizebuf_t *data, double blocktime)
1422 {
1423         int i, count = 0;
1424         qbyte sent[MAX_SCOREBOARD];
1425
1426         memset(sent, 0, sizeof(sent));
1427
1428         // simultaneously wait for the first CanSendMessage and send the message,
1429         // then wait for a second CanSendMessage (verifying it was received), or
1430         // the client drops and is no longer counted
1431         // the loop aborts when either it runs out of clients to send to, or a
1432         // timeout expires
1433         blocktime += Sys_DoubleTime();
1434         do
1435         {
1436                 count = 0;
1437                 NetConn_ClientFrame();
1438                 NetConn_ServerFrame();
1439                 for (i = 0;i < MAX_SCOREBOARD;i++)
1440                 {
1441                         if ((host_client = svs.connectedclients[i]))
1442                         {
1443                                 if (NetConn_CanSendMessage(host_client->netconnection))
1444                                 {
1445                                         if (!sent[i])
1446                                                 NetConn_SendReliableMessage(host_client->netconnection, data);
1447                                         sent[i] = true;
1448                                 }
1449                                 if (!NetConn_CanSendMessage(host_client->netconnection))
1450                                         count++;
1451                         }
1452                 }
1453         }
1454         while (count && Sys_DoubleTime() < blocktime);
1455         return count;
1456 }
1457
1458 static void Net_Heartbeat_f(void)
1459 {
1460         NetConn_Heartbeat(2);
1461 }
1462
1463 void PrintStats(netconn_t *conn)
1464 {
1465         Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, conn->canSend, conn->sendSequence, conn->receiveSequence);
1466 }
1467
1468 void Net_Stats_f(void)
1469 {
1470         netconn_t *conn;
1471         Con_Printf("unreliable messages sent   = %i\n", unreliableMessagesSent);
1472         Con_Printf("unreliable messages recv   = %i\n", unreliableMessagesReceived);
1473         Con_Printf("reliable messages sent     = %i\n", reliableMessagesSent);
1474         Con_Printf("reliable messages received = %i\n", reliableMessagesReceived);
1475         Con_Printf("packetsSent                = %i\n", packetsSent);
1476         Con_Printf("packetsReSent              = %i\n", packetsReSent);
1477         Con_Printf("packetsReceived            = %i\n", packetsReceived);
1478         Con_Printf("receivedDuplicateCount     = %i\n", receivedDuplicateCount);
1479         Con_Printf("droppedDatagrams           = %i\n", droppedDatagrams);
1480         Con_Printf("connections                =\n");
1481         for (conn = netconn_list;conn;conn = conn->next)
1482                 PrintStats(conn);
1483 }
1484
1485 void Net_Slist_f(void)
1486 {
1487         hostCacheCount = 0;
1488         memset(&pingcache, 0, sizeof(pingcache));
1489         if (m_state != m_slist)
1490                 Con_Printf("Sending requests to master servers\n");
1491         NetConn_QueryMasters();
1492         if (m_state != m_slist)
1493                 Con_Printf("Listening for replies...\n");
1494 }
1495
1496 void NetConn_Init(void)
1497 {
1498         int masternum;
1499         netconn_mempool = Mem_AllocPool("Networking");
1500         Cmd_AddCommand("net_stats", Net_Stats_f);
1501         Cmd_AddCommand("net_slist", Net_Slist_f);
1502         Cmd_AddCommand("heartbeat", Net_Heartbeat_f);
1503         Cvar_RegisterVariable(&net_messagetimeout);
1504         Cvar_RegisterVariable(&net_messagerejointimeout);
1505         Cvar_RegisterVariable(&net_connecttimeout);
1506         Cvar_RegisterVariable(&hostname);
1507         Cvar_RegisterVariable(&developer_networking);
1508         Cvar_RegisterVariable(&cl_netport);
1509         Cvar_RegisterVariable(&cl_netaddress);
1510         Cvar_RegisterVariable(&cl_netaddress_ipv6);
1511         Cvar_RegisterVariable(&sv_netport);
1512         Cvar_RegisterVariable(&sv_netaddress);
1513         Cvar_RegisterVariable(&sv_netaddress_ipv6);
1514         Cvar_RegisterVariable(&sv_maxplayers);
1515         Cvar_RegisterVariable(&sv_public);
1516         Cvar_RegisterVariable(&sv_heartbeatperiod);
1517         for (masternum = 0;sv_masters[masternum].name;masternum++)
1518                 Cvar_RegisterVariable(&sv_masters[masternum]);
1519         cl_numsockets = 0;
1520         sv_numsockets = 0;
1521         memset(&pingcache, 0, sizeof(pingcache));
1522         SZ_Alloc(&net_message, NET_MAXMESSAGE, "net_message");
1523         LHNET_Init();
1524 }
1525
1526 void NetConn_Shutdown(void)
1527 {
1528         NetConn_CloseClientPorts();
1529         NetConn_CloseServerPorts();
1530         LHNET_Shutdown();
1531 }
1532