diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile index c11b3d6c1..4177166a4 100644 --- a/src/netcode/Sourcefile +++ b/src/netcode/Sourcefile @@ -1,4 +1,5 @@ d_clisrv.c +server_connection.c client_connection.c d_net.c d_netcmd.c diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index f7a9260e1..c1264768e 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.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; diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index 3b0690a79..b8da645ba 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -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); diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c index ed310805d..fe4cf22bd 100644 --- a/src/netcode/d_netcmd.c +++ b/src/netcode/d_netcmd.c @@ -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" diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c new file mode 100644 index 000000000..9e397b0f3 --- /dev/null +++ b/src/netcode/server_connection.c @@ -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); +} diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h new file mode 100644 index 000000000..4204db2e6 --- /dev/null +++ b/src/netcode/server_connection.h @@ -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