diff --git a/engine/source/autosplitter/autosplitter.cpp b/engine/source/autosplitter/autosplitter.cpp index 0bce86ea..8fc40161 100644 --- a/engine/source/autosplitter/autosplitter.cpp +++ b/engine/source/autosplitter/autosplitter.cpp @@ -1,6 +1,7 @@ #include #include "autosplitter.h" +#include "console/console.h" #include "platform/platform.h" @@ -33,13 +34,26 @@ Autosplitter *Autosplitter::get() Autosplitter::Autosplitter() { + // Initialize AutosplitterData memory chunk + dMemset(&data, 0, sizeof(data)); + sprintf(data.signature, "OMBU_ASR_"); + // We add the "abcdef" procedurally to guarantee that the sigscan only finds this buffer, rather than potentially + // finding a full "OMBU_ASR_abcdef" string somewhere else in memory + for (int i = 0; i < 6; i++) + { + data.signature[i + 9] = 'a' + i; + } + data.currentLevel = -1; + // The rest is all zeros thanks to the memset, which is what we want. + + // Initialize autosplitter file mActive = false; mFilename = Platform::getPrefsPath(AUTOSPLITTER_FILE_NAME); mFile.open(mFilename, std::ios_base::app); if (!mFile.is_open()) { Con::errorf("Failed to open autosplitter file %s.", mFilename.c_str()); - Con::errorf("Autosplitter is disabled."); + Con::errorf("Autosplitter file is disabled. Autosplitter memory will still work."); return; } Con::printf("Autosplitter Initialized to file %s", mFilename.c_str()); @@ -70,3 +84,54 @@ ConsoleFunction(sendAutosplitterData, void, 2, 2, "") autosplitter->sendData(argv[1]); } + +ConsoleFunction(autosplitterSetLevel, void, 2, 2, "autosplitterSetLevel(level)") +{ + Autosplitter *autosplitter = Autosplitter::get(); + autosplitter->data.currentLevel = atoi(argv[1]); +} + +ConsoleFunction(autosplitterSetIsLoading, void, 2, 2, "autosplitterSetIsLoading(isLoading)") +{ + Autosplitter *autosplitter = Autosplitter::get(); + if (dStrcmp(argv[1], "false") == 0 || dStrcmp(argv[1], "0") == 0) + autosplitter->data.flags &= ~FLAG_IS_LOADING; + else + autosplitter->data.flags |= FLAG_IS_LOADING; +} + +ConsoleFunction(autosplitterSetLevelStarted, void, 2, 2, "autosplitterSetLevelStarted(levelStarted)") +{ + Autosplitter *autosplitter = Autosplitter::get(); + if (dStrcmp(argv[1], "false") == 0 || dStrcmp(argv[1], "0") == 0) + autosplitter->data.flags &= ~FLAG_LEVEL_STARTED; + else + autosplitter->data.flags |= FLAG_LEVEL_STARTED; +} + +ConsoleFunction(autosplitterSetLevelFinished, void, 2, 2, "autosplitterSetLevelFinished(levelFinished)") +{ + Autosplitter *autosplitter = Autosplitter::get(); + if (dStrcmp(argv[1], "false") == 0 || dStrcmp(argv[1], "0") == 0) + autosplitter->data.flags &= ~FLAG_LEVEL_FINISHED; + else + autosplitter->data.flags |= FLAG_LEVEL_FINISHED; +} + +ConsoleFunction(autosplitterSetEggFound, void, 2, 2, "autosplitterSetEggFound(eggFound)") +{ + Autosplitter *autosplitter = Autosplitter::get(); + if (dStrcmp(argv[1], "false") == 0 || dStrcmp(argv[1], "0") == 0) + autosplitter->data.flags &= ~FLAG_EGG_FOUND; + else + autosplitter->data.flags |= FLAG_EGG_FOUND; +} + +ConsoleFunction(autosplitterSetQuitToMenu, void, 2, 2, "autosplitterSetQuitToMenu(quitToMenu)") +{ + Autosplitter *autosplitter = Autosplitter::get(); + if (dStrcmp(argv[1], "false") == 0 || dStrcmp(argv[1], "0") == 0) + autosplitter->data.flags &= ~FLAG_QUIT_TO_MENU; + else + autosplitter->data.flags |= FLAG_QUIT_TO_MENU; +} diff --git a/engine/source/autosplitter/autosplitter.h b/engine/source/autosplitter/autosplitter.h index 6b67873b..dcc67fc8 100644 --- a/engine/source/autosplitter/autosplitter.h +++ b/engine/source/autosplitter/autosplitter.h @@ -1,14 +1,28 @@ #ifndef _AUTOSPLITTER_H_ #define _AUTOSPLITTER_H_ -#include #include -#include "console/console.h" +#include "platform/types.h" constexpr const char *AUTOSPLITTER_FILE_NAME = "autosplitter.txt"; constexpr U32 AUTOSPLITTER_BUF_SIZE = 512; +// This data can be scanned for in memory and used to drive autosplitters more precisely than via a log file +struct AutosplitterData { + char signature[16]; // Header set to "OMBU_ASR_abcdef", entry point for memory scan + S32 currentLevel; // The level index of the current loading/loaded level + U32 flags; // Boolean flags. 32 is probably overkill but is good for future proofing +}; + +enum { + FLAG_IS_LOADING = (1 << 0), + FLAG_LEVEL_STARTED = (1 << 1), + FLAG_LEVEL_FINISHED = (1 << 2), + FLAG_EGG_FOUND = (1 << 3), + FLAG_QUIT_TO_MENU = (1 << 4), +}; + class Autosplitter { public: @@ -17,6 +31,7 @@ class Autosplitter static Autosplitter *get(); bool isActive() { return mActive; } void sendData(const char *data); + AutosplitterData data; private: Autosplitter(); ~Autosplitter(); diff --git a/game/marble/client/scripts/default.bind.cs b/game/marble/client/scripts/default.bind.cs index ea10bbfb..ad5ad471 100644 --- a/game/marble/client/scripts/default.bind.cs +++ b/game/marble/client/scripts/default.bind.cs @@ -106,6 +106,8 @@ function pauseToggle(%defaultItem) { $Client::willfullDisconnect = true; + autosplitterSetQuitToMenu(true); + %killMission = MissionLoadingGui.isAwake(); // if we are hosting a multiplayer server, we just re-enter preview mode // without disconnecting diff --git a/game/marble/client/scripts/game.cs b/game/marble/client/scripts/game.cs index d8ae9c81..27c8843a 100644 --- a/game/marble/client/scripts/game.cs +++ b/game/marble/client/scripts/game.cs @@ -799,6 +799,7 @@ function clientCmdSetGameState(%state, %data) // Tell autosplitter we finished the level XBLivePresenceStopTimer(); + autosplitterSetLevelFinished(true); sendAutosplitterData("finish" SPC GameMissionInfo.getCurrentMission().level); } else @@ -860,6 +861,8 @@ function clientCmdSetGameState(%state, %data) // read the pair of leaderboards XBLiveReadStats($Leaderboard::SPOverall, %mission.level, "", true, true); } + autosplitterSetLevelFinished(false); + autosplitterSetEggFound(false); } // Check here to see if we need to pop the upsell diff --git a/game/marble/client/scripts/playGui.cs b/game/marble/client/scripts/playGui.cs index 2f96c39f..f72e5a9d 100644 --- a/game/marble/client/scripts/playGui.cs +++ b/game/marble/client/scripts/playGui.cs @@ -102,6 +102,8 @@ UpsellGui.displayPDLCUpsell = %isFreeLevel ? false : !%hasLevel; } + autosplitterSetIsLoading(false); + autosplitterSetLevelStarted(false); sendAutosplitterData("loading finished"); } diff --git a/game/marble/client/ui/LevelPreviewGui.gui b/game/marble/client/ui/LevelPreviewGui.gui index 0ecfc5ee..9e732f10 100644 --- a/game/marble/client/ui/LevelPreviewGui.gui +++ b/game/marble/client/ui/LevelPreviewGui.gui @@ -230,6 +230,10 @@ function levelPreviewGui::onA() loadMission(GameMissionInfo.getCurrentMission().file, true); // Tell autosplitter to start + autosplitterSetIsLoading(true); + autosplitterSetLevel(GameMissionInfo.getCurrentMission().level); + autosplitterSetLevelStarted(true); + autosplitterSetQuitToMenu(false); sendAutosplitterData("start" SPC GameMissionInfo.getCurrentMission().level); sendAutosplitterData("loading started"); } diff --git a/game/marble/server/scripts/easter.cs b/game/marble/server/scripts/easter.cs index 186d6980..962d1f8f 100644 --- a/game/marble/server/scripts/easter.cs +++ b/game/marble/server/scripts/easter.cs @@ -65,6 +65,7 @@ function clientCmdOnEasterEggPickup( %index ) return; } + autosplitterSetEggFound(true); sendAutosplitterData("egg" SPC %index); if( hasFoundEgg( %index ) )