From 7c3ba79f832a4ec7dfd7874facfe3d3674a366d8 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 03:37:39 +0300 Subject: [PATCH 01/11] feat(jui): (re-)add transitional screens Adds a new MessageScreen class to display custom messages and restores areas where ReceivingLevelScreen was used --- Minecraft.Client/Network/ClientConnection.cpp | 4 +-- .../UI/Screens/CreateWorldScreen.cpp | 13 +++++---- Minecraft.Client/UI/Screens/MessageScreen.cpp | 19 +++++++++++++ Minecraft.Client/UI/Screens/MessageScreen.h | 27 +++++++++++++++++++ 4 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 Minecraft.Client/UI/Screens/MessageScreen.cpp create mode 100644 Minecraft.Client/UI/Screens/MessageScreen.h diff --git a/Minecraft.Client/Network/ClientConnection.cpp b/Minecraft.Client/Network/ClientConnection.cpp index deaad03f9..65eeedc0f 100644 --- a/Minecraft.Client/Network/ClientConnection.cpp +++ b/Minecraft.Client/Network/ClientConnection.cpp @@ -292,7 +292,7 @@ void ClientConnection::handleLogin(std::shared_ptr packet) { minecraft->createPrimaryLocalPlayer(ProfileManager.GetPrimaryPad()); minecraft->player->dimension = packet->dimension; - // minecraft->setScreen(new ReceivingLevelScreen(this)); + minecraft->setScreen(new ReceivingLevelScreen(this)); minecraft->player->entityId = packet->clientVersion; std::uint8_t networkSmallId = getSocket()->getSmallId(); @@ -2636,7 +2636,7 @@ void ClientConnection::handleRespawn(std::shared_ptr packet) { // minecraft->player->dimension = packet->dimension; minecraft->localplayers[m_userIndex]->dimension = packet->dimension; - // minecraft->setScreen(new ReceivingLevelScreen(this)); + minecraft->setScreen(new ReceivingLevelScreen(this)); // minecraft->addPendingLocalConnection(m_userIndex, this); #ifdef _XBOX diff --git a/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp b/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp index 9cbd865a2..bf00975cc 100644 --- a/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp +++ b/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp @@ -11,6 +11,7 @@ #include "../../../Minecraft.World/Util/Random.h" #include "../../MinecraftServer.h" #include "../../GameState/Options.h" +#include "MessageScreen.h" #include CreateWorldScreen::CreateWorldScreen(Screen* lastScreen) { @@ -43,10 +44,8 @@ void CreateWorldScreen::init() { buttons.push_back(new Button(1, width / 2 + 5, height - 28, 150, 20, language->getElement(L"gui.cancel"))); - nameEdit = new EditBox( - this, font, width / 2 - 100, 60, 200, 20, - language->getElement( - L"testWorld")); // 4J - test - should be L"selectWorld.newWorld" + nameEdit = new EditBox(this, font, width / 2 - 100, 60, 200, 20, + language->getElement(L"selectWorld.newWorld")); nameEdit->inFocus = true; nameEdit->setMaxLength(32); @@ -294,7 +293,11 @@ void CreateWorldScreen::buttonClicked(Button* button) { loadingParams->completionData = completionData; ui.NavigateToScene(0, eUIScene_FullscreenProgress, loadingParams); -// 4J Stu - This screen is not used, so removing this to stop the build failing + Language* language = Language::getInstance(); + minecraft->setScreen( + new MessageScreen(language->getElement(L"menu.generatingLevel"))); + // 4J Stu - This screen is not used, so removing this to stop the build + // failing #if 0 minecraft->gameMode = new SurvivalMode(minecraft); minecraft->selectLevel(resultFolder, nameEdit->getValue(), seedValue); diff --git a/Minecraft.Client/UI/Screens/MessageScreen.cpp b/Minecraft.Client/UI/Screens/MessageScreen.cpp new file mode 100644 index 000000000..9cf8415d5 --- /dev/null +++ b/Minecraft.Client/UI/Screens/MessageScreen.cpp @@ -0,0 +1,19 @@ +#include "../../Platform/stdafx.h" +#include "MessageScreen.h" + +MessageScreen::MessageScreen(const std::wstring& message) { + this->message = message; +} + +void MessageScreen::keyPressed(char eventCharacter, int eventKey) {} + +void MessageScreen::init() { buttons.clear(); } + +void MessageScreen::buttonClicked(Button* button) {} + +void MessageScreen::render(int xm, int ym, float a) { + renderDirtBackground(0); + drawCenteredString(font, message, width / 2, height / 2 - 50, 0xffffff); + + Screen::render(xm, ym, a); +} \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/MessageScreen.h b/Minecraft.Client/UI/Screens/MessageScreen.h new file mode 100644 index 000000000..cab45103d --- /dev/null +++ b/Minecraft.Client/UI/Screens/MessageScreen.h @@ -0,0 +1,27 @@ +#pragma once +#include "../Screen.h" + +// 4jcraft addition + +class MessageScreen : public Screen { +private: + std::wstring message; + +public: + MessageScreen(const std::wstring& message); + +protected: + using Screen::keyPressed; + + virtual void keyPressed(char eventCharacter, int eventKey); + +public: + virtual void init(); + virtual void tick(); + +protected: + virtual void buttonClicked(Button* button); + +public: + virtual void render(int xm, int ym, float a); +}; \ No newline at end of file From b60c6e779d9f30ef68137bc4010aadf52de74bab Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 04:07:46 +0300 Subject: [PATCH 02/11] i18n: add localization for console-specific achievements --- Minecraft.Assets/Common/res/lang/en_US.lang | 49 +++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/Minecraft.Assets/Common/res/lang/en_US.lang b/Minecraft.Assets/Common/res/lang/en_US.lang index 5a9292197..752a60f39 100644 --- a/Minecraft.Assets/Common/res/lang/en_US.lang +++ b/Minecraft.Assets/Common/res/lang/en_US.lang @@ -875,8 +875,8 @@ achievement.snipeSkeleton=Sniper Duel achievement.snipeSkeleton.desc=Kill a skeleton with an arrow from more than 50 meters achievement.diamonds=DIAMONDS! achievement.diamonds.desc=Acquire diamonds with your iron tools -achievement.portal=We Need to Go Deeper -achievement.portal.desc=Build a portal to the Nether +achievement.InToTheNether=Into The Nether +achievement.InToTheNether.desc=Build a portal to the Nether achievement.ghast=Return to Sender achievement.ghast.desc=Destroy a Ghast with a fireball achievement.blazeRod=Into Fire @@ -893,7 +893,50 @@ achievement.overkill=Overkill achievement.overkill.desc=Deal eight hearts of damage in a single hit achievement.bookcase=Librarian achievement.bookcase.desc=Build some bookshelves to improve your enchantment table - +achievement.leaderOfThePack=Leader of the Pack +achievement.leaderOfThePack.desc=Befriend five wolves +achievement.MOARTools=MOAR Tools +achievement.MOARTools.desc=Construct one type of each tool (one pickaxe, one spade, one axe and one hoe) +achievement.dispenseWithThis=Dispense With This +achievement.dispenseWithThis.desc=Construct a Dispenser +achievement.eatPorkChop=Pork Chop +achievement.eatPorkChop.desc=Cook and eat a pork chop +achievement.arrowKillCreeper=Archer +achievement.arrowKillCreeper.desc=Kill a creeper with arrows +achievement.adventuringTime=Adventuring Time +achievement.adventuringTime.desc=Discover 17 of the 40 different biomes +achievement.repopulation=Repopulation +achievement.repopulation.desc=Breed two cows with wheat +achievement.diamondsToYou=Diamonds to You! +achievement.diamondsToYou.desc=Throw diamonds at another player +achievement.theHaggler=The Haggler +achievement.theHaggler.desc=Mine or purchase 30 Emeralds +achievement.potPlanter=Pot Planter +achievement.potPlanter.desc=Craft and place a Flower Pot +achievement.itsASign=It's a Sign! +achievement.itsASign.desc=Craft and place a Sign +achievement.ironBelly=Iron Belly +achievement.ironBelly.desc=Stop starvation using Rotten Flesh +achievement.haveAShearfulDay=Have a Shearful Day +achievement.haveAShearfulDay.desc=Use Shears to obtain wool from a sheep +achievement.rainbowCollection=Rainbow Collection +achievement.rainbowCollection.desc=Gather all 16 colors of wool +achievement.stayingFrosty=Stayin' Frosty +achievement.stayingFrosty.desc=Swim in lava while having the Fire Resistance effect +achievement.chestfulOfCobblestone=Chestful of Cobblestone +achievement.chestfulOfCobblestone.desc=Mine 1,728 Cobblestone and place it in a chest +achievement.renewableEnergy=Renewable Energy +achievement.renewableEnergy.desc=Smelt wood trunks using charcoal to make more charcoal +achievement.musicToMyEars=Music to My Ears +achievement.musicToMyEars.desc=Play a music disc in a Jukebox +achievement.bodyGuard=Body Guard +achievement.bodyGuard.desc=Create an Iron Golem +achievement.ironMan=Iron Man +achievement.ironMan.desc=Wear a full suit of Iron Armour +achievement.zombieDoctor=Zombie Doctor +achievement.zombieDoctor.desc=Cure a Zombie Villager +achievement.lionTamer=Lion Tamer +achievement.lionTamer.desc=Tame an Ocelot commands.generic.exception=An unknown error occurred while attempting to perform this command commands.generic.syntax=Invalid command syntax commands.generic.player.notFound=That player cannot be found From 28ea3ab8cc2d8358466c8f6454a1e256b1f122e6 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 04:17:31 +0300 Subject: [PATCH 03/11] feat(achievements): replace placeholder icons with actual game items --- Minecraft.World/Stats/Achievements.cpp | 42 +++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Minecraft.World/Stats/Achievements.cpp b/Minecraft.World/Stats/Achievements.cpp index 46cdf6467..ecd2626f7 100644 --- a/Minecraft.World/Stats/Achievements.cpp +++ b/Minecraft.World/Stats/Achievements.cpp @@ -165,21 +165,21 @@ void Achievements::staticCtor() { // "requires" are ignored on xbox. Achievements::leaderOfThePack = (new Achievement(eAward_LeaderOfThePack, L"leaderOfThePack", 0, 0, - Tile::treeTrunk, (Achievement*)buildSword)) + Item::bone, (Achievement*)buildSword)) ->setAwardLocallyOnly() ->postConstruct(); Achievements::MOARTools = - (new Achievement(eAward_MOARTools, L"MOARTools", 0, 0, Tile::treeTrunk, + (new Achievement(eAward_MOARTools, L"MOARTools", 0, 0, Item::shovel_diamond, (Achievement*)buildSword)) ->setAwardLocallyOnly() ->postConstruct(); Achievements::dispenseWithThis = (new Achievement(eAward_DispenseWithThis, L"dispenseWithThis", 0, 0, - Tile::treeTrunk, (Achievement*)buildSword)) + Tile::dispenser, (Achievement*)buildSword)) ->postConstruct(); Achievements::InToTheNether = (new Achievement(eAward_InToTheNether, L"InToTheNether", 0, 0, - Tile::treeTrunk, (Achievement*)buildSword)) + Tile::portalTile, (Achievement*)buildSword)) ->postConstruct(); // 4J : WESTY : Added other awards. @@ -196,7 +196,7 @@ void Achievements::staticCtor() { #ifdef _EXTENDED_ACHIEVEMENTS Achievements::eatPorkChop = (new Achievement(eAward_eatPorkChop, L"eatPorkChop", 0, 0, - Tile::treeTrunk, (Achievement*)buildSword)) + Item::porkChop_raw, (Achievement*)buildSword)) ->setAwardLocallyOnly() ->postConstruct(); #else @@ -298,13 +298,13 @@ void Achievements::staticCtor() { ->postConstruct(); Achievements::repopulation = (new Achievement(eAward_repopulation, L"repopulation", 0, 0, - Tile::bookshelf, (Achievement*)NULL)) + Item::wheat, (Achievement*)NULL)) ->postConstruct(); // Achievements::porkChoop // // // // // // // Achievements::diamondsToYou = (new Achievement(eAward_diamondsToYou, L"diamondsToYou", 0, 0, - Tile::bookshelf, (Achievement*)NULL)) + Item::diamond, (Achievement*)NULL)) ->postConstruct(); // Achievements::passingTheTime = (new // Achievement(eAward_play100Days, L"passingTheTime", @@ -315,63 +315,63 @@ void Achievements::staticCtor() { // )->postConstruct(); Achievements::theHaggler = (new Achievement(eAward_theHaggler, L"theHaggler", 0, 0, - Tile::bookshelf, (Achievement*)NULL)) + Item::emerald, (Achievement*)NULL)) ->setAwardLocallyOnly() ->postConstruct(); Achievements::potPlanter = (new Achievement(eAward_potPlanter, L"potPlanter", 0, 0, - Tile::bookshelf, (Achievement*)NULL)) + Item::flowerPot, (Achievement*)NULL)) ->setAwardLocallyOnly() ->postConstruct(); Achievements::itsASign = - (new Achievement(eAward_itsASign, L"itsASign", 0, 0, Tile::bookshelf, + (new Achievement(eAward_itsASign, L"itsASign", 0, 0, Item::sign, (Achievement*)NULL)) ->setAwardLocallyOnly() ->postConstruct(); Achievements::ironBelly = - (new Achievement(eAward_ironBelly, L"ironBelly", 0, 0, Tile::bookshelf, + (new Achievement(eAward_ironBelly, L"ironBelly", 0, 0, Item::rotten_flesh, (Achievement*)NULL)) ->postConstruct(); Achievements::haveAShearfulDay = (new Achievement(eAward_haveAShearfulDay, L"haveAShearfulDay", 0, 0, - Tile::bookshelf, (Achievement*)NULL)) + Item::shears, (Achievement*)NULL)) ->postConstruct(); Achievements::rainbowCollection = (new Achievement(eAward_rainbowCollection, L"rainbowCollection", 0, 0, - Tile::bookshelf, (Achievement*)NULL)) + Tile::cloth, (Achievement*)NULL)) ->setAwardLocallyOnly() ->postConstruct(); Achievements::stayinFrosty = (new Achievement(eAward_stayinFrosty, L"stayingFrosty", 0, 0, - Tile::bookshelf, (Achievement*)NULL)) + Tile::ice, (Achievement*)NULL)) ->postConstruct(); Achievements::chestfulOfCobblestone = (new Achievement(eAward_chestfulOfCobblestone, L"chestfulOfCobblestone", - 0, 0, Tile::bookshelf, (Achievement*)NULL)) + 0, 0, Tile::rock, (Achievement*)NULL)) ->setAwardLocallyOnly() ->postConstruct(); Achievements::renewableEnergy = (new Achievement(eAward_renewableEnergy, L"renewableEnergy", 0, 0, - Tile::bookshelf, (Achievement*)NULL)) + Item::coal, (Achievement*)NULL)) ->postConstruct(); Achievements::musicToMyEars = (new Achievement(eAward_musicToMyEars, L"musicToMyEars", 0, 0, - Tile::bookshelf, (Achievement*)NULL)) + Tile::musicBlock, (Achievement*)NULL)) ->postConstruct(); Achievements::bodyGuard = - (new Achievement(eAward_bodyGuard, L"bodyGuard", 0, 0, Tile::bookshelf, + (new Achievement(eAward_bodyGuard, L"bodyGuard", 0, 0, Tile::pumpkin, (Achievement*)NULL)) ->postConstruct(); Achievements::ironMan = - (new Achievement(eAward_ironMan, L"ironMan", 0, 0, Tile::bookshelf, + (new Achievement(eAward_ironMan, L"ironMan", 0, 0, Item::chestplate_iron, (Achievement*)NULL)) ->postConstruct(); Achievements::zombieDoctor = (new Achievement(eAward_zombieDoctor, L"zombieDoctor", 0, 0, - Tile::bookshelf, (Achievement*)NULL)) + Item::apple_gold, (Achievement*)NULL)) ->postConstruct(); Achievements::lionTamer = - (new Achievement(eAward_lionTamer, L"lionTamer", 0, 0, Tile::bookshelf, + (new Achievement(eAward_lionTamer, L"lionTamer", 0, 0, Item::fish_raw, (Achievement*)NULL)) ->postConstruct(); #endif From 55bc1e17b7d4c9e4f19ad40930d34a36eb32d350 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 14:52:56 +0300 Subject: [PATCH 04/11] fix(jui): remove tick method from MessageScreen --- Minecraft.Client/UI/Screens/MessageScreen.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Minecraft.Client/UI/Screens/MessageScreen.h b/Minecraft.Client/UI/Screens/MessageScreen.h index cab45103d..160e7b1dc 100644 --- a/Minecraft.Client/UI/Screens/MessageScreen.h +++ b/Minecraft.Client/UI/Screens/MessageScreen.h @@ -17,7 +17,6 @@ protected: public: virtual void init(); - virtual void tick(); protected: virtual void buttonClicked(Button* button); From 6d50ac477192498ff2029484023111fb6c9b309f Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 14:57:06 +0300 Subject: [PATCH 05/11] feat(jui): add dirt background rendering to selectworldscreen --- Minecraft.Client/UI/Screens/SelectWorldScreen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Minecraft.Client/UI/Screens/SelectWorldScreen.cpp b/Minecraft.Client/UI/Screens/SelectWorldScreen.cpp index e98cef112..080966374 100644 --- a/Minecraft.Client/UI/Screens/SelectWorldScreen.cpp +++ b/Minecraft.Client/UI/Screens/SelectWorldScreen.cpp @@ -165,6 +165,7 @@ void SelectWorldScreen::confirmResult(bool result, int id) { void SelectWorldScreen::render(int xm, int ym, float a) { // fill(0, 0, width, height, 0x40000000); + renderDirtBackground(0); worldSelectionList->render(xm, ym, a); drawCenteredString(font, title, width / 2, 20, 0xffffff); From 0fbbac1cde50c11a61c252beb63b67e2dc78dcfa Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 15:05:32 +0300 Subject: [PATCH 06/11] feat(jui): add world leaving You will now be able to leave the world from all places where you'd usually be able to (Pause screen, death screen) Should be identical to the way it's done on Iggy/XUI --- Minecraft.Client/Minecraft.cpp | 10 ++++++++++ Minecraft.Client/Minecraft.h | 1 + .../Platform/Common/Consoles_App.cpp | 2 +- .../Platform/Common/UI/IUIScene_PauseMenu.cpp | 1 + Minecraft.Client/UI/Screens/DeathScreen.cpp | 5 ++++- Minecraft.Client/UI/Screens/PauseScreen.cpp | 17 ++++++++++++++++- Minecraft.Client/UI/Screens/PauseScreen.h | 1 + 7 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 3eec0758e..bf98a63cb 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -122,6 +122,7 @@ Minecraft::Minecraft(Component* mouseComponent, Canvas* parent, user = NULL; parent = NULL; pause = false; + exitingWorldRightNow = false; textures = NULL; font = NULL; screen = NULL; @@ -1254,6 +1255,15 @@ void Minecraft::run_middle() { if (lastTime == 0) lastTime = System::nanoTime(); static int frames = 0; +#ifdef ENABLE_JAVA_GUIS + // 4jcraft: while the java ui is leaving world, don't run the rest of + // run_middle + if (exitingWorldRightNow) { + screen->render(0, 0, 1); + return; + } +#endif + EnterCriticalSection(&m_setLevelCS); if (running) { diff --git a/Minecraft.Client/Minecraft.h b/Minecraft.Client/Minecraft.h index 07b4c1a3b..876801683 100644 --- a/Minecraft.Client/Minecraft.h +++ b/Minecraft.Client/Minecraft.h @@ -137,6 +137,7 @@ public: // 4J - per player ? volatile bool pause; + volatile bool exitingWorldRightNow; Textures* textures; Font *font, *altFont; diff --git a/Minecraft.Client/Platform/Common/Consoles_App.cpp b/Minecraft.Client/Platform/Common/Consoles_App.cpp index 4bcf758e5..e91f2adb2 100644 --- a/Minecraft.Client/Platform/Common/Consoles_App.cpp +++ b/Minecraft.Client/Platform/Common/Consoles_App.cpp @@ -2826,7 +2826,7 @@ void CMinecraftApp::HandleXuiActions(void) #endif case eAppAction_ExitWorld: - + pMinecraft->exitingWorldRightNow = true; SetAction(i,eAppAction_Idle); // If we're already leaving don't exit diff --git a/Minecraft.Client/Platform/Common/UI/IUIScene_PauseMenu.cpp b/Minecraft.Client/Platform/Common/UI/IUIScene_PauseMenu.cpp index c6daed5e5..d880ba677 100644 --- a/Minecraft.Client/Platform/Common/UI/IUIScene_PauseMenu.cpp +++ b/Minecraft.Client/Platform/Common/UI/IUIScene_PauseMenu.cpp @@ -627,6 +627,7 @@ void IUIScene_PauseMenu::_ExitWorld(void *lpParameter) app.SetChangingSessionType(false); app.SetReallyChangingSessionType(false); + pMinecraft->exitingWorldRightNow = false; #if defined(_XBOX_ONE) || defined(__ORBIS__) // Make sure we don't think saving is disabled in the menus diff --git a/Minecraft.Client/UI/Screens/DeathScreen.cpp b/Minecraft.Client/UI/Screens/DeathScreen.cpp index 432378076..2ac5f8960 100644 --- a/Minecraft.Client/UI/Screens/DeathScreen.cpp +++ b/Minecraft.Client/UI/Screens/DeathScreen.cpp @@ -3,6 +3,7 @@ #include "DeathScreen.h" #include "../Button.h" #include "../../Player/MultiPlayerLocalPlayer.h" +#include "PauseScreen.h" #include "TitleScreen.h" void DeathScreen::init() { @@ -30,9 +31,11 @@ void DeathScreen::buttonClicked(Button* button) { // minecraft.setScreen(new NewLevelScreen(this)); } if (button->id == 2) { - // TODO: proper world exits // minecraft->setLevel(NULL); // minecraft->setScreen(new TitleScreen()); + + // 4jcraft: use the static method from PauseScreen to exit + PauseScreen::exitWorld(minecraft, true); } } diff --git a/Minecraft.Client/UI/Screens/PauseScreen.cpp b/Minecraft.Client/UI/Screens/PauseScreen.cpp index fbc602586..fe8fd973c 100644 --- a/Minecraft.Client/UI/Screens/PauseScreen.cpp +++ b/Minecraft.Client/UI/Screens/PauseScreen.cpp @@ -2,6 +2,7 @@ #include "PauseScreen.h" #include "../Button.h" #include "../../GameState/StatsCounter.h" +#include "MessageScreen.h" #include "OptionsScreen.h" #include "TitleScreen.h" #include "../../Level/MultiPlayerLevel.h" @@ -48,12 +49,23 @@ void PauseScreen::init() { */ } +void PauseScreen::exitWorld(Minecraft* minecraft, bool save) { + // 4jcraft: made our own static method for use in the java gui (other + // places such as the deathscreen need this) + MinecraftServer* server = MinecraftServer::getInstance(); + + minecraft->setScreen(new MessageScreen(L"Leaving world")); + if (g_NetworkManager.IsHost()) { + server->setSaveOnExit(save); + } + app.SetAction(minecraft->player->GetXboxPad(), eAppAction_ExitWorld); +} + void PauseScreen::buttonClicked(Button* button) { if (button->id == 0) { minecraft->setScreen(new OptionsScreen(this, minecraft->options)); } if (button->id == 1) { - // TODO: proper disconnects // if (minecraft->isClientSide()) // { // minecraft->level->disconnect(); @@ -61,6 +73,9 @@ void PauseScreen::buttonClicked(Button* button) { // minecraft->setLevel(NULL); // minecraft->setScreen(new TitleScreen()); + + // 4jcraft: exit with our new exitWorld method + exitWorld(minecraft, true); } if (button->id == 4) { minecraft->setScreen(NULL); diff --git a/Minecraft.Client/UI/Screens/PauseScreen.h b/Minecraft.Client/UI/Screens/PauseScreen.h index 3f4f3fd6b..375d03eb6 100644 --- a/Minecraft.Client/UI/Screens/PauseScreen.h +++ b/Minecraft.Client/UI/Screens/PauseScreen.h @@ -9,6 +9,7 @@ private: public: PauseScreen(); // 4J added virtual void init(); + static void exitWorld(Minecraft* minecraft, bool save); protected: using Screen::buttonClicked; From 32fee4334deab16eafe24b08dba259783836e312 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 15:41:55 +0300 Subject: [PATCH 07/11] fix(jui): call eXuiServerAction_PauseServer to pause server-side too --- Minecraft.Client/UI/Screens/PauseScreen.cpp | 16 ++++++++++++++++ Minecraft.Client/UI/Screens/PauseScreen.h | 1 + 2 files changed, 17 insertions(+) diff --git a/Minecraft.Client/UI/Screens/PauseScreen.cpp b/Minecraft.Client/UI/Screens/PauseScreen.cpp index fe8fd973c..72e4efd56 100644 --- a/Minecraft.Client/UI/Screens/PauseScreen.cpp +++ b/Minecraft.Client/UI/Screens/PauseScreen.cpp @@ -22,6 +22,11 @@ void PauseScreen::init() { saveStep = 0; buttons.clear(); int yo = -16; + // 4jcraft: solves the issue of client-side only pausing in the java gui + if (g_NetworkManager.IsLocalGame() && + g_NetworkManager.GetPlayerCount() == 1) + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), + eXuiServerAction_PauseServer, (void*)TRUE); buttons.push_back(new Button(1, width / 2 - 100, height / 4 + 24 * 5 + yo, L"Save and quit to title")); if (minecraft->isClientSide()) { @@ -49,6 +54,15 @@ void PauseScreen::init() { */ } +void PauseScreen::keyPressed(wchar_t eventCharacter, int eventKey) { + if (eventKey == Keyboard::KEY_ESCAPE && g_NetworkManager.IsLocalGame() && + g_NetworkManager.GetPlayerCount() == 1) + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), + eXuiServerAction_PauseServer, (void*)FALSE); + + Screen::keyPressed(eventCharacter, eventKey); +} + void PauseScreen::exitWorld(Minecraft* minecraft, bool save) { // 4jcraft: made our own static method for use in the java gui (other // places such as the deathscreen need this) @@ -78,6 +92,8 @@ void PauseScreen::buttonClicked(Button* button) { exitWorld(minecraft, true); } if (button->id == 4) { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), + eXuiServerAction_PauseServer, (void*)FALSE); minecraft->setScreen(NULL); // minecraft->grabMouse(); // 4J - removed } diff --git a/Minecraft.Client/UI/Screens/PauseScreen.h b/Minecraft.Client/UI/Screens/PauseScreen.h index 375d03eb6..6f71beb09 100644 --- a/Minecraft.Client/UI/Screens/PauseScreen.h +++ b/Minecraft.Client/UI/Screens/PauseScreen.h @@ -9,6 +9,7 @@ private: public: PauseScreen(); // 4J added virtual void init(); + virtual void keyPressed(wchar_t eventCharacter, int eventKey); static void exitWorld(Minecraft* minecraft, bool save); protected: From 009b17ef1b33fd4d6113e55a5b4c17cf4ef6e084 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 18:49:14 +0300 Subject: [PATCH 08/11] fix(gui): working pauses on iggy --- .../Platform/Common/UI/UIScene_PauseMenu.cpp | 16 ++++++++++++---- .../Platform/Linux/Linux_Minecraft.cpp | 7 ++++++- Minecraft.Client/UI/Screens/MessageScreen.h | 2 -- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Minecraft.Client/Platform/Common/UI/UIScene_PauseMenu.cpp b/Minecraft.Client/Platform/Common/UI/UIScene_PauseMenu.cpp index 1ba87c37e..b82bb65ef 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene_PauseMenu.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIScene_PauseMenu.cpp @@ -92,7 +92,10 @@ UIScene_PauseMenu::UIScene_PauseMenu(int iPad, void *initData, UILayer *parentLa XuiSetTimer(m_hObj,IGNORE_KEYPRESS_TIMERID,IGNORE_KEYPRESS_TIME); #endif - if( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 ) + // TODO: proper fix for pausing + // 4jcraft: replace IsLocalGame() with GetPlayerCount() == 1 due to + // IsLocalGame() issues on Iggy + if( /*g_NetworkManager.IsLocalGame() &&*/ g_NetworkManager.GetPlayerCount() == 1 ) { app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)TRUE); } @@ -364,8 +367,10 @@ void UIScene_PauseMenu::handleInput(int iPad, int key, bool repeat, bool pressed #ifdef _DURANGO //DurangoStatsDebugger::PrintStats(iPad); #endif - - if( iPad == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsLocalGame() ) + // TODO: proper fix for pausing + // 4jcraft: replace IsLocalGame() with GetPlayerCount() == 1 due to + // IsLocalGame() issues on Iggy + if( iPad == ProfileManager.GetPrimaryPad() && /*g_NetworkManager.IsLocalGame()*/g_NetworkManager.GetPlayerCount() == 1 ) { app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE); } @@ -509,7 +514,10 @@ void UIScene_PauseMenu::handlePress(F64 controlId, F64 childId) switch((int)controlId) { case BUTTON_PAUSE_RESUMEGAME: - if( m_iPad == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsLocalGame() ) + // TODO: proper fix for pausing + // 4jcraft: replace IsLocalGame() with GetPlayerCount() == 1 due to + // IsLocalGame() issues on Iggy + if( m_iPad == ProfileManager.GetPrimaryPad() && /*g_NetworkManager.IsLocalGame()*/g_NetworkManager.GetPlayerCount() == 1 ) { app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE); } diff --git a/Minecraft.Client/Platform/Linux/Linux_Minecraft.cpp b/Minecraft.Client/Platform/Linux/Linux_Minecraft.cpp index dd6ec8a82..0a4f3bfc7 100644 --- a/Minecraft.Client/Platform/Linux/Linux_Minecraft.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_Minecraft.cpp @@ -950,7 +950,12 @@ return -1; pMinecraft->run_middle(); #endif app.SetAppPaused( - g_NetworkManager.IsLocalGame() && + // TODO: proper fix for pausing + // 4jcraft: IsLocalGame() doesn't seem to work properly on Iggy + // UI, this should work even in multiplayer scenarios though + // since it checks for the player count anyway + // + // g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 && ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())); } else { diff --git a/Minecraft.Client/UI/Screens/MessageScreen.h b/Minecraft.Client/UI/Screens/MessageScreen.h index 160e7b1dc..42a75aa78 100644 --- a/Minecraft.Client/UI/Screens/MessageScreen.h +++ b/Minecraft.Client/UI/Screens/MessageScreen.h @@ -1,8 +1,6 @@ #pragma once #include "../Screen.h" -// 4jcraft addition - class MessageScreen : public Screen { private: std::wstring message; From e6585a67325e7ae68f35b5de295217d94058f06a Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 18:56:24 +0300 Subject: [PATCH 09/11] i18n(jui): localize disconnect button text --- Minecraft.Client/UI/Screens/PauseScreen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/UI/Screens/PauseScreen.cpp b/Minecraft.Client/UI/Screens/PauseScreen.cpp index 72e4efd56..f5ab14abb 100644 --- a/Minecraft.Client/UI/Screens/PauseScreen.cpp +++ b/Minecraft.Client/UI/Screens/PauseScreen.cpp @@ -28,9 +28,9 @@ void PauseScreen::init() { app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), eXuiServerAction_PauseServer, (void*)TRUE); buttons.push_back(new Button(1, width / 2 - 100, height / 4 + 24 * 5 + yo, - L"Save and quit to title")); + I18n::get(L"menu.returnToMenu"))); if (minecraft->isClientSide()) { - buttons[0]->msg = L"Disconnect"; + buttons[0]->msg = I18n::get(L"menu.disconnect"); } buttons.push_back(new Button(4, width / 2 - 100, height / 4 + 24 * 1 + yo, From d968855cc45f7c34047b0cdeb726f03eb2808946 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 18:57:19 +0300 Subject: [PATCH 10/11] fix(jui): correctly check if host or not for disconnect text --- Minecraft.Client/UI/Screens/PauseScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/UI/Screens/PauseScreen.cpp b/Minecraft.Client/UI/Screens/PauseScreen.cpp index f5ab14abb..8c451b4ee 100644 --- a/Minecraft.Client/UI/Screens/PauseScreen.cpp +++ b/Minecraft.Client/UI/Screens/PauseScreen.cpp @@ -29,7 +29,7 @@ void PauseScreen::init() { eXuiServerAction_PauseServer, (void*)TRUE); buttons.push_back(new Button(1, width / 2 - 100, height / 4 + 24 * 5 + yo, I18n::get(L"menu.returnToMenu"))); - if (minecraft->isClientSide()) { + if (!g_NetworkManager.IsHost()) { buttons[0]->msg = I18n::get(L"menu.disconnect"); } From 405202dad2d8299ccdefda74816faa7db91582af Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 19 Mar 2026 19:16:02 +0300 Subject: [PATCH 11/11] fix(gitignore): only include .wrap files in /subprojects --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index fdc1dc597..4a55c78cd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ # Exclude everything by default /* -/subprojects/.wraplock # Except for tracked top-level project directories !/.devcontainer/ @@ -15,7 +14,7 @@ !/Minecraft.Client/ !/Minecraft.World/ !/scripts/ -!/subprojects/ +!/subprojects/*.wrap !/.clang-format !/.git-blame-ignore-revs !/.gitattributes