mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2025-03-22 10:52:23 +00:00
Move server-side connection handling to a new file
This commit is contained in:
parent
4660d3cab6
commit
b461cb919b
6 changed files with 533 additions and 473 deletions
|
@ -1,4 +1,5 @@
|
|||
d_clisrv.c
|
||||
server_connection.c
|
||||
client_connection.c
|
||||
d_net.c
|
||||
d_netcmd.c
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "../lua_libs.h"
|
||||
#include "../md5.h"
|
||||
#include "../m_perfstats.h"
|
||||
#include "server_connection.h"
|
||||
#include "client_connection.h"
|
||||
|
||||
//
|
||||
|
@ -79,15 +80,6 @@ netnode_t netnodes[MAXNETNODES];
|
|||
|
||||
// Server specific vars
|
||||
UINT8 playernode[MAXPLAYERS];
|
||||
char playeraddress[MAXPLAYERS][64];
|
||||
|
||||
// Minimum timeout for sending the savegame
|
||||
// The actual timeout will be longer depending on the savegame length
|
||||
tic_t jointimeout = (10*TICRATE);
|
||||
|
||||
// Incremented by cv_joindelay when a client joins, decremented each tic.
|
||||
// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled.
|
||||
static tic_t joindelay = 0;
|
||||
|
||||
UINT16 pingmeasurecount = 1;
|
||||
UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone.
|
||||
|
@ -100,7 +92,6 @@ tic_t maketic;
|
|||
|
||||
static INT16 consistancy[BACKUPTICS];
|
||||
|
||||
static UINT8 player_joining = false;
|
||||
UINT8 hu_redownloadinggamestate = 0;
|
||||
|
||||
// true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks
|
||||
|
@ -501,229 +492,6 @@ void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum)
|
|||
|
||||
static INT16 Consistancy(void);
|
||||
|
||||
static INT32 FindRejoinerNum(SINT8 node)
|
||||
{
|
||||
char strippednodeaddress[64];
|
||||
const char *nodeaddress;
|
||||
char *port;
|
||||
INT32 i;
|
||||
|
||||
// Make sure there is no dead dress before proceeding to the stripping
|
||||
if (!I_GetNodeAddress)
|
||||
return -1;
|
||||
nodeaddress = I_GetNodeAddress(node);
|
||||
if (!nodeaddress)
|
||||
return -1;
|
||||
|
||||
// Strip the address of its port
|
||||
strcpy(strippednodeaddress, nodeaddress);
|
||||
port = strchr(strippednodeaddress, ':');
|
||||
if (port)
|
||||
*port = '\0';
|
||||
|
||||
// Check if any player matches the stripped address
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX
|
||||
&& !strcmp(playeraddress[i], strippednodeaddress))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static UINT8
|
||||
GetRefuseReason (INT32 node)
|
||||
{
|
||||
if (!node || FindRejoinerNum(node) != -1)
|
||||
return 0;
|
||||
else if (bannednode && bannednode[node])
|
||||
return REFUSE_BANNED;
|
||||
else if (!cv_allownewplayer.value)
|
||||
return REFUSE_JOINS_DISABLED;
|
||||
else if (D_NumPlayers() >= cv_maxplayers.value)
|
||||
return REFUSE_SLOTS_FULL;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SV_SendServerInfo(INT32 node, tic_t servertime)
|
||||
{
|
||||
UINT8 *p;
|
||||
|
||||
netbuffer->packettype = PT_SERVERINFO;
|
||||
netbuffer->u.serverinfo._255 = 255;
|
||||
netbuffer->u.serverinfo.packetversion = PACKETVERSION;
|
||||
netbuffer->u.serverinfo.version = VERSION;
|
||||
netbuffer->u.serverinfo.subversion = SUBVERSION;
|
||||
strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION,
|
||||
sizeof netbuffer->u.serverinfo.application);
|
||||
// return back the time value so client can compute their ping
|
||||
netbuffer->u.serverinfo.time = (tic_t)LONG(servertime);
|
||||
netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime);
|
||||
|
||||
netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
|
||||
netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
|
||||
|
||||
netbuffer->u.serverinfo.refusereason = GetRefuseReason(node);
|
||||
|
||||
strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
|
||||
sizeof netbuffer->u.serverinfo.gametypename);
|
||||
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
|
||||
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
|
||||
netbuffer->u.serverinfo.flags = (dedicated ? SV_DEDICATED : 0);
|
||||
strncpy(netbuffer->u.serverinfo.servername, cv_servername.string,
|
||||
MAXSERVERNAME);
|
||||
strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7);
|
||||
|
||||
M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16);
|
||||
|
||||
memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle);
|
||||
|
||||
if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl)
|
||||
{
|
||||
char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle;
|
||||
while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0')
|
||||
{
|
||||
if (!(*read & 0x80))
|
||||
{
|
||||
*writ = toupper(*read);
|
||||
writ++;
|
||||
}
|
||||
read++;
|
||||
}
|
||||
*writ = '\0';
|
||||
//strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33);
|
||||
}
|
||||
else
|
||||
strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32);
|
||||
|
||||
if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
|
||||
netbuffer->u.serverinfo.iszone = 1;
|
||||
else
|
||||
netbuffer->u.serverinfo.iszone = 0;
|
||||
|
||||
if (mapheaderinfo[gamemap-1])
|
||||
netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum;
|
||||
|
||||
p = PutFileNeeded(0);
|
||||
|
||||
HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
|
||||
}
|
||||
|
||||
static void SV_SendPlayerInfo(INT32 node)
|
||||
{
|
||||
UINT8 i;
|
||||
netbuffer->packettype = PT_PLAYERINFO;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
{
|
||||
netbuffer->u.playerinfo[i].num = 255; // This slot is empty.
|
||||
continue;
|
||||
}
|
||||
|
||||
netbuffer->u.playerinfo[i].num = i;
|
||||
strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1);
|
||||
netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0';
|
||||
|
||||
//fetch IP address
|
||||
//No, don't do that, you fuckface.
|
||||
memset(netbuffer->u.playerinfo[i].address, 0, 4);
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
if (!players[i].ctfteam)
|
||||
netbuffer->u.playerinfo[i].team = 255;
|
||||
else
|
||||
netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (players[i].spectator)
|
||||
netbuffer->u.playerinfo[i].team = 255;
|
||||
else
|
||||
netbuffer->u.playerinfo[i].team = 0;
|
||||
}
|
||||
|
||||
netbuffer->u.playerinfo[i].score = LONG(players[i].score);
|
||||
netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE));
|
||||
netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin
|
||||
#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself
|
||||
% 3
|
||||
#endif
|
||||
);
|
||||
|
||||
// Extra data
|
||||
netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor;
|
||||
|
||||
if (players[i].pflags & PF_TAGIT)
|
||||
netbuffer->u.playerinfo[i].data |= 0x20;
|
||||
|
||||
if (players[i].gotflag)
|
||||
netbuffer->u.playerinfo[i].data |= 0x40;
|
||||
|
||||
if (players[i].powers[pw_super])
|
||||
netbuffer->u.playerinfo[i].data |= 0x80;
|
||||
}
|
||||
|
||||
HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS);
|
||||
}
|
||||
|
||||
/** Sends a PT_SERVERCFG packet
|
||||
*
|
||||
* \param node The destination
|
||||
* \return True if the packet was successfully sent
|
||||
*
|
||||
*/
|
||||
static boolean SV_SendServerConfig(INT32 node)
|
||||
{
|
||||
boolean waspacketsent;
|
||||
|
||||
netbuffer->packettype = PT_SERVERCFG;
|
||||
|
||||
netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer;
|
||||
netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots);
|
||||
netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic);
|
||||
netbuffer->u.servercfg.clientnode = (UINT8)node;
|
||||
netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
|
||||
netbuffer->u.servercfg.gametype = (UINT8)gametype;
|
||||
netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
|
||||
|
||||
memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
|
||||
|
||||
{
|
||||
const size_t len = sizeof (serverconfig_pak);
|
||||
|
||||
#ifdef DEBUGFILE
|
||||
if (debugfile)
|
||||
{
|
||||
fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n",
|
||||
sizeu1(len), node);
|
||||
}
|
||||
#endif
|
||||
|
||||
waspacketsent = HSendPacket(node, true, 0, len);
|
||||
}
|
||||
|
||||
#ifdef DEBUGFILE
|
||||
if (debugfile)
|
||||
{
|
||||
if (waspacketsent)
|
||||
{
|
||||
fprintf(debugfile, "ServerConfig Packet was sent\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(debugfile, "ServerConfig Packet could not be sent right now\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return waspacketsent;
|
||||
}
|
||||
|
||||
#define SAVEGAMESIZE (768*1024)
|
||||
|
||||
static boolean SV_ResendingSavegameToAnyone(void)
|
||||
|
@ -736,7 +504,7 @@ static boolean SV_ResendingSavegameToAnyone(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void SV_SendSaveGame(INT32 node, boolean resending)
|
||||
void SV_SendSaveGame(INT32 node, boolean resending)
|
||||
{
|
||||
size_t length, compressedlen;
|
||||
UINT8 *savebuffer;
|
||||
|
@ -1190,7 +958,7 @@ static void Command_connect(void)
|
|||
CL_ConnectToServer();
|
||||
}
|
||||
|
||||
static void ResetNode(INT32 node)
|
||||
void ResetNode(INT32 node)
|
||||
{
|
||||
memset(&netnodes[node], 0, sizeof(*netnodes));
|
||||
netnodes[node].player = -1;
|
||||
|
@ -2020,17 +1788,6 @@ void D_QuitNetGame(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
// Adds a node to the game (player will follow at map change or at savegame....)
|
||||
static inline void SV_AddNode(INT32 node)
|
||||
{
|
||||
netnodes[node].tic = gametic;
|
||||
netnodes[node].supposedtic = gametic;
|
||||
// little hack because the server connects to itself and puts
|
||||
// nodeingame when connected not here
|
||||
if (node)
|
||||
netnodes[node].ingame = true;
|
||||
}
|
||||
|
||||
// Xcmd XD_ADDPLAYER
|
||||
static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
||||
{
|
||||
|
@ -2163,56 +1920,6 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
|||
LUA_HookInt(newplayernum, HOOK(PlayerJoin));
|
||||
}
|
||||
|
||||
static void SV_AddPlayer(SINT8 node, const char *name)
|
||||
{
|
||||
INT32 n;
|
||||
UINT8 buf[2 + MAXPLAYERNAME];
|
||||
UINT8 *p;
|
||||
INT32 newplayernum;
|
||||
|
||||
newplayernum = FindRejoinerNum(node);
|
||||
if (newplayernum == -1)
|
||||
{
|
||||
// search for a free playernum
|
||||
// we can't use playeringame since it is not updated here
|
||||
for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++)
|
||||
{
|
||||
if (playeringame[newplayernum])
|
||||
continue;
|
||||
for (n = 0; n < MAXNETNODES; n++)
|
||||
if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum)
|
||||
break;
|
||||
if (n == MAXNETNODES)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// should never happen since we check the playernum
|
||||
// before accepting the join
|
||||
I_Assert(newplayernum < MAXPLAYERS);
|
||||
|
||||
playernode[newplayernum] = (UINT8)node;
|
||||
|
||||
p = buf + 2;
|
||||
buf[0] = (UINT8)node;
|
||||
buf[1] = newplayernum;
|
||||
if (netnodes[node].numplayers < 1)
|
||||
{
|
||||
netnodes[node].player = newplayernum;
|
||||
}
|
||||
else
|
||||
{
|
||||
netnodes[node].player2 = newplayernum;
|
||||
buf[1] |= 0x80;
|
||||
}
|
||||
WRITESTRINGN(p, name, MAXPLAYERNAME);
|
||||
netnodes[node].numplayers++;
|
||||
|
||||
SendNetXCmd(XD_ADDPLAYER, &buf, p - buf);
|
||||
|
||||
DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
|
||||
}
|
||||
|
||||
void CL_AddSplitscreenPlayer(void)
|
||||
{
|
||||
if (cl_mode == CL_CONNECTED)
|
||||
|
@ -2298,15 +2005,6 @@ void SV_StartSinglePlayerServer(void)
|
|||
multiplayer = true;
|
||||
}
|
||||
|
||||
static void SV_SendRefuse(INT32 node, const char *reason)
|
||||
{
|
||||
strcpy(netbuffer->u.serverrefuse.reason, reason);
|
||||
|
||||
netbuffer->packettype = PT_SERVERREFUSE;
|
||||
HSendPacket(node, true, 0, strlen(netbuffer->u.serverrefuse.reason) + 1);
|
||||
Net_CloseConnection(node);
|
||||
}
|
||||
|
||||
// used at txtcmds received to check packetsize bound
|
||||
static size_t TotalTextCmdPerTic(tic_t tic)
|
||||
{
|
||||
|
@ -2323,139 +2021,6 @@ static size_t TotalTextCmdPerTic(tic_t tic)
|
|||
return total;
|
||||
}
|
||||
|
||||
static const char *
|
||||
GetRefuseMessage (SINT8 node, INT32 rejoinernum)
|
||||
{
|
||||
clientconfig_pak *cc = &netbuffer->u.clientcfg;
|
||||
|
||||
boolean rejoining = (rejoinernum != -1);
|
||||
|
||||
if (!node)/* server connecting to itself */
|
||||
return NULL;
|
||||
|
||||
if (
|
||||
cc->modversion != MODVERSION ||
|
||||
strncmp(cc->application, SRB2APPLICATION,
|
||||
sizeof cc->application)
|
||||
){
|
||||
return/* this is probably client's fault */
|
||||
"Incompatible.";
|
||||
}
|
||||
else if (bannednode && bannednode[node])
|
||||
{
|
||||
return
|
||||
"You have been banned\n"
|
||||
"from the server.";
|
||||
}
|
||||
else if (cc->localplayers != 1)
|
||||
{
|
||||
return
|
||||
"Wrong player count.";
|
||||
}
|
||||
|
||||
if (!rejoining)
|
||||
{
|
||||
if (!cv_allownewplayer.value)
|
||||
{
|
||||
return
|
||||
"The server is not accepting\n"
|
||||
"joins for the moment.";
|
||||
}
|
||||
else if (D_NumPlayers() >= cv_maxplayers.value)
|
||||
{
|
||||
return va(
|
||||
"Maximum players reached: %d",
|
||||
cv_maxplayers.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (luafiletransfers)
|
||||
{
|
||||
return
|
||||
"The serveris broadcasting a file\n"
|
||||
"requested by a Lua script.\n"
|
||||
"Please wait a bit and then\n"
|
||||
"try rejoining.";
|
||||
}
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
const tic_t th = 2 * cv_joindelay.value * TICRATE;
|
||||
|
||||
if (joindelay > th)
|
||||
{
|
||||
return va(
|
||||
"Too many people are connecting.\n"
|
||||
"Please wait %d seconds and then\n"
|
||||
"try rejoining.",
|
||||
(joindelay - th) / TICRATE);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Called when a PT_CLIENTJOIN packet is received
|
||||
*
|
||||
* \param node The packet sender
|
||||
*
|
||||
*/
|
||||
static void HandleConnect(SINT8 node)
|
||||
{
|
||||
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
|
||||
INT32 numplayers = netbuffer->u.clientcfg.localplayers;
|
||||
INT32 rejoinernum;
|
||||
INT32 i;
|
||||
|
||||
// Ignore duplicate packets
|
||||
if (netnodes[node].ingame)
|
||||
return;
|
||||
|
||||
rejoinernum = FindRejoinerNum(node);
|
||||
|
||||
const char *refuse = GetRefuseMessage(node, rejoinernum);
|
||||
if (refuse)
|
||||
{
|
||||
SV_SendRefuse(node, refuse);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < numplayers; i++)
|
||||
{
|
||||
strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1);
|
||||
if (!EnsurePlayerNameIsGood(names[i], rejoinernum))
|
||||
{
|
||||
SV_SendRefuse(node, "Bad player name");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SV_AddNode(node);
|
||||
|
||||
if (!SV_SendServerConfig(node))
|
||||
{
|
||||
/// \note Shouldn't SV_SendRefuse be called before ResetNode?
|
||||
ResetNode(node);
|
||||
SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
|
||||
/// \todo fix this !!!
|
||||
return; // restart the while
|
||||
}
|
||||
DEBFILE("new node joined\n");
|
||||
|
||||
if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)
|
||||
{
|
||||
SV_SendSaveGame(node, false); // send a complete game state
|
||||
DEBFILE("send savegame\n");
|
||||
}
|
||||
|
||||
// Splitscreen can allow 2 players in one node
|
||||
for (i = 0; i < numplayers; i++)
|
||||
SV_AddPlayer(node, names[i]);
|
||||
|
||||
joindelay += cv_joindelay.value * TICRATE;
|
||||
player_joining = true;
|
||||
}
|
||||
|
||||
/** Called when a PT_SERVERSHUTDOWN packet is received
|
||||
*
|
||||
* \param node The packet sender (should be the server)
|
||||
|
@ -2486,40 +2051,6 @@ static void HandleTimeout(SINT8 node)
|
|||
M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
|
||||
}
|
||||
|
||||
static void PT_AskInfoViaMS(SINT8 node)
|
||||
{
|
||||
Net_CloseConnection(node);
|
||||
}
|
||||
|
||||
static void PT_TellFilesNeeded(SINT8 node)
|
||||
{
|
||||
if (server && serverrunning)
|
||||
{
|
||||
UINT8 *p;
|
||||
INT32 firstfile = netbuffer->u.filesneedednum;
|
||||
|
||||
netbuffer->packettype = PT_MOREFILESNEEDED;
|
||||
netbuffer->u.filesneededcfg.first = firstfile;
|
||||
netbuffer->u.filesneededcfg.more = 0;
|
||||
|
||||
p = PutFileNeeded(firstfile);
|
||||
|
||||
HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
|
||||
}
|
||||
else // Shouldn't get this if you aren't the server...?
|
||||
Net_CloseConnection(node);
|
||||
}
|
||||
|
||||
static void PT_AskInfo(SINT8 node)
|
||||
{
|
||||
if (server && serverrunning)
|
||||
{
|
||||
SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time));
|
||||
SV_SendPlayerInfo(node); // Send extra info
|
||||
}
|
||||
Net_CloseConnection(node);
|
||||
}
|
||||
|
||||
static void PT_ClientCmd(SINT8 node, INT32 netconsole)
|
||||
{
|
||||
tic_t realend, realstart;
|
||||
|
|
|
@ -381,7 +381,6 @@ extern tic_t neededtic;
|
|||
|
||||
void Command_Ping_f(void);
|
||||
extern tic_t connectiontimeout;
|
||||
extern tic_t jointimeout;
|
||||
extern UINT16 pingmeasurecount;
|
||||
extern UINT32 realpingtable[MAXPLAYERS];
|
||||
extern UINT32 playerpingtable[MAXPLAYERS];
|
||||
|
@ -405,11 +404,13 @@ void SendKick(UINT8 playernum, UINT8 msg);
|
|||
void NetUpdate(void);
|
||||
|
||||
void GetPackets(void);
|
||||
void ResetNode(INT32 node);
|
||||
|
||||
void SV_StartSinglePlayerServer(void);
|
||||
void SV_SpawnServer(void);
|
||||
void SV_StopServer(void);
|
||||
void SV_ResetServer(void);
|
||||
void SV_SendSaveGame(INT32 node, boolean resending);
|
||||
void CL_AddSplitscreenPlayer(void);
|
||||
void CL_RemoveSplitscreenPlayer(void);
|
||||
void CL_Reset(void);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "../p_spec.h"
|
||||
#include "../m_cheat.h"
|
||||
#include "d_clisrv.h"
|
||||
#include "server_connection.h"
|
||||
#include "d_net.h"
|
||||
#include "../v_video.h"
|
||||
#include "../d_main.h"
|
||||
|
|
497
src/netcode/server_connection.c
Normal file
497
src/netcode/server_connection.c
Normal file
|
@ -0,0 +1,497 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2022 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file server_connection.c
|
||||
/// \brief Server-side part of connection handling
|
||||
|
||||
#include "server_connection.h"
|
||||
#include "i_net.h"
|
||||
#include "d_clisrv.h"
|
||||
#include "d_netfil.h"
|
||||
#include "mserv.h"
|
||||
#include "../byteptr.h"
|
||||
#include "../g_game.h"
|
||||
#include "../g_state.h"
|
||||
#include "../p_setup.h"
|
||||
#include "../p_tick.h"
|
||||
#include "../doomstat.h"
|
||||
|
||||
// Minimum timeout for sending the savegame
|
||||
// The actual timeout will be longer depending on the savegame length
|
||||
tic_t jointimeout = (10*TICRATE);
|
||||
|
||||
// Incremented by cv_joindelay when a client joins, decremented each tic.
|
||||
// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled.
|
||||
tic_t joindelay = 0;
|
||||
|
||||
// Minimum timeout for sending the savegame
|
||||
// The actual timeout will be longer depending on the savegame length
|
||||
char playeraddress[MAXPLAYERS][64];
|
||||
|
||||
UINT8 player_joining = false;
|
||||
|
||||
static INT32 FindRejoinerNum(SINT8 node)
|
||||
{
|
||||
char strippednodeaddress[64];
|
||||
const char *nodeaddress;
|
||||
char *port;
|
||||
INT32 i;
|
||||
|
||||
// Make sure there is no dead dress before proceeding to the stripping
|
||||
if (!I_GetNodeAddress)
|
||||
return -1;
|
||||
nodeaddress = I_GetNodeAddress(node);
|
||||
if (!nodeaddress)
|
||||
return -1;
|
||||
|
||||
// Strip the address of its port
|
||||
strcpy(strippednodeaddress, nodeaddress);
|
||||
port = strchr(strippednodeaddress, ':');
|
||||
if (port)
|
||||
*port = '\0';
|
||||
|
||||
// Check if any player matches the stripped address
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX
|
||||
&& !strcmp(playeraddress[i], strippednodeaddress))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static UINT8
|
||||
GetRefuseReason (INT32 node)
|
||||
{
|
||||
if (!node || FindRejoinerNum(node) != -1)
|
||||
return 0;
|
||||
else if (bannednode && bannednode[node])
|
||||
return REFUSE_BANNED;
|
||||
else if (!cv_allownewplayer.value)
|
||||
return REFUSE_JOINS_DISABLED;
|
||||
else if (D_NumPlayers() >= cv_maxplayers.value)
|
||||
return REFUSE_SLOTS_FULL;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SV_SendServerInfo(INT32 node, tic_t servertime)
|
||||
{
|
||||
UINT8 *p;
|
||||
|
||||
netbuffer->packettype = PT_SERVERINFO;
|
||||
netbuffer->u.serverinfo._255 = 255;
|
||||
netbuffer->u.serverinfo.packetversion = PACKETVERSION;
|
||||
netbuffer->u.serverinfo.version = VERSION;
|
||||
netbuffer->u.serverinfo.subversion = SUBVERSION;
|
||||
strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION,
|
||||
sizeof netbuffer->u.serverinfo.application);
|
||||
// return back the time value so client can compute their ping
|
||||
netbuffer->u.serverinfo.time = (tic_t)LONG(servertime);
|
||||
netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime);
|
||||
|
||||
netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
|
||||
netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
|
||||
|
||||
netbuffer->u.serverinfo.refusereason = GetRefuseReason(node);
|
||||
|
||||
strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
|
||||
sizeof netbuffer->u.serverinfo.gametypename);
|
||||
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
|
||||
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
|
||||
netbuffer->u.serverinfo.flags = (dedicated ? SV_DEDICATED : 0);
|
||||
strncpy(netbuffer->u.serverinfo.servername, cv_servername.string,
|
||||
MAXSERVERNAME);
|
||||
strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7);
|
||||
|
||||
M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16);
|
||||
|
||||
memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle);
|
||||
|
||||
if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl)
|
||||
{
|
||||
char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle;
|
||||
while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0')
|
||||
{
|
||||
if (!(*read & 0x80))
|
||||
{
|
||||
*writ = toupper(*read);
|
||||
writ++;
|
||||
}
|
||||
read++;
|
||||
}
|
||||
*writ = '\0';
|
||||
//strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33);
|
||||
}
|
||||
else
|
||||
strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32);
|
||||
|
||||
if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
|
||||
netbuffer->u.serverinfo.iszone = 1;
|
||||
else
|
||||
netbuffer->u.serverinfo.iszone = 0;
|
||||
|
||||
if (mapheaderinfo[gamemap-1])
|
||||
netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum;
|
||||
|
||||
p = PutFileNeeded(0);
|
||||
|
||||
HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
|
||||
}
|
||||
|
||||
static void SV_SendPlayerInfo(INT32 node)
|
||||
{
|
||||
UINT8 i;
|
||||
netbuffer->packettype = PT_PLAYERINFO;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
{
|
||||
netbuffer->u.playerinfo[i].num = 255; // This slot is empty.
|
||||
continue;
|
||||
}
|
||||
|
||||
netbuffer->u.playerinfo[i].num = i;
|
||||
strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1);
|
||||
netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0';
|
||||
|
||||
//fetch IP address
|
||||
//No, don't do that, you fuckface.
|
||||
memset(netbuffer->u.playerinfo[i].address, 0, 4);
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
if (!players[i].ctfteam)
|
||||
netbuffer->u.playerinfo[i].team = 255;
|
||||
else
|
||||
netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (players[i].spectator)
|
||||
netbuffer->u.playerinfo[i].team = 255;
|
||||
else
|
||||
netbuffer->u.playerinfo[i].team = 0;
|
||||
}
|
||||
|
||||
netbuffer->u.playerinfo[i].score = LONG(players[i].score);
|
||||
netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE));
|
||||
netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin
|
||||
#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself
|
||||
% 3
|
||||
#endif
|
||||
);
|
||||
|
||||
// Extra data
|
||||
netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor;
|
||||
|
||||
if (players[i].pflags & PF_TAGIT)
|
||||
netbuffer->u.playerinfo[i].data |= 0x20;
|
||||
|
||||
if (players[i].gotflag)
|
||||
netbuffer->u.playerinfo[i].data |= 0x40;
|
||||
|
||||
if (players[i].powers[pw_super])
|
||||
netbuffer->u.playerinfo[i].data |= 0x80;
|
||||
}
|
||||
|
||||
HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS);
|
||||
}
|
||||
|
||||
/** Sends a PT_SERVERCFG packet
|
||||
*
|
||||
* \param node The destination
|
||||
* \return True if the packet was successfully sent
|
||||
*
|
||||
*/
|
||||
static boolean SV_SendServerConfig(INT32 node)
|
||||
{
|
||||
boolean waspacketsent;
|
||||
|
||||
netbuffer->packettype = PT_SERVERCFG;
|
||||
|
||||
netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer;
|
||||
netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots);
|
||||
netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic);
|
||||
netbuffer->u.servercfg.clientnode = (UINT8)node;
|
||||
netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
|
||||
netbuffer->u.servercfg.gametype = (UINT8)gametype;
|
||||
netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
|
||||
|
||||
memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
|
||||
|
||||
{
|
||||
const size_t len = sizeof (serverconfig_pak);
|
||||
|
||||
#ifdef DEBUGFILE
|
||||
if (debugfile)
|
||||
{
|
||||
fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n",
|
||||
sizeu1(len), node);
|
||||
}
|
||||
#endif
|
||||
|
||||
waspacketsent = HSendPacket(node, true, 0, len);
|
||||
}
|
||||
|
||||
#ifdef DEBUGFILE
|
||||
if (debugfile)
|
||||
{
|
||||
if (waspacketsent)
|
||||
{
|
||||
fprintf(debugfile, "ServerConfig Packet was sent\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(debugfile, "ServerConfig Packet could not be sent right now\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return waspacketsent;
|
||||
}
|
||||
|
||||
// Adds a node to the game (player will follow at map change or at savegame....)
|
||||
static inline void SV_AddNode(INT32 node)
|
||||
{
|
||||
netnodes[node].tic = gametic;
|
||||
netnodes[node].supposedtic = gametic;
|
||||
// little hack because the server connects to itself and puts
|
||||
// nodeingame when connected not here
|
||||
if (node)
|
||||
netnodes[node].ingame = true;
|
||||
}
|
||||
|
||||
static void SV_AddPlayer(SINT8 node, const char *name)
|
||||
{
|
||||
INT32 n;
|
||||
UINT8 buf[2 + MAXPLAYERNAME];
|
||||
UINT8 *p;
|
||||
INT32 newplayernum;
|
||||
|
||||
newplayernum = FindRejoinerNum(node);
|
||||
if (newplayernum == -1)
|
||||
{
|
||||
// search for a free playernum
|
||||
// we can't use playeringame since it is not updated here
|
||||
for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++)
|
||||
{
|
||||
if (playeringame[newplayernum])
|
||||
continue;
|
||||
for (n = 0; n < MAXNETNODES; n++)
|
||||
if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum)
|
||||
break;
|
||||
if (n == MAXNETNODES)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// should never happen since we check the playernum
|
||||
// before accepting the join
|
||||
I_Assert(newplayernum < MAXPLAYERS);
|
||||
|
||||
playernode[newplayernum] = (UINT8)node;
|
||||
|
||||
p = buf + 2;
|
||||
buf[0] = (UINT8)node;
|
||||
buf[1] = newplayernum;
|
||||
if (netnodes[node].numplayers < 1)
|
||||
{
|
||||
netnodes[node].player = newplayernum;
|
||||
}
|
||||
else
|
||||
{
|
||||
netnodes[node].player2 = newplayernum;
|
||||
buf[1] |= 0x80;
|
||||
}
|
||||
WRITESTRINGN(p, name, MAXPLAYERNAME);
|
||||
netnodes[node].numplayers++;
|
||||
|
||||
SendNetXCmd(XD_ADDPLAYER, &buf, p - buf);
|
||||
|
||||
DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
|
||||
}
|
||||
|
||||
static void SV_SendRefuse(INT32 node, const char *reason)
|
||||
{
|
||||
strcpy(netbuffer->u.serverrefuse.reason, reason);
|
||||
|
||||
netbuffer->packettype = PT_SERVERREFUSE;
|
||||
HSendPacket(node, true, 0, strlen(netbuffer->u.serverrefuse.reason) + 1);
|
||||
Net_CloseConnection(node);
|
||||
}
|
||||
|
||||
static const char *
|
||||
GetRefuseMessage (SINT8 node, INT32 rejoinernum)
|
||||
{
|
||||
clientconfig_pak *cc = &netbuffer->u.clientcfg;
|
||||
|
||||
boolean rejoining = (rejoinernum != -1);
|
||||
|
||||
if (!node)/* server connecting to itself */
|
||||
return NULL;
|
||||
|
||||
if (
|
||||
cc->modversion != MODVERSION ||
|
||||
strncmp(cc->application, SRB2APPLICATION,
|
||||
sizeof cc->application)
|
||||
){
|
||||
return/* this is probably client's fault */
|
||||
"Incompatible.";
|
||||
}
|
||||
else if (bannednode && bannednode[node])
|
||||
{
|
||||
return
|
||||
"You have been banned\n"
|
||||
"from the server.";
|
||||
}
|
||||
else if (cc->localplayers != 1)
|
||||
{
|
||||
return
|
||||
"Wrong player count.";
|
||||
}
|
||||
|
||||
if (!rejoining)
|
||||
{
|
||||
if (!cv_allownewplayer.value)
|
||||
{
|
||||
return
|
||||
"The server is not accepting\n"
|
||||
"joins for the moment.";
|
||||
}
|
||||
else if (D_NumPlayers() >= cv_maxplayers.value)
|
||||
{
|
||||
return va(
|
||||
"Maximum players reached: %d",
|
||||
cv_maxplayers.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (luafiletransfers)
|
||||
{
|
||||
return
|
||||
"The serveris broadcasting a file\n"
|
||||
"requested by a Lua script.\n"
|
||||
"Please wait a bit and then\n"
|
||||
"try rejoining.";
|
||||
}
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
const tic_t th = 2 * cv_joindelay.value * TICRATE;
|
||||
|
||||
if (joindelay > th)
|
||||
{
|
||||
return va(
|
||||
"Too many people are connecting.\n"
|
||||
"Please wait %d seconds and then\n"
|
||||
"try rejoining.",
|
||||
(joindelay - th) / TICRATE);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Called when a PT_CLIENTJOIN packet is received
|
||||
*
|
||||
* \param node The packet sender
|
||||
*
|
||||
*/
|
||||
void HandleConnect(SINT8 node)
|
||||
{
|
||||
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
|
||||
INT32 numplayers = netbuffer->u.clientcfg.localplayers;
|
||||
INT32 rejoinernum;
|
||||
INT32 i;
|
||||
|
||||
// Ignore duplicate packets
|
||||
if (netnodes[node].ingame)
|
||||
return;
|
||||
|
||||
rejoinernum = FindRejoinerNum(node);
|
||||
|
||||
const char *refuse = GetRefuseMessage(node, rejoinernum);
|
||||
if (refuse)
|
||||
{
|
||||
SV_SendRefuse(node, refuse);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < numplayers; i++)
|
||||
{
|
||||
strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1);
|
||||
if (!EnsurePlayerNameIsGood(names[i], rejoinernum))
|
||||
{
|
||||
SV_SendRefuse(node, "Bad player name");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SV_AddNode(node);
|
||||
|
||||
if (!SV_SendServerConfig(node))
|
||||
{
|
||||
/// \note Shouldn't SV_SendRefuse be called before ResetNode?
|
||||
ResetNode(node);
|
||||
SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
|
||||
/// \todo fix this !!!
|
||||
return; // restart the while
|
||||
}
|
||||
DEBFILE("new node joined\n");
|
||||
|
||||
if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)
|
||||
{
|
||||
SV_SendSaveGame(node, false); // send a complete game state
|
||||
DEBFILE("send savegame\n");
|
||||
}
|
||||
|
||||
// Splitscreen can allow 2 players in one node
|
||||
for (i = 0; i < numplayers; i++)
|
||||
SV_AddPlayer(node, names[i]);
|
||||
|
||||
joindelay += cv_joindelay.value * TICRATE;
|
||||
player_joining = true;
|
||||
}
|
||||
|
||||
void PT_AskInfoViaMS(SINT8 node)
|
||||
{
|
||||
Net_CloseConnection(node);
|
||||
}
|
||||
|
||||
void PT_TellFilesNeeded(SINT8 node)
|
||||
{
|
||||
if (server && serverrunning)
|
||||
{
|
||||
UINT8 *p;
|
||||
INT32 firstfile = netbuffer->u.filesneedednum;
|
||||
|
||||
netbuffer->packettype = PT_MOREFILESNEEDED;
|
||||
netbuffer->u.filesneededcfg.first = firstfile;
|
||||
netbuffer->u.filesneededcfg.more = 0;
|
||||
|
||||
p = PutFileNeeded(firstfile);
|
||||
|
||||
HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
|
||||
}
|
||||
else // Shouldn't get this if you aren't the server...?
|
||||
Net_CloseConnection(node);
|
||||
}
|
||||
|
||||
void PT_AskInfo(SINT8 node)
|
||||
{
|
||||
if (server && serverrunning)
|
||||
{
|
||||
SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time));
|
||||
SV_SendPlayerInfo(node); // Send extra info
|
||||
}
|
||||
Net_CloseConnection(node);
|
||||
}
|
29
src/netcode/server_connection.h
Normal file
29
src/netcode/server_connection.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2022 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file server_connection.h
|
||||
/// \brief Server-side part of connection handling
|
||||
|
||||
#ifndef __D_SERVER_CONNECTION__
|
||||
#define __D_SERVER_CONNECTION__
|
||||
|
||||
#include "../doomdef.h"
|
||||
#include "../doomtype.h"
|
||||
|
||||
void HandleConnect(SINT8 node);
|
||||
void PT_AskInfoViaMS(SINT8 node);
|
||||
void PT_TellFilesNeeded(SINT8 node);
|
||||
void PT_AskInfo(SINT8 node);
|
||||
|
||||
extern tic_t jointimeout;
|
||||
extern tic_t joindelay;
|
||||
extern char playeraddress[MAXPLAYERS][64];
|
||||
extern UINT8 player_joining;
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue