Huge Netcode/Resynchcode changes #134

Merged
alufolie91 merged 89 commits from absolutehell into Saturn-Next 2024-09-15 11:45:35 +02:00
17 changed files with 596 additions and 142 deletions

No files matched your search

+24
View File
@@ -207,6 +207,18 @@ USE_FBO_OGL=1
endif
endif
ifndef NONET
ifndef NOSATURNSYNCH
DOSATURNSYNCH=1
ifndef NOSATURNJOIN
DOSATURNJOIN=1
endif
#ifndef NOSATURNPAK
#DOSATURNPAK=1
#endif
endif
endif
ifdef MINGW
include win32/Makefile.cfg
endif #ifdef MINGW
@@ -386,6 +398,18 @@ OPTS+=-DUSE_FBO_OGL
endif
endif
ifndef NONET
ifndef NOSATURNSYNCH
OPTS+=-DDOSATURNSYNCH
ifndef NOSATURNJOIN
OPTS+=-DDOSATURNJOIN
endif
#ifndef NOSATURNPAK
#OPTS+=-DDOSATURNPAK
#endif
endif
endif
ifdef HAVE_MINIUPNPC
ifdef NONET
HAVE_MINIUPNPC=''
+339 -56
View File
@@ -10,6 +10,7 @@
/// \file d_clisrv.c
/// \brief SRB2 Network game communication and protocol, all OS independent parts.
#include "doomdef.h"
#include <time.h>
#ifdef __GNUC__
#include <unistd.h> //for unlink
@@ -92,6 +93,10 @@ UINT8 playernode[MAXPLAYERS];
// The actual timeout will be longer depending on the savegame length
tic_t jointimeout = (3*TICRATE);
static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame?
#ifdef SATURNSYNCH
static boolean resendingsavegame[MAXNETNODES]; // Are we resending the savegame?
static tic_t savegameresendcooldown[MAXNETNODES]; // How long before we can resend again?
#endif
static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout?
UINT16 pingmeasurecount = 1;
@@ -124,6 +129,12 @@ static UINT8 resynch_local_inprogress = false; // WE are desynched and getting p
static UINT8 player_joining = false;
UINT8 hu_resynching = 0;
#ifdef SATURNSYNCH
UINT8 hu_redownloadinggamestate = 0;
static boolean can_receive_gamestate[MAXNETNODES];
static UINT8 gamestate_resend_counter[MAXNETNODES];
#endif
// kart, true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks
UINT8 hu_stopped = 0;
@@ -136,6 +147,18 @@ static boolean cl_packetmissed;
// here it is for the secondary local player (splitscreen)
static UINT8 mynode; // my address pointofview server
#ifdef SATURNSYNCH
static boolean cl_redownloadinggamestate = false;
#endif
#ifdef SATURNPAK
boolean is_client_saturn[MAXNETNODES];
#endif
#ifdef SATURNJOIN
#define ISSATURN 69
#endif
static UINT8 localtextcmd[MAXTEXTCMD];
static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
static UINT8 localtextcmd3[MAXTEXTCMD]; // splitscreen == 2
@@ -172,7 +195,6 @@ typedef struct textcmdtic_s
ticcmd_t netcmds[TICQUEUE][MAXPLAYERS];
static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL};
consvar_t cv_showjoinaddress = {"showjoinaddress", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_shownodeip = {"showipinnodelist", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -1430,6 +1452,9 @@ static boolean CL_SendJoin(void)
netbuffer->u.clientcfg.subversion = SUBVERSION;
strncpy(netbuffer->u.clientcfg.application, SRB2APPLICATION,
sizeof netbuffer->u.clientcfg.application);
#ifdef SATURNJOIN
netbuffer->u.clientcfg.issaturn = ISSATURN;
#endif
return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak));
}
@@ -1727,23 +1752,23 @@ static boolean SV_SendServerConfig(INT32 node)
netbuffer->u.servercfg.discordinvites = (boolean)cv_discordinvites.value;
memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
op = p = netbuffer->u.servercfg.varlengthinputs;
CV_SavePlayerNames(&p);
CV_SaveNetVars(&p, false);
{
const size_t len = sizeof (serverconfig_pak) + (size_t)(p - op);
const size_t len = sizeof (serverconfig_pak) + (size_t)(p - op);
#ifdef DEBUGFILE
if (debugfile)
{
fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n",
sizeu1(len), node);
}
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);
}
waspacketsent = HSendPacket(node, true, 0, len);
#ifdef DEBUGFILE
if (debugfile)
@@ -1765,7 +1790,7 @@ static boolean SV_SendServerConfig(INT32 node)
#ifdef JOININGAME
#define SAVEGAMESIZE (768*1024)
static void SV_SendSaveGame(INT32 node)
static void SV_SendSaveGame(INT32 node, boolean resending)
{
size_t length, compressedlen;
UINT8 *savebuffer;
@@ -1783,7 +1808,7 @@ static void SV_SendSaveGame(INT32 node)
// Leave room for the uncompressed length.
save_p = savebuffer + sizeof(UINT32);
P_SaveNetGame();
P_SaveNetGame(resending);
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
@@ -1856,7 +1881,7 @@ static void SV_SavedGame(void)
return;
}
P_SaveNetGame();
P_SaveNetGame(false);
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
@@ -1878,8 +1903,7 @@ static void SV_SavedGame(void)
#endif
#define TMPSAVENAME "$$$.sav"
static void CL_LoadReceivedSavegame(void)
static void CL_LoadReceivedSavegame(boolean reloading)
{
UINT8 *savebuffer = NULL;
size_t length, decompressedlen;
@@ -1914,29 +1938,23 @@ static void CL_LoadReceivedSavegame(void)
automapactive = false;
// load a base level
if (P_LoadNetGame())
if (P_LoadNetGame(reloading))
{
CON_LogMessage(va(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)));
if (strlen(mapheaderinfo[gamemap-1]->lvlttl) > 0)
if (!reloading)
{
CON_LogMessage(va(": %s", mapheaderinfo[gamemap-1]->lvlttl));
if (strlen(mapheaderinfo[gamemap-1]->zonttl) > 0)
CON_LogMessage(va(" %s", mapheaderinfo[gamemap-1]->zonttl));
else if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
CON_LogMessage(M_GetText(" Zone"));
if (strlen(mapheaderinfo[gamemap-1]->actnum) > 0)
CON_LogMessage(va(" %s", mapheaderinfo[gamemap-1]->actnum));
CON_LogMessage(va(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)));
if (strlen(mapheaderinfo[gamemap-1]->lvlttl) > 0)
{
CON_LogMessage(va(": %s", mapheaderinfo[gamemap-1]->lvlttl));
if (strlen(mapheaderinfo[gamemap-1]->zonttl) > 0)
CON_LogMessage(va(" %s", mapheaderinfo[gamemap-1]->zonttl));
else if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
CON_LogMessage(M_GetText(" Zone"));
if (strlen(mapheaderinfo[gamemap-1]->actnum) > 0)
CON_LogMessage(va(" %s", mapheaderinfo[gamemap-1]->actnum));
}
CON_LogMessage("\"\n");
}
CON_LogMessage("\"\n");
}
else
{
CONS_Alert(CONS_ERROR, M_GetText("Can't load the level!\n"));
Z_Free(savebuffer);
save_p = NULL;
if (unlink(tmpsave) == -1)
CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
return;
}
// done
@@ -1946,20 +1964,61 @@ static void CL_LoadReceivedSavegame(void)
CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
consistancy[gametic%TICQUEUE] = Consistancy();
CON_ToggleOff();
#ifdef SATURNSYNCH
// Tell the server we have received and reloaded the gamestate
// so they know they can resume the game
if (reloading)
{
netbuffer->packettype = PT_RECEIVEDGAMESTATE;
HSendPacket(servernode, true, 0, 0);
}
#endif
}
#ifdef SATURNSYNCH
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;
// set camera angle to somewhat match where your player is turning
for (i = 0; i <= splitscreen; i++)
{
localangle[i] = players[displayplayers[i]].cmd.angleturn<<16;
}
camera->subsector = R_PointInSubsector(camera->x, camera->y);
cl_redownloadinggamestate = false;
CONS_Printf(M_GetText("Game state reloaded\n"));
}
#endif
#endif
#ifndef NONET
static void SendAskInfo(INT32 node)
{
tic_t asktime;
#ifdef HOLEPUNCH
#ifdef HOLEPUNCH
if (node != 0 && node != BROADCASTADDR &&
cv_rendezvousserver.string[0])
{
I_NetRequestHolePunch(node);
}
#endif
#endif
asktime = I_GetTime();
@@ -2559,7 +2618,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
if (fileneeded[0].status == FS_FOUND)
{
// Gamestate is now handled within CL_LoadReceivedSavegame()
CL_LoadReceivedSavegame();
CL_LoadReceivedSavegame(false);
cl_mode = CL_CONNECTED;
break;
} // don't break case continue to CL_CONNECTED
@@ -2584,7 +2643,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
// Call it only once by tic
if (*oldtic != I_GetTime())
{
INT32 key;
I_OsPolling();
@@ -3162,10 +3220,15 @@ void CL_RemovePlayer(INT32 playernum, INT32 reason)
playerpernode[node]--;
if (playerpernode[node] <= 0)
{
// If a resynch was in progress, well, it no longer needs to be.
SV_InitResynchVars(node);
SV_InitResynchVars(node); // If a resynch was in progress, well, it no longer needs to be.
nodeingame[node] = false;
#ifdef SATURNPAK
is_client_saturn[node] = false;
#endif
#ifdef SATURNSYNCH
can_receive_gamestate[node] = false;
#endif
Net_CloseConnection(node);
ResetNode(node);
}
@@ -3271,6 +3334,9 @@ void CL_Reset(void)
if (servernode > 0 && servernode < MAXNETNODES)
{
nodeingame[(UINT8)servernode] = false;
#ifdef SATURNPAK
is_client_saturn[(UINT8)servernode] = false;
#endif
Net_CloseConnection(servernode);
}
D_CloseConnection(); // netgame = false
@@ -3966,6 +4032,36 @@ static void Command_list_http_logins (void)
}
#endif/*HAVE_CURL*/
#ifdef SATURNSYNCH
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;
}
}
#endif
static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}};
consvar_t cv_netticbuffer = {"netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -3975,6 +4071,10 @@ consvar_t cv_joinrefusemessage = {"joinrefusemessage", "The server is not accept
consvar_t cv_allownewplayer = {"allowjoin", "On", CV_SAVE|CV_CALL, CV_OnOff, Joinable_OnChange, 0, NULL, NULL, 0, 0, NULL};
#ifdef SATURNJOIN
consvar_t cv_allownewsaturnplayer = {"allowsaturnjoin", "On", CV_HIDEN, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
#ifdef VANILLAJOINNEXTROUND
consvar_t cv_joinnextround = {"joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
#endif
@@ -3987,7 +4087,16 @@ static CV_PossibleValue_t discordinvites_cons_t[] = {{0, "Admins Only"}, {1, "Ev
consvar_t cv_discordinvites = {"discordinvites", "Everyone", CV_SAVE|CV_CALL, discordinvites_cons_t, Joinable_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t resynchattempts_cons_t[] = {{0, "MIN"}, {20, "MAX"}, {0, NULL}};
consvar_t cv_resynchattempts = {"resynchattempts", "2", CV_SAVE, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
consvar_t cv_resynchattempts = {"resynchattempts", "2", CV_SAVE, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#ifdef SATURNSYNCH
static CV_PossibleValue_t gamestateattempts_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}};
consvar_t cv_gamestateattempts = {"gamestateresendattempts", "10", CV_SAVE, gamestateattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t resynchcooldown_cons_t[] = {{0, "MIN"}, {20, "MAX"}, {0, NULL}};
consvar_t cv_resynchcooldown = {"gamestatecooldown", "5", CV_SAVE, resynchcooldown_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
#endif
consvar_t cv_blamecfail = {"blamecfail", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
// max file size to send to a player (in kilobytes)
@@ -4014,6 +4123,11 @@ static void Joinable_OnChange(void)
if (!server)
return;
#ifdef SATURNJOIN
// disabling joins should also disable saturn joins unless its called with CV_StealthSet and vice versa to make life a bit easier
CV_SetValue(&cv_allownewsaturnplayer, cv_allownewplayer.value);
#endif
maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value));
WRITEUINT8(p, maxplayer);
@@ -4039,6 +4153,9 @@ void D_ClientServerInit(void)
COM_AddCommand("reloadbans", Command_ReloadBan);
COM_AddCommand("connect", Command_connect);
COM_AddCommand("nodes", Command_Nodes);
#ifdef SATURNSYNCH
COM_AddCommand("resendgamestate", Command_ResendGamestate);
#endif
COM_AddCommand("listplayers", Command_Listplayers);
COM_AddCommand("packetstat", Command_Packetstat);
#ifdef HAVE_CURL
@@ -4077,17 +4194,33 @@ void D_ClientServerInit(void)
static void ResetNode(INT32 node)
{
nodeingame[node] = false;
nodewaiting[node] = 0;
nodetoplayer[node] = -1;
nodetoplayer2[node] = -1;
nodetoplayer3[node] = -1;
nodetoplayer4[node] = -1;
playerpernode[node] = 0;
nettics[node] = gametic;
supposedtics[node] = gametic;
nodewaiting[node] = 0;
playerpernode[node] = 0;
sendingsavegame[node] = false;
bannednode[node].banid = SIZE_MAX;
bannednode[node].timeleft = NO_BAN_TIME;
// SATURN
#ifdef SATURNPAK
is_client_saturn[node] = false;
#endif
#ifdef SATURNSYNCH
resendingsavegame[node] = false;
can_receive_gamestate[node] = false;
savegameresendcooldown[node] = 0;
#endif
//
}
void SV_ResetServer(void)
@@ -4123,6 +4256,9 @@ void SV_ResetServer(void)
mynode = 0;
cl_packetmissed = false;
#ifdef SATURNSYNCH
cl_redownloadinggamestate = false;
#endif
if (dedicated)
{
@@ -4337,7 +4473,6 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
if (pnum == serverplayer)
{
CONS_Alert(CONS_WARNING, "Attempt to remove server player\n");
return;
}
@@ -4416,6 +4551,14 @@ static boolean SV_AddWaitingPlayers(void)
return newplayer;
}
#ifdef SATURNPAK
static inline void SendSaturnInfo(INT32 node)
{
netbuffer->packettype = PT_ISSATURN;
HSendPacket(node, true, 0, 0);
}
#endif
void CL_AddSplitscreenPlayer(void)
{
if (cl_mode == CL_CONNECTED)
@@ -4601,7 +4744,11 @@ static void HandleConnect(SINT8 node)
{
SV_SendRefuse(node, va(M_GetText("Different SRB2Kart versions cannot\nplay a netgame!\n(server version %d.%d)"), VERSION, SUBVERSION));
}
#ifdef SATURNJOIN
else if ((!cv_allownewplayer.value && node && netbuffer->u.clientcfg.issaturn != ISSATURN) || (!cv_allownewsaturnplayer.value && node && netbuffer->u.clientcfg.issaturn == ISSATURN))
#else
else if (!cv_allownewplayer.value && node)
#endif
{
SV_SendRefuse(node, M_GetText(cv_joinrefusemessage.string));
}
@@ -4668,7 +4815,7 @@ static void HandleConnect(SINT8 node)
{
if (node && newnode)
{
SV_SendSaveGame(node); // send a complete game state
SV_SendSaveGame(node, false); // send a complete game state
DEBFILE("send savegame\n");
}
SV_AddWaitingPlayers();
@@ -4740,6 +4887,44 @@ static void HandleServerInfo(SINT8 node)
}
#endif
#ifdef SATURNSYNCH
static void PT_WillResendGamestate(void)
{
char tmpsave[264];
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_CanReceiveGamestate(SINT8 node)
{
if (client || sendingsavegame[node])
return;
CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]);
SV_SendSaveGame(node, true); // Resend a complete game state
resendingsavegame[node] = true;
}
#endif
/** Handles a packet received from a node that isn't in game
*
* \param node The packet sender
@@ -4928,6 +5113,9 @@ static void HandlePacketFromAwayNode(SINT8 node)
#endif
DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
#ifdef SATURNPAK
SendSaturnInfo(node);
#endif
memset(playeringame, 0, sizeof(playeringame));
for (j = 0; j < MAXPLAYERS; j++)
{
@@ -5041,7 +5229,6 @@ static void HandlePacketFromPlayer(SINT8 node)
tic_t realend, realstart;
UINT8 *pak, *txtpak, numtxtpak;
txtpak = NULL;
if (dedicated && node == 0)
@@ -5165,11 +5352,69 @@ static void HandlePacketFromPlayer(SINT8 node)
--resynch_delay[node];
break;
}
#ifdef SATURNSYNCH
if ((gamestate_resend_counter[node] != 0) && (I_GetTime() % ((max(cv_resynchcooldown.value, 1) * TICRATE) *2) == 0))
gamestate_resend_counter[node]--;
// Check player consistancy during the level
if (((!can_receive_gamestate[node]) && realstart <= gametic && realstart > gametic - TICQUEUE+1 && gamestate == GS_LEVEL
&& consistancy[realstart%TICQUEUE] != SHORT(netbuffer->u.clientpak.consistancy)) || (can_receive_gamestate[node] && realstart <= gametic && realstart + TICQUEUE - 1 > gametic && gamestate == GS_LEVEL
&& consistancy[realstart%TICQUEUE] != SHORT(netbuffer->u.clientpak.consistancy)
&& !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime()))
{
// we need to send this so the client can tell us if it can receive the savegame
netbuffer->packettype = PT_WILLRESENDGAMESTATE;
HSendPacket(node, true, 0, 0);
if (can_receive_gamestate[node])
resendingsavegame[node] = true;
else
{
SV_RequireResynch(node);
resendingsavegame[node] = false;
}
if ((!can_receive_gamestate[node] && cv_resynchattempts.value && resynch_score[node] <= (unsigned)cv_resynchattempts.value*250) || (can_receive_gamestate[node] && (gamestate_resend_counter[node] < cv_gamestateattempts.value)))
{
if (can_receive_gamestate[node] && resendingsavegame[node])
{
gamestate_resend_counter[node]++;
DEBFILE(va("gamestate counter %d for player %d\n", gamestate_resend_counter[node], netconsole));
}
if (cv_blamecfail.value)
CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
netconsole+1, player_names[netconsole],
consistancy[realstart%TICQUEUE],
SHORT(netbuffer->u.clientpak.consistancy));
DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
netconsole, realstart, consistancy[realstart%TICQUEUE],
SHORT(netbuffer->u.clientpak.consistancy)));
break;
}
else
{
UINT8 buf[3];
buf[0] = (UINT8)netconsole;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
netconsole, realstart, consistancy[realstart%TICQUEUE],
SHORT(netbuffer->u.clientpak.consistancy)));
break;
}
}
else if (resynch_score[node])
--resynch_score[node];
break;
#else
// Check player consistancy during the level
if (realstart <= gametic && realstart > gametic - TICQUEUE+1 && gamestate == GS_LEVEL
&& consistancy[realstart%TICQUEUE] != SHORT(netbuffer->u.clientpak.consistancy))
{
SV_RequireResynch(node);
SV_RequireResynch(node);
if (cv_resynchattempts.value && resynch_score[node] <= (unsigned)cv_resynchattempts.value*250)
{
@@ -5199,6 +5444,8 @@ static void HandlePacketFromPlayer(SINT8 node)
else if (resynch_score[node])
--resynch_score[node];
break;
#endif
case PT_BASICKEEPALIVE:
if (client)
break;
@@ -5333,6 +5580,12 @@ static void HandlePacketFromPlayer(SINT8 node)
}
Net_CloseConnection(node);
nodeingame[node] = false;
#ifdef SATURNPAK
is_client_saturn[node] = false;
#endif
#ifdef SATURNSYNCH
can_receive_gamestate[node] = false;
#endif
break;
// -------------------------------------------- CLIENT RECEIVE ----------
case PT_RESYNCHEND:
@@ -5496,6 +5749,26 @@ static void HandlePacketFromPlayer(SINT8 node)
if (client)
Got_Filetxpak();
break;
#ifdef SATURNSYNCH
case PT_CANRECEIVEGAMESTATE:
can_receive_gamestate[node] = true;
PT_CanReceiveGamestate(node);
break;
case PT_RECEIVEDGAMESTATE:
sendingsavegame[node] = false;
resendingsavegame[node] = false;
savegameresendcooldown[node] = I_GetTime() + cv_resynchcooldown.value * TICRATE; // I_GetTime() + 5 * TICRATE;
break;
case PT_WILLRESENDGAMESTATE:
PT_WillResendGamestate();
break;
#endif
#ifdef SATURNPAK
case PT_ISSATURN:
//CONS_Printf("hi im on saturn%d\n", node);
is_client_saturn[node] = true;
break;
#endif
default:
DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
netbuffer->packettype, node));
@@ -5511,7 +5784,6 @@ static void GetPackets(void)
{
SINT8 node; // The packet sender
player_joining = false;
while (HGetPacket())
@@ -6328,13 +6600,12 @@ void NetKeepAlive(void)
MasterClient_Ticker();
#endif
#ifdef HOLEPUNCH
#ifdef HOLEPUNCH
if (netgame && serverrunning)
{
RenewHolePunch();
}
#endif
#endif
if (client)
{
@@ -6447,18 +6718,27 @@ void NetUpdate(void)
MasterClient_Ticker(); // Acking the Master Server
#endif
#ifdef HOLEPUNCH
#ifdef HOLEPUNCH
if (netgame && serverrunning)
{
RenewHolePunch();
}
#endif
#endif
if (client)
{
#ifdef SATURNSYNCH
// If the client just finished redownloading the game state, load it
if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND)
CL_ReloadReceivedSavegame();
#endif
if (!resynch_local_inprogress)
CL_SendClientCmd(); // Send tic cmd
hu_resynching = resynch_local_inprogress;
#ifdef SATURNSYNCH
hu_redownloadinggamestate = cl_redownloadinggamestate;
#endif
}
else
{
@@ -6467,6 +6747,9 @@ void NetUpdate(void)
INT32 counts;
hu_resynching = false;
#ifdef SATURNSYNCH
hu_redownloadinggamestate = false;
#endif
firstticstosend = gametic;
for (i = 0; i < MAXNETNODES; i++)
@@ -6568,7 +6851,7 @@ rewind_t *CL_SaveRewindPoint(size_t demopos)
return NULL;
save_p = rewind->savebuffer;
P_SaveNetGame();
P_SaveNetGame(false);
rewind->leveltime = leveltime;
rewind->next = rewindhead;
rewind->demopos = demopos;
@@ -6592,7 +6875,7 @@ rewind_t *CL_RewindToTime(tic_t time)
return NULL;
save_p = rewindhead->savebuffer;
P_LoadNetGame();
P_LoadNetGame(false);
wipegamestate = gamestate; // No fading back in!
timeinmap = leveltime;
+25 -1
View File
@@ -98,6 +98,16 @@ typedef enum
PT_MOREFILESNEEDED, // Server, to client: "you need these (+ more on top of those)"
PT_PING, // Packet sent to tell clients the other client's latency to server.
#ifdef SATURNSYNCH
PT_WILLRESENDGAMESTATE, // Hey Client, I am about to resend you the gamestate!
PT_CANRECEIVEGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead.
PT_RECEIVEDGAMESTATE, // Thank you Server, I am ready to play again!
// we will reserve this for now even if unused, so order wont get mangled
PT_ISSATURN, // Saturn specific identifier packet
#endif
NUMPACKETTYPE
} packettype_t;
@@ -363,6 +373,9 @@ typedef struct
UINT8 subversion; // Contains build version
UINT8 localplayers; // number of splitscreen players
UINT8 mode;
#ifdef SATURNJOIN
UINT8 issaturn;
#endif
} ATTRPACK clientconfig_pak;
#define SV_SPEEDMASK 0x03 // used to send kartspeed
@@ -567,7 +580,15 @@ extern consvar_t
#ifdef VANILLAJOINNEXTROUND
cv_joinnextround,
#endif
cv_netticbuffer, cv_allownewplayer, cv_joinrefusemessage, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed;
cv_netticbuffer, cv_allownewplayer,
#ifdef SATURNJOIN
cv_allownewsaturnplayer,
#endif
cv_joinrefusemessage, cv_maxplayers, cv_resynchattempts,
#ifdef SATURNSYNCH
cv_resynchcooldown, cv_gamestateattempts,
#endif
cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed;
extern consvar_t cv_connectawaittime;
@@ -632,6 +653,9 @@ tic_t GetLag(INT32 node);
UINT8 GetFreeXCmdSize(void);
extern UINT8 hu_resynching;
#ifdef SATURNSYNCH
extern UINT8 hu_redownloadinggamestate;
#endif
extern UINT8 hu_stopped; // kart, true when the game is stopped for players due to a disconnecting or connecting player
typedef struct rewind_s {
+1 -1
View File
@@ -2076,7 +2076,7 @@ void D_SRB2Main(void)
pagename = "TITLESKY";
levelstarttic = gametic;
G_SetGamestate(GS_LEVEL);
if (!P_SetupLevel(false))
if (!P_SetupLevel(false, false))
I_Quit(); // fail so reset game stuff
}
+10
View File
@@ -833,6 +833,16 @@ const char *packettypename[NUMPACKETTYPE] =
"MOREFILESNEEDED",
"PING"
#ifdef SATURNSYNCH
,
"WILLRESENDGAMESTATE",
"CANRECEIVEGAMESTATE",
"RECEIVEDGAMESTATE",
// we will reserve this for now even if unused, so order wont get mangled
"ISSATURN"
#endif
};
const char *Net_GetPacketName(UINT8 packettype)
+7
View File
@@ -788,6 +788,10 @@ void D_RegisterServerCommands(void)
// d_clisrv
CV_RegisterVar(&cv_maxplayers);
CV_RegisterVar(&cv_resynchattempts);
#ifdef SATURNSYNCH
CV_RegisterVar(&cv_gamestateattempts);
CV_RegisterVar(&cv_resynchcooldown);
#endif
CV_RegisterVar(&cv_maxsend);
CV_RegisterVar(&cv_noticedownload);
CV_RegisterVar(&cv_downloadspeed);
@@ -795,6 +799,9 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_httpsource);
#ifndef NONET
CV_RegisterVar(&cv_allownewplayer);
#ifdef SATURNJOIN
CV_RegisterVar(&cv_allownewsaturnplayer);
#endif
CV_RegisterVar(&cv_joinrefusemessage);
CV_RegisterVar(&cv_chatlogsize);
#ifdef VANILLAJOINNEXTROUND
+27 -4
View File
@@ -128,11 +128,11 @@ extern char logfilename[1024];
#define VERSION 1 // Game version
#define SUBVERSION 6 // more precise version number
#ifndef USE_FBO_OGL
#define VERSIONSTRING "Saturn v7 - Test "
#define VERSIONSTRINGW L"Saturn v7 - Test"
#define VERSIONSTRING "Saturn v7"
#define VERSIONSTRINGW L"Saturn v7"
#else
#define VERSIONSTRING "Saturn v7 - Test - FBO "
#define VERSIONSTRINGW L"Saturn v7 - Test - FBO"
#define VERSIONSTRING "Saturn v7 - FBO "
#define VERSIONSTRINGW L"Saturn v7 - FBO"
#endif
// Hey! If you change this, add 1 to the MODVERSION below! Otherwise we can't force updates!
// And change CMakeLists.txt (not src/, but in root), for CMake users!
@@ -568,6 +568,29 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
// None of these that are disabled in the normal build are guaranteed to work perfectly
// Compile them at your own risk!
#ifndef NONET
//-- SATURN __
/// Enable gamestate resynching between Saturn servers and clients
/// Like SRB2 and RR does
/// Still highly experimental
#ifdef DOSATURNSYNCH
#define SATURNSYNCH
/// Detect if a client is on Saturn in the clientconfig.
/// To seperately allow them to join or block joining from vanilla clients.
#ifdef DOSATURNJOIN
#define SATURNJOIN
#endif
#endif
/// Server detection for if a connecting client is on Saturn.
/// For stuff like extra synching, etc.
//#ifdef DOSATURNPAK
//#define SATURNPAK
//#endif
//-- <( ̄︶ ̄)> __
#endif
/// Undefine to use the new method of Gamma correction see colour cube in v_video.c
#define BACKWARDSCOMPATCORRECTION
+6 -2
View File
@@ -1197,7 +1197,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
// why build a ticcmd if we're paused?
// Or, for that matter, if we're being reborn.
// Kart, don't build a ticcmd if someone is resynching or the server is stopped too so we don't fly off course in bad conditions
if (paused || P_AutoPause() || (gamestate == GS_LEVEL && player->playerstate == PST_REBORN) || hu_resynching)
if (paused || P_AutoPause() || (gamestate == GS_LEVEL && player->playerstate == PST_REBORN) || hu_resynching
#ifdef SATURNSYNCH
|| hu_redownloadinggamestate
#endif
)
{
cmd->angleturn = (INT16)(lang >> 16);
cmd->aiming = G_ClipAimingPitch(&laim);
@@ -1647,7 +1651,7 @@ void G_DoLoadLevel(boolean resetplayer)
}
// Setup the level.
if (!P_SetupLevel(false))
if (!P_SetupLevel(false,false))
{
// fail so reset game stuff
Command_ExitGame_f();
+10 -2
View File
@@ -1256,7 +1256,11 @@ void HU_Ticker(void)
if (cechotimer > 0) --cechotimer;
// Animate the desynch dots
if (hu_resynching)
if (hu_resynching
#ifdef SATURNSYNCH
|| hu_redownloadinggamestate
#endif
)
resynch_ticker++; //tic tic tic tic tic
HU_TickSongCredits();
@@ -2421,7 +2425,11 @@ void HU_Drawer(void)
HU_DrawSongCredits();
// draw desynch text
if (hu_resynching)
if (hu_resynching
#ifdef SATURNSYNCH
|| hu_redownloadinggamestate
#endif
)
{
char resynch_text[14];
UINT32 i;
+9
View File
@@ -670,8 +670,17 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
lua_pop(gL, 1);
}
if (!found)
{
t++;
if (t == 0)
{
CONS_Alert(CONS_ERROR, "Too many tables to archive!\n");
WRITEUINT8(save_p, ARCH_NULL);
return 0;
}
}
WRITEUINT8(save_p, ARCH_TABLE);
WRITEUINT16(save_p, t);
+21 -2
View File
@@ -2027,6 +2027,22 @@ static menuitem_t OP_AdvServerOptionsMenu[] =
NULL, "Server Browser Address", &cv_masterserver, 10},
#endif
{IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 40},
#ifdef SATURNSYNCH
{IT_STRING | IT_CVAR, NULL, "Resend Gamestate Cooldown", &cv_resynchcooldown, 50},
{IT_STRING | IT_CVAR, NULL, "Resend Gamestate Attempts", &cv_gamestateattempts, 60},
{IT_STRING | IT_CVAR, NULL, "Delay limit (frames)", &cv_maxping, 70},
{IT_STRING | IT_CVAR, NULL, "Delay timeout (s)", &cv_pingtimeout, 80},
{IT_STRING | IT_CVAR, NULL, "Connection timeout (tics)", &cv_nettimeout, 90},
{IT_STRING | IT_CVAR, NULL, "Join timeout (tics)", &cv_jointimeout, 100},
{IT_STRING | IT_CVAR, NULL, "Max. file transfer send (KB)", &cv_maxsend, 120},
{IT_STRING | IT_CVAR, NULL, "File transfer packet rate", &cv_downloadspeed, 130},
{IT_STRING | IT_CVAR, NULL, "Log join addresses", &cv_showjoinaddress, 150},
{IT_STRING | IT_CVAR, NULL, "Log resyncs", &cv_blamecfail, 160},
{IT_STRING | IT_CVAR, NULL, "Log file transfers", &cv_noticedownload, 170},
#else
{IT_STRING | IT_CVAR, NULL, "Delay limit (frames)", &cv_maxping, 50},
{IT_STRING | IT_CVAR, NULL, "Delay timeout (s)", &cv_pingtimeout, 60},
{IT_STRING | IT_CVAR, NULL, "Connection timeout (tics)", &cv_nettimeout, 70},
@@ -2038,14 +2054,17 @@ static menuitem_t OP_AdvServerOptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Log join addresses", &cv_showjoinaddress, 130},
{IT_STRING | IT_CVAR, NULL, "Log resyncs", &cv_blamecfail, 140},
{IT_STRING | IT_CVAR, NULL, "Log file transfers", &cv_noticedownload, 150},
};
#endif
};
#ifndef NONET
static const char* OP_AdvServerOptionsTooltips[] =
{
"Server used for master server.",
"Attempts to resynchronise player to server.",
#ifdef SATURNSYNCH
"Cooldown in seconds before the Server attempts\nto resend the gamestate to a Saturn Client\nbetween resending attempts.",
"Attempts to resend the Gamestate to player\nThis increments a Counter everytime a gamestate resend occurs\nThis Counter decrements at double the time of the Cooldown\nonce the Counter reaches the threshold, the client gets kicked.",
#endif
"Maximum allowed delay.",
"Delay timeout in seconds.",
"Connection timeout in tics.",
+58 -12
View File
@@ -107,7 +107,7 @@ static inline void P_UnArchivePlayer(void)
//
// P_NetArchivePlayers
//
static void P_NetArchivePlayers(void)
static void P_NetArchivePlayers(boolean resending)
{
INT32 i, j;
UINT16 flags;
@@ -117,6 +117,9 @@ static void P_NetArchivePlayers(void)
for (i = 0; i < MAXPLAYERS; i++)
{
if (resending)
WRITESINT8(save_p, (SINT8)adminplayers[i]);
if (!playeringame[i])
continue;
@@ -124,6 +127,9 @@ static void P_NetArchivePlayers(void)
// no longer send ticcmds, player name, skin, or color
if (resending)
WRITESTRINGN(save_p, player_names[i], MAXPLAYERNAME);
WRITEANGLE(save_p, players[i].aiming);
WRITEANGLE(save_p, players[i].awayviewaiming);
WRITEINT32(save_p, players[i].awayviewtics);
@@ -148,6 +154,13 @@ static void P_NetArchivePlayers(void)
WRITEUINT16(save_p, players[i].flashpal);
WRITEUINT16(save_p, players[i].flashcount);
if (resending)
{
WRITEUINT8(save_p, players[i].skincolor);
WRITEINT32(save_p, players[i].skin);
}
WRITEUINT32(save_p, players[i].score);
WRITEFIXED(save_p, players[i].dashspeed);
WRITEINT32(save_p, players[i].dashtime);
@@ -285,7 +298,7 @@ static void P_NetArchivePlayers(void)
//
// P_NetUnArchivePlayers
//
static void P_NetUnArchivePlayers(void)
static void P_NetUnArchivePlayers(boolean resending)
{
INT32 i, j;
UINT16 flags;
@@ -295,6 +308,8 @@ static void P_NetUnArchivePlayers(void)
for (i = 0; i < MAXPLAYERS; i++)
{
if (resending)
adminplayers[i] = (INT32)READSINT8(save_p);
// Do NOT memset player struct to 0
// other areas may initialize data elsewhere
//memset(&players[i], 0, sizeof (player_t));
@@ -305,6 +320,9 @@ static void P_NetUnArchivePlayers(void)
// sending player names, skin and color should not be necessary at all!
// (that data is handled in the server config now)
if (resending)
READSTRINGN(save_p, player_names[i], MAXPLAYERNAME);
players[i].aiming = READANGLE(save_p);
players[i].awayviewaiming = READANGLE(save_p);
players[i].awayviewtics = READINT32(save_p);
@@ -329,6 +347,12 @@ static void P_NetUnArchivePlayers(void)
players[i].flashpal = READUINT16(save_p);
players[i].flashcount = READUINT16(save_p);
if (resending)
{
players[i].skincolor = READUINT8(save_p);
players[i].skin = READINT32(save_p);
}
players[i].score = READUINT32(save_p);
players[i].dashspeed = READFIXED(save_p); // dashing speed
players[i].dashtime = READINT32(save_p); // dashing speed
@@ -3228,19 +3252,25 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
playeringame[consoleplayer] = true;
}
static void P_NetArchiveMisc(void)
static void P_NetArchiveMisc(boolean resending)
{
UINT32 pig = 0;
INT32 i;
WRITEUINT32(save_p, ARCHIVEBLOCK_MISC);
if (resending)
WRITEUINT32(save_p, gametic);
WRITEINT16(save_p, gamemap);
if (gamestate != GS_LEVEL)
WRITEINT16(save_p, GS_WAITINGPLAYERS); // nice hack to put people back into waitingplayers
else
WRITEINT16(save_p, gamestate);
if (resending)
WRITEINT16(save_p, gametype);
for (i = 0; i < MAXPLAYERS; i++)
pig |= (playeringame[i] != 0)<<i;
WRITEUINT32(save_p, pig);
@@ -3323,14 +3353,18 @@ static void P_NetArchiveMisc(void)
WRITEUINT8(save_p, 0x2e);
}
FUNCINLINE static ATTRINLINE boolean P_NetUnArchiveMisc(void)
FUNCINLINE static ATTRINLINE boolean P_NetUnArchiveMisc(boolean reloading)
{
UINT32 pig;
INT32 i;
UINT8 *old_save_p;
if (READUINT32(save_p) != ARCHIVEBLOCK_MISC)
I_Error("Bad $$$.sav at archive block Misc");
if (reloading)
gametic = READUINT32(save_p);
gamemap = READINT16(save_p);
// gamemap changed; we assume that its map header is always valid,
@@ -3340,10 +3374,14 @@ FUNCINLINE static ATTRINLINE boolean P_NetUnArchiveMisc(void)
// tell the sound code to reset the music since we're skipping what
// normally sets this flag
mapmusflags |= MUSIC_RELOADRESET;
if (!reloading)
mapmusflags |= MUSIC_RELOADRESET;
G_SetGamestate(READINT16(save_p));
if (reloading)
gametype = READINT16(save_p);
pig = READUINT32(save_p);
for (i = 0; i < MAXPLAYERS; i++)
{
@@ -3357,8 +3395,16 @@ FUNCINLINE static ATTRINLINE boolean P_NetUnArchiveMisc(void)
encoremode = (boolean)READUINT8(save_p);
if (!P_SetupLevel(true))
// FIXME: save_p should not be global!!!
old_save_p = save_p;
if (!P_SetupLevel(true, reloading))
{
CONS_Alert(CONS_ERROR, M_GetText("Can't load the level!\n"));
return false;
}
save_p = old_save_p;
// get the time
leveltime = READUINT32(save_p);
@@ -3441,14 +3487,14 @@ void P_SaveGame(void)
WRITEUINT8(save_p, 0x1d); // consistency marker
}
void P_SaveNetGame(void)
void P_SaveNetGame(boolean resending)
{
thinker_t *th;
mobj_t *mobj;
INT32 i = 1; // don't start from 0, it'd be confused with a blank pointer otherwise
CV_SaveNetVars(&save_p, false);
P_NetArchiveMisc();
P_NetArchiveMisc(resending);
// Assign the mobjnumber for pointer tracking
if (gamestate == GS_LEVEL)
@@ -3467,7 +3513,7 @@ void P_SaveNetGame(void)
}
}
P_NetArchivePlayers();
P_NetArchivePlayers(resending);
if (gamestate == GS_LEVEL)
{
P_NetArchiveWorld();
@@ -3502,12 +3548,12 @@ boolean P_LoadGame(INT16 mapoverride)
return true;
}
boolean P_LoadNetGame(void)
boolean P_LoadNetGame(boolean resending)
{
CV_LoadNetVars(&save_p);
if (!P_NetUnArchiveMisc())
if (!P_NetUnArchiveMisc(resending))
return false;
P_NetUnArchivePlayers();
P_NetUnArchivePlayers(resending);
if (gamestate == GS_LEVEL)
{
P_NetUnArchiveWorld();
+2 -2
View File
@@ -22,9 +22,9 @@
// These are the load / save game routines.
void P_SaveGame(void);
void P_SaveNetGame(void);
void P_SaveNetGame(boolean resending);
boolean P_LoadGame(INT16 mapoverride);
boolean P_LoadNetGame(void);
boolean P_LoadNetGame(boolean resending);
mobj_t *P_FindNewPosition(UINT32 oldposition);
+52 -56
View File
@@ -2025,14 +2025,12 @@ lumpnum_t lastloadedmaplumpnum; // for comparative savegame
//
// Some player initialization for map start.
//
static void P_LevelInitStuff(void)
static void P_LevelInitStuff(boolean reloadinggamestate)
{
INT32 i;
leveltime = 0;
memset(localaiming, 0, sizeof(localaiming));
// map object scale
mapobjectscale = mapheaderinfo[gamemap-1]->mobj_scale;
@@ -2060,7 +2058,10 @@ static void P_LevelInitStuff(void)
// circuit, race and competition stuff
circuitmap = false;
numstarposts = 0;
totalrings = timeinmap = 0;
totalrings = 0;
if (!reloadinggamestate)
timeinmap = 0;
// special stage
stagefailed = false;
@@ -2164,7 +2165,9 @@ void P_LoadThingsOnly(void)
P_RemoveMobj(mo);
}
P_LevelInitStuff();
P_LevelInitStuff(false);
memset(localaiming, 0, sizeof(localaiming));
P_PrepareRawThings(vth->data);
P_LoadThings();
@@ -2452,30 +2455,36 @@ static void P_SetupCamera(UINT8 pnum, camera_t *cam)
}
}
static void P_InitCamera(void)
{
INT32 i;
if (!dedicated)
{
if (!demo.freecam)
for (i = 0; i <= splitscreen; i++)
P_SetupCamera(displayplayers[i], &camera[i]);
// Though, I don't think anyone would care about cam_rotate being reset back to the only value that makes sense :P
if (!cv_cam_rotate.changed)
CV_Set(&cv_cam_rotate, cv_cam_rotate.defaultvalue);
if (!cv_cam2_rotate.changed)
CV_Set(&cv_cam2_rotate, cv_cam2_rotate.defaultvalue);
if (!cv_cam3_rotate.changed)
CV_Set(&cv_cam3_rotate, cv_cam3_rotate.defaultvalue);
if (!cv_cam4_rotate.changed)
CV_Set(&cv_cam4_rotate, cv_cam4_rotate.defaultvalue);
displayplayers[0] = consoleplayer; // Start with your OWN view, please!
}
}
static boolean P_CanSave(void)
{
#if 0
// Saving is completely ignored under these conditions:
if ((cursaveslot < 0) // Playing without saving
|| (modifiedgame && !savemoddata) // Game is modified
|| (netgame || multiplayer) // Not in single-player
|| (demo.playback || demo.recording || metalrecording) // Currently in demo
|| (players[consoleplayer].lives <= 0) // Completely dead
|| (modeattacking || ultimatemode || G_IsSpecialStage(gamemap))) // Specialized instances
return false;
if (mapheaderinfo[gamemap-1]->saveoverride == SAVE_ALWAYS)
return true; // Saving should ALWAYS happen!
else if (mapheaderinfo[gamemap-1]->saveoverride == SAVE_NEVER)
return false; // Saving should NEVER happen!
// Default condition: In a non-hidden map, at the beginning of a zone or on a completed save-file, and not on save reload.
return (!(mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU)
&& (mapheaderinfo[gamemap-1]->actnum < 2 || gamecomplete)
&& (gamemap != lastmapsaved));
#else
return false; // SRB2Kart: no SP, no saving.
#endif
}
/** Loads a level from a lump or external wad.
@@ -2483,7 +2492,7 @@ static boolean P_CanSave(void)
* \param skipprecip If true, don't spawn precipitation.
* \todo Clean up, refactor, split up; get rid of the bloat.
*/
boolean P_SetupLevel(boolean skipprecip)
boolean P_SetupLevel(boolean skipprecip, boolean reloadinggamestate)
{
// use gamemap to get map number.
// 99% of the things already did, so.
@@ -2521,7 +2530,7 @@ boolean P_SetupLevel(boolean skipprecip)
if (cv_runscripts.value && mapheaderinfo[gamemap-1]->scriptname[0] != '#')
P_RunLevelScript(mapheaderinfo[gamemap-1]->scriptname);
P_LevelInitStuff();
P_LevelInitStuff(reloadinggamestate);
for (i = 0; i <= splitscreen; i++)
postimgtype[i] = postimg_none;
@@ -2554,9 +2563,12 @@ boolean P_SetupLevel(boolean skipprecip)
// will be set by player think.
players[consoleplayer].viewz = 1;
// Cancel all d_main.c fadeouts (keep fade in though).
if (reloadinggamestate)
wipegamestate = gamestate; // Don't fade if reloading the gamestate
// Encore mode fade to pink to white
// This is handled BEFORE sounds are stopped.
if (encoremode && !prevencoremode && !demo.rewinding)
else if (encoremode && !prevencoremode && !demo.rewinding)
{
tic_t locstarttime, endtime, nowtime;
@@ -2615,15 +2627,16 @@ boolean P_SetupLevel(boolean skipprecip)
// As oddly named as this is, this handles music only.
// We should be fine starting it here.
S_Start();
if (!reloadinggamestate)
S_Start();
levelfadecol = (encoremode && !ranspecialwipe ? 122 : 120);
// Let's fade to white here
// But only if we didn't do the encore startup wipe
if (!ranspecialwipe && !demo.rewinding)
if (!ranspecialwipe && !demo.rewinding && !reloadinggamestate)
{
if(rendermode != render_none)
if (rendermode != render_none)
{
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
@@ -2732,7 +2745,7 @@ boolean P_SetupLevel(boolean skipprecip)
globalweather = mapheaderinfo[gamemap-1]->weather;
// set up world state
P_SpawnSpecials(fromnetsave);
P_SpawnSpecials(fromnetsave, reloadinggamestate);
if (loadprecip) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
P_SpawnPrecipitation();
@@ -2843,8 +2856,6 @@ boolean P_SetupLevel(boolean skipprecip)
G_BeginRecording(); //this has to move here, since dedicated servers dont run got_mapcmd
}
K_InitDirector();
wantedcalcdelay = wantedfrequency*2;
indirectitemcooldown = 0;
hyubgone = 0;
@@ -2880,26 +2891,11 @@ boolean P_SetupLevel(boolean skipprecip)
// landing point for netgames.
netgameskip:
if (!dedicated)
if (!reloadinggamestate)
{
if (!demo.freecam)
for (i = 0; i <= splitscreen; i++)
P_SetupCamera(displayplayers[i], &camera[i]);
// Though, I don't think anyone would care about cam_rotate being reset back to the only value that makes sense :P
if (!cv_cam_rotate.changed)
CV_Set(&cv_cam_rotate, cv_cam_rotate.defaultvalue);
if (!cv_cam2_rotate.changed)
CV_Set(&cv_cam2_rotate, cv_cam2_rotate.defaultvalue);
if (!cv_cam3_rotate.changed)
CV_Set(&cv_cam3_rotate, cv_cam3_rotate.defaultvalue);
if (!cv_cam4_rotate.changed)
CV_Set(&cv_cam4_rotate, cv_cam4_rotate.defaultvalue);
displayplayers[0] = consoleplayer; // Start with your OWN view, please!
P_InitCamera();
memset(localaiming, 0, sizeof(localaiming));
K_InitDirector();
}
// clear special respawning que
@@ -2908,7 +2904,7 @@ boolean P_SetupLevel(boolean skipprecip)
P_MapEnd();
// Remove the loading shit from the screen
if (rendermode != render_none)
if (rendermode != render_none && !reloadinggamestate)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
if (precache || dedicated)
@@ -2955,7 +2951,7 @@ boolean P_SetupLevel(boolean skipprecip)
LUAh_MapLoad();
}
if (rendermode != render_none)
if (rendermode != render_none && !reloadinggamestate)
{
R_ResetViewInterpolation(0);
R_ResetViewInterpolation(0);
+1 -1
View File
@@ -60,7 +60,7 @@ extern mapthing_t *mapthings;
void P_SetupLevelSky(INT32 skynum, boolean global);
void P_LoadThingsOnly(void);
boolean P_SetupLevel(boolean skipprecip);
boolean P_SetupLevel(boolean skipprecip, boolean reloadinggamestate);
#ifdef HWRENDER
void HWR_LoadLevel(void);
#endif
+3 -2
View File
@@ -5234,7 +5234,7 @@ static void P_RunLevelLoadExecutors(void)
* as they'll just be erased by UnArchiveThinkers.
* \sa P_SpawnPrecipitation, P_SpawnFriction, P_SpawnPushers, P_SpawnScrollers
*/
void P_SpawnSpecials(INT32 fromnetsave)
void P_SpawnSpecials(INT32 fromnetsave, boolean reloadinggamestate)
{
sector_t *sector;
size_t i;
@@ -6261,7 +6261,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
}
}
P_RunLevelLoadExecutors();
if (!reloadinggamestate)
P_RunLevelLoadExecutors();
}
/** Adds 3Dfloors as appropriate based on a common control linedef.
+1 -1
View File
@@ -32,7 +32,7 @@ void P_InitPicAnims(void);
void P_SetupLevelFlatAnims(void);
// at map load
void P_SpawnSpecials(INT32 fromnetsave);
void P_SpawnSpecials(INT32 fromnetsave, boolean reloadinggamestate);
// every tic
void P_UpdateSpecials(void);