Move gamestate handling to a new file

This commit is contained in:
LJ Sonic 2023-01-07 13:01:48 +01:00
parent f1ad1bf13e
commit d1c5f7adcc
9 changed files with 378 additions and 312 deletions

View file

@ -21,6 +21,7 @@
#include "netcode/d_clisrv.h"
#include "netcode/net_command.h"
#include "netcode/gamestate.h"
#include "g_game.h"
#include "g_input.h"

View file

@ -3,6 +3,7 @@ server_connection.c
client_connection.c
tic_command.c
net_command.c
gamestate.c
d_net.c
d_netcmd.c
d_netfil.c

View file

@ -53,6 +53,7 @@
#include "client_connection.h"
#include "tic_command.h"
#include "net_command.h"
#include "gamestate.h"
#include "protocol.h"
//
@ -93,18 +94,12 @@ tic_t maketic;
INT16 consistancy[BACKUPTICS];
UINT8 hu_redownloadinggamestate = 0;
// true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks
boolean hu_stopped = false;
UINT8 adminpassmd5[16];
boolean adminpasswordset = false;
// Client specific
// here it is for the secondary local player (splitscreen)
static boolean cl_redownloadinggamestate = false;
tic_t neededtic;
SINT8 servernode = 0; // the number of the server node
@ -122,233 +117,6 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c
// of 512 bytes is like 0.1)
UINT16 software_MAXPACKETLENGTH;
#define SAVEGAMESIZE (768*1024)
boolean SV_ResendingSavegameToAnyone(void)
{
INT32 i;
for (i = 0; i < MAXNETNODES; i++)
if (netnodes[i].resendingsavegame)
return true;
return false;
}
void SV_SendSaveGame(INT32 node, boolean resending)
{
size_t length, compressedlen;
UINT8 *savebuffer;
UINT8 *compressedsave;
UINT8 *buffertosend;
// first save it in a malloced buffer
savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
if (!savebuffer)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
// Leave room for the uncompressed length.
save_p = savebuffer + sizeof(UINT32);
P_SaveNetGame(resending);
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
// Allocate space for compressed save: one byte fewer than for the
// uncompressed data to ensure that the compression is worthwhile.
compressedsave = malloc(length - 1);
if (!compressedsave)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
// Attempt to compress it.
if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1)))
{
// Compressing succeeded; send compressed data
free(savebuffer);
// State that we're compressed.
buffertosend = compressedsave;
WRITEUINT32(compressedsave, length - sizeof(UINT32));
length = compressedlen + sizeof(UINT32);
}
else
{
// Compression failed to make it smaller; send original
free(compressedsave);
// State that we're not compressed
buffertosend = savebuffer;
WRITEUINT32(savebuffer, 0);
}
AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0);
save_p = NULL;
// Remember when we started sending the savegame so we can handle timeouts
netnodes[node].sendingsavegame = true;
netnodes[node].freezetimeout = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte
}
#ifdef DUMPCONSISTENCY
#define TMPSAVENAME "badmath.sav"
static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
static void SV_SavedGame(void)
{
size_t length;
UINT8 *savebuffer;
char tmpsave[256];
if (!cv_dumpconsistency.value)
return;
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
// first save it in a malloced buffer
save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
if (!save_p)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
P_SaveNetGame(false);
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
// then save it!
if (!FIL_WriteFile(tmpsave, savebuffer, length))
CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave);
free(savebuffer);
save_p = NULL;
}
#undef TMPSAVENAME
#endif
#define TMPSAVENAME "$$$.sav"
void CL_LoadReceivedSavegame(boolean reloading)
{
UINT8 *savebuffer = NULL;
size_t length, decompressedlen;
char tmpsave[256];
FreeFileNeeded();
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
length = FIL_ReadFile(tmpsave, &savebuffer);
CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length));
if (!length)
{
I_Error("Can't read savegame sent");
return;
}
save_p = savebuffer;
// Decompress saved game if necessary.
decompressedlen = READUINT32(save_p);
if(decompressedlen > 0)
{
UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL);
lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen);
Z_Free(savebuffer);
save_p = savebuffer = decompressedbuffer;
}
paused = false;
demoplayback = false;
titlemapinaction = TITLEMAP_OFF;
titledemo = false;
automapactive = false;
P_StopRumble(NULL);
// load a base level
if (P_LoadNetGame(reloading))
{
const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum;
CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap));
if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, ""))
{
CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl);
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
CONS_Printf(M_GetText(" Zone"));
if (actnum > 0)
CONS_Printf(" %2d", actnum);
}
CONS_Printf("\"\n");
}
// done
Z_Free(savebuffer);
save_p = NULL;
if (unlink(tmpsave) == -1)
CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
consistancy[gametic%BACKUPTICS] = Consistancy();
CON_ToggleOff();
// Tell the server we have received and reloaded the gamestate
// so they know they can resume the game
netbuffer->packettype = PT_RECEIVEDGAMESTATE;
HSendPacket(servernode, true, 0, 0);
}
static void CL_ReloadReceivedSavegame(void)
{
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
LUA_InvalidatePlayer(&players[i]);
sprintf(player_names[i], "Player %d", i + 1);
}
CL_LoadReceivedSavegame(true);
if (neededtic < gametic)
neededtic = gametic;
maketic = neededtic;
ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn;
P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16));
if (splitscreen)
{
ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn;
P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16));
}
camera.subsector = R_PointInSubsector(camera.x, camera.y);
camera2.subsector = R_PointInSubsector(camera2.x, camera2.y);
cl_redownloadinggamestate = false;
CONS_Printf(M_GetText("Game state reloaded\n"));
}
typedef struct banreason_s
{
char *reason;
@ -1006,34 +774,6 @@ static void Command_Kick(void)
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
}
static void Command_ResendGamestate(void)
{
SINT8 playernum;
if (COM_Argc() == 1)
{
CONS_Printf(M_GetText("resendgamestate <playername/playernum>: resend the game state to a player\n"));
return;
}
else if (client)
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
}
playernum = nametonum(COM_Argv(1));
if (playernum == -1 || playernum == 0)
return;
// Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on
netbuffer->packettype = PT_WILLRESENDGAMESTATE;
if (!HSendPacket(playernode[playernum], true, 0, 0))
{
CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n"));
return;
}
}
static void Got_KickCmd(UINT8 **p, INT32 playernum)
{
INT32 pnum, msg;
@ -1736,17 +1476,6 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole)
netnodes[node].ingame = false;
}
static void PT_CanReceiveGamestate(SINT8 node)
{
if (client || netnodes[node].sendingsavegame)
return;
CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[netnodes[node].player]);
SV_SendSaveGame(node, true); // Resend a complete game state
netnodes[node].resendingsavegame = true;
}
static void PT_AskLuaFile(SINT8 node)
{
if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED)
@ -1759,13 +1488,6 @@ static void PT_HasLuaFile(SINT8 node)
SV_HandleLuaFileSent(node);
}
static void PT_ReceivedGamestate(SINT8 node)
{
netnodes[node].sendingsavegame = false;
netnodes[node].resendingsavegame = false;
netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE;
}
static void PT_Ping(SINT8 node, INT32 netconsole)
{
// Only accept PT_PING from the server.
@ -1789,34 +1511,6 @@ static void PT_Ping(SINT8 node, INT32 netconsole)
}
}
static void PT_WillResendGamestate(SINT8 node)
{
(void)node;
char tmpsave[256];
if (server || cl_redownloadinggamestate)
return;
// Send back a PT_CANRECEIVEGAMESTATE packet to the server
// so they know they can start sending the game state
netbuffer->packettype = PT_CANRECEIVEGAMESTATE;
if (!HSendPacket(servernode, true, 0, 0))
return;
CONS_Printf(M_GetText("Reloading game state...\n"));
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
// Don't get a corrupt savegame error because tmpsave already exists
if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
I_Error("Can't delete %s\n", tmpsave);
CL_PrepareDownloadSaveGame(tmpsave);
cl_redownloadinggamestate = true;
}
static void PT_SendingLuaFile(SINT8 node)
{
(void)node;

View file

@ -89,19 +89,16 @@ void NetUpdate(void);
void GetPackets(void);
void ResetNode(INT32 node);
INT16 Consistancy(void);
boolean SV_ResendingSavegameToAnyone(void);
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);
void CL_ClearPlayer(INT32 playernum);
void CL_RemovePlayer(INT32 playernum, kickreason_t reason);
void CL_LoadReceivedSavegame(boolean reloading);
// Is there a game running
boolean Playing(void);
@ -130,8 +127,6 @@ tic_t GetLag(INT32 node);
void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest);
extern UINT8 hu_redownloadinggamestate;
extern UINT8 adminpassmd5[16];
extern boolean adminpasswordset;

