From 59f9dbc8c6112eee37ab3cb3484d9581d1c6a804 Mon Sep 17 00:00:00 2001 From: itsRevela Date: Fri, 17 Apr 2026 07:06:26 -0500 Subject: [PATCH] fix: hardcore mode UI not locking difficulty/gamemode on world load Three issues fixed: - Save file path used hardcoded saveData.ms but new 4JLibs names files as .ms. ReadLevelNameFromSaveFile now constructs the correct path with fallback to saveData.ms for old saves. - The level.dat code path for reading the hardcore flag (sidecar rename) returned early without ever parsing level.dat. Now stores the sidecar name and continues to read the hardcore flag from NBT. - The thumbnail host options path could overwrite m_bHardcore to false. Now only upgrades to true, never downgrades. - Load menu constructor and tick handler both lock difficulty slider to Hardcore and gamemode to Survival when hardcore is detected. - Hide title logo on load menu to match create world menu. --- .../Common/UI/UIScene_LoadMenu.cpp | 19 +++++++------- .../Common/UI/UIScene_LoadOrJoinMenu.cpp | 26 ++++++++++++++----- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp index c0e511dc..0e72c414 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp @@ -281,6 +281,11 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye WCHAR TempString[256]; swprintf((WCHAR *)TempString, 256, L"%ls: %ls", app.GetString(IDS_SLIDER_DIFFICULTY), L"Hardcore"); m_sliderDifficulty.init(TempString, eControl_Difficulty, 0, 4, 4); + + // Hardcore locks game mode to Survival + m_iGameModeId = GameType::SURVIVAL->getId(); + m_bGameModeCreative = false; + m_buttonGamemode.setLabel(app.GetString(IDS_GAMEMODE_SURVIVAL)); } } #endif @@ -421,15 +426,7 @@ void UIScene_LoadMenu::updateTooltips() void UIScene_LoadMenu::updateComponents() { m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); - - if(RenderManager.IsWidescreen()) - { - m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); - } - else - { - m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); - } + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); } wstring UIScene_LoadMenu::getMoviePath() @@ -573,7 +570,9 @@ void UIScene_LoadMenu::tick() m_MoreOptionsParams.bAllowFriendsOfFriends = TRUE; } - m_bHardcore = app.GetGameHostOption(uiHostOptions, eGameHostOption_Hardcore) > 0; + // Use thumbnail host options if available, otherwise preserve the level.dat value + if (app.GetGameHostOption(uiHostOptions, eGameHostOption_Hardcore) > 0) + m_bHardcore = true; if (m_bHardcore) { WCHAR TempString[256]; diff --git a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp index 1e83aee6..180ae8c0 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp @@ -32,6 +32,7 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardcore = nullptr) { // Check for a worldname.txt sidecar written by the rename feature first + wstring sidecarName = L""; size_t slashPos = filePath.rfind(L'\\'); if (slashPos != wstring::npos) { @@ -50,7 +51,7 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc { wchar_t wbuf[128] = {}; MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, 127); - return wstring(wbuf); + sidecarName = wbuf; } } else fclose(fr); @@ -58,10 +59,10 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc } HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr); - if (hFile == INVALID_HANDLE_VALUE) return L""; + if (hFile == INVALID_HANDLE_VALUE) return sidecarName; DWORD fileSize = GetFileSize(hFile, nullptr); - if (fileSize < 12 || fileSize == INVALID_FILE_SIZE) { CloseHandle(hFile); return L""; } + if (fileSize < 12 || fileSize == INVALID_FILE_SIZE) { CloseHandle(hFile); return sidecarName; } unsigned char *rawData = new unsigned char[fileSize]; DWORD bytesRead = 0; @@ -69,7 +70,7 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc { CloseHandle(hFile); delete[] rawData; - return L""; + return sidecarName; } CloseHandle(hFile); @@ -84,7 +85,7 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc if (decompSize == 0 || decompSize > 128 * 1024 * 1024) { delete[] rawData; - return L""; + return sidecarName; } saveData = new unsigned char[decompSize]; Compression::getCompression()->Decompress(saveData, &decompSize, rawData + 8, fileSize - 8); @@ -140,6 +141,10 @@ static wstring ReadLevelNameFromSaveFile(const wstring& filePath, bool *outHardc if (freeSaveData) delete[] saveData; delete[] rawData; + + // Prefer the sidecar name (user-renamed) over the level.dat name + if (!sidecarName.empty()) return sidecarName; + // "world" is the engine default - it means no real name was ever set, // so return empty to let the caller fall back to the save filename (timestamp). if (result == L"world") result = L""; @@ -774,7 +779,16 @@ void UIScene_LoadOrJoinMenu::tick() wchar_t wFilename[MAX_SAVEFILENAME_LENGTH]; ZeroMemory(wFilename, sizeof(wFilename)); mbstowcs(wFilename, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1); - wstring filePath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\saveData.ms"); + wchar_t wTitle[MAX_DISPLAYNAME_LENGTH]; + ZeroMemory(wTitle, sizeof(wTitle)); + mbstowcs(wTitle, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveTitle, MAX_DISPLAYNAME_LENGTH - 1); + wstring filePath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\") + wstring(wTitle) + wstring(L".ms"); + // Fallback to legacy saveData.ms if new-style filename doesn't exist + { + DWORD attrs = GetFileAttributesW(filePath.c_str()); + if (attrs == INVALID_FILE_ATTRIBUTES) + filePath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\saveData.ms"); + } HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr); DWORD fileSize = 0;