Merge branch 'new-bans' into 'master'

New bans

See merge request KartKrew/Kart!1544
This commit is contained in:
James R. 2023-10-16 23:50:51 +00:00
commit 63bda6076f
19 changed files with 724 additions and 794 deletions

View file

@ -152,6 +152,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
k_hitlag.c
k_dialogue.cpp
k_tally.cpp
k_bans.cpp
music.cpp
music_manager.cpp
)

View file

@ -64,6 +64,7 @@
#include "k_serverstats.h"
#include "k_zvote.h"
#include "music.h"
#include "k_bans.h"
// cl loading screen
#include "v_video.h"
@ -2420,287 +2421,6 @@ static void CL_ConnectToServer(void)
}
static void Command_ShowBan(void) //Print out ban list
{
size_t i;
const char *address, *mask, *reason, *username;
time_t unbanTime = NO_BAN_TIME;
const time_t curTime = time(NULL);
if (I_GetBanAddress)
CONS_Printf(M_GetText("Ban List:\n"));
else
return;
for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++)
{
unbanTime = NO_BAN_TIME;
if (I_GetUnbanTime)
unbanTime = I_GetUnbanTime(i);
if (unbanTime != NO_BAN_TIME && curTime >= unbanTime)
continue;
CONS_Printf("%s: ", sizeu1(i+1));
if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL)
CONS_Printf("%s - ", username);
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
CONS_Printf("%s", address);
else
CONS_Printf("%s/%s", address, mask);
if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL)
CONS_Printf(" - %s", reason);
if (unbanTime != NO_BAN_TIME)
{
// these are fudged a little to match what a joiner sees
int minutes = ((unbanTime - curTime) + 30) / 60;
int hours = (minutes + 1) / 60;
int days = (hours + 1) / 24;
if (days)
CONS_Printf(" (%d day%s)", days, days > 1 ? "s" : "");
else if (hours)
CONS_Printf(" (%d hour%s)", hours, hours > 1 ? "s" : "");
else if (minutes)
CONS_Printf(" (%d minute%s)", minutes, minutes > 1 ? "s" : "");
else
CONS_Printf(" (<1 minute)");
}
CONS_Printf("\n");
}
if (i == 0 && !address)
CONS_Printf(M_GetText("(empty)\n"));
}
static boolean bansLoaded = false;
// If you're a community contributor looking to improve how bans are written, please
// offer your changes back to our Git repository. Kart Krew reserve the right to
// utilise format numbers in use by community builds for different layouts.
#define BANFORMAT 1
void D_SaveBan(void)
{
FILE *f;
size_t i;
const char *address, *mask;
const char *username, *reason;
const time_t curTime = time(NULL);
time_t unbanTime = NO_BAN_TIME;
const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt");
if (bansLoaded != true)
{
// You didn't even get to ATTEMPT to load bans.txt.
// Don't immediately save nothing over it.
return;
}
f = fopen(path, "w");
if (!f)
{
CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n"));
return;
}
// Add header.
fprintf(f, "BANFORMAT %d\n", BANFORMAT);
for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++)
{
if (I_GetUnbanTime)
{
unbanTime = I_GetUnbanTime(i);
}
else
{
unbanTime = NO_BAN_TIME;
}
if (unbanTime != NO_BAN_TIME && curTime >= unbanTime)
{
// This one has served their sentence.
// We don't need to save them in the file anymore.
continue;
}
mask = NULL;
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
fprintf(f, "%s/0", address);
else
fprintf(f, "%s/%s", address, mask);
// TODO: it'd be nice to convert this to an actual date-time,
// so it'd be easier to edit outside of the game.
fprintf(f, " %ld", (long)unbanTime);
username = NULL;
if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL)
fprintf(f, " \"%s\"", username);
else
fprintf(f, " \"%s\"", "Direct IP ban");
reason = NULL;
if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL)
fprintf(f, " \"%s\"\n", reason);
else
fprintf(f, " \"%s\"\n", "No reason given");
}
fclose(f);
}
static void Command_ClearBans(void)
{
if (!I_ClearBans)
return;
I_ClearBans();
D_SaveBan();
}
void D_LoadBan(boolean warning)
{
FILE *f;
size_t i, j;
char *address, *mask;
char *username, *reason;
time_t unbanTime = NO_BAN_TIME;
char buffer[MAX_WADPATH];
UINT8 banmode = 0;
boolean malformed = false;
if (!I_ClearBans)
return;
// We at least attempted loading bans.txt
bansLoaded = true;
f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
if (!f)
{
if (warning)
CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n"));
return;
}
I_ClearBans();
for (i = 0; fgets(buffer, (int)sizeof(buffer), f); i++)
{
address = strtok(buffer, " /\t\r\n");
mask = strtok(NULL, " \t\r\n");
if (i == 0 && !strncmp(address, "BANFORMAT", 9))
{
if (mask)
{
banmode = atoi(mask);
}
switch (banmode)
{
case BANFORMAT: // currently supported format
//case 0: -- permitted only when BANFORMAT string not present
break;
default:
{
fclose(f);
CONS_Alert(CONS_WARNING, "Could not load unknown ban.txt for ban list (BANFORMAT %s, expected %d)\n", mask, BANFORMAT);
return;
}
}
continue;
}
if (I_SetBanAddress(address, mask) == false) // invalid IP input?
{
CONS_Alert(CONS_WARNING, "\"%s/%s\" is not a valid IP address, discarding...\n", address, mask);
continue;
}
// One-way legacy format conversion -- the game will crash otherwise
if (banmode == 0)
{
unbanTime = NO_BAN_TIME;
username = NULL; // not guaranteed to be accurate, but only sane substitute
reason = strtok(NULL, "\r\n");
if (reason && reason[0] == 'N' && reason[1] == 'A' && reason[2] == '\0')
{
reason = NULL;
}
}
else
{
reason = strtok(NULL, " \"\t\r\n");
if (reason)
{
unbanTime = atoi(reason);
reason = NULL;
}
else
{
unbanTime = NO_BAN_TIME;
malformed = true;
}
username = strtok(NULL, "\"\t\r\n"); // go until next "
if (!username)
{
malformed = true;
}
strtok(NULL, "\"\t\r\n"); // remove first "
reason = strtok(NULL, "\"\r\n"); // go until next "
if (!reason)
{
malformed = true;
}
}
// Enforce MAX_REASONLENGTH.
if (reason)
{
j = 0;
while (reason[j] != '\0')
{
if ((j++) < MAX_REASONLENGTH)
continue;
reason[j] = '\0';
break;
}
}
if (I_SetUnbanTime)
I_SetUnbanTime(unbanTime);
if (I_SetBanUsername)
I_SetBanUsername(username);
if (I_SetBanReason)
I_SetBanReason(reason);
}
if (malformed)
{
CONS_Alert(CONS_WARNING, "One or more lines of ban.txt are malformed. The game can correct for this, but some data may be lost.\n");
}
fclose(f);
}
#undef BANFORMAT
static void Command_ReloadBan(void) //recheck ban.txt
{
D_LoadBan(true);
}
static void Command_connect(void)
{
@ -3122,71 +2842,6 @@ static void Command_Ban(void)
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
}
static void Command_BanIP(void)
{
size_t ac = COM_Argc();
if (ac < 2)
{
CONS_Printf(M_GetText("banip <ip> [<reason>]: ban an ip address\n"));
return;
}
if (server) // Only the server can use this, otherwise does nothing.
{
char *addressInput = Z_StrDup(COM_Argv(1));
const char *address = NULL;
const char *mask = NULL;
const char *reason = NULL;
address = strtok(addressInput, "/");
mask = strtok(NULL, "");
if (ac > 2)
{
reason = COM_Argv(2);
}
if (I_SetBanAddress && I_SetBanAddress(address, mask))
{
if (reason)
{
CONS_Printf(
"Banned IP address %s%s for: %s\n",
address,
(mask && (strlen(mask) > 0)) ? va("/%s", mask) : "",
reason
);
}
else
{
CONS_Printf(
"Banned IP address %s%s\n",
address,
(mask && (strlen(mask) > 0)) ? va("/%s", mask) : ""
);
}
if (I_SetUnbanTime)
I_SetUnbanTime(NO_BAN_TIME);
if (I_SetBanUsername)
I_SetBanUsername(NULL);
if (I_SetBanReason)
I_SetBanReason(reason);
D_SaveBan();
}
else
{
return;
}
}
}
static void Command_Kick(void)
{
if (COM_Argc() < 2)
@ -3263,6 +2918,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
{
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
}
else
{
memset(reason, 0, sizeof(buf));
}
// Is playernum authorized to make this kick?
if (playernum != serverplayer && !IsPlayerAdmin(playernum)
@ -3349,28 +3008,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
if (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN || banMinutes)
{
if (I_Ban && !I_Ban(playernode[(INT32)pnum]))
{
CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n"));
}
else
{
if (I_SetBanUsername)
I_SetBanUsername(player_names[pnum]);
if (I_SetBanReason)
I_SetBanReason(reason);
if (I_SetUnbanTime)
{
if (banMinutes)
I_SetUnbanTime(time(NULL) + (banMinutes * 60));
else
I_SetUnbanTime(NO_BAN_TIME);
}
D_SaveBan();
}
SV_BanPlayer(pnum, banMinutes, reason);
}
}
@ -3670,10 +3308,9 @@ void D_ClientServerInit(void)
COM_AddCommand("getplayernum", Command_GetPlayerNum);
COM_AddCommand("kick", Command_Kick);
COM_AddCommand("ban", Command_Ban);
COM_AddCommand("listbans", Command_Listbans);
COM_AddCommand("unban", Command_Unban);
COM_AddCommand("banip", Command_BanIP);
COM_AddCommand("clearbans", Command_ClearBans);
COM_AddCommand("showbanlist", Command_ShowBan);
COM_AddCommand("reloadbans", Command_ReloadBan);
COM_AddCommand("connect", Command_connect);
COM_AddCommand("nodes", Command_Nodes);
#ifdef HAVE_CURL
@ -3697,7 +3334,6 @@ void D_ClientServerInit(void)
CV_RegisterList(cvlist_dumpconsistency);
}
#endif
D_LoadBan(false);
gametic = 0;
localgametic = 0;
@ -4460,41 +4096,46 @@ static void HandleConnect(SINT8 node)
if (playernode[i] != UINT8_MAX) // We use this to count players because it is affected by SV_AddWaitingPlayers when more than one client joins on the same tic, unlike playeringame and D_NumPlayers. UINT8_MAX denotes no node for that player
connectedplayers++;
if (bannednode && bannednode[node].banid != SIZE_MAX)
banrecord_t *ban = SV_GetBanByAddress(node);
if (ban == NULL)
{
const char *reason = NULL;
// Get the reason...
if (!I_GetBanReason || (reason = I_GetBanReason(bannednode[node].banid)) == NULL)
reason = "No reason given";
if (bannednode[node].timeleft != NO_BAN_TIME)
for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++)
{
// these are fudged a little to allow it to sink in for impatient rejoiners
int minutes = (bannednode[node].timeleft + 30) / 60;
if (ban == NULL)
ban = SV_GetBanByKey(lastReceivedKey[node][i]);
}
}
if (ban != NULL && node != 0)
{
UINT32 timeremaining = 0;
if (ban->expires > time(NULL))
{
timeremaining = ban->expires - time(NULL);
int minutes = (timeremaining + 30) / 60;
int hours = (minutes + 1) / 60;
int days = (hours + 1) / 24;
if (days)
{
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d day%s)", reason, days, days > 1 ? "s" : ""));
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d day%s)", ban->reason, days, days > 1 ? "s" : ""));
}
else if (hours)
{
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d hour%s)", reason, hours, hours > 1 ? "s" : ""));
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d hour%s)", ban->reason, hours, hours > 1 ? "s" : ""));
}
else if (minutes)
{
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d minute%s)", reason, minutes, minutes > 1 ? "s" : ""));
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d minute%s)", ban->reason, minutes, minutes > 1 ? "s" : ""));
}
else
{
SV_SendRefuse(node, va("K|%s\n(Time remaining: <1 minute)", reason));
SV_SendRefuse(node, va("K|%s\n(Time remaining: <1 minute)", ban->reason));
}
}
else
{
SV_SendRefuse(node, va("B|%s", reason));
SV_SendRefuse(node, va("B|%s", ban->reason));
}
}
else if (netbuffer->u.clientcfg._255 != 255 ||

View file

@ -87,6 +87,7 @@
#include "k_serverstats.h"
#include "music.h"
#include "k_dialogue.h"
#include "k_bans.h"
#ifdef HWRENDER
#include "hardware/hw_main.h" // 3D View Rendering
@ -1609,6 +1610,7 @@ void D_SRB2Main(void)
PR_LoadProfiles(); // load control profiles
SV_LoadStats();
SV_LoadBans();
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen
@ -1924,6 +1926,7 @@ void D_SRB2Main(void)
}
SV_SaveStats();
SV_SaveBans();
if (autostart || netgame)
{

View file

@ -81,19 +81,8 @@ SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL;
void (*I_NetRequestHolePunch)(INT32 node) = NULL;
void (*I_NetRegisterHolePunch)(void) = NULL;
boolean (*I_NetOpenSocket)(void) = NULL;
boolean (*I_Ban) (INT32 node) = NULL;
void (*I_ClearBans)(void) = NULL;
const char *(*I_GetNodeAddress) (INT32 node) = NULL;
UINT32 (*I_GetNodeAddressInt) (INT32 node) = NULL;
const char *(*I_GetBanAddress) (size_t ban) = NULL;
const char *(*I_GetBanMask) (size_t ban) = NULL;
const char *(*I_GetBanUsername) (size_t ban) = NULL;
const char *(*I_GetBanReason) (size_t ban) = NULL;
time_t (*I_GetUnbanTime) (size_t ban) = NULL;
boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL;
boolean (*I_SetBanUsername) (const char *username) = NULL;
boolean (*I_SetBanReason) (const char *reason) = NULL;
boolean (*I_SetUnbanTime) (time_t timestamp) = NULL;
boolean (*I_IsExternalAddress) (const void *p) = NULL;
bannednode_t *bannednode = NULL;

View file

@ -58,8 +58,6 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum,
size_t packetlength);
boolean HGetPacket(void);
void D_SetDoomcom(void);
void D_SaveBan(void);
void D_LoadBan(boolean warning);
boolean D_CheckNetGame(void);
void D_CloseConnection(void);
void Net_UnAcknowledgePacket(INT32 node);

