From b461cb919bf37ae5a33b57c62d264538576b01a8 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 1 Jan 2023 11:45:09 +0100
Subject: [PATCH] Move server-side connection handling to a new file

---
 src/netcode/Sourcefile          |   1 +
 src/netcode/d_clisrv.c          | 475 +-----------------------------
 src/netcode/d_clisrv.h          |   3 +-
 src/netcode/d_netcmd.c          |   1 +
 src/netcode/server_connection.c | 497 ++++++++++++++++++++++++++++++++
 src/netcode/server_connection.h |  29 ++
 6 files changed, 533 insertions(+), 473 deletions(-)
 create mode 100644 src/netcode/server_connection.c
 create mode 100644 src/netcode/server_connection.h

diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile
index c11b3d6c1f..4177166a45 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 f7a9260e1c..c1264768e0 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 3b0690a799..b8da645baa 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 ed310805dc..fe4cf22bd1 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 0000000000..9e397b0f31
--- /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 0000000000..4204db2e6c
--- /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
-- 
GitLab