2
0
mirror of https://github.com/rehlds/rehlds.git synced 2025-01-06 03:55:32 +03:00
rehlds/rehlds/engine/net_ws.cpp
Lev f3cc1943af Add md5.cpp to project.
Add dedicated project to solution.
Warnings fixes.
2017-03-08 06:10:42 +05:00

2123 lines
47 KiB
C++

/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#include "precompiled.h"
#ifdef _WIN32
CRITICAL_SECTION net_cs;
#endif // _WIN32
qboolean net_thread_initialized;
loopback_t loopbacks[2];
packetlag_t g_pLagData[3]; // List of lag structures, if fakelag is set.
float gFakeLag;
int net_configured;
#ifdef _WIN32
netadr_t net_local_ipx_adr;
#endif
netadr_t net_local_adr;
netadr_t net_from;
sizebuf_t net_message;
qboolean noip;
qboolean noipx;
int use_thread;
unsigned char net_message_buffer[NET_MAX_PAYLOAD];
unsigned char in_message_buf[NET_MAX_PAYLOAD];
sizebuf_t in_message;
netadr_t in_from;
#if defined(REHLDS_FIXES) && !defined(HOOK_ENGINE)
// Define default to INVALID_SOCKET
#define INV_SOCK INVALID_SOCKET
#else
// Original engine behavior
#define INV_SOCK 0
#endif
#ifndef HOOK_ENGINE
SOCKET ip_sockets[3] = { INV_SOCK, INV_SOCK, INV_SOCK };
#else
SOCKET ip_sockets[3];
#endif
#ifdef _WIN32
#ifndef HOOK_ENGINE
SOCKET ipx_sockets[3] = { INV_SOCK, INV_SOCK, INV_SOCK };
#else
SOCKET ipx_sockets[3];
#endif
#endif // _WIN32
LONGPACKET gNetSplit;
net_messages_t *messages[3];
net_messages_t *normalqueue;
//void *hNetThread;
//int32 dwNetThreadId;
/*
* Globals initialization
*/
#ifndef HOOK_ENGINE
cvar_t net_address = { "net_address", "", 0, 0.0f, NULL };
cvar_t ipname = { "ip", "localhost", 0, 0.0f, NULL };
cvar_t defport = { "port", "27015", 0, 0.0f, NULL };
cvar_t ip_clientport = { "ip_clientport", "0", 0, 0.0f, NULL };
cvar_t clientport = { "clientport", "27005", 0, 0.0f, NULL };
int net_sleepforever = 1;
cvar_t clockwindow = { "clockwindow", "0.5", 0, 0.0f, NULL };
cvar_t iphostport = { "ip_hostport", "0", 0, 0.0f, NULL };
cvar_t hostport = { "hostport", "0", 0, 0.0f, NULL };
cvar_t multicastport = { "multicastport", "27025", 0, 0.0f, NULL };
#ifdef _WIN32
cvar_t ipx_hostport = { "ipx_hostport", "0", 0, 0.0f, NULL };
cvar_t ipx_clientport = { "ipx_clientport", "0", 0, 0.0f, NULL };
#endif //_WIN32
cvar_t fakelag = { "fakelag", "0.0", 0, 0.0f, NULL };
cvar_t fakeloss = { "fakeloss", "0.0", 0, 0.0f, NULL };
cvar_t net_graph = { "net_graph", "0", FCVAR_ARCHIVE, 0.0f, NULL };
cvar_t net_graphwidth = { "net_graphwidth", "150", 0, 0.0f, NULL };
cvar_t net_scale = { "net_scale", "5", FCVAR_ARCHIVE, 0.0f, NULL };
cvar_t net_graphpos = { "net_graphpos", "1", FCVAR_ARCHIVE, 0.0f, NULL };
#else // HOOK_ENGINE
cvar_t net_address;
cvar_t ipname;
cvar_t defport;
cvar_t ip_clientport;
cvar_t clientport;
int net_sleepforever;
cvar_t clockwindow;
cvar_t iphostport;
cvar_t hostport;
cvar_t multicastport;
#ifdef _WIN32
cvar_t ipx_hostport;
cvar_t ipx_clientport;
#endif // _WIN32
cvar_t fakelag;
cvar_t fakeloss;
cvar_t net_graph;
cvar_t net_graphwidth;
cvar_t net_scale;
cvar_t net_graphpos;
#endif // HOOK_ENGINE
void NET_ThreadLock(void)
{
#ifdef _WIN32
if (use_thread && net_thread_initialized)
{
EnterCriticalSection(&net_cs);
}
#endif // _WIN32
}
void NET_ThreadUnlock(void)
{
#ifdef _WIN32
if (use_thread && net_thread_initialized)
{
LeaveCriticalSection(&net_cs);
}
#endif // _WIN32
}
unsigned short Q_ntohs(unsigned short netshort)
{
return ntohs(netshort);
}
void NetadrToSockadr(const netadr_t *a, struct sockaddr *s)
{
Q_memset(s, 0, sizeof(*s));
switch (a->type)
{
case NA_BROADCAST:
((struct sockaddr_in *)s)->sin_family = AF_INET;
((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST;
((struct sockaddr_in *)s)->sin_port = a->port;
break;
case NA_IP:
((struct sockaddr_in *)s)->sin_family = AF_INET;
((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip;
((struct sockaddr_in *)s)->sin_port = a->port;
break;
#ifdef _WIN32
case NA_IPX:
s->sa_family = AF_IPX;
Q_memcpy(s->sa_data, a->ipx, 10);
*(unsigned short *)&s->sa_data[10] = a->port;
break;
case NA_BROADCAST_IPX:
s->sa_family = AF_IPX;
Q_memset(&s->sa_data, 0, 4);
Q_memset(&s->sa_data[4], 255, 6);
*(unsigned short *)&s->sa_data[10] = a->port;
break;
#endif // _WIN32
default:
break;
}
}
void SockadrToNetadr(const struct sockaddr *s, netadr_t *a)
{
if (s->sa_family == AF_INET)
{
a->type = NA_IP;
*(int *)&a->ip = ((struct sockaddr_in *)s)->sin_addr.s_addr;
a->port = ((struct sockaddr_in *)s)->sin_port;
}
#ifdef _WIN32
else if (s->sa_family == AF_IPX)
{
a->type = NA_IPX;
Q_memcpy(a->ipx, s->sa_data, 10);
a->port = *(unsigned short *)&s->sa_data[10];
}
#endif // _WIN32
}
NOXREF unsigned short NET_HostToNetShort(unsigned short us_in)
{
NOXREFCHECK;
return htons(us_in);
}
qboolean NET_CompareAdr(netadr_t& a, netadr_t& b)
{
if (a.type != b.type)
{
return FALSE;
}
if (a.type == NA_LOOPBACK)
{
return TRUE;
}
if (a.type == NA_IP)
{
if (a.ip[0] == b.ip[0] &&
a.ip[1] == b.ip[1] &&
a.ip[2] == b.ip[2] &&
a.ip[3] == b.ip[3] &&
a.port == b.port)
return TRUE;
}
#ifdef _WIN32
else if (a.type == NA_IPX)
{
if (Q_memcmp(a.ipx, b.ipx, 10) == 0 &&
a.port == b.port)
return TRUE;
}
#endif // _WIN32
return FALSE;
}
qboolean NET_CompareClassBAdr(netadr_t& a, netadr_t& b)
{
if (a.type != b.type)
{
return FALSE;
}
if (a.type == NA_LOOPBACK)
{
return TRUE;
}
if (a.type == NA_IP)
{
if (a.ip[0] == b.ip[0] &&
a.ip[1] == b.ip[1])
return TRUE;
}
#ifdef _WIN32
else if (a.type == NA_IPX)
{
return TRUE;
}
#endif // _WIN32
return FALSE;
}
qboolean NET_IsReservedAdr(netadr_t& a)
{
if (a.type == NA_LOOPBACK)
{
return TRUE;
}
if (a.type == NA_IP)
{
if (a.ip[0] == 10 || a.ip[0] == 127)
{
return TRUE;
}
if (a.ip[0] == 172 && a.ip[1] >= 16)
{
if (a.ip[1] >= 32)
{
return FALSE;
}
return TRUE;
}
if (a.ip[0] == 192 && a.ip[1] >= 168)
{
return TRUE;
}
return FALSE;
}
#ifdef _WIN32
else if (a.type == NA_IPX)
{
return TRUE;
}
#endif // _WIN32
return FALSE;
}
qboolean NET_CompareBaseAdr(const netadr_t& a, const netadr_t& b)
{
if (a.type != b.type)
{
return FALSE;
}
if (a.type == NA_LOOPBACK)
{
return TRUE;
}
if (a.type == NA_IP)
{
if (a.ip[0] == b.ip[0] &&
a.ip[1] == b.ip[1] &&
a.ip[2] == b.ip[2] &&
a.ip[3] == b.ip[3])
return TRUE;
}
#ifdef _WIN32
else if (a.type == NA_IPX)
{
if (Q_memcmp(a.ipx, b.ipx, 10) == 0)
return TRUE;
}
#endif // _WIN32
return FALSE;
}
char *NET_AdrToString(const netadr_t& a)
{
static char s[64];
Q_memset(s, 0, sizeof(s));
if (a.type == NA_LOOPBACK)
Q_snprintf(s, sizeof(s), "loopback");
else if (a.type == NA_IP)
Q_snprintf(s, sizeof(s), "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs(a.port));
#ifdef _WIN32
else // NA_IPX
Q_snprintf(s, sizeof(s), "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%i", a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9], ntohs(a.port));
#endif // _WIN32
return s;
}
char *NET_BaseAdrToString(netadr_t& a)
{
static char s[64];
if (a.type == NA_LOOPBACK)
{
Q_snprintf(s, sizeof(s), "loopback");
}
else if (a.type == NA_IP)
{
Q_snprintf(s, sizeof(s), "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]);
}
#ifdef _WIN32
else // NA_IPX
{
Q_snprintf(s, sizeof(s), "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x", a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9]);
}
#endif // _WIN32
return s;
}
qboolean NET_StringToSockaddr(const char *s, struct sockaddr *sadr)
{
struct hostent *h;
char *colon;
int val;
char copy[128];
Q_memset(sadr, 0, sizeof(*sadr));
#ifdef _WIN32
if (Q_strlen(s) >= 24 && s[8] == ':' && s[21] == ':')
{
sadr->sa_family = AF_IPX;
copy[2] = 0;
copy[0] = s[0];
copy[1] = s[1];
sscanf(copy, "%x", &val);
sadr->sa_data[0] = (char)val;
copy[0] = s[2];
copy[1] = s[3];
sscanf(copy, "%x", &val);
sadr->sa_data[1] = (char)val;
copy[0] = s[4];
copy[1] = s[5];
sscanf(copy, "%x", &val);
sadr->sa_data[2] = (char)val;
copy[0] = s[6];
copy[1] = s[7];
sscanf(copy, "%x", &val);
sadr->sa_data[3] = (char)val;
copy[0] = s[9];
copy[1] = s[10];
sscanf(copy, "%x", &val);
sadr->sa_data[4] = (char)val;
copy[0] = s[11];
copy[1] = s[12];
sscanf(copy, "%x", &val);
sadr->sa_data[5] = (char)val;
copy[0] = s[13];
copy[1] = s[14];
sscanf(copy, "%x", &val);
sadr->sa_data[6] = (char)val;
copy[0] = s[15];
copy[1] = s[16];
sscanf(copy, "%x", &val);
sadr->sa_data[7] = (char)val;
copy[0] = s[17];
copy[1] = s[18];
sscanf(copy, "%x", &val);
sadr->sa_data[8] = (char)val;
copy[0] = s[19];
copy[1] = s[20];
sscanf(copy, "%x", &val);
sadr->sa_data[9] = (char)val;
sscanf(s + 22, "%u", &val);
*(uint16 *)&sadr->sa_data[10] = htons(val);
return TRUE;
}
#endif // _WIN32
((sockaddr_in *)sadr)->sin_family = AF_INET;
((sockaddr_in *)sadr)->sin_port = 0;
Q_strncpy(copy, s, sizeof(copy) - 1);
copy[sizeof(copy) - 1] = 0;
// Parse port
colon = copy;
while (*colon != 0)
{
if (*colon == ':')
{
*colon = 0;
val = Q_atoi(colon + 1);
((sockaddr_in *)sadr)->sin_port = htons(val);
}
colon++;
}
// Parse address
((sockaddr_in *)sadr)->sin_addr.s_addr = inet_addr(copy);
if (((sockaddr_in *)sadr)->sin_addr.s_addr == INADDR_NONE)
{
h = CRehldsPlatformHolder::get()->gethostbyname(copy);
if (h == NULL || h->h_addr == NULL)
{
return FALSE;
}
((sockaddr_in *)sadr)->sin_addr.s_addr = *(uint32 *)h->h_addr;
}
return TRUE;
}
qboolean NET_StringToAdr(const char *s, netadr_t *a)
{
struct sockaddr sadr;
if (Q_strcmp(s, "localhost"))
{
if (!NET_StringToSockaddr(s, &sadr))
{
return FALSE;
}
SockadrToNetadr(&sadr, a);
}
else
{
Q_memset(a, 0, sizeof(netadr_t));
a->type = NA_LOOPBACK;
}
return TRUE;
}
qboolean NET_IsLocalAddress(netadr_t& adr)
{
return adr.type == NA_LOOPBACK ? TRUE : FALSE;
}
int NET_GetLastError()
{
#ifdef _WIN32
return CRehldsPlatformHolder::get()->WSAGetLastError();
#else
return errno;
#endif // _WIN32
}
char *NET_ErrorString(int code)
{
#ifdef _WIN32
switch (code)
{
case WSAEINTR: return "WSAEINTR";
case WSAEBADF: return "WSAEBADF";
case WSAEACCES: return "WSAEACCES";
case WSAEFAULT: return "WSAEFAULT";
case WSAEINVAL: return "WSAEINVAL";
case WSAEMFILE: return "WSAEMFILE";
case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK";
case WSAEINPROGRESS: return "WSAEINPROGRESS";
case WSAEALREADY: return "WSAEALREADY";
case WSAENOTSOCK: return "WSAENOTSOCK";
case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ";
case WSAEMSGSIZE: return "WSAEMSGSIZE";
case WSAEPROTOTYPE: return "WSAEPROTOTYPE";
case WSAENOPROTOOPT: return "WSAENOPROTOOPT";
case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT";
case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT";
case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP";
case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT";
case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT";
case WSAEADDRINUSE: return "WSAEADDRINUSE";
case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL";
case WSAENETDOWN: return "WSAENETDOWN";
case WSAENETUNREACH: return "WSAENETUNREACH";
case WSAENETRESET: return "WSAENETRESET";
case WSAECONNABORTED: return "WSWSAECONNABORTEDAEINTR";
case WSAECONNRESET: return "WSAECONNRESET";
case WSAENOBUFS: return "WSAENOBUFS";
case WSAEISCONN: return "WSAEISCONN";
case WSAENOTCONN: return "WSAENOTCONN";
case WSAESHUTDOWN: return "WSAESHUTDOWN";
case WSAETOOMANYREFS: return "WSAETOOMANYREFS";
case WSAETIMEDOUT: return "WSAETIMEDOUT";
case WSAECONNREFUSED: return "WSAECONNREFUSED";
case WSAELOOP: return "WSAELOOP";
case WSAENAMETOOLONG: return "WSAENAMETOOLONG";
case WSAEHOSTDOWN: return "WSAEHOSTDOWN";
case WSASYSNOTREADY: return "WSASYSNOTREADY";
case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED";
case WSANOTINITIALISED: return "WSANOTINITIALISED";
case WSAEDISCON: return "WSAEDISCON";
case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND";
case WSATRY_AGAIN: return "WSATRY_AGAIN";
case WSANO_RECOVERY: return "WSANO_RECOVERY";
case WSANO_DATA: return "WSANO_DATA";
default: return "NO ERROR";
}
#else // _WIN32
return strerror(code);
#endif // _WIN32
}
void NET_TransferRawData(sizebuf_t *msg, unsigned char *pStart, int nSize)
{
#ifdef REHLDS_CHECKS
if (msg->maxsize < nSize)
{
Sys_Error("%s: data size is bigger than sizebuf maxsize", __func__);
}
#endif // REHLDS_CHECKS
Q_memcpy(msg->data, pStart, nSize);
msg->cursize = nSize;
}
qboolean NET_GetLoopPacket(netsrc_t sock, netadr_t *in_from_, sizebuf_t *msg)
{
int i;
loopback_t *loop;
loop = &loopbacks[sock];
if (loop->send - loop->get > 4)
{
loop->get = loop->send - 4;
}
if (loop->get < loop->send)
{
i = loop->get & (MAX_LOOPBACK - 1);
loop->get++;
NET_TransferRawData(msg, loop->msgs[i].data, loop->msgs[i].datalen);
Q_memset(in_from_, 0, sizeof(netadr_t));
in_from_->type = NA_LOOPBACK;
return TRUE;
}
return FALSE;
}
void NET_SendLoopPacket(netsrc_t sock, int length, void *data, const netadr_t& to)
{
int i;
loopback_t *loop;
NET_ThreadLock();
loop = &loopbacks[sock ^ 1];
i = loop->send & (MAX_LOOPBACK - 1);
loop->send++;
#ifdef REHLDS_CHECKS
if (sizeof(loop->msgs[i].data) < length)
{
Sys_Error("%s: data size is bigger than message storage size", __func__);
}
#endif // REHLDS_CHECKS
Q_memcpy(loop->msgs[i].data, data, length);
loop->msgs[i].datalen = length;
NET_ThreadUnlock();
}
void NET_RemoveFromPacketList(packetlag_t *pPacket)
{
pPacket->pPrev->pNext = pPacket->pNext;
pPacket->pNext->pPrev = pPacket->pPrev;
pPacket->pPrev = 0;
pPacket->pNext = 0;
}
NOXREF int NET_CountLaggedList(packetlag_t *pList)
{
NOXREFCHECK;
int c;
packetlag_t *p;
c = 0;
p = pList->pNext;
while (p && p != pList)
{
c++;
p = p->pNext;
}
return c;
}
void NET_ClearLaggedList(packetlag_t *pList)
{
packetlag_t *p, *n;
p = pList->pNext;
while (p && p != pList)
{
n = p->pNext;
NET_RemoveFromPacketList(p);
if (p->pPacketData)
{
Mem_Free(p->pPacketData);
p->pPacketData = NULL;
}
Mem_Free(p);
p = n;
}
pList->pPrev = pList;
pList->pNext = pList;
}
void NET_AddToLagged(netsrc_t sock, packetlag_t *pList, packetlag_t *pPacket, netadr_t *net_from_, sizebuf_t messagedata, float timestamp)
{
unsigned char *pStart;
if (pPacket->pPrev || pPacket->pNext)
{
Con_Printf("Packet already linked\n");
return;
}
pPacket->pPrev = pList->pPrev;
pList->pPrev->pNext = pPacket;
pList->pPrev = pPacket;
pPacket->pNext = pList;
pStart = (unsigned char *)Mem_Malloc(messagedata.cursize);
Q_memcpy(pStart, messagedata.data, messagedata.cursize);
pPacket->pPacketData = pStart;
pPacket->nSize = messagedata.cursize;
pPacket->receivedTime = timestamp;
Q_memcpy(&pPacket->net_from_, net_from_, sizeof(netadr_t));
}
void NET_AdjustLag(void)
{
static double lasttime = realtime;
double dt;
float diff;
float converge;
dt = realtime - lasttime;
if (dt <= 0.0)
{
dt = 0.0;
}
else
{
if (dt > 0.1)
{
dt = 0.1;
}
}
lasttime = realtime;
if (allow_cheats || fakelag.value == 0.0)
{
if (fakelag.value != gFakeLag)
{
diff = fakelag.value - gFakeLag;
converge = dt * 200.0;
if (fabs(diff) < converge)
converge = fabs(diff);
if (diff < 0.0)
converge = -converge;
gFakeLag = gFakeLag + converge;
}
}
else
{
Con_Printf("Server must enable cheats to activate fakelag\n");
Cvar_SetValue("fakelag", 0.0);
gFakeLag = 0;
}
}
qboolean NET_LagPacket(qboolean newdata, netsrc_t sock, netadr_t *from, sizebuf_t *data)
{
packetlag_t *pNewPacketLag;
packetlag_t *pPacket;
float curtime;
int ninterval;
static int losscount[3];
if (gFakeLag <= 0.0)
{
NET_ClearLagData(TRUE, TRUE);
return newdata;
}
curtime = realtime;
if (newdata)
{
if (fakeloss.value != 0.0)
{
if (allow_cheats)
{
++losscount[sock];
if (fakeloss.value <= 0.0f)
{
ninterval = fabs(fakeloss.value);
if (ninterval < 2)
ninterval = 2;
if ((losscount[sock] % ninterval) == 0)
return FALSE;
}
else
{
if (RandomLong(0, 100) <= fakeloss.value)
return FALSE;
}
}
else
{
Cvar_SetValue("fakeloss", 0.0);
}
}
pNewPacketLag = (packetlag_t *)Mem_ZeroMalloc(0x28u);
NET_AddToLagged(sock, &g_pLagData[sock], pNewPacketLag, from, *data, curtime);
}
pPacket = g_pLagData[sock].pNext;
while (pPacket != &g_pLagData[sock])
{
if (pPacket->receivedTime <= curtime - gFakeLag / 1000.0)
break;
pPacket = pPacket->pNext;
}
if (pPacket == &g_pLagData[sock])
return FALSE;
NET_RemoveFromPacketList(pPacket);
NET_TransferRawData(&in_message, pPacket->pPacketData, pPacket->nSize);
Q_memcpy(&in_from, &pPacket->net_from_, sizeof(in_from));
if (pPacket->pPacketData)
free(pPacket->pPacketData);
Mem_Free(pPacket);
return TRUE;
}
void NET_FlushSocket(netsrc_t sock)
{
struct sockaddr from;
socklen_t fromlen;
SOCKET net_socket;
unsigned char buf[MAX_UDP_PACKET];
net_socket = ip_sockets[sock];
if (net_socket != INV_SOCK)
{
fromlen = 16;
while (CRehldsPlatformHolder::get()->recvfrom(net_socket, (char*)buf, sizeof buf, 0, &from, &fromlen) > 0)
;
}
}
qboolean NET_GetLong(unsigned char *pData, int size, int *outSize)
{
unsigned int packetNumber;
unsigned int packetCount;
int sequenceNumber;
unsigned char packetID;
static int gNetSplitFlags[NET_WS_MAX_FRAGMENTS];
SPLITPACKET *pHeader = (SPLITPACKET *) pData;
sequenceNumber = pHeader->sequenceNumber;
packetID = pHeader->packetID;
packetCount = packetID & 0xF;
packetNumber = (unsigned int)packetID >> 4;
if (packetNumber >= NET_WS_MAX_FRAGMENTS || packetCount > NET_WS_MAX_FRAGMENTS)
{
Con_NetPrintf("Malformed packet number (%i/%i)\n", packetNumber + 1, packetCount);
return FALSE;
}
if (gNetSplit.currentSequence == -1 || sequenceNumber != gNetSplit.currentSequence)
{
gNetSplit.currentSequence = pHeader->sequenceNumber;
gNetSplit.splitCount = packetCount;
#ifdef REHLDS_FIXES
gNetSplit.totalSize = 0;
// clear part's sequence
for (int i = 0; i < NET_WS_MAX_FRAGMENTS; i++)
gNetSplitFlags[i] = -1;
#endif
if (net_showpackets.value == 4.0f)
Con_Printf("<-- Split packet restart %i count %i seq\n", gNetSplit.splitCount, sequenceNumber);
}
unsigned int packetPayloadSize = size - sizeof(SPLITPACKET);
if (gNetSplitFlags[packetNumber] == sequenceNumber)
{
Con_NetPrintf("%s: Ignoring duplicated split packet %i of %i ( %i bytes )\n", __func__, packetNumber + 1, packetCount, packetPayloadSize);
}
else
{
if (packetNumber == packetCount - 1)
gNetSplit.totalSize = packetPayloadSize + SPLIT_SIZE * (packetCount - 1);
--gNetSplit.splitCount;
gNetSplitFlags[packetNumber] = sequenceNumber;
if (net_showpackets.value == 4.0f)
{
Con_Printf("<-- Split packet %i of %i, %i bytes %i seq\n", packetNumber + 1, packetCount, packetPayloadSize, sequenceNumber);
}
if (SPLIT_SIZE * packetNumber + packetPayloadSize > MAX_UDP_PACKET)
{
Con_NetPrintf("Malformed packet size (%i, %i)\n", SPLIT_SIZE * packetNumber, packetPayloadSize);
#ifdef REHLDS_FIXES
gNetSplit.currentSequence = -1;
#endif
return FALSE;
}
Q_memcpy(&gNetSplit.buffer[SPLIT_SIZE * packetNumber], pHeader + 1, packetPayloadSize);
}
if (gNetSplit.splitCount > 0)
return FALSE;
if (packetCount > 0)
{
for (unsigned int i = 0; i < packetCount; i++)
{
if (gNetSplitFlags[i] != gNetSplit.currentSequence)
{
Con_NetPrintf(
"Split packet without all %i parts, part %i had wrong sequence %i/%i\n",
packetCount,
i + 1,
gNetSplitFlags[i],
gNetSplit.currentSequence);
#ifdef REHLDS_FIXES
gNetSplit.currentSequence = -1; // no more parts can be attached, clear it
#endif
return FALSE;
}
}
}
gNetSplit.currentSequence = -1;
if (gNetSplit.totalSize <= MAX_UDP_PACKET)
{
Q_memcpy(pData, gNetSplit.buffer, gNetSplit.totalSize);
*outSize = gNetSplit.totalSize;
return TRUE;
}
else
{
#ifdef REHLDS_FIXES
*outSize = 0;
#endif
Con_NetPrintf("Split packet too large! %d bytes\n", gNetSplit.totalSize);
return FALSE;
}
}
qboolean NET_QueuePacket(netsrc_t sock)
{
int ret;
struct sockaddr from;
socklen_t fromlen;
SOCKET net_socket;
int protocol;
unsigned char buf[MAX_UDP_PACKET];
#ifdef REHLDS_FIXES
ret = -1;
#endif
#ifdef _WIN32
for (protocol = 0; protocol < 2; protocol++)
#else
for (protocol = 0; protocol < 1; protocol++)
#endif // _WIN32
{
if (protocol == 0)
net_socket = ip_sockets[sock];
#ifdef _WIN32
else
net_socket = ipx_sockets[sock];
#endif // _WIN32
if (net_socket == INV_SOCK)
continue;
fromlen = sizeof(from);
ret = CRehldsPlatformHolder::get()->recvfrom(net_socket, (char *)buf, sizeof buf, 0, &from, &fromlen);
if (ret == -1)
{
int err = NET_GetLastError();
#ifdef _WIN32
if (err == WSAENETRESET)
continue;
#endif // _WIN32
if (err != WSAEWOULDBLOCK && err != WSAECONNRESET && err != WSAECONNREFUSED)
{
if (err == WSAEMSGSIZE)
{
Con_DPrintf("%s: Ignoring oversized network message\n", __func__);
}
else
{
if (g_pcls.state != ca_dedicated)
{
Sys_Error("%s: %s", __func__, NET_ErrorString(err));
}
Con_Printf("%s: %s\n", __func__, NET_ErrorString(err));
}
}
continue;
}
SockadrToNetadr(&from, &in_from);
if (ret != MAX_UDP_PACKET)
break;
Con_NetPrintf("%s: Oversize packet from %s\n", __func__, NET_AdrToString(in_from));
}
if (ret == -1 || ret == MAX_UDP_PACKET)
{
return NET_LagPacket(0, sock, 0, 0);
}
NET_TransferRawData(&in_message, buf, ret);
if (*(uint32 *)in_message.data != 0xFFFFFFFE)
{
return NET_LagPacket(1, sock, &in_from, &in_message);
}
if (in_message.cursize < 9)
{
Con_NetPrintf("Invalid split packet length %i\n", in_message.cursize);
return FALSE;
}
#ifdef REHLDS_FIXES
// Only server can send split packets, there is no server<->server communication, so server can't receive split packets
if (sock == NS_SERVER)
{
Con_NetPrintf("Someone tries to send split packet to the server\n");
return FALSE;
}
#endif
return NET_GetLong(in_message.data, ret, &in_message.cursize);
}
DLL_EXPORT int NET_Sleep_Timeout(void)
{
static int32 lasttime;
static int numFrames;
static int staggerFrames;
int fps = (int)sys_ticrate.value;
int32 curtime = (int)Sys_FloatTime();
if (lasttime)
{
if (curtime - lasttime > 1)
{
lasttime = curtime;
numFrames = fps;
staggerFrames = fps / 100 + 1;
}
}
else
{
lasttime = curtime;
}
fd_set fdset;
FD_ZERO(&fdset);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = (1000 / fps) * 1000; // TODO: entirely bad code, fix it completely
if (tv.tv_usec <= 0)
tv.tv_usec = 1;
int res;
if (numFrames > 0 && numFrames % staggerFrames)
{
SOCKET number = 0;
for (int sock = 0; sock < 3; sock++)
{
SOCKET net_socket = ip_sockets[sock];
if (net_socket != INV_SOCK)
{
FD_SET(net_socket, &fdset);
if (number < net_socket)
number = net_socket;
}
#ifdef _WIN32
net_socket = ipx_sockets[sock];
if (net_socket != INV_SOCK)
{
FD_SET(net_socket, &fdset);
if (number < net_socket)
number = net_socket;
}
#endif // _WIN32
}
res = select((int)(number + 1), &fdset, 0, 0, &tv);
}
else
{
res = select(0, 0, 0, 0, &tv);
}
--numFrames;
return res;
}
int NET_Sleep(void)
{
fd_set fdset;
struct timeval tv;
SOCKET number;
FD_ZERO(&fdset);
number = 0;
for (int sock = 0; sock < 3; sock++)
{
SOCKET net_socket = ip_sockets[sock];
if (net_socket != INV_SOCK)
{
FD_SET(net_socket, &fdset);
if (number < net_socket)
number = net_socket;
}
#ifdef _WIN32
net_socket = ipx_sockets[sock];
if (net_socket != INV_SOCK)
{
FD_SET(net_socket, &fdset);
if (number < net_socket)
number = net_socket;
}
#endif // _WIN32
}
tv.tv_sec = 0;
tv.tv_usec = 20 * 1000;
return select((int)(number + 1), &fdset, 0, 0, net_sleepforever == 0 ? &tv : NULL);
}
void NET_StartThread(void)
{
if (use_thread)
{
if (!net_thread_initialized)
{
net_thread_initialized = TRUE;
Sys_Error("%s: -netthread is not reversed yet", __func__);
#ifdef _WIN32
/*
InitializeCriticalSection(&net_cs);
hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)NET_ThreadMain, 0, 0, &ThreadId);
if (!hThread)
{
DeleteCriticalSection(&net_cs);
net_thread_initialized = 0;
use_thread = 0;
Sys_Error("%s: Couldn't initialize network thread, run without -netthread\n", __func__);
}
*/
#endif // _WIN32
}
}
}
void NET_StopThread(void)
{
if (use_thread)
{
if (net_thread_initialized)
{
#ifdef _WIN32
/*
TerminateThread(hThread, 0);
DeleteCriticalSection(&net_cs);
*/
#endif // _WIN32
net_thread_initialized = FALSE;
Sys_Error("%s: -netthread is not reversed yet", __func__);
}
}
}
void *net_malloc(size_t size)
{
return Mem_ZeroMalloc(size);
}
net_messages_t *NET_AllocMsg(int size)
{
net_messages_t *pmsg;
if (size <= MSG_QUEUE_SIZE && normalqueue)
{
pmsg = normalqueue->next;
normalqueue->buffersize = size;
normalqueue = pmsg;
}
else
{
pmsg = (net_messages_t *)net_malloc(sizeof(net_messages_t));
pmsg->buffer = (unsigned char *)net_malloc(size);
pmsg->buffersize = size;
pmsg->preallocated = FALSE;
}
return pmsg;
}
void NET_FreeMsg(net_messages_t *pmsg)
{
if (pmsg->preallocated)
{
net_messages_t* tmp = normalqueue;
normalqueue = pmsg;
pmsg->next = tmp;
}
else
{
Mem_Free(pmsg->buffer);
Mem_Free(pmsg);
}
}
qboolean NET_GetPacket(netsrc_t sock)
{
net_messages_t *pmsg;
qboolean bret;
NET_AdjustLag();
NET_ThreadLock();
if (NET_GetLoopPacket(sock, &in_from, &in_message))
{
bret = NET_LagPacket(1, sock, &in_from, &in_message);
}
else
{
if (!use_thread)
{
bret = NET_QueuePacket(sock);
if (!bret)
bret = NET_LagPacket(0, sock, 0, 0);
}
else
{
bret = NET_LagPacket(0, sock, 0, 0);
}
}
if (bret)
{
Q_memcpy(net_message.data, in_message.data, in_message.cursize);
net_message.cursize = in_message.cursize;
Q_memcpy(&net_from, &in_from, 0x14u);
NET_ThreadUnlock();
return bret;
}
pmsg = messages[sock];
if (pmsg)
{
net_message.cursize = pmsg->buffersize;
messages[sock] = pmsg->next;
Q_memcpy(net_message.data, pmsg->buffer, net_message.cursize);
net_from = pmsg->from;
msg_readcount = 0;
NET_FreeMsg(pmsg);
bret = 1;
}
NET_ThreadUnlock();
return bret;
}
void NET_AllocateQueues(void)
{
net_messages_t *p;
for (int i = 0; i < NUM_MSG_QUEUES; i++)
{
p = (net_messages_t *)Mem_ZeroMalloc(sizeof(net_messages_t));
p->buffer = (unsigned char *)Mem_ZeroMalloc(MSG_QUEUE_SIZE);
p->preallocated = 1;
p->next = normalqueue;
normalqueue = p;
}
NET_StartThread();
}
void NET_FlushQueues(void)
{
for (int i = 0; i < 3; i++)
{
net_messages_t *p = messages[i];
while (p)
{
net_messages_t *n = p->next;
Mem_Free(p->buffer);
Mem_Free(p);
p = n;
}
messages[i] = NULL;
}
net_messages_t *p = normalqueue;
while (p)
{
net_messages_t *n = p->next;
Mem_Free(p->buffer);
Mem_Free(p);
p = n;
}
normalqueue = NULL;
}
int NET_SendLong(netsrc_t sock, SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen)
{
static long gSequenceNumber = 1;
// Do we need to break this packet up?
if (sock == NS_SERVER && len > MAX_ROUTEABLE_PACKET)
{
// yep
char packet[MAX_ROUTEABLE_PACKET];
int totalSent, ret, size, packetCount, packetNumber;
SPLITPACKET *pPacket;
gSequenceNumber++;
if (gSequenceNumber < 0)
{
gSequenceNumber = 1;
}
pPacket = (SPLITPACKET *)packet;
pPacket->netID = -2;
pPacket->sequenceNumber = gSequenceNumber;
packetNumber = 0;
totalSent = 0;
packetCount = (len + SPLIT_SIZE - 1) / SPLIT_SIZE;
while (len > 0)
{
size = Q_min(int(SPLIT_SIZE), len);
pPacket->packetID = (packetNumber << 4) + packetCount;
Q_memcpy(packet + sizeof(SPLITPACKET), buf + (packetNumber * SPLIT_SIZE), size);
if (net_showpackets.value == 4.0f)
{
netadr_t adr;
Q_memset(&adr, 0, sizeof(adr));
SockadrToNetadr((struct sockaddr *)to, &adr);
Con_DPrintf("Sending split %i of %i with %i bytes and seq %i to %s\n",
packetNumber + 1,
packetCount,
size,
gSequenceNumber,
NET_AdrToString(adr));
}
ret = CRehldsPlatformHolder::get()->sendto(s, packet, size + sizeof(SPLITPACKET), flags, to, tolen);
if (ret < 0)
{
return ret;
}
if (ret >= size)
{
totalSent += size;
}
len -= size;
packetNumber++;
}
return totalSent;
}
int nSend = CRehldsPlatformHolder::get()->sendto(s, buf, len, flags, to, tolen);
return nSend;
}
void EXT_FUNC NET_SendPacket_api(unsigned int length, void *data, const netadr_t &to)
{
NET_SendPacket(NS_SERVER, length, data, to);
}
void NET_SendPacket(netsrc_t sock, int length, void *data, const netadr_t& to)
{
int ret;
struct sockaddr addr;
SOCKET net_socket;
if (to.type == NA_LOOPBACK)
{
NET_SendLoopPacket(sock, length, data, to);
return;
}
if (to.type == NA_BROADCAST)
{
net_socket = ip_sockets[sock];
if (net_socket == INV_SOCK)
return;
}
else if (to.type == NA_IP)
{
net_socket = ip_sockets[sock];
if (net_socket == INV_SOCK)
return;
}
#ifdef _WIN32
else if (to.type == NA_IPX)
{
net_socket = ipx_sockets[sock];
if (net_socket == INV_SOCK)
return;
}
else if (to.type == NA_BROADCAST_IPX)
{
net_socket = ipx_sockets[sock];
if (net_socket == INV_SOCK)
return;
}
#endif // _WIN32
else
{
Sys_Error("%s: bad address type", __func__);
}
NetadrToSockadr(&to, &addr);
ret = NET_SendLong(sock, net_socket, (const char *)data, length, 0, &addr, sizeof(addr));
if (ret == -1)
{
int err = NET_GetLastError();
// wouldblock is silent
if (err == WSAEWOULDBLOCK)
return;
if (err == WSAECONNREFUSED || err == WSAECONNRESET)
return;
// some PPP links dont allow broadcasts
if (err == WSAEADDRNOTAVAIL && (to.type == NA_BROADCAST
#ifdef _WIN32
|| to.type == NA_BROADCAST_IPX
#endif // _WIN32
))
return;
// let dedicated servers continue after errors
if (g_pcls.state == ca_dedicated)
{
Con_Printf("%s: ERROR: %s\n", __func__, NET_ErrorString(err));
}
else
{
if (err == WSAEADDRNOTAVAIL || err == WSAENOBUFS)
{
Con_DPrintf("%s: Warning: %s : %s\n", __func__, NET_ErrorString(err), NET_AdrToString(to));
}
else
{
Sys_Error("%s: ERROR: %s\n", __func__, NET_ErrorString(err));
}
}
}
}
SOCKET NET_IPSocket(char *net_interface, int port, qboolean multicast)
{
SOCKET newsocket;
struct sockaddr_in address;
qboolean _true = TRUE;
int i = 1;
int err;
#ifdef _WIN32
if ((newsocket = CRehldsPlatformHolder::get()->socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
#else
if ((newsocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
#endif // _WIN32
{
err = NET_GetLastError();
if (err != WSAEAFNOSUPPORT)
{
Con_Printf("WARNING: UDP_OpenSocket: port: %d socket: %s", port, NET_ErrorString(err));
}
return INV_SOCK;
}
#ifdef _WIN32
if (CRehldsPlatformHolder::get()->ioctlsocket(newsocket, FIONBIO, (u_long *)&_true) == SOCKET_ERROR)
#else
if (SOCKET_FIONBIO(newsocket, _true) == SOCKET_ERROR)
#endif // _WIN32
{
Con_Printf("WARNING: UDP_OpenSocket: port: %d ioctl FIONBIO: %s\n", port, NET_ErrorString(NET_GetLastError()));
return INV_SOCK;
}
#ifdef _WIN32
if (CRehldsPlatformHolder::get()->setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == SOCKET_ERROR)
#else
if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == SOCKET_ERROR)
#endif
{
Con_Printf ("WARNING: UDP_OpenSocket: port: %d setsockopt SO_BROADCAST: %s\n", port, NET_ErrorString(NET_GetLastError()));
return INV_SOCK;
}
if (COM_CheckParm("-reuse") || multicast)
{
#ifdef _WIN32
if (CRehldsPlatformHolder::get()->setsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&_true, sizeof(qboolean)) == SOCKET_ERROR)
#else
if (setsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&_true, sizeof(qboolean)) == SOCKET_ERROR)
#endif // _WIN32
{
Con_Printf ("WARNING: UDP_OpenSocket: port: %d setsockopt SO_REUSEADDR: %s\n", port, NET_ErrorString(NET_GetLastError()));
return INV_SOCK;
}
}
#ifndef _WIN32
if (COM_CheckParm("-tos"))
{
i = 16;
Con_Printf("Enabling LOWDELAY TOS option\n");
if (setsockopt(newsocket, IPPROTO_IP, IP_TOS, (char *)&i, sizeof(i)) == SOCKET_ERROR)
{
err = NET_GetLastError();
if (err != WSAENOPROTOOPT)
Con_Printf("WARNING: UDP_OpenSocket: port: %d setsockopt IP_TOS: %s\n", port, NET_ErrorString(err));
return INV_SOCK;
}
}
#endif // _WIN32
if (net_interface && *net_interface && Q_stricmp(net_interface, "localhost"))
NET_StringToSockaddr(net_interface, (sockaddr *)&address);
else
address.sin_addr.s_addr = INADDR_ANY;
if (port == -1)
address.sin_port = 0;
else
address.sin_port = htons((u_short)port);
address.sin_family = AF_INET;
#ifdef _WIN32
if (CRehldsPlatformHolder::get()->bind(newsocket, (struct sockaddr *)&address, sizeof(address)) == SOCKET_ERROR)
#else
if (bind(newsocket, (struct sockaddr *)&address, sizeof(address)) == SOCKET_ERROR)
#endif // _WIN32
{
Con_Printf("WARNING: UDP_OpenSocket: port: %d bind: %s\n", port, NET_ErrorString(NET_GetLastError()));
#ifdef _WIN32
CRehldsPlatformHolder::get()->closesocket(newsocket);
#else
SOCKET_CLOSE(newsocket);
#endif // _WIN32
return INV_SOCK;
}
i = COM_CheckParm("-loopback") != 0;
#ifdef _WIN32
if (CRehldsPlatformHolder::get()->setsockopt(newsocket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&i, sizeof(i)) == SOCKET_ERROR)
#else
if (setsockopt(newsocket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&i, sizeof(i)) == SOCKET_ERROR)
#endif // _WIN32
{
Con_DPrintf("WARNING: UDP_OpenSocket: port %d setsockopt IP_MULTICAST_LOOP: %s\n", port, NET_ErrorString(NET_GetLastError()));
}
#if !defined _WIN32 && defined REHLDS_FIXES
i = IP_PMTUDISC_DONT;
if (setsockopt(newsocket, IPPROTO_IP, IP_MTU_DISCOVER, (char *)&i, sizeof(i)) == SOCKET_ERROR)
{
Con_Printf("WARNING: UDP_OpenSocket: port %d setsockopt IP_MTU_DISCOVER: %s\n", port, NET_ErrorString(NET_GetLastError()));
}
#endif
return newsocket;
}
void NET_OpenIP(void)
{
//cvar_t *ip;//unused?
int port;
int dedicated;
int sv_port = 0;
int cl_port = 0;
//int mc_port;//unused?
static qboolean bFirst = TRUE;
dedicated = g_pcls.state == ca_dedicated;
NET_ThreadLock();
if (ip_sockets[NS_SERVER] == INV_SOCK)
{
port = (int)iphostport.value;
if (!port)
{
port = (int)hostport.value;
if (!port)
{
port = (int)defport.value;
hostport.value = defport.value;
}
}
ip_sockets[NS_SERVER] = NET_IPSocket(ipname.string, port, FALSE);
if (ip_sockets[NS_SERVER] == INV_SOCK && dedicated)
{
Sys_Error("%s: Couldn't allocate dedicated server IP port %d.", __func__, port);
}
sv_port = port;
}
NET_ThreadUnlock();
if (dedicated)
return;
NET_ThreadLock();
if (ip_sockets[NS_CLIENT] == INV_SOCK)
{
port = (int)ip_clientport.value;
if (!port)
{
port = (int)clientport.value;
if (!port)
port = -1;
}
ip_sockets[NS_CLIENT] = NET_IPSocket(ipname.string, port, FALSE);
if (ip_sockets[NS_CLIENT] == INV_SOCK)
ip_sockets[NS_CLIENT] = NET_IPSocket(ipname.string, -1, FALSE);
cl_port = port;
}
if (ip_sockets[NS_MULTICAST] == INV_SOCK)
{
ip_sockets[NS_MULTICAST] = NET_IPSocket(ipname.string, multicastport.value, TRUE);
if (ip_sockets[NS_MULTICAST] == INV_SOCK && !dedicated)
Con_Printf("Warning! Couldn't allocate multicast IP port.\n");
}
NET_ThreadUnlock();
if (bFirst)
{
bFirst = FALSE;
Con_Printf("NET Ports: server %i, client %i\n", sv_port, cl_port);
}
}
#ifdef _WIN32
SOCKET NET_IPXSocket(int hostshort)
{
int err;
u_long optval = 1;
SOCKET newsocket;
SOCKADDR_IPX address;
if((newsocket = CRehldsPlatformHolder::get()->socket(PF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == INVALID_SOCKET)
{
err = NET_GetLastError();
if (err != WSAEAFNOSUPPORT)
{
Con_Printf("WARNING: IPX_Socket: port: %d socket: %s\n", hostshort, NET_ErrorString(err));
}
return INV_SOCK;
}
if (CRehldsPlatformHolder::get()->ioctlsocket(newsocket, FIONBIO, &optval) == SOCKET_ERROR)
{
err = NET_GetLastError();
Con_Printf("WARNING: IPX_Socket: port: %d ioctl FIONBIO: %s\n", hostshort, NET_ErrorString(err));
return INV_SOCK;
}
if (CRehldsPlatformHolder::get()->setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (const char *)&optval, sizeof(optval)) == SOCKET_ERROR)
{
err = NET_GetLastError();
Con_Printf("WARNING: IPX_Socket: port: %d setsockopt SO_BROADCAST: %s\n", hostshort, NET_ErrorString(err));
return INV_SOCK;
}
if (CRehldsPlatformHolder::get()->setsockopt(newsocket, SOL_SOCKET, 4, (const char *)&optval, sizeof(optval)) == SOCKET_ERROR)
{
#ifndef REHLDS_FIXES
err = NET_GetLastError();
#endif
return INV_SOCK;
}
address.sa_family = AF_IPX;
Q_memset(address.sa_netnum, 0, 4);
Q_memset(address.sa_nodenum, 0, 6);
if (hostshort == -1)
address.sa_socket = 0;
else address.sa_socket = htons((u_short)hostshort);
if (CRehldsPlatformHolder::get()->bind(newsocket, (struct sockaddr *)&address, sizeof(SOCKADDR_IPX)) == SOCKET_ERROR)
{
err = NET_GetLastError();
Con_Printf("WARNING: IPX_Socket: port: %d bind: %s\n", hostshort, NET_ErrorString(err));
CRehldsPlatformHolder::get()->closesocket(newsocket);
return INV_SOCK;
}
return newsocket;
}
void NET_OpenIPX(void)
{
int port;
int dedicated;
dedicated = g_pcls.state == ca_dedicated;
NET_ThreadLock();
if (ipx_sockets[NS_SERVER] == INV_SOCK)
{
port = ipx_hostport.value;
if (!port)
{
port = hostport.value;
if (!port)
{
hostport.value = defport.value;
port = defport.value;
}
}
ipx_sockets[NS_SERVER] = NET_IPXSocket(port);
}
NET_ThreadUnlock();
if(dedicated)
return;
NET_ThreadLock();
if (ipx_sockets[NS_CLIENT] == INV_SOCK)
{
port = ipx_clientport.value;
if (!port)
{
port = clientport.value;
if (!port)
port = -1;
}
ipx_sockets[NS_CLIENT] = NET_IPXSocket(port);
if (ipx_sockets[NS_CLIENT] == INV_SOCK)
ipx_sockets[NS_CLIENT] = NET_IPXSocket(-1);
}
NET_ThreadUnlock();
}
#endif // _WIN32
void NET_GetLocalAddress(void)
{
char buff[512];
struct sockaddr_in address;
int namelen;
int net_error;
Q_memset(&net_local_adr, 0, sizeof(netadr_t));
#ifdef _WIN32
Q_memset(&net_local_ipx_adr, 0, sizeof(netadr_t));
#endif // _WIN32
if (noip)
{
Con_Printf("TCP/IP Disabled.\n");
}
else
{
if (Q_strcmp(ipname.string, "localhost"))
Q_strncpy(buff, ipname.string, ARRAYSIZE(buff) - 1);
else
{
#ifdef _WIN32
CRehldsPlatformHolder::get()->gethostname(buff, ARRAYSIZE(buff));
#else
gethostname(buff, ARRAYSIZE(buff));
#endif // _WIN32
}
buff[ARRAYSIZE(buff) - 1] = 0;
#ifdef REHLDS_FIXES
//check if address is valid
if (NET_StringToAdr(buff, &net_local_adr))
{
#else
NET_StringToAdr(buff, &net_local_adr);
#endif
namelen = sizeof(address);
#ifdef _WIN32
if (CRehldsPlatformHolder::get()->getsockname(ip_sockets[NS_SERVER], (struct sockaddr *)&address, (socklen_t *)&namelen) == SOCKET_ERROR)
#else
if (getsockname(ip_sockets[NS_SERVER], (struct sockaddr *)&address, (socklen_t *)&namelen) == SOCKET_ERROR)
#endif // _WIN32
{
noip = TRUE;
net_error = NET_GetLastError();
Con_Printf("Could not get TCP/IP address, TCP/IP disabled\nReason: %s\n", NET_ErrorString(net_error));
}
else
{
net_local_adr.port = address.sin_port;
Con_Printf("Server IP address %s\n", NET_AdrToString(net_local_adr));
Cvar_Set("net_address", va(NET_AdrToString(net_local_adr)));
}
#ifdef REHLDS_FIXES
}
else
{
Con_Printf("Could not get TCP/IP address, Invalid hostname '%s'\n", buff);
}
#endif
}
#ifdef _WIN32
if (noipx)
{
Con_Printf("No IPX Support.\n");
}
else
{
namelen = 14;
if (CRehldsPlatformHolder::get()->getsockname(ipx_sockets[NS_SERVER], (struct sockaddr *)&address, (socklen_t *)&namelen) == SOCKET_ERROR)
{
noipx = TRUE;
net_error = NET_GetLastError();
#ifdef REHLDS_FIXES
Con_Printf("Could not get IPX address, IPX disabled\nReason: %s\n", NET_ErrorString(net_error));
#endif
}
else
{
SockadrToNetadr((struct sockaddr *)&address, &net_local_ipx_adr);
Con_Printf("Server IPX address %s\n", NET_AdrToString(net_local_ipx_adr));
}
}
#endif //_WIN32
}
int NET_IsConfigured(void)
{
return net_configured;
}
void NET_Config(qboolean multiplayer)
{
static qboolean old_config;
static qboolean bFirst = TRUE;
if (old_config == multiplayer)
{
return;
}
old_config = multiplayer;
if (multiplayer)
{
if (!noip)
NET_OpenIP();
#ifdef _WIN32
if (!noipx)
NET_OpenIPX();
#endif //_WIN32
if (bFirst)
{
bFirst = FALSE;
NET_GetLocalAddress();
}
}
else
{
NET_ThreadLock();
for (int sock = 0; sock < 3; sock++)
{
if (ip_sockets[sock] != INV_SOCK)
{
#ifdef _WIN32
CRehldsPlatformHolder::get()->closesocket(ip_sockets[sock]);
#else //_WIN32
SOCKET_CLOSE(ip_sockets[sock]);
#endif //_WIN32
ip_sockets[sock] = INV_SOCK;
}
#ifdef _WIN32
if (ipx_sockets[sock] != INV_SOCK)
{
CRehldsPlatformHolder::get()->closesocket(ipx_sockets[sock]);
ipx_sockets[sock] = INV_SOCK;
}
#endif //_WIN32
}
NET_ThreadUnlock();
}
net_configured = multiplayer ? 1 : 0;
}
void MaxPlayers_f(void)
{
if (Cmd_Argc() != 2)
{
Con_Printf("\"maxplayers\" is \"%u\"\n", g_psvs.maxclients);
return;
}
if (g_psv.active)
{
Con_Printf("maxplayers cannot be changed while a server is running.\n");
return;
}
int n = Q_atoi(Cmd_Argv(1));
if (n < 1)
n = 1;
if (n > g_psvs.maxclientslimit)
{
n = g_psvs.maxclientslimit;
Con_Printf("\"maxplayers\" set to \"%u\"\n", g_psvs.maxclientslimit);
}
g_psvs.maxclients = n;
if (n == 1)
Cvar_Set("deathmatch", "0");
else
Cvar_Set("deathmatch", "1");
}
void NET_Init(void)
{
#ifdef HOOK_ENGINE
Cmd_AddCommand("maxplayers", (xcommand_t)GetOriginalFuncAddrOrDefault("MaxPlayers_f", (void *)MaxPlayers_f));
#else
Cmd_AddCommand("maxplayers", MaxPlayers_f);
#endif // HOOK_ENGINE
Cvar_RegisterVariable(&net_address);
Cvar_RegisterVariable(&ipname);
Cvar_RegisterVariable(&iphostport);
Cvar_RegisterVariable(&hostport);
Cvar_RegisterVariable(&defport);
Cvar_RegisterVariable(&ip_clientport);
Cvar_RegisterVariable(&clientport);
Cvar_RegisterVariable(&clockwindow);
Cvar_RegisterVariable(&multicastport);
#ifdef _WIN32
Cvar_RegisterVariable(&ipx_hostport);
Cvar_RegisterVariable(&ipx_clientport);
#endif // _WIN32
Cvar_RegisterVariable(&fakelag);
Cvar_RegisterVariable(&fakeloss);
Cvar_RegisterVariable(&net_graph);
Cvar_RegisterVariable(&net_graphwidth);
Cvar_RegisterVariable(&net_scale);
Cvar_RegisterVariable(&net_graphpos);
if (COM_CheckParm("-netthread"))
{
use_thread = 1;
Sys_Error("%s: -netthread is not reversed yet", __func__);
}
if (COM_CheckParm("-netsleep"))
net_sleepforever = 0;
#ifdef _WIN32
if (COM_CheckParm("-noipx"))
noipx = TRUE;
#endif // _WIN32
if (COM_CheckParm("-noip"))
noip = TRUE;
int port = COM_CheckParm("-port");
if (port)
Cvar_SetValue("hostport", Q_atof(com_argv[port + 1]));
int clockwindow_ = COM_CheckParm("-clockwindow");
if (clockwindow_)
Cvar_SetValue("clockwindow", Q_atof(com_argv[clockwindow_ + 1]));
net_message.data = (byte *)&net_message_buffer;
net_message.maxsize = sizeof(net_message_buffer);
net_message.flags = 0;
net_message.buffername = "net_message";
in_message.data = (byte *)&in_message_buf;
in_message.maxsize = sizeof(in_message_buf);
in_message.flags = 0;
in_message.buffername = "in_message";
for (int i = 0; i < 3; i++)
{
g_pLagData[i].pPrev = &g_pLagData[i];
g_pLagData[i].pNext = &g_pLagData[i];
}
NET_AllocateQueues();
Con_DPrintf("Base networking initialized.\n");
}
void NET_ClearLagData(qboolean bClient, qboolean bServer)
{
NET_ThreadLock();
if (bClient)
{
NET_ClearLaggedList(&g_pLagData[0]);
NET_ClearLaggedList(&g_pLagData[2]);
}
if (bServer)
{
NET_ClearLaggedList(&g_pLagData[1]);
}
NET_ThreadUnlock();
}
void NET_Shutdown(void)
{
NET_ThreadLock();
NET_ClearLaggedList(g_pLagData);
NET_ClearLaggedList(&g_pLagData[1]);
NET_ThreadUnlock();
NET_Config(FALSE);
NET_FlushQueues();
}
qboolean NET_JoinGroup(netsrc_t sock, netadr_t& addr)
{
ip_mreq mreq;
SOCKET net_socket = ip_sockets[sock];
SIN_SET_ADDR(&mreq.imr_multiaddr, *(unsigned int*)&addr.ip[0]);
SIN_SET_ADDR(&mreq.imr_interface, 0);
if (CRehldsPlatformHolder::get()->setsockopt(net_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)) == SOCKET_ERROR)
{
int err = NET_GetLastError();
if (err != WSAEAFNOSUPPORT)
{
Con_Printf("WARNING: NET_JoinGroup: IP_ADD_MEMBERSHIP: %s", NET_ErrorString(err));
}
return FALSE;
}
return TRUE;
}
qboolean NET_LeaveGroup(netsrc_t sock, netadr_t& addr)
{
ip_mreq mreq;
SOCKET net_socket = ip_sockets[sock];
SIN_SET_ADDR(&mreq.imr_multiaddr, *(unsigned int*)&addr.ip[0]);
SIN_SET_ADDR(&mreq.imr_interface, 0);
if (CRehldsPlatformHolder::get()->setsockopt(net_socket, 0, 6, (char *)&mreq, sizeof(mreq)) != SOCKET_ERROR)
{
int err = NET_GetLastError();
if (err != WSAEAFNOSUPPORT)
{
return FALSE;
}
}
return TRUE;
}