View file

@ -69,6 +69,7 @@
#include "k_bot.h"
#include "k_powerup.h"
#include "k_roulette.h"
#include "k_bans.h"
#ifdef SRB2_CONFIG_ENABLE_WEBM_MOVIES
#include "m_avrecorder.h"

View file

@ -163,19 +163,8 @@ extern void (*I_NetRequestHolePunch)(INT32 node);
extern void (*I_NetRegisterHolePunch)(void);
extern boolean (*I_Ban) (INT32 node);
extern void (*I_ClearBans)(void);
extern const char *(*I_GetNodeAddress) (INT32 node);
extern UINT32 (*I_GetNodeAddressInt) (INT32 node);
extern const char *(*I_GetBanAddress) (size_t ban);
extern const char *(*I_GetBanMask) (size_t ban);
extern const char *(*I_GetBanUsername) (size_t ban);
extern const char *(*I_GetBanReason) (size_t ban);
extern time_t (*I_GetUnbanTime) (size_t ban);
extern boolean (*I_SetBanAddress) (const char *address,const char *mask);
extern boolean (*I_SetBanUsername) (const char *username);
extern boolean (*I_SetBanReason) (const char *reason);
extern boolean (*I_SetUnbanTime) (time_t timestamp);
extern boolean (*I_IsExternalAddress) (const void *p);
struct bannednode_t