341
src/netcode/gamestate.c Normal file
View file

@ -0,0 +1,341 @@
// 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 gamestate.c
/// \brief Gamestate (re)sending
#include "d_clisrv.h"
#include "d_netfil.h"
#include "gamestate.h"
#include "i_net.h"
#include "protocol.h"
#include "server_connection.h"
#include "../am_map.h"
#include "../byteptr.h"
#include "../console.h"
#include "../d_main.h"
#include "../doomstat.h"
#include "../doomtype.h"
#include "../f_finale.h"
#include "../g_demo.h"
#include "../g_game.h"
#include "../i_time.h"
#include "../lua_script.h"
#include "../lzf.h"
#include "../m_misc.h"
#include "../p_haptic.h"
#include "../p_local.h"
#include "../p_saveg.h"
#include "../r_main.h"
#include "../tables.h"
#include "../z_zone.h"
#define SAVEGAMESIZE (768*1024)
UINT8 hu_redownloadinggamestate = 0;
boolean cl_redownloadinggamestate = false;
boolean SV_ResendingSavegameToAnyone(void)
{
INT32 i;
for (i = 0; i < MAXNETNODES; i++)
if (netnodes[i].resendingsavegame)
return true;
return false;
}
void SV_SendSaveGame(INT32 node, boolean resending)
{
size_t length, compressedlen;
UINT8 *savebuffer;
UINT8 *compressedsave;
UINT8 *buffertosend;
// first save it in a malloced buffer
savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
if (!savebuffer)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
// Leave room for the uncompressed length.
save_p = savebuffer + sizeof(UINT32);
P_SaveNetGame(resending);
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
// Allocate space for compressed save: one byte fewer than for the
// uncompressed data to ensure that the compression is worthwhile.
compressedsave = malloc(length - 1);
if (!compressedsave)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
// Attempt to compress it.
if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1)))
{
// Compressing succeeded; send compressed data
free(savebuffer);
// State that we're compressed.
buffertosend = compressedsave;
WRITEUINT32(compressedsave, length - sizeof(UINT32));
length = compressedlen + sizeof(UINT32);
}
else
{
// Compression failed to make it smaller; send original
free(compressedsave);
// State that we're not compressed
buffertosend = savebuffer;
WRITEUINT32(savebuffer, 0);
}
AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0);
save_p = NULL;
// Remember when we started sending the savegame so we can handle timeouts
netnodes[node].sendingsavegame = true;
netnodes[node].freezetimeout = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte
}
#ifdef DUMPCONSISTENCY
#define TMPSAVENAME "badmath.sav"
static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
void SV_SavedGame(void)
{
size_t length;
UINT8 *savebuffer;
char tmpsave[256];
if (!cv_dumpconsistency.value)
return;
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
// first save it in a malloced buffer
save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
if (!save_p)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
P_SaveNetGame(false);
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
// then save it!
if (!FIL_WriteFile(tmpsave, savebuffer, length))
CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave);
free(savebuffer);
save_p = NULL;
}
#undef TMPSAVENAME
#endif
#define TMPSAVENAME "$$$.sav"
void CL_LoadReceivedSavegame(boolean reloading)
{
UINT8 *savebuffer = NULL;
size_t length, decompressedlen;
char tmpsave[256];
FreeFileNeeded();
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
length = FIL_ReadFile(tmpsave, &savebuffer);
CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length));
if (!length)
{
I_Error("Can't read savegame sent");
return;
}
save_p = savebuffer;
// Decompress saved game if necessary.
decompressedlen = READUINT32(save_p);
if(decompressedlen > 0)
{
UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL);
lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen);
Z_Free(savebuffer);
save_p = savebuffer = decompressedbuffer;
}
paused = false;
demoplayback = false;
titlemapinaction = TITLEMAP_OFF;
titledemo = false;
automapactive = false;
P_StopRumble(NULL);
// load a base level
if (P_LoadNetGame(reloading))
{
const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum;
CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap));
if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, ""))
{
CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl);
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
CONS_Printf(M_GetText(" Zone"));
if (actnum > 0)
CONS_Printf(" %2d", actnum);
}
CONS_Printf("\"\n");
}
// done
Z_Free(savebuffer);
save_p = NULL;
if (unlink(tmpsave) == -1)
CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
consistancy[gametic%BACKUPTICS] = Consistancy();
CON_ToggleOff();
// Tell the server we have received and reloaded the gamestate
// so they know they can resume the game
netbuffer->packettype = PT_RECEIVEDGAMESTATE;
HSendPacket(servernode, true, 0, 0);
}
void CL_ReloadReceivedSavegame(void)
{
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
LUA_InvalidatePlayer(&players[i]);
sprintf(player_names[i], "Player %d", i + 1);
}
CL_LoadReceivedSavegame(true);
if (neededtic < gametic)
neededtic = gametic;
maketic = neededtic;
ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn;
P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16));
if (splitscreen)
{
ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn;
P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16));
}
camera.subsector = R_PointInSubsector(camera.x, camera.y);
camera2.subsector = R_PointInSubsector(camera2.x, camera2.y);
cl_redownloadinggamestate = false;
CONS_Printf(M_GetText("Game state reloaded\n"));
}
void Command_ResendGamestate(void)
{
SINT8 playernum;
if (COM_Argc() == 1)
{
CONS_Printf(M_GetText("resendgamestate <playername/playernum>: resend the game state to a player\n"));
return;
}
else if (client)
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
}
playernum = nametonum(COM_Argv(1));
if (playernum == -1 || playernum == 0)
return;
// Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on
netbuffer->packettype = PT_WILLRESENDGAMESTATE;
if (!HSendPacket(playernode[playernum], true, 0, 0))
{
CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n"));
return;
}
}
void PT_CanReceiveGamestate(SINT8 node)
{
if (client || netnodes[node].sendingsavegame)
return;
CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[netnodes[node].player]);
SV_SendSaveGame(node, true); // Resend a complete game state
netnodes[node].resendingsavegame = true;
}
void PT_ReceivedGamestate(SINT8 node)
{
netnodes[node].sendingsavegame = false;
netnodes[node].resendingsavegame = false;
netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE;
}
void PT_WillResendGamestate(SINT8 node)
{
(void)node;
char tmpsave[256];
if (server || cl_redownloadinggamestate)
return;
// Send back a PT_CANRECEIVEGAMESTATE packet to the server
// so they know they can start sending the game state
netbuffer->packettype = PT_CANRECEIVEGAMESTATE;
if (!HSendPacket(servernode, true, 0, 0))
return;
CONS_Printf(M_GetText("Reloading game state...\n"));
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
// Don't get a corrupt savegame error because tmpsave already exists
if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
I_Error("Can't delete %s\n", tmpsave);
CL_PrepareDownloadSaveGame(tmpsave);
cl_redownloadinggamestate = true;
}

