sizebuf_t net_message;
cvar_t net_messagetimeout = {0, "net_messagetimeout","300"};
+cvar_t net_messagerejointimeout = {0, "net_messagerejointimeout","10"};
+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;
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];
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);
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;
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;
}
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;
}
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;
}
LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
}
+void NetConn_OpenClientPort(const char *addressstring, int defaultport)
+{
+ lhnetaddress_t address;
+ lhnetsocket_t *s;
+ char addressstring2[1024];
+ if (LHNETADDRESS_FromString(&address, addressstring, defaultport))
+ {
+ if ((s = LHNET_OpenSocket_Connectionless(&address)))
+ {
+ cl_sockets[cl_numsockets++] = s;
+ LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
+ Con_Printf("Client opened a socket on address %s\n", addressstring2);
+ }
+ else
+ {
+ LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
+ Con_Printf("Client failed to open a socket on address %s\n", addressstring2);
+ }
+ }
+ else
+ Con_Printf("Client unable to parse address %s\n", addressstring);
+}
+
void NetConn_OpenClientPorts(void)
{
int port;
- lhnetaddress_t address;
NetConn_CloseClientPorts();
port = bound(0, cl_netport.integer, 65535);
if (cl_netport.integer != port)
Cvar_SetValueQuick(&cl_netport, port);
- LHNETADDRESS_FromString(&address, "local", port);
- cl_sockets[cl_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
- LHNETADDRESS_FromString(&address, cl_netaddress.string, port);
- cl_sockets[cl_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
- LHNETADDRESS_FromString(&address, cl_netaddress_ipv6.string, port);
- cl_sockets[cl_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
+ Con_Printf("Client using port %i\n", port);
+ NetConn_OpenClientPort("local", port);
+ NetConn_OpenClientPort(cl_netaddress.string, port);
+ NetConn_OpenClientPort(cl_netaddress_ipv6.string, port);
}
void NetConn_CloseServerPorts(void)
LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
}
+void NetConn_OpenServerPort(const char *addressstring, int defaultport)
+{
+ lhnetaddress_t address;
+ lhnetsocket_t *s;
+ char addressstring2[1024];
+ if (LHNETADDRESS_FromString(&address, addressstring, defaultport))
+ {
+ if ((s = LHNET_OpenSocket_Connectionless(&address)))
+ {
+ sv_sockets[sv_numsockets++] = s;
+ LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
+ Con_Printf("Server listening on address %s\n", addressstring2);
+ }
+ else
+ {
+ LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
+ Con_Printf("Server failed to open socket on address %s\n", addressstring2);
+ }
+ }
+ else
+ Con_Printf("Server unable to parse address %s\n", addressstring);
+}
+
void NetConn_OpenServerPorts(int opennetports)
{
int port;
- lhnetaddress_t address;
NetConn_CloseServerPorts();
port = bound(0, sv_netport.integer, 65535);
if (port == 0)
port = 26000;
+ Con_Printf("Server using port %i\n", port);
if (sv_netport.integer != port)
Cvar_SetValueQuick(&sv_netport, port);
- LHNETADDRESS_FromString(&address, "local", port);
- sv_sockets[sv_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
+ if (cls.state != ca_dedicated)
+ NetConn_OpenServerPort("local", port);
if (opennetports)
{
- LHNETADDRESS_FromString(&address, sv_netaddress.string, port);
- sv_sockets[sv_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
- LHNETADDRESS_FromString(&address, sv_netaddress_ipv6.string, port);
- sv_sockets[sv_numsockets++] = LHNET_OpenSocket_Connectionless(&address);
+ NetConn_OpenServerPort(sv_netaddress.string, port);
+ NetConn_OpenServerPort(sv_netaddress_ipv6.string, port);
}
+ if (sv_numsockets == 0)
+ Host_Error("NetConn_OpenServerPorts: unable to open any ports!\n");
}
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;
}
{
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;
}
conn->canSend = true;
conn->connecttime = realtime;
conn->lastMessageTime = realtime;
+ // LordHavoc: (inspired by ProQuake) use a short connect timeout to
+ // reduce effectiveness of connection request floods
+ conn->timeout = realtime + net_connecttimeout.value;
LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
conn->next = netconn_list;
netconn_list = conn;
unsigned int flags;
unsigned int sequence;
- if (length < 8)
- return 0;
-
- length = BigLong(((int *)data)[0]);
- flags = length & ~NETFLAG_LENGTH_MASK;
- length &= NETFLAG_LENGTH_MASK;
- // control packets were already handled
- if (!(flags & NETFLAG_CTL))
+ if (length >= 8)
{
- sequence = BigLong(((int *)data)[1]);
- packetsReceived++;
- data += 8;
- length -= 8;
- if (flags & NETFLAG_UNRELIABLE)
+ length = BigLong(((int *)data)[0]);
+ flags = length & ~NETFLAG_LENGTH_MASK;
+ length &= NETFLAG_LENGTH_MASK;
+ // control packets were already handled
+ if (!(flags & NETFLAG_CTL))
{
- if (sequence >= conn->unreliableReceiveSequence)
+ sequence = BigLong(((int *)data)[1]);
+ packetsReceived++;
+ data += 8;
+ length -= 8;
+ if (flags & NETFLAG_UNRELIABLE)
{
- if (sequence > conn->unreliableReceiveSequence)
+ if (sequence >= conn->unreliableReceiveSequence)
{
- count = sequence - conn->unreliableReceiveSequence;
- droppedDatagrams += count;
- Con_DPrintf("Dropped %u datagram(s)\n", count);
+ if (sequence > conn->unreliableReceiveSequence)
+ {
+ count = sequence - conn->unreliableReceiveSequence;
+ droppedDatagrams += count;
+ Con_DPrintf("Dropped %u datagram(s)\n", count);
+ }
+ conn->unreliableReceiveSequence = sequence + 1;
+ conn->lastMessageTime = realtime;
+ conn->timeout = realtime + net_messagetimeout.value;
+ unreliableMessagesReceived++;
+ if (length > 0)
+ {
+ SZ_Clear(&net_message);
+ SZ_Write(&net_message, data, length);
+ MSG_BeginReading();
+ return 2;
+ }
}
- conn->unreliableReceiveSequence = sequence + 1;
- unreliableMessagesReceived++;
- SZ_Clear(&net_message);
- SZ_Write(&net_message, data, length);
- MSG_BeginReading();
- return 2;
+ else
+ Con_DPrintf("Got a stale datagram\n");
+ return 1;
}
- else
- Con_DPrintf("Got a stale datagram\n");
- return 1;
- }
- else if (flags & NETFLAG_ACK)
- {
- if (sequence == (conn->sendSequence - 1))
+ else if (flags & NETFLAG_ACK)
{
- if (sequence == conn->ackSequence)
+ if (sequence == (conn->sendSequence - 1))
{
- conn->ackSequence++;
- if (conn->ackSequence != conn->sendSequence)
- Con_DPrintf("ack sequencing error\n");
- conn->sendMessageLength -= MAX_DATAGRAM;
- if (conn->sendMessageLength > 0)
+ if (sequence == conn->ackSequence)
{
- memcpy(conn->sendMessage, conn->sendMessage+MAX_DATAGRAM, conn->sendMessageLength);
- conn->sendNext = true;
- NetConn_SendMessageNext(conn);
+ conn->ackSequence++;
+ if (conn->ackSequence != conn->sendSequence)
+ Con_DPrintf("ack sequencing error\n");
+ conn->lastMessageTime = realtime;
+ conn->timeout = realtime + net_messagetimeout.value;
+ conn->sendMessageLength -= MAX_PACKETFRAGMENT;
+ if (conn->sendMessageLength > 0)
+ {
+ memcpy(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
+ conn->sendNext = true;
+ NetConn_SendMessageNext(conn);
+ }
+ else
+ {
+ conn->sendMessageLength = 0;
+ conn->canSend = true;
+ }
}
else
- {
- conn->sendMessageLength = 0;
- conn->canSend = true;
- }
+ Con_DPrintf("Duplicate ACK received\n");
}
else
- Con_DPrintf("Duplicate ACK received\n");
+ Con_DPrintf("Stale ACK received\n");
+ return 1;
}
- else
- Con_DPrintf("Stale ACK received\n");
- return 1;
- }
- else if (flags & NETFLAG_DATA)
- {
- unsigned int temppacket[2];
- temppacket[0] = BigLong(8 | NETFLAG_ACK);
- temppacket[1] = BigLong(sequence);
- NetConn_Write(conn->mysocket, (qbyte *)temppacket, 8, &conn->peeraddress);
- if (sequence == conn->receiveSequence)
+ else if (flags & NETFLAG_DATA)
{
- conn->receiveSequence++;
- memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
- conn->receiveMessageLength += length;
- if (flags & NETFLAG_EOM)
+ unsigned int temppacket[2];
+ temppacket[0] = BigLong(8 | NETFLAG_ACK);
+ temppacket[1] = BigLong(sequence);
+ NetConn_Write(conn->mysocket, (qbyte *)temppacket, 8, &conn->peeraddress);
+ if (sequence == conn->receiveSequence)
{
- reliableMessagesReceived++;
- SZ_Clear(&net_message);
- SZ_Write(&net_message, conn->receiveMessage, conn->receiveMessageLength);
- conn->receiveMessageLength = 0;
- MSG_BeginReading();
- return 2;
+ conn->lastMessageTime = realtime;
+ conn->timeout = realtime + net_messagetimeout.value;
+ conn->receiveSequence++;
+ memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
+ conn->receiveMessageLength += length;
+ if (flags & NETFLAG_EOM)
+ {
+ reliableMessagesReceived++;
+ length = conn->receiveMessageLength;
+ conn->receiveMessageLength = 0;
+ if (length > 0)
+ {
+ SZ_Clear(&net_message);
+ SZ_Write(&net_message, conn->receiveMessage, length);
+ MSG_BeginReading();
+ return 2;
+ }
+ }
}
+ else
+ receivedDuplicateCount++;
+ return 1;
}
- else
- receivedDuplicateCount++;
- return 1;
}
}
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)
+{
+ if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
+ return true;
+ return false;
+}
+
static struct
{
double senttime;
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;
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;
{
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))
// 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];
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);
- 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();
+ lhnetaddress_t clientportaddress;
+ clientportaddress = *peeraddress;
+ if (length >= 4)
+ {
+ unsigned int port = (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+ data += 4;
+ length -= 4;
+ LHNETADDRESS_SetPort(&clientportaddress, port);
+ }
+ NetConn_ConnectionEstablished(mysocket, &clientportaddress);
}
break;
case CCREP_REJECT:
if (cls.connect_remainingtries == 0)
{
cls.connect_trying = false;
- Host_Error("Connect failed\n");
+ Con_Printf("Connect failed\n");
return;
}
if (cls.connect_nextsendtime)
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);
for (i = 0;i < cl_numsockets;i++)
while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
- if (cls.netcon && realtime > cls.netcon->lastMessageTime + net_messagetimeout.value)
+ if (cls.netcon && realtime > cls.netcon->timeout)
{
Con_Printf("Connection timed out\n");
CL_Disconnect();
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;
+ 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)
{
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, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+ if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
+ break;
+ if (clientnum < svs.maxclients)
+ {
+ // 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 (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+ if (!client->active)
+ break;
+ if (clientnum < svs.maxclients)
+ {
+ // prepare the client struct
+ 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
+ SV_ConnectClient(clientnum, conn);
+ NetConn_Heartbeat(1);
+ }
+ else
+ EntityFrame4_FreeDatabase(client->entitydatabase4);
+ }
+ }
+ 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 (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;
}
{
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))
// we're done processing this packet now
return true;
}
- 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;
length -= 5;
LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
-#if 1
switch (c)
{
case CCREQ_CONNECT:
{
if (memcmp(data, "QUAKE", strlen("QUAKE") + 1) != 0 || (int)data[strlen("QUAKE") + 1] != NET_PROTOCOL_VERSION)
{
- //if (developer.integer)
+ 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);
*((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 client is already connected
+ // see if this is a duplicate connection request
for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
- if (client->active && (ret = LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress)) == 0)//>= 0)
+ if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
break;
if (clientnum < svs.maxclients)
{
- // is this a duplicate connection request?
- if (ret == 0 && realtime - client->netconnection->connecttime < 2.0)
+ // duplicate connection request
+ if (realtime - client->netconnection->connecttime < 2.0)
{
- //if (developer.integer)
+ // client is still trying to connect,
+ // so we send a duplicate reply
+ if (developer.integer)
Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
- // yes, so send a duplicate reply
-#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);
*((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
+ else if (realtime - client->netconnection->lastMessageTime >= net_messagerejointimeout.value)
{
- //if (developer.integer)
- Con_Printf("Datagram_ParseConnectionless: removing crashed/disconnected client #%i \"%s\" (address %s).\n", clientnum, client->name, client->netconnection->address);
- // it's somebody coming back from a
- // crash/disconnect so kill old connection
- // asap and let them retry to get in
+ // the old client hasn't sent us anything
+ // in quite a while, so kick off and let
+ // the retry take care of it...
client->deadsocket = true;
}
}
else
{
+ // this is a new client, find a slot
for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
if (!client->active)
break;
// connect to the client
// everything is allocated, just fill in the details
strcpy(conn->address, addressstring2);
- //if (developer.integer)
+ 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
*((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);
//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);
*((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
}
}
}
default:
break;
}
-#endif
// we may not have liked the packet, but it was a valid control
// packet, so we're done processing this packet now
return true;
}
}
+#endif
for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
{
- if (host_client->active && host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
+ if (host_client->netconnection && 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)
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++)
+ for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
{
- if (host_client->active && realtime > host_client->netconnection->lastMessageTime + net_messagetimeout.value)
+ if (host_client->netconnection && realtime > host_client->netconnection->timeout)
{
Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
sv_player = host_client->edict;
{
int i;
int masternum;
- int requestlen;
lhnetaddress_t masteraddress;
char request[256];
#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);
}
}
}
{
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
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);
}
}
int NetConn_SendToAll(sizebuf_t *data, double blocktime)
{
int i, count = 0;
- qbyte state[MAX_SCOREBOARD];
+ qbyte sent[MAX_SCOREBOARD];
- for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
- state[i] = 0;
+ memset(sent, 0, sizeof(sent));
// simultaneously wait for the first CanSendMessage and send the message,
- // then wait for a second CanSendMessage (verifying it was received)
+ // then wait for a second CanSendMessage (verifying it was received), or
+ // the client drops and is no longer counted
+ // the loop aborts when either it runs out of clients to send to, or a
+ // timeout expires
blocktime += Sys_DoubleTime();
do
{
NetConn_ServerFrame();
for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
{
- if (host_client->active && state[i] < 2)
+ if (host_client->netconnection)
{
- count++;
- // need to send to this one
if (NetConn_CanSendMessage(host_client->netconnection))
{
- if (state[i] == 0 && NetConn_SendReliableMessage(host_client->netconnection, data) == -1)
- state[i] = 2; // connection lost
- state[i]++;
+ if (!sent[i])
+ NetConn_SendReliableMessage(host_client->netconnection, data);
+ sent[i] = true;
}
+ if (!NetConn_CanSendMessage(host_client->netconnection))
+ count++;
}
}
}
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);
void NetConn_Init(void)
{
- int masternum;
+ int i;
+ lhnetaddress_t tempaddress;
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);
Cvar_RegisterVariable(&sv_netaddress_ipv6);
Cvar_RegisterVariable(&sv_public);
Cvar_RegisterVariable(&sv_heartbeatperiod);
- for (masternum = 0;sv_masters[masternum].name;masternum++)
- Cvar_RegisterVariable(&sv_masters[masternum]);
+ for (i = 0;sv_masters[i].name;i++)
+ Cvar_RegisterVariable(&sv_masters[i]);
+ if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
+ {
+ if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
+ {
+ Con_Printf("-ip option used, setting cl_netaddress and sv_netaddress to address \"%s\"\n");
+ Cvar_SetQuick(&cl_netaddress, com_argv[i + 1]);
+ Cvar_SetQuick(&sv_netaddress, com_argv[i + 1]);
+ }
+ else
+ Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
+ }
+ if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
+ {
+ i = atoi(com_argv[i + 1]);
+ if (i >= 0 && i < 65536)
+ {
+ Con_Printf("-port option used, setting port cvar to %i\n", i);
+ Cvar_SetValueQuick(&sv_netport, i);
+ }
+ else
+ Con_Printf("-port option used, but %i is not a valid port number\n", i);
+ }
cl_numsockets = 0;
sv_numsockets = 0;
memset(&pingcache, 0, sizeof(pingcache));