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);
+ 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];
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;
}
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);
}
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;
extern void SV_ConnectClient(int clientnum, netconn_t *netconnection);
int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, qbyte *data, int length, lhnetaddress_t *peeraddress)
{
- int i, n, ret, clientnum, responselength, best;
+ int i, n, ret, clientnum, responselength, best, clientcount;
double besttime;
client_t *client;
netconn_t *conn;
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)
else
{
// this is a new client, find a slot
- for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
- if (!client->active)
+ 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 (clientnum == svs.maxclients)
+ 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);
}
- else
- {
- if ((conn = NetConn_Open(mysocket, peeraddress)))
- {
- // allocated connection
- strcpy(conn->address, addressstring2);
- if (developer.integer)
- Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
- NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
- // now set up the client struct
- SV_ConnectClient(clientnum, conn);
- NetConn_Heartbeat(1);
- }
- }
}
}
}
// 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))
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)
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
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);
}
}
#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);
+ }
}
}
}
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;
// 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++)
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))
{
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));