View file

@ -12,120 +12,12 @@
/// This is not really OS-dependent because all OSes have the same socket API.
/// Just use ifdef for OS-dependent parts.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef __GNUC__
#include <unistd.h>
#endif
#ifndef NO_IPV6
#define HAVE_IPV6
#endif
#ifdef _WIN32
#define USE_WINSOCK
#if defined (_WIN64) || defined (HAVE_IPV6)
#define USE_WINSOCK2
#else //_WIN64/HAVE_IPV6
#define USE_WINSOCK1
#endif
#endif //WIN32 OS
#ifdef USE_WINSOCK2
#include <ws2tcpip.h>
#endif
#include "doomdef.h"
#ifdef USE_WINSOCK1
#include <winsock.h>
#else
#ifndef USE_WINSOCK
#include <arpa/inet.h>
#ifdef __APPLE_CC__
#ifndef _BSD_SOCKLEN_T_
#define _BSD_SOCKLEN_T_
#endif //_BSD_SOCKLEN_T_
#endif //__APPLE_CC__
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#endif //normal BSD API
#include <errno.h>
#include <time.h>
#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)
#include <sys/time.h>
#endif // UNIXCOMMON
#endif
#ifdef USE_WINSOCK
// some undefined under win32
#undef errno
//#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right?
#define errno h_errno // some very strange things happen when not using h_error?!?
#ifdef EWOULDBLOCK
#undef EWOULDBLOCK
#endif
#define EWOULDBLOCK WSAEWOULDBLOCK
#ifdef EMSGSIZE
#undef EMSGSIZE
#endif
#define EMSGSIZE WSAEMSGSIZE
#ifdef ECONNREFUSED
#undef ECONNREFUSED
#endif
#define ECONNREFUSED WSAECONNREFUSED
#ifdef ETIMEDOUT
#undef ETIMEDOUT
#endif
#define ETIMEDOUT WSAETIMEDOUT
#ifndef IOC_VENDOR
#define IOC_VENDOR 0x18000000
#endif
#ifndef _WSAIOW
#define _WSAIOW(x,y) (IOC_IN|(x)|(y))
#endif
#ifndef SIO_UDP_CONNRESET
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
#endif
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0x00000400
#endif
#ifndef STATUS_INVALID_PARAMETER
#define STATUS_INVALID_PARAMETER 0xC000000D
#endif
#endif // USE_WINSOCK
typedef union
{
struct sockaddr any;
struct sockaddr_in ip4;
#ifdef HAVE_IPV6
struct sockaddr_in6 ip6;
#endif
} mysockaddr_t;
#ifdef HAVE_MINIUPNPC
#ifdef STATIC_MINIUPNPC
#define STATICLIB
#endif
#include "miniupnpc/miniwget.h"
#include "miniupnpc/miniupnpc.h"
#include "miniupnpc/upnpcommands.h"
#undef STATICLIB
static UINT8 UPNP_support = TRUE;
#endif // HAVE_MINIUPNC
#include "i_tcp_detail.h"
#include "i_system.h"
#include "i_time.h"
#include "i_net.h"
#include "d_net.h"
#include "d_netfil.h"
#include "i_tcp.h"
#include "m_argv.h"
#include "stun.h"
#include "z_zone.h"
@ -175,16 +67,12 @@ static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET};
static size_t mysocketses = 0;
static int myfamily[MAXNETNODES+1] = {0};
static SOCKET_TYPE nodesocket[MAXNETNODES+1] = {ERRSOCKET};
static mysockaddr_t clientaddress[MAXNETNODES+1];
mysockaddr_t clientaddress[MAXNETNODES+1];
static mysockaddr_t broadcastaddress[MAXNETNODES+1];
static size_t broadcastaddresses = 0;
static boolean nodeconnected[MAXNETNODES+1];
static banned_t *banned;
static const INT32 hole_punch_magic = MSBF_LONG (0x52eb11);
static size_t numbans = 0;
static size_t banned_size = 0;
static bannednode_t SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1?
static boolean init_tcp_driver = false;
@ -341,7 +229,14 @@ static inline void I_UPnP_rem(const char *port, const char * servicetype)
}
#endif
static const char *SOCK_AddrToStr(mysockaddr_t *sk)
// I spent 3 hours trying to work out why I couldn't sensibly access this from anywhere,
// even when extern'd. I am not the person who should be doing this. -Tyron, 2023-10-08
mysockaddr_t SOCK_DirectNodeToAddr(UINT8 node)
{
return clientaddress[node];
}
const char *SOCK_AddrToStr(mysockaddr_t *sk)
{
static char s[64]; // 255.255.255.255:65535 or IPv6:65535
#ifdef HAVE_NTOP
@ -401,45 +296,7 @@ static UINT32 SOCK_GetNodeAddressInt(INT32 node)
return 0;
}
static const char *SOCK_GetBanAddress(size_t ban)
{
if (ban >= numbans)
return NULL;
return SOCK_AddrToStr(&banned[ban].address);
}
static const char *SOCK_GetBanMask(size_t ban)
{
static char s[16]; //255.255.255.255 netmask? no, just CDIR for only
if (ban >= numbans)
return NULL;
if (sprintf(s,"%d",banned[ban].mask) > 0)
return s;
return NULL;
}
static const char *SOCK_GetBanUsername(size_t ban)
{
if (ban >= numbans)
return NULL;
return banned[ban].username;
}
static const char *SOCK_GetBanReason(size_t ban)
{
if (ban >= numbans)
return NULL;
return banned[ban].reason;
}
static time_t SOCK_GetUnbanTime(size_t ban)
{
if (ban >= numbans)
return NO_BAN_TIME;
return banned[ban].timestamp;
}
static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
{
UINT32 bitmask = INADDR_NONE;
@ -565,7 +422,7 @@ static boolean hole_punch(ssize_t c)
// Returns true if a packet was received from a new node, false in all other cases
static boolean SOCK_Get(void)
{
size_t i, n;
size_t n;
int j;
ssize_t c;
mysockaddr_t fromaddress;
@ -607,8 +464,6 @@ static boolean SOCK_Get(void)
j = getfreenode();
if (j > 0)
{
const time_t curTime = time(NULL);
M_Memcpy(&clientaddress[j], &fromaddress, fromlen);
nodesocket[j] = mysockets[n];
DEBFILE(va("New node detected: node:%d address:%s\n", j,
@ -616,42 +471,6 @@ static boolean SOCK_Get(void)
doomcom->remotenode = (INT16)j; // good packet from a game player
doomcom->datalength = (INT16)c;
// check if it's a banned dude so we can send a refusal later
for (i = 0; i < numbans; i++)
{
if (SOCK_cmpaddr(&fromaddress, &banned[i].address, banned[i].mask))
{
if (banned[i].timestamp != NO_BAN_TIME)
{
if (curTime >= banned[i].timestamp)
{
SOCK_bannednode[j].timeleft = NO_BAN_TIME;
SOCK_bannednode[j].banid = SIZE_MAX;
DEBFILE("This dude was banned, but enough time has passed\n");
break;
}
SOCK_bannednode[j].timeleft = banned[i].timestamp - curTime;
SOCK_bannednode[j].banid = i;
DEBFILE("This dude has been temporarily banned\n");
break;
}
else
{
SOCK_bannednode[j].timeleft = NO_BAN_TIME;
SOCK_bannednode[j].banid = i;
DEBFILE("This dude has been banned\n");
break;
}
}
}
if (i == numbans)
{
SOCK_bannednode[j].timeleft = NO_BAN_TIME;
SOCK_bannednode[j].banid = SIZE_MAX;
}
return true;
}
else
@ -1357,171 +1176,6 @@ static boolean SOCK_OpenSocket(void)
return UDP_Socket();
}
static void AddBannedIndex(void)
{
if (numbans >= banned_size)
{
if (banned_size == 0)
{
banned_size = 8;
}
else
{
banned_size *= 2;
}
banned = Z_ReallocAlign(
(void*) banned,
sizeof(banned_t) * banned_size,
PU_STATIC,
NULL,
sizeof(banned_t) * 8
);
}
numbans++;
}
static boolean SOCK_Ban(INT32 node)
{
INT32 ban;
if (node > MAXNETNODES)
return false;
ban = numbans;
AddBannedIndex();
M_Memcpy(&banned[ban].address, &clientaddress[node], sizeof (mysockaddr_t));
if (banned[ban].address.any.sa_family == AF_INET)
{
banned[ban].address.ip4.sin_port = 0;
banned[ban].mask = 32;
}
#ifdef HAVE_IPV6
else if (banned[ban].address.any.sa_family == AF_INET6)
{
banned[ban].address.ip6.sin6_port = 0;
banned[ban].mask = 128;
}
#endif
return true;
}
static boolean SOCK_SetBanUsername(const char *username)
{
if (username == NULL || strlen(username) == 0)
{
username = "Direct IP ban";
}
if (banned[numbans - 1].username)
{
Z_Free(banned[numbans - 1].username);
banned[numbans - 1].username = NULL;
}
banned[numbans - 1].username = Z_StrDup(username);
return true;
}
static boolean SOCK_SetBanReason(const char *reason)
{
if (reason == NULL || strlen(reason) == 0)
{
reason = "No reason given";
}
if (banned[numbans - 1].reason)
{
Z_Free(banned[numbans - 1].reason);
banned[numbans - 1].reason = NULL;
}
banned[numbans - 1].reason = Z_StrDup(reason);
return true;
}
static boolean SOCK_SetUnbanTime(time_t timestamp)
{
banned[numbans - 1].timestamp = timestamp;
return true;
}
static boolean SOCK_SetBanAddress(const char *address, const char *mask)
{
struct my_addrinfo *ai, *runp, hints;
int gaie;
if (!address)
return false;
memset(&hints, 0x00, sizeof(hints));
hints.ai_flags = 0;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
gaie = I_getaddrinfo(address, "0", &hints, &ai);
if (gaie != 0)
return false;
runp = ai;
while (runp != NULL)
{
INT32 ban;
UINT8 numericalmask;
ban = numbans;
AddBannedIndex();
memcpy(&banned[ban].address, runp->ai_addr, runp->ai_addrlen);
#ifdef HAVE_IPV6
if (runp->ai_family == AF_INET6)
banned[ban].mask = 128;
else
#endif
banned[ban].mask = 32;
if (mask)
{
numericalmask = (UINT8)atoi(mask);
}
else
{
numericalmask = 0;
}
if (numericalmask > 0 && numericalmask < banned[ban].mask)
{
banned[ban].mask = numericalmask;
}
// Set defaults, in case anything funny happens.
SOCK_SetBanUsername(NULL);
SOCK_SetBanReason(NULL);
SOCK_SetUnbanTime(NO_BAN_TIME);
runp = runp->ai_next;
}
I_freeaddrinfo(ai);
return true;
}
static void SOCK_ClearBans(void)
{
numbans = 0;
banned_size = 0;
Z_Free(banned);
banned = NULL;
}
// https://github.com/jameds/holepunch/blob/master/holepunch.c#L75
static int SOCK_IsExternalAddress (const void *p)
{
@ -1634,19 +1288,8 @@ boolean I_InitTcpNetwork(void)
}
I_NetOpenSocket = SOCK_OpenSocket;
I_Ban = SOCK_Ban;
I_ClearBans = SOCK_ClearBans;
I_GetNodeAddress = SOCK_GetNodeAddress;
I_GetNodeAddressInt = SOCK_GetNodeAddressInt;
I_GetBanAddress = SOCK_GetBanAddress;
I_GetBanMask = SOCK_GetBanMask;
I_GetBanUsername = SOCK_GetBanUsername;
I_GetBanReason = SOCK_GetBanReason;
I_GetUnbanTime = SOCK_GetUnbanTime;
I_SetBanAddress = SOCK_SetBanAddress;
I_SetBanUsername = SOCK_SetBanUsername;
I_SetBanReason = SOCK_SetBanReason;
I_SetUnbanTime = SOCK_SetUnbanTime;
I_IsExternalAddress = SOCK_IsExternalAddress;
bannednode = SOCK_bannednode;

View file

@ -17,6 +17,8 @@
extern "C" {
#endif
#include "d_net.h"
extern UINT16 current_port;
/** \brief The I_InitTcpNetwork function

140
src/i_tcp_detail.h Normal file
View file

@ -0,0 +1,140 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
#ifndef i_tcp_detail_h
#define i_tcp_detail_h
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef __GNUC__
#include <unistd.h>
#endif
#ifndef NO_IPV6
#define HAVE_IPV6
#endif
#ifdef _WIN32
#define USE_WINSOCK
#if defined (_WIN64) || defined (HAVE_IPV6)
#define USE_WINSOCK2
#else //_WIN64/HAVE_IPV6
#define USE_WINSOCK1
#endif
#endif //WIN32 OS
#ifdef USE_WINSOCK2
#include <ws2tcpip.h>
#endif
#include "doomdef.h"
#ifdef USE_WINSOCK1
#include <winsock.h>
#else
#ifndef USE_WINSOCK
#include <arpa/inet.h>
#ifdef __APPLE_CC__
#ifndef _BSD_SOCKLEN_T_
#define _BSD_SOCKLEN_T_
#endif //_BSD_SOCKLEN_T_
#endif //__APPLE_CC__
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#endif //normal BSD API
#include <errno.h>
#include <time.h>
#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)
#include <sys/time.h>
#endif // UNIXCOMMON
#endif
#ifdef USE_WINSOCK
// some undefined under win32
#undef errno
//#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right?
#define errno h_errno // some very strange things happen when not using h_error?!?
#ifdef EWOULDBLOCK
#undef EWOULDBLOCK
#endif
#define EWOULDBLOCK WSAEWOULDBLOCK
#ifdef EMSGSIZE
#undef EMSGSIZE
#endif
#define EMSGSIZE WSAEMSGSIZE
#ifdef ECONNREFUSED
#undef ECONNREFUSED
#endif
#define ECONNREFUSED WSAECONNREFUSED
#ifdef ETIMEDOUT
#undef ETIMEDOUT
#endif
#define ETIMEDOUT WSAETIMEDOUT
#ifndef IOC_VENDOR
#define IOC_VENDOR 0x18000000
#endif
#ifndef _WSAIOW
#define _WSAIOW(x,y) (IOC_IN|(x)|(y))
#endif
#ifndef SIO_UDP_CONNRESET
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
#endif
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0x00000400
#endif
#ifndef STATUS_INVALID_PARAMETER
#define STATUS_INVALID_PARAMETER 0xC000000D
#endif
#endif // USE_WINSOCK
#ifdef HAVE_MINIUPNPC
#ifdef STATIC_MINIUPNPC
#define STATICLIB
#endif
#include "miniupnpc/miniwget.h"
#include "miniupnpc/miniupnpc.h"
#include "miniupnpc/upnpcommands.h"
#undef STATICLIB
static UINT8 UPNP_support = TRUE;
#endif // HAVE_MINIUPNC
#include "d_net.h"
#include "doomtype.h"
#include "i_tcp.h"
union mysockaddr_t
{
struct sockaddr any;
struct sockaddr_in ip4;
#ifdef HAVE_IPV6
struct sockaddr_in6 ip6;
#endif
};
extern mysockaddr_t clientaddress[MAXNETNODES+1];
const char *SOCK_AddrToStr(mysockaddr_t *sk);
mysockaddr_t SOCK_DirectNodeToAddr(UINT8 node);
boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask);
#ifdef __cplusplus
} // extern "C"
#endif
#endif/*i_tcp_detail_h*/

439
src/k_bans.cpp Normal file
View file

@ -0,0 +1,439 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
// Copyright (C) 2023 by AJ "Tyron" Martinez
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file k_bans.c
/// \brief replacement for DooM Legacy ban system
#include <fstream>
#include <stdexcept>
#include <vector>
#include <fmt/format.h>
#include <nlohmann/json.hpp>
#include "i_tcp_detail.h" // clientaddress
#include "k_bans.h"
#include "byteptr.h" // READ/WRITE macros
#include "command.h"
#include "d_clisrv.h" // playernode
#include "d_main.h" // pandf
#include "d_net.h" // MAXNETNODES
#include "doomtype.h"
#include "k_profiles.h"
#include "m_misc.h" //FIL_WriteFile()
#include "p_saveg.h" // savebuffer_t
#include "time.h"
#include "z_zone.h"
#include "i_addrinfo.h" // I_getaddrinfo
using nlohmann::json;
static mysockaddr_t *DuplicateSockAddr(const mysockaddr_t *source)
{
auto dup = static_cast<mysockaddr_t*>(Z_Malloc(sizeof *source, PU_STATIC, NULL));
memcpy(dup, source, sizeof *source);
return dup;
}
static std::vector<banrecord_t> bans;
static uint8_t allZero[PUBKEYLENGTH];
static void load_bans_array_v1(json& array)
{
for (json& object : array)
{
uint8_t public_key_bin[PUBKEYLENGTH];
std::string public_key = object.at("public_key");
std::string ip_address = object.at("ip_address");
time_t expires = object.at("expires");
UINT8 subnet_mask = object.at("subnet_mask");
std::string username = object.at("username");
std::string reason = object.at("reason");
if (!FromPrettyRRID(public_key_bin, public_key.c_str()))
{
throw std::runtime_error("public_key has invalid format");
}
bool banned = SV_BanIP(
ip_address.c_str(),
subnet_mask,
public_key_bin,
expires,
username.c_str(),
reason.c_str()
);
if (!banned)
{
throw std::runtime_error(fmt::format("{}: IP address is invalid", ip_address));
}
}
}
void SV_LoadBans(void)
{
if (!server)
return;
json object;
try
{
std::ifstream f(va(pandf, srb2home, BANFILE));
if (f.is_open())
{
f >> object;
}
}
catch (const std::exception& ex)
{
const char* gdfolder = "the Ring Racers folder";
if (strcmp(srb2home, "."))
gdfolder = srb2home;
I_Error("%s\nNot a valid ban file.\nDelete %s (maybe in %s) and try again.", ex.what(), BANFILE, gdfolder);
}
if (!object.is_object())
{
return;
}
try
{
if (object.value("version", 1) == 1)
{
json& array = object.at("bans");
if (array.is_array())
{
load_bans_array_v1(array);
}
}
}
catch (const std::exception& ex)
{
I_Error("%s: %s", BANFILE, ex.what());
}
}
void SV_SaveBans(void)
{
json object = json::object();
object["version"] = 1;
json& array = object["bans"];
array = json::array();
for (banrecord_t& ban : bans)
{
if (ban.deleted)
continue;
array.push_back({
{"public_key", GetPrettyRRID(ban.public_key, false)},
{"ip_address", SOCK_AddrToStr(ban.address)},
{"subnet_mask", ban.mask},
{"expires", ban.expires},
{"username", ban.username},
{"reason", ban.reason},
});
}
try
{
std::ofstream(va(pandf, srb2home, BANFILE)) << object;
}
catch (const std::exception& ex)
{
I_Error("%s\nCouldn't save bans. Are you out of disk space / playing in a protected folder?", ex.what());
}
}
mysockaddr_t convertedaddress;
static mysockaddr_t* SV_NodeToBanAddress(UINT8 node)
{
convertedaddress = SOCK_DirectNodeToAddr(node);
if (convertedaddress.any.sa_family == AF_INET)
{
convertedaddress.ip4.sin_port = 0;
// mask was 32?
}
#ifdef HAVE_IPV6
else if (convertedaddress.any.sa_family == AF_INET6)
{
convertedaddress.ip6.sin6_port = 0;
// mask was 128?
}
#endif
return &convertedaddress;
}
static boolean SV_IsBanEnforced(banrecord_t *ban)
{
if (ban->deleted)
return false;
if ((ban->expires != 0) && (time(NULL) > ban->expires))
return false;
return true;
}
banrecord_t* SV_GetBanByAddress(UINT8 node)
{
mysockaddr_t* address = SV_NodeToBanAddress(node);
for (banrecord_t& ban : bans)
{
if (!SV_IsBanEnforced(&ban))
continue;
if (SOCK_cmpaddr(ban.address, address, ban.mask))
return &ban;
}
return NULL;
}
banrecord_t* SV_GetBanByKey(uint8_t* key)
{
UINT32 hash;
hash = quickncasehash((char*) key, PUBKEYLENGTH);
for (banrecord_t& ban : bans)
{
if (!SV_IsBanEnforced(&ban))
continue;
if (hash != ban.hash) // Not crypto magic, just an early out with a faster comparison
continue;
if (memcmp(&ban.public_key, allZero, PUBKEYLENGTH) == 0)
continue; // Don't ban GUESTs on accident, we have a cvar for this.
if (memcmp(&ban.public_key, key, PUBKEYLENGTH) == 0)
return &ban;
}
return NULL;
}
void SV_BanPlayer(INT32 pnum, time_t minutes, char* reason)
{
mysockaddr_t targetaddress;
UINT8 node = playernode[pnum];
time_t expires = 0;
memcpy(&targetaddress, SV_NodeToBanAddress(node), sizeof(mysockaddr_t));
if (minutes != 0)
expires = time(NULL) + minutes * 60;
SV_Ban(targetaddress, 0, players[pnum].public_key, expires, player_names[pnum], reason);
}
boolean SV_BanIP(const char *address, UINT8 mask, uint8_t* public_key, time_t expires, const char* username, const char* reason)
{
struct my_addrinfo *ai, *runp, hints;
memset(&hints, 0x00, sizeof(hints));
hints.ai_flags = 0;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
int gaie;
gaie = I_getaddrinfo(address, "0", &hints, &ai);
if (gaie != 0)
return false;
runp = ai;
mysockaddr_t newaddr;
memcpy(&newaddr, runp->ai_addr, runp->ai_addrlen);
SV_Ban(newaddr, mask, public_key, expires, username, reason);
return true;
}
static char noreason[] = "N/A";
static char nouser[] = "[Direct IP ban]";
void SV_Ban(mysockaddr_t address, UINT8 mask, uint8_t* public_key, time_t expires, const char* username, const char* reason)
{
if (reason == NULL || strlen(reason) == 0)
reason = noreason;
if (!public_key)
public_key = allZero;
if (!expires)
expires = 0;
if (!username)
username = nouser;
banrecord_t ban{};
ban.address = DuplicateSockAddr(&address);
memcpy(ban.public_key, public_key, PUBKEYLENGTH);
memcpy(&ban.expires, &expires, sizeof(time_t));
ban.mask = mask;
strlcpy(ban.username, username, MAXBANUSERNAME);
strlcpy(ban.reason, reason, MAXBANREASON);
ban.hash = quickncasehash((char*) ban.public_key, PUBKEYLENGTH);
bans.push_back(ban);
SV_SaveBans();
}
static void SV_BanSearch(boolean remove)
{
const char* filtertarget = (COM_Argc() > 1) ? COM_Argv(1) : NULL;
if (remove && !filtertarget)
{
CONS_Printf("unban <target>: removes all bans matching target username, key, or address\n");
return;
}
time_t now = time(NULL);
UINT32 records = 0;
UINT32 matchedrecords = 0;
boolean force = COM_CheckPartialParm("-f");
// First pass: What records are we even looking for?
for (banrecord_t& ban : bans)
{
ban.matchesquery = false;
if (ban.deleted)
continue;
records++;
const char* stringaddress = SOCK_AddrToStr(ban.address);
const char* stringkey = GetPrettyRRID(ban.public_key, true);
if (filtertarget != NULL && !(strcasestr(filtertarget, ban.username) || strcasestr(filtertarget, stringkey) ||
strcasestr(filtertarget, stringaddress)))
continue;
ban.matchesquery = true;
matchedrecords++;
}
boolean saferemove = remove && (matchedrecords <= 1 || force);
// Second pass: Report and/or act on records.
for (banrecord_t& ban : bans)
{
if (!ban.matchesquery)
continue;
const char* stringaddress = SOCK_AddrToStr(ban.address);
const char* stringkey = GetPrettyRRID(ban.public_key, true);
std::string recordprint = fmt::format(
"{}{} - {} [{}] - {}",
stringaddress,
ban.mask && ban.mask != 32 ? fmt::format("/{}", ban.mask) : "",
ban.username,
stringkey,
ban.reason
);
if (ban.expires)
{
if (ban.expires < now)
recordprint += " - EXPIRED";
else
recordprint += fmt::format(" - expires {}m", (ban.expires - now)/60);
}
CONS_Printf("%s\n", recordprint.c_str());
if (saferemove)
ban.deleted = true;
}
if (records == 0)
CONS_Printf("You haven't banned anyone yet.\n");
else if (matchedrecords == 0)
CONS_Printf("\x82""No matches in %d bans.\n", records);
else if (remove && !saferemove)
CONS_Printf("\x82""Didn't unban, would remove %d bans.\x80 Refine search or use 'unban <search> -f'.\n", matchedrecords);
else if (saferemove)
CONS_Printf("\x83""Removed %d/%d bans.\n", matchedrecords, records);
else if (filtertarget)
CONS_Printf("Matched %d/%d bans.\n", matchedrecords, records);
else
CONS_Printf("Showing %d bans. Try 'listbans [search]' to refine results.\n", matchedrecords);
if (saferemove)
SV_SaveBans();
}
void Command_Listbans(void)
{
if (netgame && consoleplayer != serverplayer)
{
CONS_Printf("You must be the netgame host to do this.\n");
return;
}
SV_BanSearch(false);
}
void Command_Unban(void)
{
if (netgame && consoleplayer != serverplayer)
{
CONS_Printf("You must be the netgame host to do this.\n");
return;
}
SV_BanSearch(true);
}
void Command_BanIP(void)
{
if (COM_Argc() <= 1)
{
CONS_Printf("banip <target> [reason]: bans the specified IP from netgames you host\n");
return;
}
char* input = Z_StrDup(COM_Argv(1));
const char *address = strtok(input, "/");
const char *mask = strtok(NULL, "");
if (!mask)
mask = "";
char reason[MAXBANREASON+1] = "";
if (COM_Argc() > 2)
strncpy(reason, COM_Argv(2), MAXBANREASON);
UINT8 nummask = atoi(mask);
if (SV_BanIP(address, nummask, NULL, 0, NULL, reason))
{
CONS_Printf("Banned address %s.\n", address);
}
else
{
CONS_Printf("Not a valid IP address.\n");
}
Z_Free(input);
}

62
src/k_bans.h Normal file
View file

@ -0,0 +1,62 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2023 by AJ "Tyron" Martinez
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file k_bans.h
/// \brief ban system definitions
#ifndef __BANS_H__
#define __BANS_H__
#include "doomdef.h"
#ifdef __cplusplus
extern "C"
{
#endif
#define BANFILE "srvbans.json"
#define MAXBANUSERNAME 64
#define MAXBANREASON 256
struct banrecord_t
{
uint8_t public_key[PUBKEYLENGTH];
mysockaddr_t *address;
UINT8 mask;
time_t expires;
char username[MAXBANUSERNAME+1];
char reason[MAXBANREASON+1];
UINT32 hash; // Not persisted! Used for early outs during key comparisons
boolean deleted; // Not persisted! Deleted records are ignored and not written back to file.
boolean matchesquery; // Not persisted! Used when filtering listbans/unban searches.
};
banrecord_t *SV_GetBanByKey(uint8_t *key);
banrecord_t *SV_GetBanByAddress(UINT8 node);
void SV_LoadBans(void);
void SV_SaveBans(void);
void SV_BanPlayer(int pnum, time_t duration, char *reason);
boolean SV_BanIP(const char *address, UINT8 mask, uint8_t *public_key, time_t expires, const char *username, const char *reason);
void SV_Ban(mysockaddr_t address, UINT8 mask, uint8_t *public_key, time_t expires, const char *username, const char *reason);
void Command_Listbans(void);
void Command_Unban(void);
void Command_BanIP(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif

View file

@ -627,6 +627,28 @@ char *GetPrettyRRID(const unsigned char *bin, boolean brief)
return rrid_buf;
}
unsigned char *FromPrettyRRID(unsigned char *bin, const char *text)
{
size_t i;
size_t len = PUBKEYLENGTH * 2;
if (strlen(text) != len)
return NULL;
for (i = 0; i < len; i += 2)
{
char byt[3] = { text[i], text[i+1], '\0' };
char *p;
bin[i/2] = strtol(byt, &p, 16);
if (*p) // input is not hexadecimal
return NULL;
}
return bin;
}
static char allZero[PUBKEYLENGTH];

View file

@ -162,6 +162,7 @@ profile_t *PR_GetLocalPlayerProfile(INT32 player);
boolean PR_IsLocalPlayerGuest(INT32 player);
char *GetPrettyRRID(const unsigned char *bin, boolean brief);
unsigned char *FromPrettyRRID(unsigned char *bin, const char *text);
boolean PR_IsKeyGuest(uint8_t *key);

View file

@ -105,7 +105,7 @@ void SV_LoadStats(void)
}
else if (version < SERVERSTATSVER)
{
// We're converting - let'd create a backup.
// We're converting - let's create a backup.
FIL_WriteFile(va("%s" PATHSEP "%s.bak", srb2home, SERVERSTATSFILE), save.buffer, save.size);
}
@ -173,7 +173,7 @@ void SV_SaveStats(void)
if (!FIL_WriteFile(va(pandf, srb2home, SERVERSTATSFILE), save.buffer, length))
{
P_SaveBufferFree(&save);
I_Error("Couldn't save server stats. Are you out of Disk space / playing in a protected folder?");
I_Error("Couldn't save server stats. Are you out of disk space / playing in a protected folder?");
}
P_SaveBufferFree(&save);
}

View file

@ -422,8 +422,6 @@ boolean I_InitNetwork(void)
mypacket.maxlen = hardware_MAXPACKETLENGTH;
I_NetOpenSocket = NET_OpenSocket;
I_Ban = NET_Ban;
I_ClearBans = NET_ClearBans;
I_GetNodeAddress = NET_GetNodeAddress;
I_GetBenAddress = NET_GetBenAddress;
I_SetBanAddress = NET_SetBanAddress;

View file

@ -1620,7 +1620,6 @@ void I_Quit(void)
SDLforceUngrabMouse();
quiting = SDL_FALSE;
M_SaveConfig(NULL); //save game config, cvars..
D_SaveBan(); // save the ban list
M_SaveJoinedIPs();
// Make sure you lose points for ALT-F4
@ -1752,7 +1751,6 @@ void I_Error(const char *error, ...)
// ---
M_SaveConfig(NULL); // save game config, cvars..
D_SaveBan(); // save the ban list
G_DirtyGameData(); // done first in case an error is in G_SaveGameData
G_SaveGameData(); // Tails 12-08-2002

View file

@ -2981,9 +2981,6 @@ void I_Quit(void)
quiting = SDL_FALSE;
I_ShutdownConsole();
M_SaveConfig(NULL); //save game config, cvars..
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
// Make sure you lose points for ALT-F4
if (Playing())
@ -3146,9 +3143,6 @@ void I_Error(const char *error, ...)
fflush(stderr);
#endif
M_SaveConfig(NULL); // save game config, cvars..
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
G_DirtyGameData(); // done first in case an error is in G_SaveGameData
G_SaveGameData(); // Tails 12-08-2002

View file

@ -18,8 +18,11 @@
extern "C" {
#endif
#define TYPEDEF3(tag, id, new_type) \
typedef tag id new_type
#define TYPEDEF2(struct_id, new_type) \
typedef struct struct_id new_type
TYPEDEF3 (struct, struct_id, new_type)
#define TYPEDEF(id) TYPEDEF2 (id, id)
@ -163,6 +166,9 @@ TYPEDEF (JoyFF_t);
// i_time.h
TYPEDEF (timestate_t);
// i_tcp.h
TYPEDEF3 (union, mysockaddr_t, mysockaddr_t);
// info.h
TYPEDEF (state_t);
TYPEDEF (mobjinfo_t);
@ -204,9 +210,12 @@ TYPEDEF (pathfindsetup_t);
// k_profiles.h
TYPEDEF (profile_t);
// h_serverstats.h
// k_serverstats.h
TYPEDEF (serverplayer_t);
// k_bans.h
TYPEDEF (banrecord_t);
// k_terrain.h
TYPEDEF (t_splash_t);
TYPEDEF (t_footstep_t);