mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-12-11 02:23:09 +00:00
Merge branch 'new-bans' into 'master'
New bans See merge request KartKrew/Kart!1544
This commit is contained in:
commit
63bda6076f
19 changed files with 724 additions and 794 deletions
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
415
src/d_clisrv.c
415
src/d_clisrv.c
|
|
@ -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 ||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
11
src/d_net.c
11
src/d_net.c
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
11
src/i_net.h
11
src/i_net.h
|
|
@ -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
|
||||
|
|
|
|||
381
src/i_tcp.c
381
src/i_tcp.c
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
140
src/i_tcp_detail.h
Normal 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
439
src/k_bans.cpp
Normal 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
62
src/k_bans.h
Normal 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
|
||||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue