From 0373848e05ba66dc9da0b16b1ae31f1bbc3bd555 Mon Sep 17 00:00:00 2001 From: Terry Hearst Date: Mon, 28 Feb 2022 21:30:13 -0500 Subject: [PATCH 1/9] POC autosplitter using pipes. Not functional at the moment --- .../source/autosplitter/consolesplitter.cpp | 92 +++++++++++++++++++ engine/source/autosplitter/consolesplitter.h | 27 ++++++ engine/source/game/main.cpp | 11 +++ game/MBU.torsion.exports | 2 + tools/cmake/marbleblast.cmake | 3 + 5 files changed, 135 insertions(+) create mode 100644 engine/source/autosplitter/consolesplitter.cpp create mode 100644 engine/source/autosplitter/consolesplitter.h diff --git a/engine/source/autosplitter/consolesplitter.cpp b/engine/source/autosplitter/consolesplitter.cpp new file mode 100644 index 00000000..62589265 --- /dev/null +++ b/engine/source/autosplitter/consolesplitter.cpp @@ -0,0 +1,92 @@ +#include +#include + +#include "consolesplitter.h" + + +Autosplitter *Autosplitter::smInstance = nullptr; + +void Autosplitter::init() +{ + if (smInstance) + return; + + smInstance = new Autosplitter(); +} + +void Autosplitter::destroy() +{ + if (!smInstance) + return; + + delete smInstance; +} + +Autosplitter *Autosplitter::get() +{ + if (!smInstance) + init(); + + return smInstance; +} + +Autosplitter::Autosplitter() +{ + mActive = false; + mPipe = CreateNamedPipeA( + AUTOSPLITTER_PIPE_NAME, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + AUTOSPLITTER_BUF_SIZE, + AUTOSPLITTER_BUF_SIZE, + 0, + nullptr); + if (mPipe == INVALID_HANDLE_VALUE) + { + Con::errorf("CreateNamePipe failed, GLE=%d.", GetLastError()); + Con::errorf("Autosplitter is disabled."); + return; + } + bool connected = ConnectNamedPipe(mPipe, nullptr); + if (!connected || GetLastError() != ERROR_PIPE_CONNECTED) + { + Con::errorf("Client not connected to autosplitter"); + Con::errorf("Autosplitter is disabled."); + return; + } + Con::printf("Autosplitter Initialized"); + mActive = true; +} + +Autosplitter::~Autosplitter() +{ + CloseHandle(mPipe); +} + +bool Autosplitter::sendData(const char *data) +{ + DWORD bytesWritten; + char buf[AUTOSPLITTER_BUF_SIZE]; + sprintf(buf, "%s\n", data); + U32 length = strlen(buf) + 1; + bool fSuccess = WriteFile(mPipe, buf, length, &bytesWritten, nullptr); + + return (fSuccess && bytesWritten == length); +} + +ConsoleFunction(sendAutosplitterData, void, 2, 2, "") +{ + Autosplitter *autosplitter = Autosplitter::get(); + if (!autosplitter->isActive()) + return; + + Con::printf("Sending autosplitter message '%s'", argv[1]); + + bool success = autosplitter->sendData(argv[1]); + + if (!success) + { + Con::warnf("Did not successfully send message to autosplitter"); + } +} diff --git a/engine/source/autosplitter/consolesplitter.h b/engine/source/autosplitter/consolesplitter.h new file mode 100644 index 00000000..8cb40f0a --- /dev/null +++ b/engine/source/autosplitter/consolesplitter.h @@ -0,0 +1,27 @@ +#ifndef _CONSOLESPLITTER_H_ +#define _CONSOLESPLITTER_H_ + +#include + +#include "console/console.h" + +constexpr const char *AUTOSPLITTER_PIPE_NAME = "\\\\.\\pipe\\mbuautosplitter"; +constexpr U32 AUTOSPLITTER_BUF_SIZE = 512; + +class Autosplitter +{ +public: + static void init(); + static void destroy(); + static Autosplitter *get(); + bool isActive() { return mActive; } + bool sendData(const char *data); +private: + Autosplitter(); + ~Autosplitter(); + static Autosplitter *smInstance; + bool mActive; + HANDLE mPipe; +}; + +#endif // _CONSOLESPLITTER_H_ diff --git a/engine/source/game/main.cpp b/engine/source/game/main.cpp index d062fa81..a2c3ea8e 100644 --- a/engine/source/game/main.cpp +++ b/engine/source/game/main.cpp @@ -62,6 +62,9 @@ #include "lightingSystem/sgFormatManager.h" #include "sfx/sfxSystem.h" +#ifdef TORQUE_OS_WIN +#include "autosplitter/consolesplitter.h" +#endif // TORQUE_OS_WIN #ifndef BUILD_TOOLS DemoGame GameObject; @@ -188,6 +191,10 @@ static bool initLibraries() RedBook::init(); SFXSystem::init(); +#ifdef TORQUE_OS_WIN + Autosplitter::init(); +#endif // TORQUE_OS_WIN + return true; } @@ -200,6 +207,10 @@ static void shutdownLibraries() if (ResourceManager) ResourceManager->purge(); +#ifdef TORQUE_OS_WIN + Autosplitter::destroy(); +#endif // TORQUE_OS_WIN + RedBook::destroy(); TSShapeInstance::destroy(); InteriorInstance::destroy(); diff --git a/game/MBU.torsion.exports b/game/MBU.torsion.exports index b8956332..c2a459f1 100644 --- a/game/MBU.torsion.exports +++ b/game/MBU.torsion.exports @@ -7895,6 +7895,8 @@ Valid inputs for xRumble/yRumble are [0 - 1]. Take a screenshot. @param format One of JPEG or PNG. + + sendAutosplitterData setClipboard string text diff --git a/tools/cmake/marbleblast.cmake b/tools/cmake/marbleblast.cmake index cf77384b..0f79a3b3 100644 --- a/tools/cmake/marbleblast.cmake +++ b/tools/cmake/marbleblast.cmake @@ -71,6 +71,9 @@ endif() # Always enabled paths first ############################################################################### addPath("${srcDir}/") # must come first :) +if (WIN32) + addPath("${srcDir}/autosplitter") +endif() addPath("${srcDir}/collision") addPath("${srcDir}/console") addPath("${srcDir}/core") From bf019fc2b9dae34ba2603f97b8d310212e4d67fb Mon Sep 17 00:00:00 2001 From: Terry Hearst Date: Sun, 6 Mar 2022 23:49:19 -0500 Subject: [PATCH 2/9] Use autosplitter.txt file instead of pipes --- .../source/autosplitter/consolesplitter.cpp | 49 ++++++------------- engine/source/autosplitter/consolesplitter.h | 10 ++-- game/marble/client/scripts/game.cs | 3 ++ game/marble/client/ui/LevelPreviewGui.gui | 3 ++ 4 files changed, 26 insertions(+), 39 deletions(-) diff --git a/engine/source/autosplitter/consolesplitter.cpp b/engine/source/autosplitter/consolesplitter.cpp index 62589265..7e47de75 100644 --- a/engine/source/autosplitter/consolesplitter.cpp +++ b/engine/source/autosplitter/consolesplitter.cpp @@ -1,7 +1,7 @@ -#include -#include +#include #include "consolesplitter.h" +#include "platform/platform.h" Autosplitter *Autosplitter::smInstance = nullptr; @@ -33,46 +33,30 @@ Autosplitter *Autosplitter::get() Autosplitter::Autosplitter() { mActive = false; - mPipe = CreateNamedPipeA( - AUTOSPLITTER_PIPE_NAME, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - AUTOSPLITTER_BUF_SIZE, - AUTOSPLITTER_BUF_SIZE, - 0, - nullptr); - if (mPipe == INVALID_HANDLE_VALUE) + mFilename = Platform::getPrefsPath(AUTOSPLITTER_FILE_NAME); + mFile.open(mFilename, std::ios_base::app); + if (!mFile.is_open()) { - Con::errorf("CreateNamePipe failed, GLE=%d.", GetLastError()); + Con::errorf("Failed to open autosplitter file %s.", mFilename); Con::errorf("Autosplitter is disabled."); return; } - bool connected = ConnectNamedPipe(mPipe, nullptr); - if (!connected || GetLastError() != ERROR_PIPE_CONNECTED) - { - Con::errorf("Client not connected to autosplitter"); - Con::errorf("Autosplitter is disabled."); - return; - } - Con::printf("Autosplitter Initialized"); + Con::printf("Autosplitter Initialized to file %s", mFilename.c_str()); mActive = true; } Autosplitter::~Autosplitter() { - CloseHandle(mPipe); + mFile.close(); + std::remove(mFilename.c_str()); } -bool Autosplitter::sendData(const char *data) +void Autosplitter::sendData(const char *data) { - DWORD bytesWritten; - char buf[AUTOSPLITTER_BUF_SIZE]; - sprintf(buf, "%s\n", data); - U32 length = strlen(buf) + 1; - bool fSuccess = WriteFile(mPipe, buf, length, &bytesWritten, nullptr); + if (!mActive) + return; - return (fSuccess && bytesWritten == length); + mFile << data << std::endl; } ConsoleFunction(sendAutosplitterData, void, 2, 2, "") @@ -83,10 +67,5 @@ ConsoleFunction(sendAutosplitterData, void, 2, 2, "") Con::printf("Sending autosplitter message '%s'", argv[1]); - bool success = autosplitter->sendData(argv[1]); - - if (!success) - { - Con::warnf("Did not successfully send message to autosplitter"); - } + autosplitter->sendData(argv[1]); } diff --git a/engine/source/autosplitter/consolesplitter.h b/engine/source/autosplitter/consolesplitter.h index 8cb40f0a..a6792c52 100644 --- a/engine/source/autosplitter/consolesplitter.h +++ b/engine/source/autosplitter/consolesplitter.h @@ -1,11 +1,12 @@ #ifndef _CONSOLESPLITTER_H_ #define _CONSOLESPLITTER_H_ -#include +#include +#include #include "console/console.h" -constexpr const char *AUTOSPLITTER_PIPE_NAME = "\\\\.\\pipe\\mbuautosplitter"; +constexpr const char *AUTOSPLITTER_FILE_NAME = "autosplitter.txt"; constexpr U32 AUTOSPLITTER_BUF_SIZE = 512; class Autosplitter @@ -15,13 +16,14 @@ class Autosplitter static void destroy(); static Autosplitter *get(); bool isActive() { return mActive; } - bool sendData(const char *data); + void sendData(const char *data); private: Autosplitter(); ~Autosplitter(); static Autosplitter *smInstance; bool mActive; - HANDLE mPipe; + std::string mFilename; + std::fstream mFile; }; #endif // _CONSOLESPLITTER_H_ diff --git a/game/marble/client/scripts/game.cs b/game/marble/client/scripts/game.cs index 4de89ffa..5b817aab 100644 --- a/game/marble/client/scripts/game.cs +++ b/game/marble/client/scripts/game.cs @@ -778,6 +778,9 @@ function clientCmdSetGameState(%state, %data) { $GameEndUserName = XBLiveGetUserName(); $GameEndNoAllowPause = true; + + // Tell autosplitter we finished the level + sendAutosplitterData("finish" SPC GameMissionInfo.getCurrentMission().level); } else $GameEndNoAllowPause = false; diff --git a/game/marble/client/ui/LevelPreviewGui.gui b/game/marble/client/ui/LevelPreviewGui.gui index 3f4f7dd1..b8e93d0b 100644 --- a/game/marble/client/ui/LevelPreviewGui.gui +++ b/game/marble/client/ui/LevelPreviewGui.gui @@ -219,6 +219,9 @@ function levelPreviewGui::onA() // Create a new server loadMission(GameMissionInfo.getCurrentMission().file, true); + + // Tell autosplitter to start + sendAutosplitterData("start" SPC GameMissionInfo.getCurrentMission().level); } else { From 30e811402cb8eeace71f5aa8a5f9ed163b209ed0 Mon Sep 17 00:00:00 2001 From: Terry Hearst Date: Mon, 7 Mar 2022 00:25:27 -0500 Subject: [PATCH 3/9] Send loading info to autosplitter (TODO: Figure out better line of code for the loading start data) --- game/common/server/missionLoad.cs | 4 +++- game/marble/server/scripts/game.cs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/game/common/server/missionLoad.cs b/game/common/server/missionLoad.cs index d00c8df5..50aedaf5 100644 --- a/game/common/server/missionLoad.cs +++ b/game/common/server/missionLoad.cs @@ -21,7 +21,9 @@ function loadMission( %missionName, %isFirstMission ) endMission(); echo("*** LOADING MISSION: " @ %missionName); echo("*** Stage 1 load"); - + + sendAutosplitterData("loading started"); + // Reset all of these clearCenterPrintAll(); clearBottomPrintAll(); diff --git a/game/marble/server/scripts/game.cs b/game/marble/server/scripts/game.cs index 4fea5e38..99484574 100644 --- a/game/marble/server/scripts/game.cs +++ b/game/marble/server/scripts/game.cs @@ -193,6 +193,7 @@ function startGame() return; } echo("Starting game"); + sendAutosplitterData("loading finished"); $Game::Running = true; $Game::Qualified = false; From 7c4bbd0c32c06328fe15f328a684a4e0fdc55a7e Mon Sep 17 00:00:00 2001 From: Terry Hearst Date: Mon, 7 Mar 2022 21:48:41 -0500 Subject: [PATCH 4/9] Support auto-splitting on easter egg collection --- game/marble/server/scripts/easter.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/game/marble/server/scripts/easter.cs b/game/marble/server/scripts/easter.cs index aa76a66f..ec93cec0 100644 --- a/game/marble/server/scripts/easter.cs +++ b/game/marble/server/scripts/easter.cs @@ -58,6 +58,8 @@ function clientCmdOnEasterEggPickup( %index ) return; } + sendAutosplitterData("egg" SPC %index); + if( hasFoundEgg( %index ) ) { serverPlay2d(easterNotNewSfx); From 16564d2cc8eb1bd5b4f7b77b08586764a6329005 Mon Sep 17 00:00:00 2001 From: Terry Hearst Date: Tue, 22 Mar 2022 18:45:18 -0400 Subject: [PATCH 5/9] Moving loading started and finished messages into (maybe?) better places --- game/common/server/missionLoad.cs | 2 -- game/marble/client/ui/LevelPreviewGui.gui | 1 + game/marble/server/scripts/game.cs | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/game/common/server/missionLoad.cs b/game/common/server/missionLoad.cs index 50aedaf5..b4a58900 100644 --- a/game/common/server/missionLoad.cs +++ b/game/common/server/missionLoad.cs @@ -22,8 +22,6 @@ function loadMission( %missionName, %isFirstMission ) echo("*** LOADING MISSION: " @ %missionName); echo("*** Stage 1 load"); - sendAutosplitterData("loading started"); - // Reset all of these clearCenterPrintAll(); clearBottomPrintAll(); diff --git a/game/marble/client/ui/LevelPreviewGui.gui b/game/marble/client/ui/LevelPreviewGui.gui index b8e93d0b..53992264 100644 --- a/game/marble/client/ui/LevelPreviewGui.gui +++ b/game/marble/client/ui/LevelPreviewGui.gui @@ -222,6 +222,7 @@ function levelPreviewGui::onA() // Tell autosplitter to start sendAutosplitterData("start" SPC GameMissionInfo.getCurrentMission().level); + sendAutosplitterData("loading started"); } else { diff --git a/game/marble/server/scripts/game.cs b/game/marble/server/scripts/game.cs index 99484574..e0c5343d 100644 --- a/game/marble/server/scripts/game.cs +++ b/game/marble/server/scripts/game.cs @@ -193,7 +193,6 @@ function startGame() return; } echo("Starting game"); - sendAutosplitterData("loading finished"); $Game::Running = true; $Game::Qualified = false; @@ -1842,6 +1841,8 @@ function setStartState(%client) %client.player.setMarbleTime($timeKeeper.player.getMarbleTime()); %client.player.setMode(Start); + + sendAutosplitterData("loading finished"); } // This is used to inform all of the clients that the From ce7179464f167d14913a3673f596e736a4a1522e Mon Sep 17 00:00:00 2001 From: Terry Hearst Date: Tue, 22 Mar 2022 19:33:37 -0400 Subject: [PATCH 6/9] Renamed autosplitter code, made platform agnostic --- .../{consolesplitter.cpp => autosplitter.cpp} | 2 +- .../autosplitter/{consolesplitter.h => autosplitter.h} | 6 +++--- engine/source/game/main.cpp | 8 +------- tools/cmake/marbleblast.cmake | 4 +--- 4 files changed, 6 insertions(+), 14 deletions(-) rename engine/source/autosplitter/{consolesplitter.cpp => autosplitter.cpp} (97%) rename engine/source/autosplitter/{consolesplitter.h => autosplitter.h} (86%) diff --git a/engine/source/autosplitter/consolesplitter.cpp b/engine/source/autosplitter/autosplitter.cpp similarity index 97% rename from engine/source/autosplitter/consolesplitter.cpp rename to engine/source/autosplitter/autosplitter.cpp index 7e47de75..3c320f3c 100644 --- a/engine/source/autosplitter/consolesplitter.cpp +++ b/engine/source/autosplitter/autosplitter.cpp @@ -1,6 +1,6 @@ #include -#include "consolesplitter.h" +#include "autosplitter.h" #include "platform/platform.h" diff --git a/engine/source/autosplitter/consolesplitter.h b/engine/source/autosplitter/autosplitter.h similarity index 86% rename from engine/source/autosplitter/consolesplitter.h rename to engine/source/autosplitter/autosplitter.h index a6792c52..6b67873b 100644 --- a/engine/source/autosplitter/consolesplitter.h +++ b/engine/source/autosplitter/autosplitter.h @@ -1,5 +1,5 @@ -#ifndef _CONSOLESPLITTER_H_ -#define _CONSOLESPLITTER_H_ +#ifndef _AUTOSPLITTER_H_ +#define _AUTOSPLITTER_H_ #include #include @@ -26,4 +26,4 @@ class Autosplitter std::fstream mFile; }; -#endif // _CONSOLESPLITTER_H_ +#endif // _AUTOSPLITTER_H_ diff --git a/engine/source/game/main.cpp b/engine/source/game/main.cpp index a2c3ea8e..237ad149 100644 --- a/engine/source/game/main.cpp +++ b/engine/source/game/main.cpp @@ -62,9 +62,7 @@ #include "lightingSystem/sgFormatManager.h" #include "sfx/sfxSystem.h" -#ifdef TORQUE_OS_WIN -#include "autosplitter/consolesplitter.h" -#endif // TORQUE_OS_WIN +#include "autosplitter/autosplitter.h" #ifndef BUILD_TOOLS DemoGame GameObject; @@ -191,9 +189,7 @@ static bool initLibraries() RedBook::init(); SFXSystem::init(); -#ifdef TORQUE_OS_WIN Autosplitter::init(); -#endif // TORQUE_OS_WIN return true; } @@ -207,9 +203,7 @@ static void shutdownLibraries() if (ResourceManager) ResourceManager->purge(); -#ifdef TORQUE_OS_WIN Autosplitter::destroy(); -#endif // TORQUE_OS_WIN RedBook::destroy(); TSShapeInstance::destroy(); diff --git a/tools/cmake/marbleblast.cmake b/tools/cmake/marbleblast.cmake index 0f79a3b3..3a95e405 100644 --- a/tools/cmake/marbleblast.cmake +++ b/tools/cmake/marbleblast.cmake @@ -71,9 +71,7 @@ endif() # Always enabled paths first ############################################################################### addPath("${srcDir}/") # must come first :) -if (WIN32) - addPath("${srcDir}/autosplitter") -endif() +addPath("${srcDir}/autosplitter") addPath("${srcDir}/collision") addPath("${srcDir}/console") addPath("${srcDir}/core") From a2f8ba80e7d1ef208c2bd42ec0cb31385f0f4cf4 Mon Sep 17 00:00:00 2001 From: Terry Hearst Date: Tue, 29 Mar 2022 22:05:08 -0400 Subject: [PATCH 7/9] Move loading finished autosplitter message into a more accurate place Still seems to be a few frames early --- game/marble/client/scripts/game.cs | 2 ++ game/marble/server/scripts/game.cs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/game/marble/client/scripts/game.cs b/game/marble/client/scripts/game.cs index 5b817aab..22f7497f 100644 --- a/game/marble/client/scripts/game.cs +++ b/game/marble/client/scripts/game.cs @@ -52,6 +52,8 @@ function clientCmdGameStart() // Map Pack Achievement code: $UserAchievements::MPGotABlueGem = 0; + + sendAutosplitterData("loading finished"); } function clientCmdGameEnd() diff --git a/game/marble/server/scripts/game.cs b/game/marble/server/scripts/game.cs index e0c5343d..4fea5e38 100644 --- a/game/marble/server/scripts/game.cs +++ b/game/marble/server/scripts/game.cs @@ -1841,8 +1841,6 @@ function setStartState(%client) %client.player.setMarbleTime($timeKeeper.player.getMarbleTime()); %client.player.setMode(Start); - - sendAutosplitterData("loading finished"); } // This is used to inform all of the clients that the From 93b917f4ccc65e82c0948803ad93a0a080e01471 Mon Sep 17 00:00:00 2001 From: Terry Hearst Date: Tue, 29 Mar 2022 22:38:52 -0400 Subject: [PATCH 8/9] End load removal in the actually correct spot this time --- game/marble/client/scripts/game.cs | 2 -- game/marble/client/scripts/playGui.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/game/marble/client/scripts/game.cs b/game/marble/client/scripts/game.cs index 22f7497f..5b817aab 100644 --- a/game/marble/client/scripts/game.cs +++ b/game/marble/client/scripts/game.cs @@ -52,8 +52,6 @@ function clientCmdGameStart() // Map Pack Achievement code: $UserAchievements::MPGotABlueGem = 0; - - sendAutosplitterData("loading finished"); } function clientCmdGameEnd() diff --git a/game/marble/client/scripts/playGui.cs b/game/marble/client/scripts/playGui.cs index 1947685e..432a8da0 100644 --- a/game/marble/client/scripts/playGui.cs +++ b/game/marble/client/scripts/playGui.cs @@ -80,6 +80,8 @@ %isFreeLevel = (%levelPlaying == $PDLC::MapPack0LevelStart); UpsellGui.displayPDLCUpsell = %isFreeLevel ? false : !%hasLevel; } + + sendAutosplitterData("loading finished"); } function PlayGui::show(%this) From 6dc385c329c58c814effbb855e0b38b0f75f8ac3 Mon Sep 17 00:00:00 2001 From: Terry Hearst Date: Sun, 3 Apr 2022 17:37:19 -0400 Subject: [PATCH 9/9] PR cleanup --- engine/source/autosplitter/autosplitter.cpp | 1 + game/common/server/missionLoad.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/source/autosplitter/autosplitter.cpp b/engine/source/autosplitter/autosplitter.cpp index 3c320f3c..c285e0cd 100644 --- a/engine/source/autosplitter/autosplitter.cpp +++ b/engine/source/autosplitter/autosplitter.cpp @@ -20,6 +20,7 @@ void Autosplitter::destroy() return; delete smInstance; + smInstance = nullptr; } Autosplitter *Autosplitter::get() diff --git a/game/common/server/missionLoad.cs b/game/common/server/missionLoad.cs index b4a58900..d00c8df5 100644 --- a/game/common/server/missionLoad.cs +++ b/game/common/server/missionLoad.cs @@ -21,7 +21,7 @@ function loadMission( %missionName, %isFirstMission ) endMission(); echo("*** LOADING MISSION: " @ %missionName); echo("*** Stage 1 load"); - + // Reset all of these clearCenterPrintAll(); clearBottomPrintAll();