From e7c79ab461122fd0179e858d3dfffc967241cbb4 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 9 Dec 2022 17:26:52 +0000 Subject: [PATCH] `M_CheckNetUnlockByID` System for netsyncing unlocks, inspired by but with nowhere near as many moving parts as (STJr/SRB2!1756). * `gamedata->unlocked[MAXUNLOCKABLES]` is duplicated to `netUnlocked[MAXUNLOCKABLES]` (or all `true` in `dedicated` * New `local` boolean for M_SecretUnlocked * Removed last vestiges of SRB2 special stage token code because it occupied the spot in the netsave we wanted to use. * Correct typing of multiple `m_cond` functions that returned `boolean` constants as `UINT8`s. --- src/command.c | 6 +++--- src/d_clisrv.c | 6 ++++++ src/d_netcmd.c | 4 ++-- src/doomstat.h | 4 ---- src/g_game.c | 12 ++--------- src/k_follower.c | 1 + src/k_menudraw.c | 6 +++--- src/k_menufunc.c | 8 ++++---- src/lua_script.c | 2 -- src/m_cond.c | 52 +++++++++++++++++++++++++++--------------------- src/m_cond.h | 8 ++++++-- src/p_saveg.c | 27 ++++++++++++++++--------- src/p_setup.c | 1 - src/p_spec.c | 4 ++-- src/r_skins.c | 2 ++ 15 files changed, 78 insertions(+), 65 deletions(-) diff --git a/src/command.c b/src/command.c index 28b73bdbc..88d11105a 100644 --- a/src/command.c +++ b/src/command.c @@ -1966,13 +1966,13 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth) return; } - if (var == &cv_kartencore && !M_SecretUnlocked(SECRET_ENCORE)) + if (var == &cv_kartencore && !M_SecretUnlocked(SECRET_ENCORE, false)) { CONS_Printf(M_GetText("You haven't unlocked Encore Mode yet!\n")); return; } - if (var == &cv_kartspeed && !M_SecretUnlocked(SECRET_HARDSPEED)) + if (var == &cv_kartspeed && !M_SecretUnlocked(SECRET_HARDSPEED, false)) { if (!stricmp(value, "Hard") || atoi(value) >= KARTSPEED_HARD) { @@ -2227,7 +2227,7 @@ void CV_AddValue(consvar_t *var, INT32 increment) || var->PossibleValue == dummykartspeed_cons_t || var->PossibleValue == gpdifficulty_cons_t) { - if (!M_SecretUnlocked(SECRET_HARDSPEED)) + if (!M_SecretUnlocked(SECRET_HARDSPEED, false)) { max = KARTSPEED_NORMAL+1; if (var->PossibleValue == kartspeed_cons_t) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 29c460434..27a489916 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -57,6 +57,7 @@ #include "k_boss.h" #include "doomstat.h" #include "s_sound.h" // sfx_syfail +#include "m_cond.h" // netUnlocked // cl loading screen #include "v_video.h" @@ -3463,6 +3464,11 @@ void SV_ResetServer(void) CV_RevertNetVars(); + // Copy our unlocks to a place where net material can grab at/overwrite them safely. + // (permits all unlocks in dedicated) + for (i = 0; i < MAXUNLOCKABLES; i++) + netUnlocked[i] = (dedicated || gamedata->unlocked[i]); + DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n"); } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index cb144ab45..5b76aa16d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2857,7 +2857,7 @@ static void Command_Map_f(void) { newencoremode = !newencoremode; - if (!M_SecretUnlocked(SECRET_ENCORE) && newencoremode == true && !usingcheats) + if (!M_SecretUnlocked(SECRET_ENCORE, false) && newencoremode == true && !usingcheats) { CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n")); return; @@ -4848,7 +4848,7 @@ void ItemFinder_OnChange(void) if (!cv_itemfinder.value) return; // it's fine. - if (!M_SecretUnlocked(SECRET_ITEMFINDER)) + if (!M_SecretUnlocked(SECRET_ITEMFINDER, true)) { CONS_Printf(M_GetText("You haven't earned this yet.\n")); CV_StealthSetValue(&cv_itemfinder, 0); diff --git a/src/doomstat.h b/src/doomstat.h index d7b09f060..dfbb23966 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -573,10 +573,6 @@ extern INT32 luabanks[NUM_LUABANKS]; extern INT32 nummaprings; //keep track of spawned rings/coins -extern UINT32 token; ///< Number of tokens collected in a level -extern UINT32 tokenlist; ///< List of tokens collected -extern boolean gottoken; ///< Did you get a token? Used for end of act -extern INT32 tokenbits; ///< Used for setting token bits extern UINT32 bluescore; ///< Blue Team Scores extern UINT32 redscore; ///< Red Team Scores diff --git a/src/g_game.c b/src/g_game.c index f00ffcbe5..8407a7f8d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -204,10 +204,6 @@ UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage. UINT16 emeralds; INT32 luabanks[NUM_LUABANKS]; -UINT32 token; // Number of tokens collected in a level -UINT32 tokenlist; // List of tokens collected -boolean gottoken; // Did you get a token? Used for end of act -INT32 tokenbits; // Used for setting token bits // Temporary holding place for nights data for the current map //nightsdata_t ntemprecords; @@ -3309,7 +3305,7 @@ INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype) // Most of the gametype references in this condition are intentionally not prefgametype. // This is so a server CAN continue playing a gametype if they like the taste of it. // The encore check needs prefgametype so can't use G_RaceGametype... - boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1) + boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE, false) || encorescramble == 1) && ((gametyperules|gametypedefaultrules[prefgametype]) & GTR_CIRCUIT)); UINT8 encoremodifier = 0; @@ -3870,7 +3866,7 @@ static void G_GetNextMap(void) while (cup) { // Not unlocked? Grab the next result afterwards - if (!marathonmode && cup->unlockrequired < MAXUNLOCKABLES && !gamedata->unlocked[cup->unlockrequired]) + if (!marathonmode && M_CheckNetUnlockByID(cup->unlockrequired)) { cup = cup->next; gettingresult = 1; @@ -4218,10 +4214,6 @@ static void G_DoContinued(void) // Reset score pl->score = 0; - // Allow tokens to come back - tokenlist = 0; - token = 0; - if (!(netgame || multiplayer || demo.playback || demo.recording || metalrecording || modeattacking) && !usedCheats && cursaveslot > 0) G_SaveGameOver((UINT32)cursaveslot, true); diff --git a/src/k_follower.c b/src/k_follower.c index b340ba266..0a8869077 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -79,6 +79,7 @@ boolean K_FollowerUsable(INT32 skinnum) } // Use the unlockables table directly + // DEFINITELY not M_CheckNetUnlockByID return (boolean)(gamedata->unlocked[i]); } diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 2a2adabae..d23da7114 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1930,7 +1930,7 @@ static void M_DrawCupPreview(INT16 y, cupheader_t *cup) V_DrawFill(0, y, BASEVIDWIDTH, 54, 31); - if (cup && (cup->unlockrequired >= MAXUNLOCKABLES || gamedata->unlocked[cup->unlockrequired])) + if (cup && (cup->unlockrequired >= MAXUNLOCKABLES || M_CheckNetUnlockByID(cup->unlockrequired))) { i = (cupgrid.previewanim / 82) % cup->numlevels; while (x < BASEVIDWIDTH + pad) @@ -1968,7 +1968,7 @@ static void M_DrawCupTitle(INT16 y, cupheader_t *cup) if (cup) { - boolean unlocked = (cup->unlockrequired >= MAXUNLOCKABLES || gamedata->unlocked[cup->unlockrequired]); + boolean unlocked = (cup->unlockrequired >= MAXUNLOCKABLES || M_CheckNetUnlockByID(cup->unlockrequired)); UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE); patch_t *icon = W_CachePatchName(cup->icon, PU_CACHE); const char *str = (unlocked ? va("%s Cup", cup->name) : "???"); @@ -2036,7 +2036,7 @@ void M_DrawCupSelect(void) V_DrawScaledPatch(x, y, 0, patch); - if (iconcup->unlockrequired < MAXUNLOCKABLES && !gamedata->unlocked[iconcup->unlockrequired]) + if (iconcup->unlockrequired < MAXUNLOCKABLES && !M_CheckNetUnlockByID(iconcup->unlockrequired)) { patch_t *st = W_CachePatchName(va("ICONST0%d", (cupgrid.previewanim % 4) + 1), PU_CACHE); V_DrawScaledPatch(x + 8, y + icony, 0, st); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 325eee093..5e5a82cd0 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3362,7 +3362,7 @@ void M_SetupDifficultySelect(INT32 choice) PLAY_RaceDifficultyDef.lastOn = drace_cupselect; // Select cup select by default. } - if (M_SecretUnlocked(SECRET_ENCORE)) + if (M_SecretUnlocked(SECRET_ENCORE, false)) { PLAY_RaceDifficulty[drace_encore].status = IT_STRING2|IT_CVAR; // Encore on/off } @@ -3477,7 +3477,7 @@ static void M_LevelListFromGametype(INT16 gt) while (cup) { - if (cup->unlockrequired >= MAXUNLOCKABLES || gamedata->unlocked[cup->unlockrequired]) + if (cup->unlockrequired >= MAXUNLOCKABLES || M_CheckNetUnlockByID(cup->unlockrequired)) { highestid = cup->id; if (Playing() && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == cup) @@ -3611,7 +3611,7 @@ void M_CupSelectHandler(INT32 choice) M_SetMenuDelay(pid); if ((!newcup) - || (newcup && newcup->unlockrequired < MAXUNLOCKABLES && !gamedata->unlocked[newcup->unlockrequired]) + || (newcup->unlockrequired < MAXUNLOCKABLES && !M_CheckNetUnlockByID(newcup->unlockrequired)) || (newcup->cachedlevels[0] == NEXTMAP_INVALID)) { S_StartSound(NULL, sfx_s3kb2); @@ -4614,7 +4614,7 @@ void M_InitOptions(INT32 choice) OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_SUBMENU; OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_SUBMENU; OPTIONS_GameplayDef.menuitems[gopt_encore].status = - (M_SecretUnlocked(SECRET_ENCORE) ? (IT_STRING | IT_CVAR) : IT_DISABLED); + (M_SecretUnlocked(SECRET_ENCORE, false) ? (IT_STRING | IT_CVAR) : IT_DISABLED); } OPTIONS_DataDef.menuitems[dopt_erase].status = (gamestate == GS_MENU diff --git a/src/lua_script.c b/src/lua_script.c index 4fcd7dd00..4f9eab969 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -403,8 +403,6 @@ int LUA_WriteGlobals(lua_State *L, const char *word) skincolor_bluering = (UINT16)luaL_checkinteger(L, 2); else if (fastcmp(word, "emeralds")) emeralds = (UINT16)luaL_checkinteger(L, 2); - else if (fastcmp(word, "token")) - token = (UINT32)luaL_checkinteger(L, 2); else if (fastcmp(word, "gravity")) gravity = (fixed_t)luaL_checkinteger(L, 2); else if (fastcmp(word, "stoppedclock")) diff --git a/src/m_cond.c b/src/m_cond.c index 20d57d85e..050eb2e9e 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -27,6 +27,7 @@ #include "k_profiles.h" gamedata_t *gamedata = NULL; +boolean netUnlocked[MAXUNLOCKABLES]; // Map triggers for linedef executors // 32 triggers, one bit each @@ -442,7 +443,7 @@ void M_ClearSecrets(void) for (i = 0; i < MAXEMBLEMS; ++i) gamedata->collected[i] = false; for (i = 0; i < MAXUNLOCKABLES; ++i) - gamedata->unlocked[i] = false; + gamedata->unlocked[i] = netUnlocked[i] = false; for (i = 0; i < MAXCONDITIONSETS; ++i) gamedata->achieved[i] = false; @@ -728,37 +729,47 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa // Quick unlock checks // ------------------- -UINT8 M_SecretUnlocked(INT32 type) +boolean M_CheckNetUnlockByID(UINT8 unlockid) +{ + if (unlockid >= MAXUNLOCKABLES) + { + return true; // default permit + } + + if (netgame) + { + return netUnlocked[unlockid]; + } + + return gamedata->unlocked[unlockid]; +} + +boolean M_SecretUnlocked(INT32 type, boolean local) { INT32 i; - if (dedicated) - return true; - #if 0 (void)type; (void)i; return false; // for quick testing #else -#ifdef DEVELOP -#define CHADYES true -#else -#define CHADYES false -#endif - for (i = 0; i < MAXUNLOCKABLES; ++i) { - if (unlockables[i].type == type && gamedata->unlocked[i] != CHADYES) - return !CHADYES; + if (unlockables[i].type != type) + continue; + if ((local && gamedata->unlocked[i]) + || M_CheckNetUnlockByID(i)) + continue; + return false; } - return CHADYES; -#undef CHADYES + return true; + #endif //if 0 } -UINT8 M_MapLocked(INT32 mapnum) +boolean M_MapLocked(INT32 mapnum) { // Don't lock maps in dedicated servers. // That just makes hosts' lives hell. @@ -774,16 +785,11 @@ UINT8 M_MapLocked(INT32 mapnum) if (mapheaderinfo[mapnum-1]->cup) { - if ((mapheaderinfo[mapnum-1]->cup->unlockrequired < MAXUNLOCKABLES) - && (!gamedata->unlocked[mapheaderinfo[mapnum-1]->cup->unlockrequired])) + if (!M_CheckNetUnlockByID(mapheaderinfo[mapnum-1]->cup->unlockrequired)) return true; } - if ((mapheaderinfo[mapnum-1]->unlockrequired < MAXUNLOCKABLES) - && (!gamedata->unlocked[mapheaderinfo[mapnum-1]->unlockrequired])) - return true; - - return false; + return !M_CheckNetUnlockByID(mapheaderinfo[mapnum-1]->unlockrequired); } INT32 M_CountEmblems(void) diff --git a/src/m_cond.h b/src/m_cond.h index 5854808c8..5348b12f5 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -158,6 +158,9 @@ typedef struct extern gamedata_t *gamedata; +// Netsynced functional alternative to gamedata->unlocked +extern boolean netUnlocked[MAXUNLOCKABLES]; + extern conditionset_t conditionSets[MAXCONDITIONSETS]; extern emblem_t emblemlocations[MAXEMBLEMS]; extern unlockable_t unlockables[MAXUNLOCKABLES]; @@ -191,8 +194,9 @@ UINT8 M_CheckLevelEmblems(void); UINT8 M_CompletionEmblems(void); // Checking unlockable status -UINT8 M_SecretUnlocked(INT32 type); -UINT8 M_MapLocked(INT32 mapnum); +boolean M_CheckNetUnlockByID(UINT8 unlockid); +boolean M_SecretUnlocked(INT32 type, boolean local); +boolean M_MapLocked(INT32 mapnum); INT32 M_CountEmblems(void); // Emblem shit diff --git a/src/p_saveg.c b/src/p_saveg.c index 100672534..1882e2602 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -35,6 +35,7 @@ #include "p_polyobj.h" #include "lua_script.h" #include "p_slopes.h" +#include "m_cond.h" // netUnlocked // SRB2Kart #include "k_battle.h" @@ -4600,9 +4601,6 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride) //lastmapsaved = gamemap; lastmaploaded = gamemap; - tokenlist = 0; - token = 0; - savedata.emeralds = READUINT16(save_p)-357; READSTRINGN(save_p, testname, sizeof(testname)); @@ -4621,7 +4619,7 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride) static void P_NetArchiveMisc(boolean resending) { - size_t i; + size_t i, j; WRITEUINT32(save_p, ARCHIVEBLOCK_MISC); @@ -4642,7 +4640,14 @@ static void P_NetArchiveMisc(boolean resending) WRITEUINT32(save_p, pig); } - WRITEUINT32(save_p, tokenlist); + for (i = 0; i < MAXUNLOCKABLES;) + { + UINT8 btemp = 0; + for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j) + btemp |= (netUnlocked[j+i] << j); + WRITEUINT8(save_p, btemp); + i += j; + } WRITEUINT8(save_p, encoremode); @@ -4671,7 +4676,6 @@ static void P_NetArchiveMisc(boolean resending) WRITEUINT8(save_p, globools); } - WRITEUINT32(save_p, token); WRITEUINT32(save_p, bluescore); WRITEUINT32(save_p, redscore); @@ -4767,7 +4771,7 @@ static void P_NetArchiveMisc(boolean resending) static inline boolean P_NetUnArchiveMisc(boolean reloading) { - size_t i; + size_t i, j; size_t numTasks; if (READUINT32(save_p) != ARCHIVEBLOCK_MISC) @@ -4801,7 +4805,13 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) } } - tokenlist = READUINT32(save_p); + for (i = 0; i < MAXUNLOCKABLES;) + { + UINT8 rtemp = READUINT8(save_p); + for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j) + netUnlocked[j+i] = ((rtemp >> j) & 1); + i += j; + } encoremode = (boolean)READUINT8(save_p); @@ -4834,7 +4844,6 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) stoppedclock = !!(globools & (1<<1)); } - token = READUINT32(save_p); bluescore = READUINT32(save_p); redscore = READUINT32(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 3e9747505..3a4dd06af 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -6807,7 +6807,6 @@ static void P_InitLevelSettings(void) modulothing = 0; // special stage tokens, emeralds, and ring total - tokenbits = 0; runemeraldmanager = false; emeraldspawndelay = 60*TICRATE; diff --git a/src/p_spec.c b/src/p_spec.c index 06c658ee1..70450c576 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1495,14 +1495,14 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller { // An unlockable itself must be unlocked! INT32 unlockid = triggerline->args[1]; - if ((modifiedgame && !savemoddata) || (netgame || multiplayer)) + if (modifiedgame && !savemoddata) return false; else if (unlockid <= 0 || unlockid > MAXUNLOCKABLES) // limited by unlockable count { CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %hu): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid); return false; } - else if (!(gamedata->unlocked[unlockid])) + else if (!(M_CheckNetUnlockByID(unlockid))) return false; } break; diff --git a/src/r_skins.c b/src/r_skins.c index 3fea4b2cc..d38e7a41b 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -171,6 +171,7 @@ UINT8 *R_GetSkinAvailabilities(boolean demolock) if (unlockables[i].type != SECRET_SKIN) continue; + // NEVER EVER EVER M_CheckNetUnlockByID if (gamedata->unlocked[i] != true && !demolock) continue; @@ -250,6 +251,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins) } // Use the unlockables table directly + // NOTE: M_CheckNetUnlockByID would be correct in many circumstances... but not all. TODO figure out how to discern. return (boolean)(gamedata->unlocked[i]); }