31
src/netcode/gamestate.h Normal file
View file

@ -0,0 +1,31 @@
// 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 gamestate.h
/// \brief Gamestate (re)sending
#ifndef __GAMESTATE__
#define __GAMESTATE__
#include "../doomtype.h"
extern UINT8 hu_redownloadinggamestate;
extern boolean cl_redownloadinggamestate;
boolean SV_ResendingSavegameToAnyone(void);
void SV_SendSaveGame(INT32 node, boolean resending);
void SV_SavedGame(void);
void CL_LoadReceivedSavegame(boolean reloading);
void CL_ReloadReceivedSavegame(void);
void Command_ResendGamestate(void);
void PT_CanReceiveGamestate(SINT8 node);
void PT_ReceivedGamestate(SINT8 node);
void PT_WillResendGamestate(SINT8 node);
#endif

View file

@ -12,6 +12,7 @@
#include "net_command.h"
#include "tic_command.h"
#include "gamestate.h"
#include "d_clisrv.h"
#include "i_net.h"
#include "../g_game.h"

View file

@ -16,6 +16,7 @@
#include "d_netfil.h"
#include "mserv.h"
#include "net_command.h"
#include "gamestate.h"
#include "../byteptr.h"
#include "../g_game.h"
#include "../g_state.h"

View file

@ -14,6 +14,7 @@
#include "d_clisrv.h"
#include "net_command.h"
#include "client_connection.h"
#include "gamestate.h"
#include "i_net.h"
#include "../d_main.h"
#include "../g_game.h"