From 3304b2e3db6ea88e5d8222fb80247b3fdf7e9d4e Mon Sep 17 00:00:00 2001 From: MatthewBeshay <92357869+MatthewBeshay@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:56:35 +1000 Subject: [PATCH] refactor: replace XuiActionPayload polling with server-owned typed action queue Drops the polymorphic XuiActionPayload variant and the per-pad setXuiServerAction/getXuiServerAction polling on IGameServices in favour of a std::variant of typed action structs in minecraft/server/ServerAction.h. MinecraftServer owns the queue, drains it from the tick loop via std::visit, and exposes queueServerAction() that any thread can call. Eliminates the dynamic_cast across the minecraft<-app boundary, the per-pad slot, and two busy-wait-for-Idle loops. --- targets/app/common/AppGameServices.cpp | 13 - targets/app/common/AppGameServices.h | 4 - targets/app/common/Game.h | 19 +- .../LevelGeneration/ConsoleSchematicFile.h | 21 - targets/app/common/GameSettingsManager.cpp | 78 ++-- targets/app/common/MenuController.cpp | 1 - targets/app/common/MenuController.h | 21 - .../app/common/Network/GameNetworkManager.cpp | 13 +- targets/app/common/SaveManager.cpp | 9 +- .../UI/All Platforms/IUIScene_PauseMenu.cpp | 22 +- .../app/common/UI/All Platforms/UIStructs.h | 6 - .../Debug/UIScene_DebugCreateSchematic.cpp | 86 ++-- .../Debug/UIScene_DebugCreateSchematic.h | 6 +- .../UI/Scenes/Debug/UIScene_DebugOverlay.cpp | 11 +- .../Scenes/Debug/UIScene_DebugSetCamera.cpp | 48 +-- .../UI/Scenes/Debug/UIScene_DebugSetCamera.h | 14 +- .../UIScene_PauseMenu.cpp | 14 +- targets/minecraft/GameEnums.h | 19 - targets/minecraft/IGameServices.h | 6 - targets/minecraft/XuiActionPayload.h | 25 -- targets/minecraft/client/gui/PauseScreen.cpp | 9 +- targets/minecraft/client/gui/Screen.cpp | 7 +- targets/minecraft/server/MinecraftServer.cpp | 386 ++++++++---------- targets/minecraft/server/MinecraftServer.h | 25 ++ targets/minecraft/server/ServerAction.h | 78 ++++ 25 files changed, 438 insertions(+), 503 deletions(-) delete mode 100644 targets/minecraft/XuiActionPayload.h create mode 100644 targets/minecraft/server/ServerAction.h diff --git a/targets/app/common/AppGameServices.cpp b/targets/app/common/AppGameServices.cpp index 323996113..139d6af18 100644 --- a/targets/app/common/AppGameServices.cpp +++ b/targets/app/common/AppGameServices.cpp @@ -173,23 +173,10 @@ void AppGameServices::setAction(int iPad, eXuiAction action, void* param) { game_.SetAction(iPad, action, param); } -void AppGameServices::setXuiServerAction(int iPad, eXuiServerAction action, - XuiActionPayload param) { - game_.SetXuiServerAction(iPad, action, std::move(param)); -} - eXuiAction AppGameServices::getXuiAction(int iPad) { return game_.GetXuiAction(iPad); } -eXuiServerAction AppGameServices::getXuiServerAction(int iPad) { - return game_.GetXuiServerAction(iPad); -} - -const XuiActionPayload& AppGameServices::getXuiServerActionParam(int iPad) { - return game_.GetXuiServerActionParam(iPad); -} - void AppGameServices::setGlobalXuiAction(eXuiAction action) { game_.SetGlobalXuiAction(action); } diff --git a/targets/app/common/AppGameServices.h b/targets/app/common/AppGameServices.h index 4cc9b43a1..6e2e4d9bf 100644 --- a/targets/app/common/AppGameServices.h +++ b/targets/app/common/AppGameServices.h @@ -75,11 +75,7 @@ public: // -- UI dispatch -- void setAction(int iPad, eXuiAction action, void* param) override; - void setXuiServerAction(int iPad, eXuiServerAction action, - XuiActionPayload param) override; eXuiAction getXuiAction(int iPad) override; - eXuiServerAction getXuiServerAction(int iPad) override; - const XuiActionPayload& getXuiServerActionParam(int iPad) override; void setGlobalXuiAction(eXuiAction action) override; void handleButtonPresses() override; void setTMSAction(int iPad, eTMSAction action) override; diff --git a/targets/app/common/Game.h b/targets/app/common/Game.h index a67163e99..49e25afe2 100644 --- a/targets/app/common/Game.h +++ b/targets/app/common/Game.h @@ -273,23 +273,6 @@ public: eTMSAction GetTMSAction(int iPad) { return m_menuController.getTMSAction(iPad); } - eXuiServerAction GetXuiServerAction(int iPad) { - return m_menuController.getXuiServerAction(iPad); - } - const XuiActionPayload& GetXuiServerActionParam(int iPad) { - return m_menuController.getXuiServerActionParam(iPad); - } - void SetXuiServerAction(int iPad, eXuiServerAction action, - XuiActionPayload param = {}) { - m_menuController.setXuiServerAction(iPad, action, std::move(param)); - } - eXuiServerAction GetGlobalXuiServerAction() { - return m_menuController.getGlobalXuiServerAction(); - } - void SetGlobalXuiServerAction(eXuiServerAction action) { - m_menuController.setGlobalXuiServerAction(action); - } - DisconnectPacket::eDisconnectReason GetDisconnectReason() { return m_networkController.getDisconnectReason(); } @@ -931,7 +914,7 @@ public: // void OverrideFontRenderer(bool set, bool immediate = true); // void ToggleFontRenderer() { // OverrideFontRenderer(!m_bFontRendererOverridden,false); } - BANNEDLIST (&BannedListA) + BANNEDLIST(&BannedListA) [XUSER_MAX_COUNT] = m_bannedListManager.BannedListA; public: diff --git a/targets/app/common/GameRules/LevelGeneration/ConsoleSchematicFile.h b/targets/app/common/GameRules/LevelGeneration/ConsoleSchematicFile.h index e51e3d46e..ee0e184f9 100644 --- a/targets/app/common/GameRules/LevelGeneration/ConsoleSchematicFile.h +++ b/targets/app/common/GameRules/LevelGeneration/ConsoleSchematicFile.h @@ -13,7 +13,6 @@ #include #include -#include "minecraft/XuiActionPayload.h" #include "minecraft/world/level/storage/ConsoleSaveFileIO/compression.h" #include "minecraft/world/phys/Vec3.h" @@ -43,26 +42,6 @@ public: void decrementRefCount() { --m_refCount; } bool shouldDelete() { return m_refCount <= 0; } - struct XboxSchematicInitParam : minecraft::XuiActionOwnedPayload { - char name[64]; - int startX; - int startY; - int startZ; - int endX; - int endY; - int endZ; - bool bSaveMobs; - - Compression::ECompressionTypes compressionType; - - XboxSchematicInitParam() { - memset(name, 0, 64 * (sizeof(char))); - startX = startY = startZ = endX = endY = endZ = 0; - bSaveMobs = false; - compressionType = Compression::eCompressionType_None; - } - }; - private: int m_xSize, m_ySize, m_zSize; std::vector > m_tileEntities; diff --git a/targets/app/common/GameSettingsManager.cpp b/targets/app/common/GameSettingsManager.cpp index 4b7cd0db1..9220fb7d2 100644 --- a/targets/app/common/GameSettingsManager.cpp +++ b/targets/app/common/GameSettingsManager.cpp @@ -1,14 +1,16 @@ #include "app/common/GameSettingsManager.h" +#include + +#include "app/common/Audio/SoundEngine.h" #include "app/common/Game.h" -#include "minecraft/GameHostOptions.h" -#include "minecraft/GameTypes.h" -#include "platform/profile/ProfileConstants.h" -#include "minecraft/GameEnums.h" -#include "minecraft/Console_Debug_enum.h" #include "app/common/Network/GameNetworkManager.h" #include "app/linux/LinuxGame.h" #include "app/linux/Linux_UIController.h" +#include "minecraft/Console_Debug_enum.h" +#include "minecraft/GameEnums.h" +#include "minecraft/GameHostOptions.h" +#include "minecraft/GameTypes.h" #include "minecraft/client/Minecraft.h" #include "minecraft/client/Options.h" #include "minecraft/client/gui/Gui.h" @@ -19,15 +21,14 @@ #include "minecraft/client/skins/TexturePackRepository.h" #include "minecraft/server/MinecraftServer.h" #include "minecraft/server/PlayerList.h" +#include "minecraft/server/ServerAction.h" #include "minecraft/server/level/ServerPlayer.h" #include "minecraft/world/entity/player/Player.h" #include "minecraft/world/level/tile/Tile.h" #include "platform/input/input.h" +#include "platform/profile/ProfileConstants.h" #include "platform/renderer/renderer.h" #include "platform/storage/storage.h" -#include "app/common/Audio/SoundEngine.h" - -#include GameSettingsManager::GameSettingsManager() { memset(GameSettingsA, 0, sizeof(GameSettingsA)); @@ -125,17 +126,16 @@ int GameSettingsManager::setDefaultOptions( setGameSettings(iPad, eGameSetting_PS3_EULA_Read, 0); if (!app.GetGameStarted()) { - GameSettingsA[iPad]->ucLanguage = - MINECRAFT_LANGUAGE_DEFAULT; - GameSettingsA[iPad]->ucLocale = - MINECRAFT_LANGUAGE_DEFAULT; + GameSettingsA[iPad]->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; + GameSettingsA[iPad]->ucLocale = MINECRAFT_LANGUAGE_DEFAULT; } return 0; } int GameSettingsManager::defaultOptionsCallback( - void* pParam, IPlatformProfile::PROFILESETTINGS* pSettings, const int iPad) { + void* pParam, IPlatformProfile::PROFILESETTINGS* pSettings, + const int iPad) { Game* pApp = (Game*)pParam; pApp->DebugPrintf("Setting default options for player %d", iPad); @@ -196,8 +196,7 @@ int GameSettingsManager::oldProfileVersionCallback( pGameSettings->uiBitmaskValues |= GAMESETTING_DISPLAYHAND; pGameSettings->uiBitmaskValues |= GAMESETTING_CUSTOMSKINANIM; pGameSettings->uiBitmaskValues |= GAMESETTING_DEATHMESSAGES; - pGameSettings->uiBitmaskValues |= - (GAMESETTING_UISIZE & 0x00000800); + pGameSettings->uiBitmaskValues |= (GAMESETTING_UISIZE & 0x00000800); pGameSettings->uiBitmaskValues |= (GAMESETTING_UISIZE_SPLITSCREEN & 0x00004000); pGameSettings->uiBitmaskValues |= GAMESETTING_ANIMATEDCHARACTER; @@ -285,8 +284,10 @@ void GameSettingsManager::actionGameSettings(int iPad, eGameSetting eVal) { if (bInGame && g_NetworkManager.IsHost() && (iPad == PlatformProfile.GetPrimaryPad())) { - app.SetXuiServerAction( - iPad, eXuiServerAction_ServerSettingChanged_Difficulty); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::BroadcastSettingChanged{ + minecraft::server::BroadcastSettingChanged::Kind:: + Difficulty}); } } else { app.DebugPrintf( @@ -311,30 +312,30 @@ void GameSettingsManager::actionGameSettings(int iPad, eGameSetting eVal) { case eGameSetting_ControlSouthPaw: if (GameSettingsA[iPad]->usBitmaskValues & 0x80) { PlatformInput.SetJoypadStickAxisMap(iPad, AXIS_MAP_LX, - AXIS_MAP_RX); + AXIS_MAP_RX); PlatformInput.SetJoypadStickAxisMap(iPad, AXIS_MAP_LY, - AXIS_MAP_RY); + AXIS_MAP_RY); PlatformInput.SetJoypadStickAxisMap(iPad, AXIS_MAP_RX, - AXIS_MAP_LX); + AXIS_MAP_LX); PlatformInput.SetJoypadStickAxisMap(iPad, AXIS_MAP_RY, - AXIS_MAP_LY); + AXIS_MAP_LY); PlatformInput.SetJoypadStickTriggerMap(iPad, TRIGGER_MAP_0, - TRIGGER_MAP_1); + TRIGGER_MAP_1); PlatformInput.SetJoypadStickTriggerMap(iPad, TRIGGER_MAP_1, - TRIGGER_MAP_0); + TRIGGER_MAP_0); } else { PlatformInput.SetJoypadStickAxisMap(iPad, AXIS_MAP_LX, - AXIS_MAP_LX); + AXIS_MAP_LX); PlatformInput.SetJoypadStickAxisMap(iPad, AXIS_MAP_LY, - AXIS_MAP_LY); + AXIS_MAP_LY); PlatformInput.SetJoypadStickAxisMap(iPad, AXIS_MAP_RX, - AXIS_MAP_RX); + AXIS_MAP_RX); PlatformInput.SetJoypadStickAxisMap(iPad, AXIS_MAP_RY, - AXIS_MAP_RY); + AXIS_MAP_RY); PlatformInput.SetJoypadStickTriggerMap(iPad, TRIGGER_MAP_0, - TRIGGER_MAP_0); + TRIGGER_MAP_0); PlatformInput.SetJoypadStickTriggerMap(iPad, TRIGGER_MAP_1, - TRIGGER_MAP_1); + TRIGGER_MAP_1); } break; case eGameSetting_SplitScreenVertical: @@ -352,8 +353,10 @@ void GameSettingsManager::actionGameSettings(int iPad, eGameSetting eVal) { eGameHostOption_Gamertags, ((GameSettingsA[iPad]->usBitmaskValues & 0x0008) != 0) ? 1 : 0); - app.SetXuiServerAction( - iPad, eXuiServerAction_ServerSettingChanged_Gamertags); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::BroadcastSettingChanged{ + minecraft::server::BroadcastSettingChanged::Kind:: + Gamertags}); PlayerList* players = MinecraftServer::getInstance()->getPlayerList(); @@ -409,8 +412,10 @@ void GameSettingsManager::actionGameSettings(int iPad, eGameSetting eVal) { app.SetGameHostOption( eGameHostOption_BedrockFog, getGameSettings(iPad, eGameSetting_BedrockFog) ? 1 : 0); - app.SetXuiServerAction( - iPad, eXuiServerAction_ServerSettingChanged_BedrockFog); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::BroadcastSettingChanged{ + minecraft::server::BroadcastSettingChanged::Kind:: + BedrockFog}); } } break; case eGameSetting_DisplayHUD: @@ -466,8 +471,7 @@ unsigned char GameSettingsManager::getMinecraftLanguage(int iPad) { } } -void GameSettingsManager::setMinecraftLocale(int iPad, - unsigned char ucLocale) { +void GameSettingsManager::setMinecraftLocale(int iPad, unsigned char ucLocale) { GameSettingsA[iPad]->ucLocale = ucLocale; GameSettingsA[iPad]->bSettingsChanged = true; } @@ -1347,8 +1351,8 @@ void GameSettingsManager::setGameHostOption(unsigned int& uiHostSettings, } } -unsigned int GameSettingsManager::getGameHostOption( - unsigned int uiHostSettings, eGameHostOption eVal) { +unsigned int GameSettingsManager::getGameHostOption(unsigned int uiHostSettings, + eGameHostOption eVal) { switch (eVal) { case eGameHostOption_FriendsOfFriends: return (uiHostSettings & GAME_HOST_OPTION_BITMASK_FRIENDSOFFRIENDS); diff --git a/targets/app/common/MenuController.cpp b/targets/app/common/MenuController.cpp index 2c5f6c323..d5e0f541a 100644 --- a/targets/app/common/MenuController.cpp +++ b/targets/app/common/MenuController.cpp @@ -36,7 +36,6 @@ MenuController::MenuController() { m_uiOpacityCountDown[i] = 0; } m_eGlobalXuiAction = eAppAction_Idle; - m_eGlobalXuiServerAction = eXuiServerAction_Idle; } void MenuController::setAction(int iPad, eXuiAction action, void* param) { diff --git a/targets/app/common/MenuController.h b/targets/app/common/MenuController.h index fbaaa0071..f3fb04008 100644 --- a/targets/app/common/MenuController.h +++ b/targets/app/common/MenuController.h @@ -5,7 +5,6 @@ #include #include "app/common/App_structs.h" -#include "minecraft/XuiActionPayload.h" #include "platform/XboxStubs.h" #include "platform/storage/storage.h" @@ -70,25 +69,8 @@ public: // Action management void setAction(int iPad, eXuiAction action, void* param = nullptr); eXuiAction getXuiAction(int iPad) { return m_eXuiAction[iPad]; } - void setXuiServerAction(int iPad, eXuiServerAction action, - XuiActionPayload param = {}) { - m_eXuiServerAction[iPad] = action; - m_eXuiServerActionParam[iPad] = std::move(param); - } - eXuiServerAction getXuiServerAction(int iPad) { - return m_eXuiServerAction[iPad]; - } - const XuiActionPayload& getXuiServerActionParam(int iPad) { - return m_eXuiServerActionParam[iPad]; - } eXuiAction getGlobalXuiAction() { return m_eGlobalXuiAction; } void setGlobalXuiAction(eXuiAction action) { m_eGlobalXuiAction = action; } - eXuiServerAction getGlobalXuiServerAction() { - return m_eGlobalXuiServerAction; - } - void setGlobalXuiServerAction(eXuiServerAction action) { - m_eGlobalXuiServerAction = action; - } // TMS action void setTMSAction(int iPad, eTMSAction action) { @@ -141,9 +123,6 @@ private: eTMSAction m_eTMSAction[XUSER_MAX_COUNT]; void* m_eXuiActionParam[XUSER_MAX_COUNT]; eXuiAction m_eGlobalXuiAction; - eXuiServerAction m_eXuiServerAction[XUSER_MAX_COUNT]; - XuiActionPayload m_eXuiServerActionParam[XUSER_MAX_COUNT]; - eXuiServerAction m_eGlobalXuiServerAction; unsigned int m_uiOpacityCountDown[XUSER_MAX_COUNT]; diff --git a/targets/app/common/Network/GameNetworkManager.cpp b/targets/app/common/Network/GameNetworkManager.cpp index d1f308480..99f39911d 100644 --- a/targets/app/common/Network/GameNetworkManager.cpp +++ b/targets/app/common/Network/GameNetworkManager.cpp @@ -35,6 +35,7 @@ #include "minecraft/network/platform/NetworkPlayerInterface.h" #include "minecraft/server/MinecraftServer.h" #include "minecraft/server/PlayerList.h" +#include "minecraft/server/ServerAction.h" #include "minecraft/server/level/ServerPlayer.h" #include "minecraft/server/network/PlayerConnection.h" #include "minecraft/world/entity/Entity.h" @@ -883,13 +884,7 @@ int CGameNetworkManager::ChangeSessionTypeThreadProc(void* lpParam) { pMinecraft->progressRenderer->progressStage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME); - while (app.GetXuiServerAction(PlatformProfile.GetPrimaryPad()) != - eXuiServerAction_Idle && - !MinecraftServer::serverHalted()) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_PauseServer, true); + pServer->queueServerAction(minecraft::server::PauseServer{true}); // wait for the server to be in a non-ticking state pServer->m_serverPausedEvent->waitForSignal(C4JThread::kInfiniteTimeout); @@ -1016,8 +1011,8 @@ int CGameNetworkManager::ChangeSessionTypeThreadProc(void* lpParam) { // Start the game again app.SetGameStarted(true); - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_PauseServer, false); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::PauseServer{false}); app.SetChangingSessionType(false); app.SetReallyChangingSessionType(false); diff --git a/targets/app/common/SaveManager.cpp b/targets/app/common/SaveManager.cpp index d3db14610..ae1fa00de 100644 --- a/targets/app/common/SaveManager.cpp +++ b/targets/app/common/SaveManager.cpp @@ -6,6 +6,7 @@ #include "app/common/Network/GameNetworkManager.h" #include "app/linux/LinuxGame.h" #include "minecraft/server/MinecraftServer.h" +#include "minecraft/server/ServerAction.h" #include "platform/profile/profile.h" void SaveManager::setAutosaveTimerTime(int settingValue) { @@ -34,8 +35,8 @@ void SaveManager::lock() { if (g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1) { - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_PauseServer, true); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::PauseServer{true}); } } } @@ -52,8 +53,8 @@ void SaveManager::unlock() { if (g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1) { - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_PauseServer, false); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::PauseServer{false}); } } } diff --git a/targets/app/common/UI/All Platforms/IUIScene_PauseMenu.cpp b/targets/app/common/UI/All Platforms/IUIScene_PauseMenu.cpp index ad1c9c2f6..bd3f67303 100644 --- a/targets/app/common/UI/All Platforms/IUIScene_PauseMenu.cpp +++ b/targets/app/common/UI/All Platforms/IUIScene_PauseMenu.cpp @@ -9,8 +9,6 @@ #include #include -#include "platform/profile/profile.h" -#include "minecraft/GameEnums.h" #include "app/common/DLC/DLCManager.h" #include "app/common/DLC/DLCPack.h" #include "app/common/GameRules/GameRuleManager.h" @@ -18,7 +16,7 @@ #include "app/common/UI/UIScene.h" #include "app/linux/LinuxGame.h" #include "app/linux/Linux_UIController.h" -#include "minecraft/world/level/storage/ConsoleSaveFileIO/compression.h" +#include "minecraft/GameEnums.h" #include "minecraft/client/Minecraft.h" #include "minecraft/client/ProgressRenderer.h" #include "minecraft/client/multiplayer/MultiPlayerLevel.h" @@ -26,6 +24,9 @@ #include "minecraft/client/skins/TexturePackRepository.h" #include "minecraft/network/packet/DisconnectPacket.h" #include "minecraft/server/MinecraftServer.h" +#include "minecraft/server/ServerAction.h" +#include "minecraft/world/level/storage/ConsoleSaveFileIO/compression.h" +#include "platform/profile/profile.h" #include "strings.h" class TexturePack; @@ -211,13 +212,8 @@ int IUIScene_PauseMenu::WarningTrialTexturePackReturned( int IUIScene_PauseMenu::SaveWorldThreadProc(void* lpParameter) { bool bAutosave = (bool)lpParameter; - if (bAutosave) { - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_AutoSaveGame); - } else { - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_SaveGame); - } + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::SaveGame{bAutosave}); // Share AABB & Vec3 pools with default (main thread) - should be ok as long // as we don't tick the main thread whilst this thread is running @@ -229,12 +225,6 @@ int IUIScene_PauseMenu::SaveWorldThreadProc(void* lpParameter) { app.SetGameStarted(false); - while (app.GetXuiServerAction(PlatformProfile.GetPrimaryPad()) != - eXuiServerAction_Idle && - !MinecraftServer::serverHalted()) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - if (!MinecraftServer::serverHalted() && !app.GetChangingSessionType()) app.SetGameStarted(true); diff --git a/targets/app/common/UI/All Platforms/UIStructs.h b/targets/app/common/UI/All Platforms/UIStructs.h index 6ca6d7545..61cb842b3 100644 --- a/targets/app/common/UI/All Platforms/UIStructs.h +++ b/targets/app/common/UI/All Platforms/UIStructs.h @@ -8,7 +8,6 @@ #include "UIEnums.h" #include "minecraft/GameHostOptions.h" -#include "minecraft/XuiActionPayload.h" #include "platform/C4JThread.h" #include "platform/storage/storage.h" @@ -418,11 +417,6 @@ typedef struct _InGamePlayerOptionsInitData { unsigned int playerPrivileges; } InGamePlayerOptionsInitData; -struct DebugSetCameraPosition : minecraft::XuiActionOwnedPayload { - int player; - double m_camX, m_camY, m_camZ, m_yRot, m_elev; -}; - typedef struct _TeleportMenuInitData { int iPad; bool teleportToPlayer; diff --git a/targets/app/common/UI/Scenes/Debug/UIScene_DebugCreateSchematic.cpp b/targets/app/common/UI/Scenes/Debug/UIScene_DebugCreateSchematic.cpp index b44a29812..ead8d9ff0 100644 --- a/targets/app/common/UI/Scenes/Debug/UIScene_DebugCreateSchematic.cpp +++ b/targets/app/common/UI/Scenes/Debug/UIScene_DebugCreateSchematic.cpp @@ -3,7 +3,6 @@ #include -#include "app/common/GameRules/LevelGeneration/ConsoleSchematicFile.h" #include "app/common/UI/Controls/UIControl_Button.h" #include "app/common/UI/Controls/UIControl_CheckBox.h" #include "app/common/UI/Controls/UIControl_Label.h" @@ -13,6 +12,8 @@ #include "app/linux/LinuxGame.h" #include "app/linux/Linux_UIController.h" #include "minecraft/GameEnums.h" +#include "minecraft/server/MinecraftServer.h" +#include "minecraft/server/ServerAction.h" #include "minecraft/world/level/Level.h" #include "minecraft/world/level/chunk/ChunkSource.h" #include "minecraft/world/level/storage/ConsoleSaveFileIO/compression.h" @@ -52,7 +53,8 @@ UIScene_DebugCreateSchematic::UIScene_DebugCreateSchematic(int iPad, m_buttonCreate.init("Create", eControl_Create); - m_data = new ConsoleSchematicFile::XboxSchematicInitParam(); + m_data = {}; + m_data.compressionType = Compression::eCompressionType_None; } std::string UIScene_DebugCreateSchematic::getMoviePath() { @@ -86,40 +88,36 @@ void UIScene_DebugCreateSchematic::handlePress(F64 controlId, F64 childId) { switch ((int)controlId) { case eControl_Create: { // We want the start to be even - if (m_data->startX > 0 && m_data->startX % 2 != 0) - m_data->startX -= 1; - else if (m_data->startX < 0 && m_data->startX % 2 != 0) - m_data->startX -= 1; - if (m_data->startY < 0) - m_data->startY = 0; - else if (m_data->startY > 0 && m_data->startY % 2 != 0) - m_data->startY -= 1; - if (m_data->startZ > 0 && m_data->startZ % 2 != 0) - m_data->startZ -= 1; - else if (m_data->startZ < 0 && m_data->startZ % 2 != 0) - m_data->startZ -= 1; + if (m_data.startX > 0 && m_data.startX % 2 != 0) + m_data.startX -= 1; + else if (m_data.startX < 0 && m_data.startX % 2 != 0) + m_data.startX -= 1; + if (m_data.startY < 0) + m_data.startY = 0; + else if (m_data.startY > 0 && m_data.startY % 2 != 0) + m_data.startY -= 1; + if (m_data.startZ > 0 && m_data.startZ % 2 != 0) + m_data.startZ -= 1; + else if (m_data.startZ < 0 && m_data.startZ % 2 != 0) + m_data.startZ -= 1; // We want the end to be odd to have a total size that is even - if (m_data->endX > 0 && m_data->endX % 2 == 0) - m_data->endX += 1; - else if (m_data->endX < 0 && m_data->endX % 2 == 0) - m_data->endX += 1; - if (m_data->endY > Level::maxBuildHeight) - m_data->endY = Level::maxBuildHeight; - else if (m_data->endY > 0 && m_data->endY % 2 == 0) - m_data->endY += 1; - else if (m_data->endY < 0 && m_data->endY % 2 == 0) - m_data->endY += 1; - if (m_data->endZ > 0 && m_data->endZ % 2 == 0) - m_data->endZ += 1; - else if (m_data->endZ < 0 && m_data->endZ % 2 == 0) - m_data->endZ += 1; + if (m_data.endX > 0 && m_data.endX % 2 == 0) + m_data.endX += 1; + else if (m_data.endX < 0 && m_data.endX % 2 == 0) + m_data.endX += 1; + if (m_data.endY > Level::maxBuildHeight) + m_data.endY = Level::maxBuildHeight; + else if (m_data.endY > 0 && m_data.endY % 2 == 0) + m_data.endY += 1; + else if (m_data.endY < 0 && m_data.endY % 2 == 0) + m_data.endY += 1; + if (m_data.endZ > 0 && m_data.endZ % 2 == 0) + m_data.endZ += 1; + else if (m_data.endZ < 0 && m_data.endZ % 2 == 0) + m_data.endZ += 1; - std::unique_ptr payload(m_data); - m_data = nullptr; // ownership transferred to the action - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_ExportSchematic, - std::move(payload)); + MinecraftServer::getInstance()->queueServerAction(m_data); navigateBack(); } break; @@ -145,13 +143,13 @@ void UIScene_DebugCreateSchematic::handleCheckboxToggled(F64 controlId, bool selected) { switch ((int)controlId) { case eControl_SaveMobs: - m_data->bSaveMobs = selected; + m_data.saveMobs = selected; break; case eControl_UseCompression: if (selected) - m_data->compressionType = APPROPRIATE_COMPRESSION_TYPE; + m_data.compressionType = APPROPRIATE_COMPRESSION_TYPE; else - m_data->compressionType = Compression::eCompressionType_RLE; + m_data.compressionType = Compression::eCompressionType_RLE; break; } } @@ -166,9 +164,9 @@ int UIScene_DebugCreateSchematic::handleKeyboardComplete(bool bRes) { case eControl_Name: m_textInputName.setLabel(value); if (!value.empty()) { - snprintf(m_data->name, 64, "%s", value.c_str()); + snprintf(m_data.name, 64, "%s", value.c_str()); } else { - snprintf(m_data->name, 64, "schematic"); + snprintf(m_data.name, 64, "schematic"); } break; case eControl_StartX: @@ -176,7 +174,7 @@ int UIScene_DebugCreateSchematic::handleKeyboardComplete(bool bRes) { if (iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) { - m_data->startX = iVal; + m_data.startX = iVal; } break; case eControl_StartY: @@ -184,7 +182,7 @@ int UIScene_DebugCreateSchematic::handleKeyboardComplete(bool bRes) { if (iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) { - m_data->startY = iVal; + m_data.startY = iVal; } break; case eControl_StartZ: @@ -192,7 +190,7 @@ int UIScene_DebugCreateSchematic::handleKeyboardComplete(bool bRes) { if (iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) { - m_data->startZ = iVal; + m_data.startZ = iVal; } break; case eControl_EndX: @@ -200,7 +198,7 @@ int UIScene_DebugCreateSchematic::handleKeyboardComplete(bool bRes) { if (iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) { - m_data->endX = iVal; + m_data.endX = iVal; } break; case eControl_EndY: @@ -208,7 +206,7 @@ int UIScene_DebugCreateSchematic::handleKeyboardComplete(bool bRes) { if (iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) { - m_data->endY = iVal; + m_data.endY = iVal; } break; case eControl_EndZ: @@ -216,7 +214,7 @@ int UIScene_DebugCreateSchematic::handleKeyboardComplete(bool bRes) { if (iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) { - m_data->endZ = iVal; + m_data.endZ = iVal; } break; default: diff --git a/targets/app/common/UI/Scenes/Debug/UIScene_DebugCreateSchematic.h b/targets/app/common/UI/Scenes/Debug/UIScene_DebugCreateSchematic.h index 8cfeb20da..cf96543d1 100644 --- a/targets/app/common/UI/Scenes/Debug/UIScene_DebugCreateSchematic.h +++ b/targets/app/common/UI/Scenes/Debug/UIScene_DebugCreateSchematic.h @@ -2,7 +2,6 @@ #ifdef _DEBUG_MENUS_ENABLED #include -#include "app/common/GameRules/LevelGeneration/ConsoleSchematicFile.h" #include "app/common/UI/All Platforms/UIEnums.h" #include "app/common/UI/Controls/UIControl_Button.h" #include "app/common/UI/Controls/UIControl_CheckBox.h" @@ -10,6 +9,7 @@ #include "app/common/UI/Controls/UIControl_TextInput.h" #include "app/common/UI/UIScene.h" #include "app/linux/Iggy/include/rrCore.h" +#include "minecraft/server/ServerAction.h" class UILayer; @@ -30,7 +30,9 @@ private: eControls m_keyboardCallbackControl; - ConsoleSchematicFile::XboxSchematicInitParam* m_data; + // Local UI state collected from the form. Sent to the server as an + // ExportSchematic action when the user hits Create. + minecraft::server::ExportSchematic m_data; public: UIScene_DebugCreateSchematic(int iPad, void* initData, diff --git a/targets/app/common/UI/Scenes/Debug/UIScene_DebugOverlay.cpp b/targets/app/common/UI/Scenes/Debug/UIScene_DebugOverlay.cpp index d07d22aac..dcf252c21 100644 --- a/targets/app/common/UI/Scenes/Debug/UIScene_DebugOverlay.cpp +++ b/targets/app/common/UI/Scenes/Debug/UIScene_DebugOverlay.cpp @@ -39,6 +39,7 @@ class UILayer; #include "minecraft/client/multiplayer/MultiPlayerLocalPlayer.h" #include "minecraft/client/renderer/GameRenderer.h" #include "minecraft/server/MinecraftServer.h" +#include "minecraft/server/ServerAction.h" #include "util/StringHelpers.h" UIScene_DebugOverlay::UIScene_DebugOverlay(int iPad, void* initData, @@ -208,9 +209,9 @@ void UIScene_DebugOverlay::handlePress(F64 controlId, F64 childId) { case eControl_Mobs: { int id = childId; if (id < m_mobFactories.size()) { - app.SetXuiServerAction( - PlatformProfile.GetPrimaryPad(), eXuiServerAction_SpawnMob, - static_cast(m_mobFactories[id])); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::SpawnDebugMob{ + 0, static_cast(m_mobFactories[id])}); } } break; case eControl_Enchantments: { @@ -245,8 +246,8 @@ void UIScene_DebugOverlay::handlePress(F64 controlId, F64 childId) { conn->send(ToggleDownfallCommand::preparePacket()); } break; case eControl_Thunder: - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_ToggleThunder); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::ToggleThunder{}); break; case eControl_ResetTutorial: Tutorial::debugResetPlayerSavedProgress( diff --git a/targets/app/common/UI/Scenes/Debug/UIScene_DebugSetCamera.cpp b/targets/app/common/UI/Scenes/Debug/UIScene_DebugSetCamera.cpp index 5037a116d..cc301bee4 100644 --- a/targets/app/common/UI/Scenes/Debug/UIScene_DebugSetCamera.cpp +++ b/targets/app/common/UI/Scenes/Debug/UIScene_DebugSetCamera.cpp @@ -5,7 +5,6 @@ #include -#include "app/common/UI/All Platforms/UIStructs.h" #include "app/common/UI/Controls/UIControl_Button.h" #include "app/common/UI/Controls/UIControl_CheckBox.h" #include "app/common/UI/Controls/UIControl_Label.h" @@ -23,6 +22,8 @@ class UILayer; #ifdef _DEBUG_MENUS_ENABLED #include "minecraft/client/Minecraft.h" #include "minecraft/client/multiplayer/MultiPlayerLocalPlayer.h" +#include "minecraft/server/MinecraftServer.h" +#include "minecraft/server/ServerAction.h" #include "util/StringHelpers.h" UIScene_DebugSetCamera::UIScene_DebugSetCamera(int iPad, void* initData, @@ -32,38 +33,38 @@ UIScene_DebugSetCamera::UIScene_DebugSetCamera(int iPad, void* initData, initialiseMovie(); int playerNo = 0; - currentPosition = new DebugSetCameraPosition(); - currentPosition->player = playerNo; + currentPosition = {}; + currentPosition.player = playerNo; Minecraft* pMinecraft = Minecraft::GetInstance(); if (pMinecraft != nullptr) { Vec3 vec = pMinecraft->localplayers[playerNo]->getPos(1.0); - currentPosition->m_camX = vec.x; - currentPosition->m_camY = + currentPosition.m_camX = vec.x; + currentPosition.m_camY = vec.y - 1.62; // pMinecraft->localplayers[playerNo]->getHeadHeight(); - currentPosition->m_camZ = vec.z; + currentPosition.m_camZ = vec.z; - currentPosition->m_yRot = pMinecraft->localplayers[playerNo]->yRot; - currentPosition->m_elev = pMinecraft->localplayers[playerNo]->xRot; + currentPosition.m_yRot = pMinecraft->localplayers[playerNo]->yRot; + currentPosition.m_elev = pMinecraft->localplayers[playerNo]->xRot; } char TempString[256]; - snprintf(TempString, 256, "%f", currentPosition->m_camX); + snprintf(TempString, 256, "%f", currentPosition.m_camX); m_textInputX.init(TempString, eControl_CamX); - snprintf(TempString, 256, "%f", currentPosition->m_camY); + snprintf(TempString, 256, "%f", currentPosition.m_camY); m_textInputY.init(TempString, eControl_CamY); - snprintf(TempString, 256, "%f", currentPosition->m_camZ); + snprintf(TempString, 256, "%f", currentPosition.m_camZ); m_textInputZ.init(TempString, eControl_CamZ); - snprintf(TempString, 256, "%f", currentPosition->m_yRot); + snprintf(TempString, 256, "%f", currentPosition.m_yRot); m_textInputYRot.init(TempString, eControl_YRot); - snprintf(TempString, 256, "%f", currentPosition->m_elev); + snprintf(TempString, 256, "%f", currentPosition.m_elev); m_textInputElevation.init(TempString, eControl_Elevation); m_checkboxLockPlayer.init("Lock Player", eControl_LockPlayer, @@ -106,12 +107,11 @@ void UIScene_DebugSetCamera::handleInput(int iPad, int key, bool repeat, void UIScene_DebugSetCamera::handlePress(F64 controlId, F64 childId) { switch ((int)controlId) { case eControl_Teleport: { - std::unique_ptr payload( - currentPosition); - currentPosition = nullptr; // ownership transferred - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_SetCameraLocation, - std::move(payload)); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::SetCameraLocation{ + currentPosition.player, currentPosition.m_camX, + currentPosition.m_camY, currentPosition.m_camZ, + currentPosition.m_yRot, currentPosition.m_elev}); } break; case eControl_CamX: case eControl_CamY: @@ -147,23 +147,23 @@ int UIScene_DebugSetCamera::handleKeyboardComplete(bool bRes) { switch (m_keyboardCallbackControl) { case eControl_CamX: m_textInputX.setLabel(value); - currentPosition->m_camX = val; + currentPosition.m_camX = val; break; case eControl_CamY: m_textInputY.setLabel(value); - currentPosition->m_camY = val; + currentPosition.m_camY = val; break; case eControl_CamZ: m_textInputZ.setLabel(value); - currentPosition->m_camZ = val; + currentPosition.m_camZ = val; break; case eControl_YRot: m_textInputYRot.setLabel(value); - currentPosition->m_yRot = val; + currentPosition.m_yRot = val; break; case eControl_Elevation: m_textInputElevation.setLabel(value); - currentPosition->m_elev = val; + currentPosition.m_elev = val; break; default: break; diff --git a/targets/app/common/UI/Scenes/Debug/UIScene_DebugSetCamera.h b/targets/app/common/UI/Scenes/Debug/UIScene_DebugSetCamera.h index 061f00481..aaf5ffd84 100644 --- a/targets/app/common/UI/Scenes/Debug/UIScene_DebugSetCamera.h +++ b/targets/app/common/UI/Scenes/Debug/UIScene_DebugSetCamera.h @@ -3,7 +3,6 @@ #include #include "app/common/UI/All Platforms/UIEnums.h" -#include "app/common/UI/All Platforms/UIStructs.h" #include "app/common/UI/Controls/UIControl_Button.h" #include "app/common/UI/Controls/UIControl_CheckBox.h" #include "app/common/UI/Controls/UIControl_Label.h" @@ -30,7 +29,18 @@ private: bool freeze; } FreezePlayerParam; - DebugSetCameraPosition* currentPosition; + // Local UI state collected from the form. Sent to the server as a + // SetCameraLocation action when the user hits Teleport. + struct CameraFormState { + int player = 0; + double m_camX = 0.0; + double m_camY = 0.0; + double m_camZ = 0.0; + double m_yRot = 0.0; + double m_elev = 0.0; + }; + + CameraFormState currentPosition; FreezePlayerParam* fpp; eControls m_keyboardCallbackControl; diff --git a/targets/app/common/UI/Scenes/In-Game Menu Screens/UIScene_PauseMenu.cpp b/targets/app/common/UI/Scenes/In-Game Menu Screens/UIScene_PauseMenu.cpp index fdf4dba56..59f633013 100644 --- a/targets/app/common/UI/Scenes/In-Game Menu Screens/UIScene_PauseMenu.cpp +++ b/targets/app/common/UI/Scenes/In-Game Menu Screens/UIScene_PauseMenu.cpp @@ -21,6 +21,8 @@ #include "minecraft/client/multiplayer/MultiPlayerLocalPlayer.h" #include "minecraft/client/skins/DLCTexturePack.h" #include "minecraft/client/skins/TexturePackRepository.h" +#include "minecraft/server/MinecraftServer.h" +#include "minecraft/server/ServerAction.h" #include "minecraft/sounds/SoundTypes.h" #include "platform/profile/profile.h" #include "strings.h" @@ -64,8 +66,8 @@ UIScene_PauseMenu::UIScene_PauseMenu(int iPad, void* initData, // IsLocalGame() issues on Iggy if (/*g_NetworkManager.IsLocalGame() &&*/ g_NetworkManager .GetPlayerCount() == 1) { - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_PauseServer, true); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::PauseServer{true}); } Minecraft* pMinecraft = Minecraft::GetInstance(); @@ -194,8 +196,8 @@ void UIScene_PauseMenu::handleInput(int iPad, int key, bool repeat, if (iPad == PlatformProfile.GetPrimaryPad() && /*g_NetworkManager.IsLocalGame()*/ g_NetworkManager .GetPlayerCount() == 1) { - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_PauseServer, false); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::PauseServer{false}); } ui.PlayUISFX(eSFX_Back); @@ -262,8 +264,8 @@ void UIScene_PauseMenu::handlePress(F64 controlId, F64 childId) { if (m_iPad == PlatformProfile.GetPrimaryPad() && /*g_NetworkManager.IsLocalGame()*/ g_NetworkManager .GetPlayerCount() == 1) { - app.SetXuiServerAction(PlatformProfile.GetPrimaryPad(), - eXuiServerAction_PauseServer, false); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::PauseServer{false}); } navigateBack(); break; diff --git a/targets/minecraft/GameEnums.h b/targets/minecraft/GameEnums.h index 27499d143..f429e10d2 100644 --- a/targets/minecraft/GameEnums.h +++ b/targets/minecraft/GameEnums.h @@ -100,25 +100,6 @@ enum eTMSAction { eTMSAction_TMSPP_RetrieveUserFilelist_DLCFileOnly, }; -// The server runs on its own thread, so we need to call its actions there -// rather than where all other Xui actions are performed In general these are -// debugging options -enum eXuiServerAction { - eXuiServerAction_Idle = 0, - eXuiServerAction_DropItem, // Debug - eXuiServerAction_SaveGame, - eXuiServerAction_AutoSaveGame, - eXuiServerAction_SpawnMob, // Debug - eXuiServerAction_PauseServer, - eXuiServerAction_ToggleRain, // Debug - eXuiServerAction_ToggleThunder, // Debug - eXuiServerAction_ServerSettingChanged_Gamertags, - eXuiServerAction_ServerSettingChanged_Difficulty, - eXuiServerAction_ExportSchematic, // Debug - eXuiServerAction_ServerSettingChanged_BedrockFog, - eXuiServerAction_SetCameraLocation, // Debug -}; - enum eGameSetting { eGameSetting_MusicVolume = 0, eGameSetting_SoundFXVolume, diff --git a/targets/minecraft/IGameServices.h b/targets/minecraft/IGameServices.h index 47a23f155..a4453d2b5 100644 --- a/targets/minecraft/IGameServices.h +++ b/targets/minecraft/IGameServices.h @@ -16,7 +16,6 @@ class DLCPack; #include "minecraft/GameEnums.h" #include "minecraft/GameTypes.h" -#include "minecraft/XuiActionPayload.h" #include "minecraft/client/IMenuService.h" #include "minecraft/client/model/SkinBox.h" #include "minecraft/network/packet/DisconnectPacket.h" @@ -112,12 +111,7 @@ public: virtual void setAction(int iPad, eXuiAction action, void* param = nullptr) = 0; - virtual void setXuiServerAction(int iPad, eXuiServerAction action, - XuiActionPayload param = {}) = 0; [[nodiscard]] virtual eXuiAction getXuiAction(int iPad) = 0; - [[nodiscard]] virtual eXuiServerAction getXuiServerAction(int iPad) = 0; - [[nodiscard]] virtual const XuiActionPayload& getXuiServerActionParam( - int iPad) = 0; virtual void setGlobalXuiAction(eXuiAction action) = 0; virtual void handleButtonPresses() = 0; virtual void setTMSAction(int iPad, eTMSAction action) = 0; diff --git a/targets/minecraft/XuiActionPayload.h b/targets/minecraft/XuiActionPayload.h deleted file mode 100644 index aeb49dadc..000000000 --- a/targets/minecraft/XuiActionPayload.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include -#include - -// Type-safe payload carried by IGameServices::setAction / -// setXuiServerAction. Replaces the old `void* param` which mixed -// integers, booleans, and heap-allocated pointers without ownership -// tracking. Concrete heap-allocated payload types (e.g. -// _DebugSetCameraPosition, _XboxSchematicInitParam) inherit from -// XuiActionOwnedPayload so they can be destroyed polymorphically through -// the unique_ptr alternative inside the variant. - -namespace minecraft { - -struct XuiActionOwnedPayload { - virtual ~XuiActionOwnedPayload() = default; -}; - -} // namespace minecraft - -using XuiActionPayload = - std::variant>; diff --git a/targets/minecraft/client/gui/PauseScreen.cpp b/targets/minecraft/client/gui/PauseScreen.cpp index d97aef333..de6095493 100644 --- a/targets/minecraft/client/gui/PauseScreen.cpp +++ b/targets/minecraft/client/gui/PauseScreen.cpp @@ -18,6 +18,7 @@ #include "minecraft/locale/I18n.h" #include "minecraft/network/INetworkService.h" #include "minecraft/server/MinecraftServer.h" +#include "minecraft/server/ServerAction.h" #include "platform/input/input.h" PauseScreen::PauseScreen() { @@ -31,8 +32,8 @@ void PauseScreen::init() { int yo = -16; // 4jcraft: solves the issue of client-side only pausing in the java gui if (NetworkService.IsLocalGame() && NetworkService.GetPlayerCount() == 1) - gameServices().setXuiServerAction(PlatformInput.GetPrimaryPad(), - eXuiServerAction_PauseServer, true); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::PauseServer{true}); buttons.push_back(new Button(1, width / 2 - 100, height / 4 + 24 * 5 + yo, I18n::get("menu.returnToMenu"))); if (!NetworkService.IsHost()) { @@ -90,8 +91,8 @@ void PauseScreen::buttonClicked(Button* button) { exitWorld(minecraft, true); } if (button->id == 4) { - gameServices().setXuiServerAction(PlatformInput.GetPrimaryPad(), - eXuiServerAction_PauseServer, false); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::PauseServer{false}); minecraft->setScreen(nullptr); // minecraft->grabMouse(); // 4J - removed } diff --git a/targets/minecraft/client/gui/Screen.cpp b/targets/minecraft/client/gui/Screen.cpp index adfa8080c..86e51b9f1 100644 --- a/targets/minecraft/client/gui/Screen.cpp +++ b/targets/minecraft/client/gui/Screen.cpp @@ -10,6 +10,8 @@ #include "minecraft/client/gui/particle/GuiParticles.h" #include "minecraft/client/renderer/Tesselator.h" #include "minecraft/network/INetworkService.h" +#include "minecraft/server/MinecraftServer.h" +#include "minecraft/server/ServerAction.h" #include "minecraft/sounds/SoundTypes.h" #include "platform/input/input.h" #include "platform/profile/profile.h" @@ -42,9 +44,8 @@ void Screen::keyPressed(char eventCharacter, int eventKey) { // unpausing is done in all scenarios if (NetworkService.IsLocalGame() && NetworkService.GetPlayerCount() == 1) - gameServices().setXuiServerAction(PlatformInput.GetPrimaryPad(), - eXuiServerAction_PauseServer, - false); + MinecraftServer::getInstance()->queueServerAction( + minecraft::server::PauseServer{false}); } } diff --git a/targets/minecraft/server/MinecraftServer.cpp b/targets/minecraft/server/MinecraftServer.cpp index 3092f8a9f..c286eee64 100644 --- a/targets/minecraft/server/MinecraftServer.cpp +++ b/targets/minecraft/server/MinecraftServer.cpp @@ -67,7 +67,6 @@ #endif #include "app/common/GameRules/LevelGeneration/ConsoleSchematicFile.h" #include "app/common/Network/Socket.h" -#include "app/common/UI/All Platforms/UIStructs.h" #include "minecraft/Console_Debug_enum.h" #include "minecraft/client/Minecraft.h" #include "minecraft/client/ProgressRenderer.h" @@ -1183,218 +1182,9 @@ void MinecraftServer::run(int64_t seed, void* lpParameter) { } } - // Process delayed actions - eXuiServerAction eAction; - for (int i = 0; i < XUSER_MAX_COUNT; i++) { - eAction = gameServices().getXuiServerAction(i); - const XuiActionPayload& param = - gameServices().getXuiServerActionParam(i); - - switch (eAction) { - case eXuiServerAction_AutoSaveGame: - case eXuiServerAction_SaveGame: - gameServices().lockSaveNotification(); - if (players != nullptr) { - players->saveAll( - Minecraft::GetInstance()->progressRenderer); - } - - players->broadcastAll( - std::shared_ptr( - new UpdateProgressPacket(20))); - - for (unsigned int j = 0; j < levels.size(); j++) { - if (s_bServerHalted) break; - // 4J Stu - Save the levels in reverse order so we - // don't overwrite the level.dat with the data from - // the nethers leveldata. Fix for #7418 - - // Functional: Gameplay: Saving after sleeping in a - // bed will place player at nighttime when - // restarting. - ServerLevel* level = levels[levels.size() - 1 - j]; - level->save( - true, - Minecraft::GetInstance()->progressRenderer, - (eAction == eXuiServerAction_AutoSaveGame)); - - players->broadcastAll( - std::shared_ptr( - new UpdateProgressPacket(33 + (j * 33)))); - } - if (!s_bServerHalted) { - saveGameRules(); - - levels[0]->saveToDisc( - Minecraft::GetInstance()->progressRenderer, - (eAction == eXuiServerAction_AutoSaveGame)); - } - gameServices().unlockSaveNotification(); - break; - case eXuiServerAction_DropItem: - // Find the player, and drop the id at their feet - if (auto* id = std::get_if(¶m)) { - std::shared_ptr player = - players->players.at(0); - player->drop(std::shared_ptr( - new ItemInstance(static_cast(*id), 1, 0))); - } - break; - case eXuiServerAction_SpawnMob: { - auto* id = std::get_if(¶m); - if (!id) break; - std::shared_ptr player = - players->players.at(0); - eINSTANCEOF factory = static_cast(*id); - std::shared_ptr mob = - std::dynamic_pointer_cast( - EntityIO::newByEnumType(factory, - player->level)); - mob->moveTo(player->x + 1, player->y, player->z + 1, - player->level->random->nextFloat() * 360, - 0); - mob->setDespawnProtected(); // 4J added, default to - // being protected against - // despawning (has to be - // done after initial - // position is set) - player->level->addEntity(mob); - } break; - case eXuiServerAction_PauseServer: - if (auto* val = std::get_if(¶m)) { - m_isServerPaused = *val; - if (m_isServerPaused) { - m_serverPausedEvent->set(); - } - } - break; - case eXuiServerAction_ToggleRain: { - bool isRaining = levels[0]->getLevelData()->isRaining(); - levels[0]->getLevelData()->setRaining(!isRaining); - levels[0]->getLevelData()->setRainTime( - levels[0]->random->nextInt(Level::TICKS_PER_DAY * - 7) + - Level::TICKS_PER_DAY / 2); - } break; - case eXuiServerAction_ToggleThunder: { - bool isThundering = - levels[0]->getLevelData()->isThundering(); - levels[0]->getLevelData()->setThundering(!isThundering); - levels[0]->getLevelData()->setThunderTime( - levels[0]->random->nextInt(Level::TICKS_PER_DAY * - 7) + - Level::TICKS_PER_DAY / 2); - } break; - case eXuiServerAction_ServerSettingChanged_Gamertags: - players->broadcastAll( - std::shared_ptr( - new ServerSettingsChangedPacket( - ServerSettingsChangedPacket::HOST_OPTIONS, - gameServices().getGameHostOption( - eGameHostOption_Gamertags)))); - break; - case eXuiServerAction_ServerSettingChanged_BedrockFog: - players->broadcastAll( - std::shared_ptr( - new ServerSettingsChangedPacket( - ServerSettingsChangedPacket:: - HOST_IN_GAME_SETTINGS, - gameServices().getGameHostOption( - eGameHostOption_All)))); - break; - - case eXuiServerAction_ServerSettingChanged_Difficulty: - players->broadcastAll(std::shared_ptr< - ServerSettingsChangedPacket>( - new ServerSettingsChangedPacket( - ServerSettingsChangedPacket::HOST_DIFFICULTY, - Minecraft::GetInstance() - ->options->difficulty))); - break; - case eXuiServerAction_ExportSchematic: -#if !defined(_CONTENT_PACKAGE) - gameServices().lockSaveNotification(); - - // players->broadcastAll( - // shared_ptr( new - // UpdateProgressPacket(20) ) ); - - if (!s_bServerHalted) { - auto* owned = std::get_if>(¶m); - ConsoleSchematicFile::XboxSchematicInitParam* - initData = - owned ? dynamic_cast< - ConsoleSchematicFile:: - XboxSchematicInitParam*>( - owned->get()) - : nullptr; - if (initData) { - File targetFileDir("Schematics"); - if (!targetFileDir.exists()) - targetFileDir.mkdir(); - - char filename[128]; - snprintf( - filename, 128, "%s%dx%dx%d.sch", - initData->name, - (initData->endX - initData->startX + 1), - (initData->endY - initData->startY + 1), - (initData->endZ - initData->startZ + 1)); - - File dataFile = - File(targetFileDir, std::string(filename)); - if (dataFile.exists()) dataFile._delete(); - FileOutputStream fos = - FileOutputStream(dataFile); - DataOutputStream dos = DataOutputStream(&fos); - ConsoleSchematicFile::generateSchematicFile( - &dos, levels[0], initData->startX, - initData->startY, initData->startZ, - initData->endX, initData->endY, - initData->endZ, initData->bSaveMobs, - initData->compressionType); - dos.close(); - // owned unique_ptr is destroyed when the - // payload is overwritten on the next - // setXuiServerAction - } - } - gameServices().unlockSaveNotification(); -#endif - break; - case eXuiServerAction_SetCameraLocation: -#if !defined(_CONTENT_PACKAGE) - { - auto* owned = std::get_if< - std::unique_ptr>( - ¶m); - DebugSetCameraPosition* pos = - owned ? dynamic_cast( - owned->get()) - : nullptr; - if (pos) { - Log::info("DEBUG: Player=%i\n", pos->player); - Log::info( - "DEBUG: Teleporting to pos=(%f.2, %f.2, %f.2), " - "looking at=(%f.2,%f.2)\n", - pos->m_camX, pos->m_camY, pos->m_camZ, - pos->m_yRot, pos->m_elev); - - std::shared_ptr player = - players->players.at(pos->player); - player->debug_setPosition(pos->m_camX, pos->m_camY, - pos->m_camZ, pos->m_yRot, - pos->m_elev); - } - } -#endif - break; - default: - break; - } - - gameServices().setXuiServerAction(i, eXuiServerAction_Idle); - } + // Drain typed action queue (queued by app/server consumers + // via MinecraftServer::queueServerAction). + drainServerActions(); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } @@ -1746,3 +1536,173 @@ bool MinecraftServer::flagEntitiesToBeRemoved(unsigned int* flags) { } return removedFound; } + +void MinecraftServer::queueServerAction( + minecraft::server::ServerAction action) { + std::lock_guard lock(m_actionQueueMutex); + m_actionQueue.push_back(std::move(action)); +} + +void MinecraftServer::drainServerActions() { + std::vector queue; + { + std::lock_guard lock(m_actionQueueMutex); + if (m_actionQueue.empty()) return; + queue.swap(m_actionQueue); + } + for (auto& action : queue) { + std::visit([this](auto& a) { handleServerAction(a); }, action); + } +} + +void MinecraftServer::handleServerAction(const minecraft::server::SaveGame& a) { + gameServices().lockSaveNotification(); + if (players != nullptr) { + players->saveAll(Minecraft::GetInstance()->progressRenderer); + } + + players->broadcastAll( + std::shared_ptr(new UpdateProgressPacket(20))); + + for (unsigned int j = 0; j < levels.size(); j++) { + if (s_bServerHalted) break; + // 4J Stu - Save the levels in reverse order so we don't overwrite + // the level.dat with the data from the nethers leveldata. Fix for + // #7418 - Functional: Gameplay: Saving after sleeping in a bed will + // place player at nighttime when restarting. + ServerLevel* level = levels[levels.size() - 1 - j]; + level->save(true, Minecraft::GetInstance()->progressRenderer, + a.autoSave); + + players->broadcastAll(std::shared_ptr( + new UpdateProgressPacket(33 + (j * 33)))); + } + if (!s_bServerHalted) { + saveGameRules(); + levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, + a.autoSave); + } + gameServices().unlockSaveNotification(); +} + +void MinecraftServer::handleServerAction( + const minecraft::server::DropDebugItem& a) { + if (players == nullptr || players->players.empty()) return; + std::shared_ptr player = players->players.at(a.playerIndex); + player->drop( + std::shared_ptr(new ItemInstance(a.itemId, 1, 0))); +} + +void MinecraftServer::handleServerAction( + const minecraft::server::SpawnDebugMob& a) { + if (players == nullptr || players->players.empty()) return; + std::shared_ptr player = players->players.at(a.playerIndex); + auto factory = static_cast(a.mobFactoryId); + std::shared_ptr mob = std::dynamic_pointer_cast( + EntityIO::newByEnumType(factory, player->level)); + if (mob == nullptr) return; + mob->moveTo(player->x + 1, player->y, player->z + 1, + player->level->random->nextFloat() * 360, 0); + mob->setDespawnProtected(); // 4J added, default to being protected against + // despawning (has to be done after initial + // position is set) + player->level->addEntity(mob); +} + +void MinecraftServer::handleServerAction( + const minecraft::server::PauseServer& a) { + m_isServerPaused = a.paused; + if (m_isServerPaused) { + m_serverPausedEvent->set(); + } +} + +void MinecraftServer::handleServerAction(const minecraft::server::ToggleRain&) { + bool isRaining = levels[0]->getLevelData()->isRaining(); + levels[0]->getLevelData()->setRaining(!isRaining); + levels[0]->getLevelData()->setRainTime( + levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + + Level::TICKS_PER_DAY / 2); +} + +void MinecraftServer::handleServerAction( + const minecraft::server::ToggleThunder&) { + bool isThundering = levels[0]->getLevelData()->isThundering(); + levels[0]->getLevelData()->setThundering(!isThundering); + levels[0]->getLevelData()->setThunderTime( + levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + + Level::TICKS_PER_DAY / 2); +} + +void MinecraftServer::handleServerAction( + const minecraft::server::BroadcastSettingChanged& a) { + using Kind = minecraft::server::BroadcastSettingChanged::Kind; + switch (a.kind) { + case Kind::Gamertags: + players->broadcastAll(std::shared_ptr( + new ServerSettingsChangedPacket( + ServerSettingsChangedPacket::HOST_OPTIONS, + gameServices().getGameHostOption( + eGameHostOption_Gamertags)))); + break; + case Kind::BedrockFog: + players->broadcastAll(std::shared_ptr( + new ServerSettingsChangedPacket( + ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, + gameServices().getGameHostOption(eGameHostOption_All)))); + break; + case Kind::Difficulty: + players->broadcastAll(std::shared_ptr( + new ServerSettingsChangedPacket( + ServerSettingsChangedPacket::HOST_DIFFICULTY, + Minecraft::GetInstance()->options->difficulty))); + break; + } +} + +void MinecraftServer::handleServerAction( + const minecraft::server::ExportSchematic& a) { +#if !defined(_CONTENT_PACKAGE) + gameServices().lockSaveNotification(); + if (!s_bServerHalted) { + File targetFileDir("Schematics"); + if (!targetFileDir.exists()) targetFileDir.mkdir(); + + char filename[128]; + snprintf(filename, 128, "%s%dx%dx%d.sch", a.name, + (a.endX - a.startX + 1), (a.endY - a.startY + 1), + (a.endZ - a.startZ + 1)); + + File dataFile = File(targetFileDir, std::string(filename)); + if (dataFile.exists()) dataFile._delete(); + FileOutputStream fos = FileOutputStream(dataFile); + DataOutputStream dos = DataOutputStream(&fos); + ConsoleSchematicFile::generateSchematicFile( + &dos, levels[0], a.startX, a.startY, a.startZ, a.endX, a.endY, + a.endZ, a.saveMobs, a.compressionType); + dos.close(); + } + gameServices().unlockSaveNotification(); +#else + (void)a; +#endif +} + +void MinecraftServer::handleServerAction( + const minecraft::server::SetCameraLocation& a) { +#if !defined(_CONTENT_PACKAGE) + Log::info("DEBUG: Player=%i\n", a.playerIndex); + Log::info( + "DEBUG: Teleporting to pos=(%f.2, %f.2, %f.2), looking " + "at=(%f.2,%f.2)\n", + a.x, a.y, a.z, a.yRot, a.elev); + + if (players == nullptr || + a.playerIndex >= static_cast(players->players.size())) + return; + std::shared_ptr player = players->players.at(a.playerIndex); + player->debug_setPosition(a.x, a.y, a.z, a.yRot, a.elev); +#else + (void)a; +#endif +} diff --git a/targets/minecraft/server/MinecraftServer.h b/targets/minecraft/server/MinecraftServer.h index f7c9a746c..e88f0d5f3 100644 --- a/targets/minecraft/server/MinecraftServer.h +++ b/targets/minecraft/server/MinecraftServer.h @@ -7,6 +7,7 @@ #include #include "ConsoleInputSource.h" +#include "ServerAction.h" #include "minecraft/SharedConstants.h" #include "minecraft/world/level/chunk/ChunkSource.h" #include "minecraft/world/level/storage/ConsoleSaveFileIO/FileHeader.h" @@ -300,6 +301,25 @@ private: bool IsServerPaused() { return m_isServerPaused; } private: + // Drain the action queue and dispatch each one. Called from the + // server tick loop. The drain takes the mutex briefly to swap the + // queue out, then dispatches without holding the lock. + void drainServerActions(); + + void handleServerAction(const minecraft::server::SaveGame& a); + void handleServerAction(const minecraft::server::DropDebugItem& a); + void handleServerAction(const minecraft::server::SpawnDebugMob& a); + void handleServerAction(const minecraft::server::PauseServer& a); + void handleServerAction(const minecraft::server::ToggleRain& a); + void handleServerAction(const minecraft::server::ToggleThunder& a); + void handleServerAction( + const minecraft::server::BroadcastSettingChanged& a); + void handleServerAction(const minecraft::server::ExportSchematic& a); + void handleServerAction(const minecraft::server::SetCameraLocation& a); + + std::mutex m_actionQueueMutex; + std::vector m_actionQueue; + // 4J Added bool m_saveOnExit; bool m_suspending; @@ -314,6 +334,11 @@ public: void chunkPacketManagement_PreTick(); void chunkPacketManagement_PostTick(); + // Queue a typed action for the server to handle on its next tick. + // Safe to call from any thread; the queue is mutex-protected and + // drained from the server tick loop. + void queueServerAction(minecraft::server::ServerAction action); + void setSaveOnExit(bool save) { m_saveOnExit = save; s_bSaveOnExitAnswered = true; diff --git a/targets/minecraft/server/ServerAction.h b/targets/minecraft/server/ServerAction.h new file mode 100644 index 000000000..a1501a27a --- /dev/null +++ b/targets/minecraft/server/ServerAction.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include + +#include "minecraft/world/level/storage/ConsoleSaveFileIO/compression.h" + +// Typed actions queued onto MinecraftServer from outside the server +// thread (UI, network, save manager). Each variant alternative is a +// plain data struct describing the requested operation; the server +// drains the queue in its tick loop and dispatches via std::visit. +// +// This replaces the old eXuiServerAction enum + per-pad polling + +// XuiActionPayload variant. The previous design forced the server to +// poll IGameServices every tick and pull a UI-shaped payload through a +// polymorphic base; now the server owns its own queue and the action +// types live alongside the consumer. + +namespace minecraft::server { + +struct SaveGame { + bool autoSave = false; +}; + +struct DropDebugItem { + int playerIndex = 0; + int itemId = 0; +}; + +struct SpawnDebugMob { + int playerIndex = 0; + int mobFactoryId = 0; +}; + +struct PauseServer { + bool paused = false; +}; + +struct ToggleRain {}; +struct ToggleThunder {}; + +struct BroadcastSettingChanged { + enum class Kind { + Gamertags, + BedrockFog, + Difficulty, + }; + Kind kind = Kind::Gamertags; +}; + +struct ExportSchematic { + char name[64] = {}; + int startX = 0; + int startY = 0; + int startZ = 0; + int endX = 0; + int endY = 0; + int endZ = 0; + bool saveMobs = false; + Compression::ECompressionTypes compressionType = + Compression::eCompressionType_None; +}; + +struct SetCameraLocation { + int playerIndex = 0; + double x = 0.0; + double y = 0.0; + double z = 0.0; + double yRot = 0.0; + double elev = 0.0; +}; + +using ServerAction = + std::variant; + +} // namespace minecraft::server