diff --git a/src/Makefile b/src/Makefile index 72f82265b..4668518b9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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='' diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d738bad96..b6eb9930b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -10,6 +10,7 @@ /// \file d_clisrv.c /// \brief SRB2 Network game communication and protocol, all OS independent parts. +#include "doomdef.h" #include #ifdef __GNUC__ #include //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 : 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; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 6ddeb09bb..cc123b1d2 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -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 { diff --git a/src/d_main.c b/src/d_main.c index aeee75c2e..c437a9dcf 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -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 } diff --git a/src/d_net.c b/src/d_net.c index 15ab46b5c..ad031a76b 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -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) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 8e9bfb9e3..3cd815dee 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -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 diff --git a/src/doomdef.h b/src/doomdef.h index 3540c3f4a..bdc82a0ca 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -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 diff --git a/src/g_game.c b/src/g_game.c index c2eb93416..27758119b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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(); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 934b77105..0594d28cd 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -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; diff --git a/src/lua_script.c b/src/lua_script.c index c4a0df135..9d5bef4a7 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -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); diff --git a/src/m_menu.c b/src/m_menu.c index da0c53c3c..0e0dd10be 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -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.", diff --git a/src/p_saveg.c b/src/p_saveg.c index 3d28a6eac..b8a517f0e 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -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)<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); diff --git a/src/p_setup.h b/src/p_setup.h index fe8b4afb5..c6a18915a 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -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 diff --git a/src/p_spec.c b/src/p_spec.c index 378db7269..133176db6 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -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. diff --git a/src/p_spec.h b/src/p_spec.h index 1231aeda8..477dd4950 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -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);