X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=netconn.c;h=a297a054a879000bbc276f81117759adc1258737;hp=c83966422ba4048a1c055826ad33dc047e29a123;hb=71fa14060b3cf97a78917841da5143420ed8c3d4;hpb=49bfdcaf745787c90f104b59dfa605f35301259c diff --git a/netconn.c b/netconn.c index c8396642..a297a054 100755 --- a/netconn.c +++ b/netconn.c @@ -49,6 +49,14 @@ cvar_t net_connecttimeout = {0, "net_connecttimeout","10"}; cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED"}; cvar_t developer_networking = {0, "developer_networking", "0"}; +cvar_t cl_fakelocalping_min = {0, "cl_fakelocalping_min","0"}; +cvar_t cl_fakelocalping_max = {0, "cl_fakelocalping_max","0"}; +static cvar_t cl_fakepacketloss_receive = {0, "cl_fakepacketloss_receive","0"}; +static cvar_t cl_fakepacketloss_send = {0, "cl_fakepacketloss_send","0"}; +static cvar_t sv_fakepacketloss_receive = {0, "sv_fakepacketloss_receive","0"}; +static cvar_t sv_fakepacketloss_send = {0, "sv_fakepacketloss_send","0"}; + + /* statistic counters */ static int packetsSent = 0; static int packetsReSent = 0; @@ -86,6 +94,15 @@ cvar_t sv_netaddress_ipv6 = {0, "sv_netaddress_ipv6", "[0:0:0:0:0:0:0:0]:26000"} int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress) { int length = LHNET_Read(mysocket, data, maxlength, peeraddress); + int i; + if (cl_fakepacketloss_receive.integer) + for (i = 0;i < cl_numsockets;i++) + if (cl_sockets[i] == mysocket && (rand() % 100) < cl_fakepacketloss_receive.integer) + return 0; + if (sv_fakepacketloss_receive.integer) + for (i = 0;i < cl_numsockets;i++) + if (sv_sockets[i] == mysocket && (rand() % 100) < sv_fakepacketloss_receive.integer) + return 0; if (developer_networking.integer && length != 0) { char addressstring[128], addressstring2[128]; @@ -104,8 +121,18 @@ int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddres int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress) { - int ret = LHNET_Write(mysocket, data, length, peeraddress); - if (developer_networking.integer && ret != 0) + int ret; + int i; + if (cl_fakepacketloss_send.integer) + for (i = 0;i < cl_numsockets;i++) + if (cl_sockets[i] == mysocket && (rand() % 100) < cl_fakepacketloss_send.integer) + return length; + if (sv_fakepacketloss_send.integer) + for (i = 0;i < cl_numsockets;i++) + if (sv_sockets[i] == mysocket && (rand() % 100) < sv_fakepacketloss_send.integer) + return length; + ret = LHNET_Write(mysocket, data, length, peeraddress); + if (developer_networking.integer) { char addressstring[128], addressstring2[128]; LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true); @@ -116,6 +143,12 @@ int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const l return ret; } +int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress) +{ + // note this does not include the trailing NULL because we add that in the parser + return NetConn_Write(mysocket, string, strlen(string), peeraddress); +} + int NetConn_SendReliableMessage(netconn_t *conn, sizebuf_t *data) { unsigned int packetLen; @@ -137,14 +170,14 @@ int NetConn_SendReliableMessage(netconn_t *conn, sizebuf_t *data) memcpy(conn->sendMessage, data->data, data->cursize); conn->sendMessageLength = data->cursize; - if (conn->sendMessageLength <= MAX_DATAGRAM) + if (conn->sendMessageLength <= MAX_PACKETFRAGMENT) { dataLen = conn->sendMessageLength; eom = NETFLAG_EOM; } else { - dataLen = MAX_DATAGRAM; + dataLen = MAX_PACKETFRAGMENT; eom = 0; } @@ -176,14 +209,14 @@ static void NetConn_SendMessageNext(netconn_t *conn) if (conn->sendMessageLength && !conn->canSend && conn->sendNext) { - if (conn->sendMessageLength <= MAX_DATAGRAM) + if (conn->sendMessageLength <= MAX_PACKETFRAGMENT) { dataLen = conn->sendMessageLength; eom = NETFLAG_EOM; } else { - dataLen = MAX_DATAGRAM; + dataLen = MAX_PACKETFRAGMENT; eom = 0; } @@ -214,14 +247,14 @@ static void NetConn_ReSendMessage(netconn_t *conn) if (conn->sendMessageLength && !conn->canSend && (realtime - conn->lastSendTime) > 1.0) { - if (conn->sendMessageLength <= MAX_DATAGRAM) + if (conn->sendMessageLength <= MAX_PACKETFRAGMENT) { dataLen = conn->sendMessageLength; eom = NETFLAG_EOM; } else { - dataLen = MAX_DATAGRAM; + dataLen = MAX_PACKETFRAGMENT; eom = 0; } @@ -332,7 +365,7 @@ lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address) { int i, a = LHNETADDRESS_GetAddressType(address); for (i = 0;i < cl_numsockets;i++) - if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a) + if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a) return cl_sockets[i]; return NULL; } @@ -341,7 +374,7 @@ lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address) { int i, a = LHNETADDRESS_GetAddressType(address); for (i = 0;i < sv_numsockets;i++) - if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a) + if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a) return sv_sockets[i]; return NULL; } @@ -450,10 +483,13 @@ int NetConn_ReceivedMessage(netconn_t *conn, qbyte *data, int length) conn->lastMessageTime = realtime; conn->timeout = realtime + net_messagetimeout.value; unreliableMessagesReceived++; - SZ_Clear(&net_message); - SZ_Write(&net_message, data, length); - MSG_BeginReading(); - return 2; + if (length > 0) + { + SZ_Clear(&net_message); + SZ_Write(&net_message, data, length); + MSG_BeginReading(); + return 2; + } } else Con_DPrintf("Got a stale datagram\n"); @@ -470,10 +506,10 @@ int NetConn_ReceivedMessage(netconn_t *conn, qbyte *data, int length) Con_DPrintf("ack sequencing error\n"); conn->lastMessageTime = realtime; conn->timeout = realtime + net_messagetimeout.value; - conn->sendMessageLength -= MAX_DATAGRAM; + conn->sendMessageLength -= MAX_PACKETFRAGMENT; if (conn->sendMessageLength > 0) { - memcpy(conn->sendMessage, conn->sendMessage+MAX_DATAGRAM, conn->sendMessageLength); + memcpy(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength); conn->sendNext = true; NetConn_SendMessageNext(conn); } @@ -506,11 +542,15 @@ int NetConn_ReceivedMessage(netconn_t *conn, qbyte *data, int length) if (flags & NETFLAG_EOM) { reliableMessagesReceived++; - SZ_Clear(&net_message); - SZ_Write(&net_message, conn->receiveMessage, conn->receiveMessageLength); + length = conn->receiveMessageLength; conn->receiveMessageLength = 0; - MSG_BeginReading(); - return 2; + if (length > 0) + { + SZ_Clear(&net_message); + SZ_Write(&net_message, conn->receiveMessage, length); + MSG_BeginReading(); + return 2; + } } } else @@ -522,6 +562,34 @@ int NetConn_ReceivedMessage(netconn_t *conn, qbyte *data, int length) return 0; } +void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress) +{ + cls.netcon = NetConn_Open(mysocket, peeraddress); + Con_Printf("Connection accepted to %s\n", cls.netcon->address); + key_dest = key_game; + m_state = m_none; + cls.connect_trying = false; + cls.demonum = -1; // not in the demo loop now + cls.state = ca_connected; + cls.signon = 0; // need all the signon messages before playing + CL_ClearState(); + Host_Reconnect_f(); +} + +int NetConn_IsLocalGame(void) +{ + int i; + if (cls.state == ca_connected && sv.active/* && LHNETADDRESS_GetAddressType(cl.netcon->peeraddress) == LHNETADDRESSTYPE_LOOP*/) + { + // make sure there are no other connected clients + for (i = 1;i < MAX_SCOREBOARD;i++) + if (svs.connectedclients[i]) + return false; + return true; + } + return false; +} + static struct { double senttime; @@ -535,18 +603,48 @@ int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t svaddress; const char *s; char *string, addressstring2[128], cname[128], ipstring[32]; + char stringbuf[16384]; if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255) { + // received a command string - strip off the packaging and put it + // into our string buffer with NULL termination data += 4; length -= 4; - string = (char *)data; + length = min(length, (int)sizeof(stringbuf) - 1); + memcpy(stringbuf, data, length); + stringbuf[length] = 0; + string = stringbuf; + if (developer.integer) { LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true); Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2); Com_HexDumpToConsole(data, length); } + + if (length > 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying) + { + LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true); + Con_Printf("\"%s\" received, sending connect request back to %s\n", string, addressstring2); + NetConn_WriteString(mysocket, va("\377\377\377\377connect\\protocol\\darkplaces 3\\challenge\\%s", string + 10), peeraddress); + return true; + } + if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying) + { + NetConn_ConnectionEstablished(mysocket, peeraddress); + return true; + } + if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying) + { + cls.connect_trying = false; + string += 7; + length = max(length - 7, (int)sizeof(m_return_reason) - 1); + memcpy(m_return_reason, string, length); + m_return_reason[length] = 0; + Con_Printf("%s\n", m_return_reason); + return true; + } if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13)) { int i, j, c, n, users, maxusers; @@ -637,7 +735,7 @@ int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, if (developer.integer) Con_Printf("Requesting info from server %s\n", ipstring); LHNETADDRESS_FromString(&svaddress, ipstring, 0); - NetConn_Write(mysocket, "\377\377\377\377getinfo", 11, &svaddress); + NetConn_WriteString(mysocket, "\377\377\377\377getinfo", &svaddress); // replace oldest or matching entry in ping cache // we scan this later when getting a reply to see how long it took besttime = realtime; @@ -671,7 +769,7 @@ int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, { if (developer.integer) Con_Printf("Received ping from %s, sending ack\n", UDP_AddrToString(readaddr)); - NetConn_Write(mysocket, "\377\377\377\377ack", 7, peeraddress); + NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress); return true; } if (!strncmp(string, "ack", 3)) @@ -681,6 +779,7 @@ int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, // we're done processing this packet now return true; } + // netquake control packets, supported for compatibility only if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length) { c = data[4]; @@ -694,23 +793,16 @@ int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, Con_Printf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2); if (cls.connect_trying) { - Con_Printf("Connection accepted to %s\n", addressstring2); - key_dest = key_game; - m_state = m_none; - cls.netcon = NetConn_Open(mysocket, peeraddress); + lhnetaddress_t clientportaddress; + clientportaddress = *peeraddress; if (length >= 4) { - unsigned int port = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + unsigned int port = (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); data += 4; length -= 4; - LHNETADDRESS_SetPort(&cls.netcon->peeraddress, port); + LHNETADDRESS_SetPort(&clientportaddress, port); } - cls.connect_trying = false; - cls.demonum = -1; // not in the demo loop now - cls.state = ca_connected; - cls.signon = 0; // need all the signon messages before playing - CL_ClearState(); - Host_Reconnect_f(); + NetConn_ConnectionEstablished(mysocket, &clientportaddress); } break; case CCREP_REJECT: @@ -800,6 +892,9 @@ void NetConn_ClientFrame(void) Con_Printf("Trying...\n"); cls.connect_nextsendtime = realtime + 1; cls.connect_remainingtries--; + // try challenge first (newer server) + NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address); + // then try netquake as a fallback (old server, or netquake) SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); @@ -822,21 +917,51 @@ void NetConn_ClientFrame(void) NetConn_ReSendMessage(conn); } +#define MAX_CHALLENGES 128 +struct +{ + lhnetaddress_t address; + double time; + char string[12]; +} +challenge[MAX_CHALLENGES]; + +static void NetConn_BuildChallengeString(char *buffer, int bufferlength) +{ + int i; + char c; + for (i = 0;i < bufferlength - 1;i++) + { + do + { + c = rand () % (127 - 33) + 33; + } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/'); + buffer[i] = c; + } + buffer[i] = 0; +} + extern void SV_ConnectClient(int clientnum, netconn_t *netconnection); int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress) { - int i, c, n, ret, clientnum, control, responselength; + int i, n, ret, clientnum, responselength, best, clientcount; + double besttime; client_t *client; netconn_t *conn; - char *string, response[512], addressstring2[128]; + char *s, *string, response[512], addressstring2[128], stringbuf[16384]; if (sv.active) { if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255) { + // received a command string - strip off the packaging and put it + // into our string buffer with NULL termination data += 4; length -= 4; - string = (char *)data; + length = min(length, (int)sizeof(stringbuf) - 1); + memcpy(stringbuf, data, length); + stringbuf[length] = 0; + string = stringbuf; if (developer.integer) { @@ -845,26 +970,142 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, Com_HexDumpToConsole(data, length); } + if (length >= 12 && !memcmp(string, "getchallenge", 12)) + { + for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++) + { + if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address)) + break; + if (besttime > challenge[i].time) + besttime = challenge[best = i].time; + } + // if we did not find an exact match, choose the oldest and + // update address and string + if (i == MAX_CHALLENGES) + { + i = best; + challenge[i].address = *peeraddress; + NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string)); + } + challenge[i].time = realtime; + // send the challenge + NetConn_WriteString(mysocket, va("\377\377\377\377challenge %s", challenge[i].string), peeraddress); + return true; + } + if (length > 8 && !memcmp(string, "connect\\", 8)) + { + string += 7; + length -= 7; + if ((s = SearchInfostring(string, "challenge"))) + { + // validate the challenge + for (i = 0;i < MAX_CHALLENGES;i++) + if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s)) + break; + if (i < MAX_CHALLENGES) + { + // check engine protocol + if (strcmp(SearchInfostring(string, "protocol"), "darkplaces 3")) + { + if (developer.integer) + Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2); + NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress); + } + else + { + // see if this is a duplicate connection request + for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++) + if ((client = svs.connectedclients[clientnum]) && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0) + break; + if (clientnum < MAX_SCOREBOARD) + { + // duplicate connection request + if (realtime - client->netconnection->connecttime < 2.0) + { + // client is still trying to connect, + // so we send a duplicate reply + if (developer.integer) + Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2); + NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress); + } + // only kick if old connection seems dead + if (realtime - client->netconnection->lastMessageTime >= net_messagerejointimeout.value) + { + // kick off connection and await retry + client->deadsocket = true; + } + } + else + { + // this is a new client, find a slot + for (clientcount = 0, clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++) + if (svs.connectedclients[clientnum]) + clientcount++; + for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++) + if (!svs.connectedclients[clientnum]) + break; + if (clientcount < max(1, sv_maxplayers.integer) && clientnum < MAX_SCOREBOARD) + { + // allocate and prepare the client struct + if ((client = Mem_Alloc(sv_clients_mempool, sizeof(client_t)))) + { + if ((client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_clients_mempool))) + { + if ((conn = NetConn_Open(mysocket, peeraddress))) + { + // allocated connection + LHNETADDRESS_ToString(peeraddress, conn->address, sizeof(conn->address), true); + if (developer.integer) + Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address); + NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress); + // now set up the client struct + svs.connectedclients[clientnum] = client; + SV_ConnectClient(clientnum, conn); + NetConn_Heartbeat(1); + } + else + { + EntityFrame4_FreeDatabase(client->entitydatabase4); + Mem_Free(client); + } + } + else + Mem_Free(client); + } + } + else + { + // server is full + if (developer.integer) + Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2); + NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress); + } + } + } + } + } + return true; + } if (length >= 7 && !memcmp(string, "getinfo", 7)) { const char *challenge = NULL; // If there was a challenge in the getinfo message if (length > 8 && string[7] == ' ') challenge = string + 8; - for (i = 0, n = 0;i < svs.maxclients;i++) - if (svs.clients[i].active) + for (i = 0, n = 0;i < MAX_SCOREBOARD;i++) + if (svs.connectedclients[i]) n++; responselength = snprintf(response, sizeof(response), "\377\377\377\377infoResponse\x0A" "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d" "\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d%s%s", - gamename, com_modname, svs.maxclients, n, + gamename, com_modname, min(sv_maxplayers.integer, MAX_SCOREBOARD), n, sv.name, hostname.string, NET_PROTOCOL_VERSION, challenge ? "\\challenge\\" : "", challenge ? challenge : ""); // does it fit in the buffer? if (responselength < (int)sizeof(response)) { if (developer.integer) Con_Printf("Sending reply to master %s - %s\n", addressstring2, response); - NetConn_Write(mysocket, response, responselength, peeraddress); + NetConn_WriteString(mysocket, response, peeraddress); } return true; } @@ -873,7 +1114,7 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, { if (developer.integer) Con_Printf("Received ping from %s, sending ack\n", UDP_AddrToString(readaddr)); - NetConn_Write(mysocket, "\377\377\377\377ack", 7, peeraddress); + NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress); return true; } if (!strncmp(string, "ack", 3)) @@ -883,11 +1124,12 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, // we're done processing this packet now return true; } -#if 1 - if (length >= 5) + // LordHavoc: disabled netquake control packet support in server +#if 0 { - control = BigLong(*((int *)data)); - if ((control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length) + int c, control; + // netquake control packets, supported for compatibility only + if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length) { c = data[4]; data += 5; @@ -904,9 +1146,6 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, { if (developer.integer) Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2); -#if 0 - NetConn_Write(mysocket, "\377\377\377\377reject Incompatible version.", 32, peeraddress); -#else SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); @@ -915,15 +1154,14 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); -#endif } else { // see if this is a duplicate connection request - for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++) - if (client->active && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0) + for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++) + if ((client = svs.connectedclients[clientnum]) && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0) break; - if (clientnum < svs.maxclients) + if (clientnum < MAX_SCOREBOARD) { // duplicate connection request if (realtime - client->netconnection->connecttime < 2.0) @@ -932,9 +1170,6 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, // so we send a duplicate reply if (developer.integer) Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2); -#if 0 - NetConn_Write(mysocket, "\377\377\377\377accept", 10, peeraddress); -#else SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); @@ -943,7 +1178,6 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); -#endif } else if (realtime - client->netconnection->lastMessageTime >= net_messagerejointimeout.value) { @@ -956,19 +1190,17 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, else { // this is a new client, find a slot - for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++) - if (!client->active) + for (clientnum = 0;clientnum < MAX_SCOREBOARD;clientnum++) + if (!(client = svs.connectedclients[clientnum])) break; - if (clientnum < svs.maxclients && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL) + // WARNING: this is broken code + if (clientnum < MAX_SCOREBOARD && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL) { // connect to the client // everything is allocated, just fill in the details strcpy(conn->address, addressstring2); if (developer.integer) Con_Printf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2); -#if 0 - NetConn_Write(mysocket, "\377\377\377\377accept", 10, peeraddress); -#else // send back the info about the server connection SZ_Clear(&net_message); // save space for the header, filled in later @@ -978,7 +1210,6 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); -#endif // now set up the client struct SV_ConnectClient(clientnum, conn); NetConn_Heartbeat(1); @@ -988,9 +1219,6 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, //if (developer.integer) Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2); // no room; try to let player know -#if 0 - NetConn_Write(mysocket, "\377\377\377\377reject Server is full.", 26, peeraddress); -#else SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); @@ -999,7 +1227,6 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); -#endif } } } @@ -1022,7 +1249,7 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, MSG_WriteString(&net_message, hostname.string); MSG_WriteString(&net_message, sv.name); MSG_WriteByte(&net_message, net_activeconnections); - MSG_WriteByte(&net_message, svs.maxclients); + MSG_WriteByte(&net_message, min(sv_maxplayers.integer, MAX_SCOREBOARD)); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); @@ -1097,14 +1324,25 @@ int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, } } #endif - for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) + for (i = 0;i < MAX_SCOREBOARD;i++) { - if (host_client->active && host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress)) + if ((host_client = svs.connectedclients[i])) { - sv_player = host_client->edict; - if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2) - SV_ReadClientMessage(); - return ret; + if (host_client->netconnection) + { + if (host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress)) + { + sv_player = host_client->edict; + if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2) + SV_ReadClientMessage(); + return ret; + } + } + else + { + Con_Printf("Removing client with no netconnection!\n"); + SV_DropClient(true); + } } } } @@ -1120,9 +1358,9 @@ void NetConn_ServerFrame(void) for (i = 0;i < sv_numsockets;i++) while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0) NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress); - for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) + for (i = 0;i < MAX_SCOREBOARD;i++) { - if (host_client->active && realtime > host_client->netconnection->timeout) + if ((host_client = svs.connectedclients[i]) && realtime > host_client->netconnection->timeout) { Con_Printf("Client \"%s\" connection timed out\n", host_client->name); sv_player = host_client->edict; @@ -1137,7 +1375,6 @@ void NetConn_QueryMasters(void) { int i; int masternum; - int requestlen; lhnetaddress_t masteraddress; char request[256]; @@ -1166,12 +1403,12 @@ void NetConn_QueryMasters(void) #endif // build the getservers - requestlen = snprintf(request, sizeof(request), "\377\377\377\377getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION); + snprintf(request, sizeof(request), "\377\377\377\377getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION); // search internet for (masternum = 0;sv_masters[masternum].name;masternum++) if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, MASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]))) - NetConn_Write(cl_sockets[i], request, requestlen, &masteraddress); + NetConn_WriteString(cl_sockets[i], request, &masteraddress); } } } @@ -1180,8 +1417,6 @@ void NetConn_Heartbeat(int priority) { lhnetaddress_t masteraddress; int masternum; - char *request = "\377\377\377\377heartbeat DarkPlaces\x0A"; - int requestlen = strlen(request); lhnetsocket_t *mysocket; // if it's a state change (client connected), limit next heartbeat to no @@ -1200,12 +1435,12 @@ void NetConn_Heartbeat(int priority) // make advertising optional and don't advertise singleplayer games, and // only send a heartbeat as often as the admin wants - if (sv.active && sv_public.integer && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime)) + if (sv.active && sv_public.integer && (!cl.islocalgame || sv_maxplayers.integer >= 2) && (priority > 1 || realtime > nextheartbeattime)) { nextheartbeattime = realtime + sv_heartbeatperiod.value; for (masternum = 0;sv_masters[masternum].name;masternum++) if (sv_masters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, MASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress))) - NetConn_Write(mysocket, request, requestlen, &masteraddress); + NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress); } } @@ -1227,9 +1462,9 @@ int NetConn_SendToAll(sizebuf_t *data, double blocktime) count = 0; NetConn_ClientFrame(); NetConn_ServerFrame(); - for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) + for (i = 0;i < MAX_SCOREBOARD;i++) { - if (host_client->active) + if ((host_client = svs.connectedclients[i])) { if (NetConn_CanSendMessage(host_client->netconnection)) { @@ -1246,30 +1481,6 @@ int NetConn_SendToAll(sizebuf_t *data, double blocktime) return count; } -static void MaxPlayers_f(void) -{ - int n; - - if (Cmd_Argc() != 2) - { - Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients); - return; - } - - if (sv.active) - { - Con_Printf("maxplayers can not be changed while a server is running.\n"); - return; - } - - n = atoi(Cmd_Argv(1)); - n = bound(1, n, MAX_SCOREBOARD); - if (svs.maxclients != n) - Con_Printf("\"maxplayers\" set to \"%u\"\n", n); - - SV_SetMaxClients(n); -} - static void Net_Heartbeat_f(void) { NetConn_Heartbeat(2); @@ -1314,11 +1525,16 @@ void NetConn_Init(void) netconn_mempool = Mem_AllocPool("Networking"); Cmd_AddCommand("net_stats", Net_Stats_f); Cmd_AddCommand("net_slist", Net_Slist_f); - Cmd_AddCommand("maxplayers", MaxPlayers_f); Cmd_AddCommand("heartbeat", Net_Heartbeat_f); Cvar_RegisterVariable(&net_messagetimeout); Cvar_RegisterVariable(&net_messagerejointimeout); Cvar_RegisterVariable(&net_connecttimeout); + Cvar_RegisterVariable(&cl_fakelocalping_min); + Cvar_RegisterVariable(&cl_fakelocalping_max); + Cvar_RegisterVariable(&cl_fakepacketloss_receive); + Cvar_RegisterVariable(&cl_fakepacketloss_send); + Cvar_RegisterVariable(&sv_fakepacketloss_receive); + Cvar_RegisterVariable(&sv_fakepacketloss_send); Cvar_RegisterVariable(&hostname); Cvar_RegisterVariable(&developer_networking); Cvar_RegisterVariable(&cl_netport);