From 1444581cb61e62cd71c855c278c840dd009149f0 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Tue, 3 Mar 2026 17:58:47 +0800 Subject: [PATCH 01/26] chore: remove duplicated /Ob3 flag from CMake Release optimization settings --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 977bce2d0..74851754d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ function(configure_msvc_target target) $<$,$>:/W0> $<$:/MP> $<$:/EHsc> - $<$,$>:/GL /O2 /Ob3 /Oi /GT /GF> + $<$,$>:/GL /O2 /Oi /GT /GF> ) endfunction() From 42bb19d4903ba5283c3a6d57690f8970c4a4e1da Mon Sep 17 00:00:00 2001 From: Violet Date: Tue, 3 Mar 2026 17:53:28 +0200 Subject: [PATCH 02/26] chore: add `.clang-format` based on the style 4J seemed to use (#30) (#273) The style 4J used seems to be based on the Microsoft style (presumably the default settings of whatever Visual Studio they used to write this). However, the source files do not have much consistency so I highly doubt 4J cared too much about styling, just going with whatever happened to be the default. This style is therefore basically the Microsoft style (4-space indents, C#/Allman style braces) with some settings set based on my observations about the code. Fixes: #30 --- .clang-format | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..383e62104 --- /dev/null +++ b/.clang-format @@ -0,0 +1,55 @@ +--- +BasedOnStyle: Microsoft +AccessModifierOffset: -2 +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterExternBlock: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: false + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +ColumnLimit: 0 +IncludeBlocks: Preserve +IndentAccessModifiers: false +IndentCaseBlocks: true +IndentCaseLabels: false +IndentExportBlock: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 4 +InsertBraces: true +InsertNewlineAtEOF: true +NamespaceIndentation: None +PointerAlignment: Right +RemoveParentheses: Leave +RemoveSemicolon: false +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: + Enabled: true + IgnoreCase: false +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +TabWidth: 4 +UseTab: Never From a3095a705089145587835346378d9051e1d5db45 Mon Sep 17 00:00:00 2001 From: void_17 <61356189+void2012@users.noreply.github.com> Date: Tue, 3 Mar 2026 23:07:37 +0700 Subject: [PATCH 03/26] Ship updated binary alongside with the whole archive in nightly builds Now ship both the entire .zip archive and the separate .exe binary updated on each commit. --- .github/workflows/nightly.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index df01b57af..b4830a6db 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -35,5 +35,7 @@ jobs: with: tag_name: nightly name: Nightly Release - body: Compiled with MSVC v14.44.35207 in Release mode with Whole Program Optimization, as well as `/O2 /Ot /Oi /Ob3 /GF`. (So far the floating point model is `/fp:strict` to avoid undefined behavior before we refactor for performance). Requires at least Windows 7 and DirectX 11 compatible GPU to run. - files: LCEWindows64.zip + body: Compiled with MSVC v14.44.35207 in Release mode with Whole Program Optimization, as well as `/O2 /Ot /Oi /Ob3 /GF /fp:precise`. Requires at least Windows 7 and DirectX 11 compatible GPU to run. + files: | + LCEWindows64.zip + ./x64/Release/Minecraft.Client.exe From b44d29b2ff6742af5223582960518c5ea948748f Mon Sep 17 00:00:00 2001 From: void_17 <61356189+void2012@users.noreply.github.com> Date: Tue, 3 Mar 2026 23:18:57 +0700 Subject: [PATCH 04/26] Ship the .pdb file in nightly builds too --- .github/workflows/nightly.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b4830a6db..872834c3d 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -35,7 +35,8 @@ jobs: with: tag_name: nightly name: Nightly Release - body: Compiled with MSVC v14.44.35207 in Release mode with Whole Program Optimization, as well as `/O2 /Ot /Oi /Ob3 /GF /fp:precise`. Requires at least Windows 7 and DirectX 11 compatible GPU to run. + body: Requires at least Windows 7 and DirectX 11 compatible GPU to run. Compiled with MSVC v14.44.35207 in Release mode with Whole Program Optimization, as well as `/O2 /Ot /Oi /Ob3 /GF /fp:precise`. files: | LCEWindows64.zip ./x64/Release/Minecraft.Client.exe + ./x64/Release/Minecraft.Client.pdb From 5c91c2608690872aabba4f62d5ceb02c934f5f32 Mon Sep 17 00:00:00 2001 From: slcyed <91488376+slcyed@users.noreply.github.com> Date: Tue, 3 Mar 2026 11:27:23 -0500 Subject: [PATCH 05/26] shift + click for quickmove (#278) * shift + click for quickmove * shift click quick move in inventory --- Minecraft.Client/Common/App_enums.h | 1 + .../UI/IUIScene_AbstractContainerMenu.cpp | 47 +++++++++++++------ Minecraft.Client/Common/UI/UIController.cpp | 1 + 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/Minecraft.Client/Common/App_enums.h b/Minecraft.Client/Common/App_enums.h index db7bf70b3..e8e0d147e 100644 --- a/Minecraft.Client/Common/App_enums.h +++ b/Minecraft.Client/Common/App_enums.h @@ -829,6 +829,7 @@ enum EControllerActions ACTION_MENU_OTHER_STICK_LEFT, ACTION_MENU_OTHER_STICK_RIGHT, ACTION_MENU_PAUSEMENU, + ACTION_MENU_QUICK_MOVE, #ifdef _DURANGO ACTION_MENU_GTC_PAUSE, diff --git a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp index 1b76141d1..667431b2b 100644 --- a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp @@ -1304,42 +1304,60 @@ bool IUIScene_AbstractContainerMenu::handleKeyDown(int iPad, int iAction, bool b #endif int buttonNum=0; // 0 = LeftMouse, 1 = RightMouse - BOOL quickKeyHeld=FALSE; // Represents shift key on PC - - BOOL validKeyPress = FALSE; + BOOL quickKeyHeld=false; // Represents shift key on PC + BOOL quickKeyDown = false; // Represents shift key on PC + BOOL validKeyPress = false; bool itemEditorKeyPress = false; // Ignore input from other players //if(pMinecraft->player->GetXboxPad()!=pInputData->UserIndex) return S_OK; - + switch(iAction) { #ifdef _DEBUG_MENUS_ENABLED case ACTION_MENU_OTHER_STICK_PRESS: itemEditorKeyPress = TRUE; break; -#endif +#endif case ACTION_MENU_A: #ifdef __ORBIS__ case ACTION_MENU_TOUCHPAD_PRESS: #endif - if(!bRepeat) + if (!bRepeat) { validKeyPress = TRUE; // Standard left click buttonNum = 0; - quickKeyHeld = FALSE; - - if( IsSectionSlotList( m_eCurrSection ) ) + if (KMInput.IsKeyDown(VK_SHIFT)) { - int currentIndex = getCurrentIndex( m_eCurrSection ) - getSectionStartOffset(m_eCurrSection); + { + validKeyPress = TRUE; - bool bSlotHasItem = !isSlotEmpty(m_eCurrSection, currentIndex); - if ( bSlotHasItem ) - ui.PlayUISFX(eSFX_Press); + // Shift and left click + buttonNum = 0; + quickKeyHeld = TRUE; + if (IsSectionSlotList(m_eCurrSection)) + { + int currentIndex = getCurrentIndex(m_eCurrSection) - getSectionStartOffset(m_eCurrSection); + + bool bSlotHasItem = !isSlotEmpty(m_eCurrSection, currentIndex); + if (bSlotHasItem) + ui.PlayUISFX(eSFX_Press); + } + } + } + else { + if (IsSectionSlotList(m_eCurrSection)) + { + int currentIndex = getCurrentIndex(m_eCurrSection) - getSectionStartOffset(m_eCurrSection); + + bool bSlotHasItem = !isSlotEmpty(m_eCurrSection, currentIndex); + if (bSlotHasItem) + ui.PlayUISFX(eSFX_Press); + } + // } - // } break; case ACTION_MENU_X: @@ -1361,6 +1379,7 @@ bool IUIScene_AbstractContainerMenu::handleKeyDown(int iPad, int iAction, bool b } } break; + case ACTION_MENU_Y: if(!bRepeat) { diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp index 8b38bbb3b..9e4a32024 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -1012,6 +1012,7 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key) case ACTION_MENU_PAUSEMENU: kbDown = KMInput.IsKeyDown(VK_ESCAPE); kbPressed = KMInput.IsKeyPressed(VK_ESCAPE); kbReleased = KMInput.IsKeyReleased(VK_ESCAPE); break; case ACTION_MENU_LEFT_SCROLL: kbDown = KMInput.IsKeyDown('Q'); kbPressed = KMInput.IsKeyPressed('Q'); kbReleased = KMInput.IsKeyReleased('Q'); break; case ACTION_MENU_RIGHT_SCROLL: kbDown = KMInput.IsKeyDown('E'); kbPressed = KMInput.IsKeyPressed('E'); kbReleased = KMInput.IsKeyReleased('E'); break; + case ACTION_MENU_QUICK_MOVE: kbDown = KMInput.IsKeyDown(VK_SHIFT); kbPressed = KMInput.IsKeyPressed(VK_SHIFT); kbReleased = KMInput.IsKeyReleased(VK_SHIFT); break; } pressed = pressed || kbPressed; released = released || kbReleased; From 515f91cad8e0625334954acd4024c6bfefcc89e8 Mon Sep 17 00:00:00 2001 From: Slenderman Date: Tue, 3 Mar 2026 11:58:22 -0500 Subject: [PATCH 06/26] Fix player save data issue & multiple username implementations (#257) * fix saving issue & multiple username implementations * Update README.md Updated the method for overriding in-game username from '-name' to 'username.txt'. * remove unused include i forgot to get rid of while testing --- .../Common/Network/GameNetworkManager.cpp | 2 +- .../Common/Network/GameNetworkManager.h | 2 +- Minecraft.Client/Extrax64Stubs.cpp | 17 ++++++++- .../Windows64/4JLibs/inc/4J_Profile.h | 2 +- Minecraft.Client/Windows64/Windows64_App.h | 1 + .../Windows64/Windows64_Minecraft.cpp | 38 ------------------- README.md | 8 +--- 7 files changed, 20 insertions(+), 50 deletions(-) diff --git a/Minecraft.Client/Common/Network/GameNetworkManager.cpp b/Minecraft.Client/Common/Network/GameNetworkManager.cpp index b3fb5cd70..a65a61aa9 100644 --- a/Minecraft.Client/Common/Network/GameNetworkManager.cpp +++ b/Minecraft.Client/Common/Network/GameNetworkManager.cpp @@ -1978,7 +1978,7 @@ bool CGameNetworkManager::AllowedToPlayMultiplayer(int playerIdx) return ProfileManager.AllowedToPlayMultiplayer(playerIdx); } -char *CGameNetworkManager::GetOnlineName(int playerIdx) +const char *CGameNetworkManager::GetOnlineName(int playerIdx) { return ProfileManager.GetGamertag(playerIdx); } diff --git a/Minecraft.Client/Common/Network/GameNetworkManager.h b/Minecraft.Client/Common/Network/GameNetworkManager.h index 01db27240..bb7633c28 100644 --- a/Minecraft.Client/Common/Network/GameNetworkManager.h +++ b/Minecraft.Client/Common/Network/GameNetworkManager.h @@ -196,7 +196,7 @@ private: int GetLockedProfile(); bool IsSignedInLive(int playerIdx); bool AllowedToPlayMultiplayer(int playerIdx); - char *GetOnlineName(int playerIdx); + const char *GetOnlineName(int playerIdx); C4JThread::Event* m_hServerStoppedEvent; C4JThread::Event* m_hServerReadyEvent; diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index 3a86cbeaf..22ad578fb 100644 --- a/Minecraft.Client/Extrax64Stubs.cpp +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -589,8 +589,21 @@ char fakeGamerTag[32] = "PlayerName"; void SetFakeGamertag(char* name) { strcpy_s(fakeGamerTag, name); } char* C_4JProfile::GetGamertag(int iPad) { return fakeGamerTag; } #else -char* C_4JProfile::GetGamertag(int iPad) { extern char g_Win64Username[17]; return g_Win64Username; } -wstring C_4JProfile::GetDisplayName(int iPad) { extern wchar_t g_Win64UsernameW[17]; return g_Win64UsernameW; } +#include + +const char* C_4JProfile::GetGamertag(int iPad) +{ + static std::string narrowName; + const wchar_t* wideName = g_playerName.c_str(); + + int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, wideName, -1, nullptr, 0, nullptr, nullptr); + + narrowName.resize(sizeNeeded); + WideCharToMultiByte(CP_UTF8, 0, wideName, -1, &narrowName[0], sizeNeeded, nullptr, nullptr); + + return narrowName.c_str(); +} +wstring C_4JProfile::GetDisplayName(int iPad) { return g_playerName; } #endif bool C_4JProfile::IsFullVersion() { return s_bProfileIsFullVersion; } void C_4JProfile::SetSignInChangeCallback(void (*Func)(LPVOID, bool, unsigned int), LPVOID lpParam) {} diff --git a/Minecraft.Client/Windows64/4JLibs/inc/4J_Profile.h b/Minecraft.Client/Windows64/4JLibs/inc/4J_Profile.h index f1bd85bbe..f7718a835 100644 --- a/Minecraft.Client/Windows64/4JLibs/inc/4J_Profile.h +++ b/Minecraft.Client/Windows64/4JLibs/inc/4J_Profile.h @@ -75,7 +75,7 @@ public: // SYS int GetPrimaryPad(); void SetPrimaryPad(int iPad); - char* GetGamertag(int iPad); + const char* GetGamertag(int iPad); wstring GetDisplayName(int iPad); bool IsFullVersion(); void SetSignInChangeCallback(void ( *Func)(LPVOID, bool, unsigned int),LPVOID lpParam); diff --git a/Minecraft.Client/Windows64/Windows64_App.h b/Minecraft.Client/Windows64/Windows64_App.h index bff916ec7..32d204ad0 100644 --- a/Minecraft.Client/Windows64/Windows64_App.h +++ b/Minecraft.Client/Windows64/Windows64_App.h @@ -33,6 +33,7 @@ public: virtual void TemporaryCreateGameStart(); bool m_bShutdown; + wstring g_playerName; }; extern CConsoleMinecraftApp app; diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index b49af8535..78ab76fef 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -85,8 +85,6 @@ BOOL g_bWidescreen = TRUE; int g_iScreenWidth = 1920; int g_iScreenHeight = 1080; -char g_Win64Username[17] = { 0 }; -wchar_t g_Win64UsernameW[17] = { 0 }; UINT g_ScreenWidth = 1920; UINT g_ScreenHeight = 1080; @@ -764,42 +762,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, //g_iScreenWidth = 960; //g_iScreenHeight = 544; } - - char cmdLineA[1024]; - strncpy_s(cmdLineA, sizeof(cmdLineA), lpCmdLine, _TRUNCATE); - - char* nameArg = strstr(cmdLineA, "-name "); - if (nameArg) - { - nameArg += 6; - while (*nameArg == ' ') nameArg++; - char nameBuf[17]; - int n = 0; - while (nameArg[n] && nameArg[n] != ' ' && n < 16) { nameBuf[n] = nameArg[n]; n++; } - nameBuf[n] = 0; - strncpy_s(g_Win64Username, 17, nameBuf, _TRUNCATE); - } } - if (g_Win64Username[0] == 0) - { - DWORD sz = 17; - static bool seeded = false; - if (!seeded) - { - seeded = true; - srand((unsigned int)time(NULL)); - } - - int r = rand() % 10000; // 0�9999 - - snprintf(g_Win64Username, 17, "Player%04d", r); - - g_Win64Username[16] = 0; - } - - MultiByteToWideChar(CP_ACP, 0, g_Win64Username, -1, g_Win64UsernameW, 17); - // Initialize global strings MyRegisterClass(hInstance); @@ -971,8 +935,6 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, IQNet::m_player[i].m_isHostPlayer = (i == 0); swprintf_s(IQNet::m_player[i].m_gamertag, 32, L"Player%d", i); } - extern wchar_t g_Win64UsernameW[17]; - wcscpy_s(IQNet::m_player[0].m_gamertag, 32, g_Win64UsernameW); WinsockNetLayer::Initialize(); diff --git a/README.md b/README.md index 999626e9a..bc45ef229 100644 --- a/README.md +++ b/README.md @@ -33,13 +33,7 @@ Basic LAN multiplayer is available on the Windows build - Other players on the same LAN can discover the session from the in-game Join Game menu - Game connections use TCP port `25565` by default - LAN discovery uses UDP port `25566` -- You can override your in-game username at launch with `-name` - -Example: - -```powershell -Minecraft.Client.exe -name Steve -``` +- You can override your in-game username at launch with `username.txt` This feature is based on [LCEMP](https://github.com/LCEMP/LCEMP/) From 1b0e5df27e339d616c4b79f7cc30ab5f7582fd9e Mon Sep 17 00:00:00 2001 From: DetectivEren <55319774+detectiveren@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:55:27 +0000 Subject: [PATCH 07/26] chunk optimization (#246) makes chunks load a bit faster --- Minecraft.Client/LevelRenderer.cpp | 4 ++-- Minecraft.Client/LevelRenderer.h | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Minecraft.Client/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp index 8216f1fe9..fc56494f9 100644 --- a/Minecraft.Client/LevelRenderer.cpp +++ b/Minecraft.Client/LevelRenderer.cpp @@ -1964,8 +1964,8 @@ bool LevelRenderer::updateDirtyChunks() { if( (!onlyRebuild) || globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_COMPILED || - ( distSq < 20 * 20 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data - { + ( distSq < 96 * 96 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data + { // distSq adjusted from 20 * 20 to 96 * 96 - updated by detectiveren considered++; // Is this chunk nearer than our nearest? #ifdef _LARGE_WORLDS diff --git a/Minecraft.Client/LevelRenderer.h b/Minecraft.Client/LevelRenderer.h index 88280b1e4..d9e56e34d 100644 --- a/Minecraft.Client/LevelRenderer.h +++ b/Minecraft.Client/LevelRenderer.h @@ -58,8 +58,10 @@ public: static const int MAX_COMMANDBUFFER_ALLOCATIONS = 448 * 1024 * 1024; // 4J - added - hard limit is 512 so giving a lot of headroom here for fragmentation (have seen 16MB lost to fragmentation in multiplayer crash dump before) #elif defined __PS3__ static const int MAX_COMMANDBUFFER_ALLOCATIONS = 110 * 1024 * 1024; // 4J - added +#elif defined _WINDOWS64 + static const int MAX_COMMANDBUFFER_ALLOCATIONS = 2047 * 1024 * 1024; // added by Twest #else - static const int MAX_COMMANDBUFFER_ALLOCATIONS = 55 * 1024 * 1024; // 4J - added + static const int MAX_COMMANDBUFFER_ALLOCATIONS = 55 * 1024 * 1024; // 4J - added #endif public: LevelRenderer(Minecraft *mc, Textures *textures); @@ -270,10 +272,10 @@ public: bool dirtyChunkPresent; __int64 lastDirtyChunkFound; - static const int FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS = 250; + static const int FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS = 125; // decreased from 250 to 125 - updated by detectiveren #ifdef _LARGE_WORLDS - static const int MAX_CONCURRENT_CHUNK_REBUILDS = 4; + static const int MAX_CONCURRENT_CHUNK_REBUILDS = 8; // increased from 4 to 8 - updated by detectiveren static const int MAX_CHUNK_REBUILD_THREADS = MAX_CONCURRENT_CHUNK_REBUILDS - 1; static Chunk permaChunk[MAX_CONCURRENT_CHUNK_REBUILDS]; static C4JThread *rebuildThreads[MAX_CHUNK_REBUILD_THREADS]; From 540e33d787ee46902fb49373a8a18b2d2e6d766d Mon Sep 17 00:00:00 2001 From: FancyEX <30706150+fancythedeveloper@users.noreply.github.com> Date: Tue, 3 Mar 2026 12:58:04 -0500 Subject: [PATCH 08/26] Separate _WINDOWS64 and _XBOX_ONE (#248) The latter returning to the original 512 value. --- Minecraft.Client/LevelRenderer.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/LevelRenderer.h b/Minecraft.Client/LevelRenderer.h index d9e56e34d..a9e73a604 100644 --- a/Minecraft.Client/LevelRenderer.h +++ b/Minecraft.Client/LevelRenderer.h @@ -52,8 +52,10 @@ public: static const int CHUNK_SIZE = 16; #endif static const int CHUNK_Y_COUNT = Level::maxBuildHeight / CHUNK_SIZE; -#if (defined _XBOX_ONE || defined _WINDOWS64) - static const int MAX_COMMANDBUFFER_ALLOCATIONS = 2047 * 1024 * 1024; // Changed to 2047. 4J had set to 512. +#if defined _WINDOWS64 + static const int MAX_COMMANDBUFFER_ALLOCATIONS = 2047 * 1024 * 1024; // Changed to 2047. 4J had set to 512. +#elif defined _XBOX_ONE + static const int MAX_COMMANDBUFFER_ALLOCATIONS = 512 * 1024 * 1024; // 4J - added #elif defined __ORBIS__ static const int MAX_COMMANDBUFFER_ALLOCATIONS = 448 * 1024 * 1024; // 4J - added - hard limit is 512 so giving a lot of headroom here for fragmentation (have seen 16MB lost to fragmentation in multiplayer crash dump before) #elif defined __PS3__ From 942ef7e99fdd49b3b72251eed7fd12f1de288866 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Tue, 3 Mar 2026 18:17:33 +0000 Subject: [PATCH 09/26] Tidy up PR template --- .github/pull_request_template.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 9578777cb..a1c3e74aa 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,23 +1,23 @@ -# Pull Request - -## Note: IF YOUR PR CHANGES THE GAME BEHAVIOR VISIBLY, REMEMBER TO ATTACH A GAMEPLAY FOOTAGE (or at least a screenshot) OF YOU *ACTUALLY* PLAYING THE GAME WITH YOUR CHANGES. Untested PRs are *NOT* welcome. Please don't forget to describe what did you do in each commit in your PR. + ## Description -Briefly describe the changes this PR introduces. + ## Changes ### Previous Behavior -*Describe how the code behaved before this change.* + ### Root Cause -*Explain the core reason behind the erroneous/old behavior (e.g., bug, design flaw, missing edge case).* + ### New Behavior -*Describe how the code behaves after this change.* + ### Fix Implementation -*Detail exactly how the issue was resolved (specific code changes, algorithms, logic flows).* + ## Related Issues - Fixes #[issue-number] From 9b5348113c696107cfa6620aae5b4b241adf6a2e Mon Sep 17 00:00:00 2001 From: rtm516 Date: Tue, 3 Mar 2026 18:20:14 +0000 Subject: [PATCH 10/26] Don't build when PR template is updated --- .github/workflows/nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 872834c3d..4ef1274d2 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -8,6 +8,7 @@ on: paths-ignore: - '.gitignore' - '*.md' + - '.github/*.md' jobs: build: From 31cc598b755951f8bdca1bfbf797b1c96b076c2e Mon Sep 17 00:00:00 2001 From: TGS <93544652+zephiii@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:22:31 +0000 Subject: [PATCH 11/26] Remove duplicate elif from LevelRender.h (#296) --- Minecraft.Client/LevelRenderer.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Minecraft.Client/LevelRenderer.h b/Minecraft.Client/LevelRenderer.h index a9e73a604..bb2c0d13c 100644 --- a/Minecraft.Client/LevelRenderer.h +++ b/Minecraft.Client/LevelRenderer.h @@ -60,8 +60,6 @@ public: static const int MAX_COMMANDBUFFER_ALLOCATIONS = 448 * 1024 * 1024; // 4J - added - hard limit is 512 so giving a lot of headroom here for fragmentation (have seen 16MB lost to fragmentation in multiplayer crash dump before) #elif defined __PS3__ static const int MAX_COMMANDBUFFER_ALLOCATIONS = 110 * 1024 * 1024; // 4J - added -#elif defined _WINDOWS64 - static const int MAX_COMMANDBUFFER_ALLOCATIONS = 2047 * 1024 * 1024; // added by Twest #else static const int MAX_COMMANDBUFFER_ALLOCATIONS = 55 * 1024 * 1024; // 4J - added #endif From 384b9f444587ec326a1a98e1b445306c79e4b77d Mon Sep 17 00:00:00 2001 From: rtm516 Date: Tue, 3 Mar 2026 20:40:53 +0000 Subject: [PATCH 12/26] Fix ender dragon hit not making left side red (#309) --- Minecraft.Client/EnderDragonRenderer.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Minecraft.Client/EnderDragonRenderer.cpp b/Minecraft.Client/EnderDragonRenderer.cpp index 037552ed3..4119e5b9f 100644 --- a/Minecraft.Client/EnderDragonRenderer.cpp +++ b/Minecraft.Client/EnderDragonRenderer.cpp @@ -77,12 +77,7 @@ void EnderDragonRenderer::renderModel(shared_ptr _mob, float wp, f glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(1, 0, 0, 0.5f); -#ifdef __PSVITA__ - // AP - not sure that the usecompiled flag is supposed to be false. This makes it really slow on vita. Making it true still seems to look the same model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, true); -#else - model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, false); -#endif glEnable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDepthFunc(GL_LEQUAL); From 30ecc8025019fddd7c87a25ad19634ab008f1637 Mon Sep 17 00:00:00 2001 From: Fin <169037250+DigitalScorerFin@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:13:35 +0000 Subject: [PATCH 13/26] Add menu display check for sneak toggle in flying mode (#319) Fixes being able to fly down while a menu is open. --- Minecraft.Client/Minecraft.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index bc9aec3b9..164417abe 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -1456,7 +1456,7 @@ void Minecraft::run_middle() if (KMInput.ConsumeKeyPress('C')) localplayers[i]->ullButtonsPressed |= 1LL<ullButtonsPressed |= 1LL<abilities.flying && KMInput.IsKeyDown(VK_SHIFT)) + if (localplayers[i]->abilities.flying && KMInput.IsKeyDown(VK_SHIFT) && !ui.GetMenuDisplayed(i)) localplayers[i]->ullButtonsPressed |= 1LL< Date: Tue, 3 Mar 2026 16:50:28 -0500 Subject: [PATCH 14/26] Update username logic and implement latest LCEMP changes (#311) * Update username logic and implement latest LCEMP changes * Update old reference * Fix tutorial world crash * Restore deleted comment --- Minecraft.Client/ClientConnection.cpp | 65 +++++- Minecraft.Client/Extrax64Stubs.cpp | 52 +++-- .../Windows64/4JLibs/inc/4J_Profile.h | 2 +- .../Windows64/Network/WinsockNetLayer.cpp | 217 ++++++++++++------ .../Windows64/Network/WinsockNetLayer.h | 16 +- Minecraft.Client/Windows64/Windows64_App.cpp | 39 +--- Minecraft.Client/Windows64/Windows64_App.h | 1 - .../Windows64/Windows64_Minecraft.cpp | 44 ++++ 8 files changed, 288 insertions(+), 148 deletions(-) diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index f3510d336..8123a2f0b 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -55,6 +55,12 @@ #endif #include "DLCTexturePack.h" +#ifdef _WINDOWS64 +#include "Xbox\Network\NetworkPlayerXbox.h" +#include "Common\Network\PlatformNetworkManagerStub.h" +#endif + + #ifdef _DURANGO #include "..\Minecraft.World\DurangoStats.h" #include "..\Minecraft.World\GenericStats.h" @@ -421,7 +427,6 @@ void ClientConnection::handleAddEntity(shared_ptr packet) { case AddEntityPacket::MINECART: e = Minecart::createMinecart(level, x, y, z, packet->data); - break; case AddEntityPacket::FISH_HOOK: { // 4J Stu - Brought forward from 1.4 to be able to drop XP from fishing @@ -444,7 +449,7 @@ void ClientConnection::handleAddEntity(shared_ptr packet) } } - if (owner->instanceof(eTYPE_PLAYER)) + if (owner != NULL && owner->instanceof(eTYPE_PLAYER)) { shared_ptr player = dynamic_pointer_cast(owner); shared_ptr hook = shared_ptr( new FishingHook(level, x, y, z, player) ); @@ -793,7 +798,28 @@ void ClientConnection::handleAddPlayer(shared_ptr packet) if (networkPlayer != NULL) player->m_displayName = networkPlayer->GetDisplayName(); #else // On all other platforms display name is just gamertag so don't check with the network manager - player->m_displayName = player->name; + player->m_displayName = player->getName(); +#endif + +#ifdef _WINDOWS64 + { + PlayerUID pktXuid = player->getXuid(); + const PlayerUID WIN64_XUID_BASE = (PlayerUID)0xe000d45248242f2e; + if (pktXuid >= WIN64_XUID_BASE && pktXuid < WIN64_XUID_BASE + MINECRAFT_NET_MAX_PLAYERS) + { + BYTE smallId = (BYTE)(pktXuid - WIN64_XUID_BASE); + INetworkPlayer* np = g_NetworkManager.GetPlayerBySmallId(smallId); + if (np != NULL) + { + NetworkPlayerXbox* npx = (NetworkPlayerXbox*)np; + IQNetPlayer* qp = npx->GetQNetPlayer(); + if (qp != NULL && qp->m_gamertag[0] == 0) + { + wcsncpy_s(qp->m_gamertag, 32, packet->name.c_str(), _TRUNCATE); + } + } + } + } #endif // printf("\t\t\t\t%d: Add player\n",packet->id,packet->yRot); @@ -938,6 +964,39 @@ void ClientConnection::handleMoveEntitySmall(shared_ptr p void ClientConnection::handleRemoveEntity(shared_ptr packet) { +#ifdef _WINDOWS64 + if (!g_NetworkManager.IsHost()) + { + for (int i = 0; i < packet->ids.length; i++) + { + shared_ptr entity = getEntity(packet->ids[i]); + if (entity != NULL && entity->GetType() == eTYPE_PLAYER) + { + shared_ptr player = dynamic_pointer_cast(entity); + if (player != NULL) + { + PlayerUID xuid = player->getXuid(); + INetworkPlayer* np = g_NetworkManager.GetPlayerByXuid(xuid); + if (np != NULL) + { + NetworkPlayerXbox* npx = (NetworkPlayerXbox*)np; + IQNetPlayer* qp = npx->GetQNetPlayer(); + if (qp != NULL) + { + extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager; + g_pPlatformNetworkManager->NotifyPlayerLeaving(qp); + qp->m_smallId = 0; + qp->m_isRemote = false; + qp->m_isHostPlayer = false; + qp->m_gamertag[0] = 0; + qp->SetCustomDataValue(0); + } + } + } + } + } + } +#endif for (int i = 0; i < packet->ids.length; i++) { level->removeEntity(packet->ids[i]); diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index 22ad578fb..5a3c52790 100644 --- a/Minecraft.Client/Extrax64Stubs.cpp +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -199,11 +199,10 @@ DWORD IQNetPlayer::GetSendQueueSize(IQNetPlayer * player, DWORD dwFlags) { retur DWORD IQNetPlayer::GetCurrentRtt() { return 0; } bool IQNetPlayer::IsHost() { return m_isHostPlayer; } bool IQNetPlayer::IsGuest() { return false; } -bool IQNetPlayer::IsLocal() { return true; } +bool IQNetPlayer::IsLocal() { return !m_isRemote; } PlayerUID IQNetPlayer::GetXuid() { return (PlayerUID)(0xe000d45248242f2e + m_smallId); } // todo: restore to INVALID_XUID once saves support this -extern wstring g_playerName; -LPCWSTR IQNetPlayer::GetGamertag() { return g_playerName.empty() ? L"Windows" : g_playerName.c_str(); } -int IQNetPlayer::GetSessionIndex() { return 0; } +LPCWSTR IQNetPlayer::GetGamertag() { return m_gamertag; } +int IQNetPlayer::GetSessionIndex() { return m_smallId; } bool IQNetPlayer::IsTalking() { return false; } bool IQNetPlayer::IsMutedByLocalUser(DWORD dwUserIndex) { return false; } bool IQNetPlayer::HasVoice() { return false; } @@ -232,13 +231,17 @@ void Win64_SetupRemoteQNetPlayer(IQNetPlayer * player, BYTE smallId, bool isHost IQNet::s_playerCount = smallId + 1; } +static bool Win64_IsActivePlayer(IQNetPlayer* p, DWORD index); + HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex) { return S_OK; } IQNetPlayer* IQNet::GetHostPlayer() { return &m_player[0]; } IQNetPlayer* IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex) { if (s_isHosting) { - if (dwUserIndex < MINECRAFT_NET_MAX_PLAYERS && !m_player[dwUserIndex].m_isRemote) + if (dwUserIndex < MINECRAFT_NET_MAX_PLAYERS && + !m_player[dwUserIndex].m_isRemote && + Win64_IsActivePlayer(&m_player[dwUserIndex], dwUserIndex)) return &m_player[dwUserIndex]; return NULL; } @@ -246,7 +249,7 @@ IQNetPlayer* IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex) return NULL; for (DWORD i = 0; i < s_playerCount; i++) { - if (!m_player[i].m_isRemote) + if (!m_player[i].m_isRemote && Win64_IsActivePlayer(&m_player[i], i)) return &m_player[i]; } return NULL; @@ -299,15 +302,28 @@ QNET_STATE IQNet::GetState() { return _iQNetStubState; } bool IQNet::IsHost() { return s_isHosting; } HRESULT IQNet::JoinGameFromInviteInfo(DWORD dwUserIndex, DWORD dwUserMask, const INVITE_INFO * pInviteInfo) { return S_OK; } void IQNet::HostGame() { _iQNetStubState = QNET_STATE_SESSION_STARTING; s_isHosting = true; } -void IQNet::ClientJoinGame() { _iQNetStubState = QNET_STATE_SESSION_STARTING; s_isHosting = false; } +void IQNet::ClientJoinGame() +{ + _iQNetStubState = QNET_STATE_SESSION_STARTING; + s_isHosting = false; + + for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++) + { + m_player[i].m_smallId = (BYTE)i; + m_player[i].m_isRemote = true; + m_player[i].m_isHostPlayer = false; + m_player[i].m_gamertag[0] = 0; + m_player[i].SetCustomDataValue(0); + } +} void IQNet::EndGame() { _iQNetStubState = QNET_STATE_IDLE; s_isHosting = false; s_playerCount = 1; - for (int i = 1; i < MINECRAFT_NET_MAX_PLAYERS; i++) + for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++) { - m_player[i].m_smallId = 0; + m_player[i].m_smallId = (BYTE)i; m_player[i].m_isRemote = false; m_player[i].m_isHostPlayer = false; m_player[i].m_gamertag[0] = 0; @@ -587,23 +603,9 @@ void C_4JProfile::SetPrimaryPad(int iPad) {} #ifdef _DURANGO char fakeGamerTag[32] = "PlayerName"; void SetFakeGamertag(char* name) { strcpy_s(fakeGamerTag, name); } -char* C_4JProfile::GetGamertag(int iPad) { return fakeGamerTag; } #else -#include - -const char* C_4JProfile::GetGamertag(int iPad) -{ - static std::string narrowName; - const wchar_t* wideName = g_playerName.c_str(); - - int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, wideName, -1, nullptr, 0, nullptr, nullptr); - - narrowName.resize(sizeNeeded); - WideCharToMultiByte(CP_UTF8, 0, wideName, -1, &narrowName[0], sizeNeeded, nullptr, nullptr); - - return narrowName.c_str(); -} -wstring C_4JProfile::GetDisplayName(int iPad) { return g_playerName; } +char* C_4JProfile::GetGamertag(int iPad) { extern char g_Win64Username[17]; return g_Win64Username; } +wstring C_4JProfile::GetDisplayName(int iPad) { extern wchar_t g_Win64UsernameW[17]; return g_Win64UsernameW; } #endif bool C_4JProfile::IsFullVersion() { return s_bProfileIsFullVersion; } void C_4JProfile::SetSignInChangeCallback(void (*Func)(LPVOID, bool, unsigned int), LPVOID lpParam) {} diff --git a/Minecraft.Client/Windows64/4JLibs/inc/4J_Profile.h b/Minecraft.Client/Windows64/4JLibs/inc/4J_Profile.h index f7718a835..f1bd85bbe 100644 --- a/Minecraft.Client/Windows64/4JLibs/inc/4J_Profile.h +++ b/Minecraft.Client/Windows64/4JLibs/inc/4J_Profile.h @@ -75,7 +75,7 @@ public: // SYS int GetPrimaryPad(); void SetPrimaryPad(int iPad); - const char* GetGamertag(int iPad); + char* GetGamertag(int iPad); wstring GetDisplayName(int iPad); bool IsFullVersion(); void SetSignInChangeCallback(void ( *Func)(LPVOID, bool, unsigned int),LPVOID lpParam); diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp index 19fc25987..d3ea1c3aa 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp @@ -1,3 +1,6 @@ +// Code implemented by LCEMP, credit if used on other repos +// https://github.com/LCEMP/LCEMP + #include "stdafx.h" #ifdef _WINDOWS64 @@ -151,7 +154,7 @@ bool WinsockNetLayer::HostGame(int port) LeaveCriticalSection(&s_freeSmallIdLock); struct addrinfo hints = {}; - struct addrinfo *result = NULL; + struct addrinfo* result = NULL; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; @@ -177,7 +180,7 @@ bool WinsockNetLayer::HostGame(int port) } int opt = 1; - setsockopt(s_listenSocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); + setsockopt(s_listenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); iResult = ::bind(s_listenSocket, result->ai_addr, (int)result->ai_addrlen); freeaddrinfo(result); @@ -207,15 +210,23 @@ bool WinsockNetLayer::HostGame(int port) return true; } -bool WinsockNetLayer::JoinGame(const char *ip, int port) +bool WinsockNetLayer::JoinGame(const char* ip, int port) { if (!s_initialized && !Initialize()) return false; s_isHost = false; s_hostSmallId = 0; + s_connected = false; + s_active = false; + + if (s_hostConnectionSocket != INVALID_SOCKET) + { + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + } struct addrinfo hints = {}; - struct addrinfo *result = NULL; + struct addrinfo* result = NULL; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; @@ -231,37 +242,55 @@ bool WinsockNetLayer::JoinGame(const char *ip, int port) return false; } - s_hostConnectionSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); - if (s_hostConnectionSocket == INVALID_SOCKET) + bool connected = false; + BYTE assignedSmallId = 0; + const int maxAttempts = 12; + + for (int attempt = 0; attempt < maxAttempts; ++attempt) { - app.DebugPrintf("socket() failed: %d\n", WSAGetLastError()); - freeaddrinfo(result); - return false; + s_hostConnectionSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (s_hostConnectionSocket == INVALID_SOCKET) + { + app.DebugPrintf("socket() failed: %d\n", WSAGetLastError()); + break; + } + + int noDelay = 1; + setsockopt(s_hostConnectionSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&noDelay, sizeof(noDelay)); + + iResult = connect(s_hostConnectionSocket, result->ai_addr, (int)result->ai_addrlen); + if (iResult == SOCKET_ERROR) + { + int err = WSAGetLastError(); + app.DebugPrintf("connect() to %s:%d failed (attempt %d/%d): %d\n", ip, port, attempt + 1, maxAttempts, err); + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + Sleep(200); + continue; + } + + BYTE assignBuf[1]; + int bytesRecv = recv(s_hostConnectionSocket, (char*)assignBuf, 1, 0); + if (bytesRecv != 1) + { + app.DebugPrintf("Failed to receive small ID assignment from host (attempt %d/%d)\n", attempt + 1, maxAttempts); + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + Sleep(200); + continue; + } + + assignedSmallId = assignBuf[0]; + connected = true; + break; } - - int noDelay = 1; - setsockopt(s_hostConnectionSocket, IPPROTO_TCP, TCP_NODELAY, (const char *)&noDelay, sizeof(noDelay)); - - iResult = connect(s_hostConnectionSocket, result->ai_addr, (int)result->ai_addrlen); freeaddrinfo(result); - if (iResult == SOCKET_ERROR) - { - app.DebugPrintf("connect() to %s:%d failed: %d\n", ip, port, WSAGetLastError()); - closesocket(s_hostConnectionSocket); - s_hostConnectionSocket = INVALID_SOCKET; - return false; - } - BYTE assignBuf[1]; - int bytesRecv = recv(s_hostConnectionSocket, (char *)assignBuf, 1, 0); - if (bytesRecv != 1) + if (!connected) { - app.DebugPrintf("Failed to receive small ID assignment from host\n"); - closesocket(s_hostConnectionSocket); - s_hostConnectionSocket = INVALID_SOCKET; return false; } - s_localSmallId = assignBuf[0]; + s_localSmallId = assignedSmallId; app.DebugPrintf("Win64 LAN: Connected to %s:%d, assigned smallId=%d\n", ip, port, s_localSmallId); @@ -273,7 +302,7 @@ bool WinsockNetLayer::JoinGame(const char *ip, int port) return true; } -bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void *data, int dataSize) +bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void* data, int dataSize) { if (sock == INVALID_SOCKET || dataSize <= 0) return false; @@ -289,7 +318,7 @@ bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void *data, int dataSize) int toSend = 4; while (totalSent < toSend) { - int sent = send(sock, (const char *)header + totalSent, toSend - totalSent, 0); + int sent = send(sock, (const char*)header + totalSent, toSend - totalSent, 0); if (sent == SOCKET_ERROR || sent == 0) { LeaveCriticalSection(&s_sendLock); @@ -301,7 +330,7 @@ bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void *data, int dataSize) totalSent = 0; while (totalSent < dataSize) { - int sent = send(sock, (const char *)data + totalSent, dataSize - totalSent, 0); + int sent = send(sock, (const char*)data + totalSent, dataSize - totalSent, 0); if (sent == SOCKET_ERROR || sent == 0) { LeaveCriticalSection(&s_sendLock); @@ -314,7 +343,7 @@ bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void *data, int dataSize) return true; } -bool WinsockNetLayer::SendToSmallId(BYTE targetSmallId, const void *data, int dataSize) +bool WinsockNetLayer::SendToSmallId(BYTE targetSmallId, const void* data, int dataSize) { if (!s_active) return false; @@ -346,34 +375,34 @@ SOCKET WinsockNetLayer::GetSocketForSmallId(BYTE smallId) return INVALID_SOCKET; } -static bool RecvExact(SOCKET sock, BYTE *buf, int len) +static bool RecvExact(SOCKET sock, BYTE* buf, int len) { int totalRecv = 0; while (totalRecv < len) { - int r = recv(sock, (char *)buf + totalRecv, len - totalRecv, 0); + int r = recv(sock, (char*)buf + totalRecv, len - totalRecv, 0); if (r <= 0) return false; totalRecv += r; } return true; } -void WinsockNetLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char *data, unsigned int dataSize) +void WinsockNetLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char* data, unsigned int dataSize) { - INetworkPlayer *pPlayerFrom = g_NetworkManager.GetPlayerBySmallId(fromSmallId); - INetworkPlayer *pPlayerTo = g_NetworkManager.GetPlayerBySmallId(toSmallId); + INetworkPlayer* pPlayerFrom = g_NetworkManager.GetPlayerBySmallId(fromSmallId); + INetworkPlayer* pPlayerTo = g_NetworkManager.GetPlayerBySmallId(toSmallId); if (pPlayerFrom == NULL || pPlayerTo == NULL) return; if (s_isHost) { - ::Socket *pSocket = pPlayerFrom->GetSocket(); + ::Socket* pSocket = pPlayerFrom->GetSocket(); if (pSocket != NULL) pSocket->pushDataToQueue(data, dataSize, false); } else { - ::Socket *pSocket = pPlayerTo->GetSocket(); + ::Socket* pSocket = pPlayerTo->GetSocket(); if (pSocket != NULL) pSocket->pushDataToQueue(data, dataSize, true); } @@ -392,7 +421,7 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param) } int noDelay = 1; - setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (const char *)&noDelay, sizeof(noDelay)); + setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&noDelay, sizeof(noDelay)); extern QNET_STATE _iQNetStubState; if (_iQNetStubState != QNET_STATE_GAME_PLAY) @@ -423,7 +452,7 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param) LeaveCriticalSection(&s_freeSmallIdLock); BYTE assignBuf[1] = { assignedSmallId }; - int sent = send(clientSocket, (const char *)assignBuf, 1, 0); + int sent = send(clientSocket, (const char*)assignBuf, 1, 0); if (sent != 1) { app.DebugPrintf("Failed to send small ID to client\n"); @@ -444,15 +473,15 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param) app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId); - IQNetPlayer *qnetPlayer = &IQNet::m_player[assignedSmallId]; + IQNetPlayer* qnetPlayer = &IQNet::m_player[assignedSmallId]; - extern void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, BYTE smallId, bool isHost, bool isLocal); + extern void Win64_SetupRemoteQNetPlayer(IQNetPlayer * player, BYTE smallId, bool isHost, bool isLocal); Win64_SetupRemoteQNetPlayer(qnetPlayer, assignedSmallId, false, false); - extern CPlatformNetworkManagerStub *g_pPlatformNetworkManager; + extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager; g_pPlatformNetworkManager->NotifyPlayerJoined(qnetPlayer); - DWORD *threadParam = new DWORD; + DWORD* threadParam = new DWORD; *threadParam = connIdx; HANDLE hThread = CreateThread(NULL, 0, RecvThreadProc, threadParam, 0, NULL); @@ -466,8 +495,8 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param) DWORD WINAPI WinsockNetLayer::RecvThreadProc(LPVOID param) { - DWORD connIdx = *(DWORD *)param; - delete (DWORD *)param; + DWORD connIdx = *(DWORD*)param; + delete (DWORD*)param; EnterCriticalSection(&s_connectionsLock); if (connIdx >= (DWORD)s_connections.size()) @@ -479,7 +508,8 @@ DWORD WINAPI WinsockNetLayer::RecvThreadProc(LPVOID param) BYTE clientSmallId = s_connections[connIdx].smallId; LeaveCriticalSection(&s_connectionsLock); - BYTE *recvBuf = new BYTE[WIN64_NET_RECV_BUFFER_SIZE]; + std::vector recvBuf; + recvBuf.resize(WIN64_NET_RECV_BUFFER_SIZE); while (s_active) { @@ -490,33 +520,47 @@ DWORD WINAPI WinsockNetLayer::RecvThreadProc(LPVOID param) break; } - int packetSize = (header[0] << 24) | (header[1] << 16) | (header[2] << 8) | header[3]; + int packetSize = + ((uint32_t)header[0] << 24) | + ((uint32_t)header[1] << 16) | + ((uint32_t)header[2] << 8) | + ((uint32_t)header[3]); - if (packetSize <= 0 || packetSize > WIN64_NET_RECV_BUFFER_SIZE) + if (packetSize <= 0 || packetSize > WIN64_NET_MAX_PACKET_SIZE) { - app.DebugPrintf("Win64 LAN: Invalid packet size %d from client smallId=%d\n", packetSize, clientSmallId); + app.DebugPrintf("Win64 LAN: Invalid packet size %d from client smallId=%d (max=%d)\n", + packetSize, + clientSmallId, + (int)WIN64_NET_MAX_PACKET_SIZE); break; } - if (!RecvExact(sock, recvBuf, packetSize)) + if ((int)recvBuf.size() < packetSize) + { + recvBuf.resize(packetSize); + app.DebugPrintf("Win64 LAN: Resized host recv buffer to %d bytes for client smallId=%d\n", packetSize, clientSmallId); + } + + if (!RecvExact(sock, &recvBuf[0], packetSize)) { app.DebugPrintf("Win64 LAN: Client smallId=%d disconnected (body)\n", clientSmallId); break; } - HandleDataReceived(clientSmallId, s_hostSmallId, recvBuf, packetSize); + HandleDataReceived(clientSmallId, s_hostSmallId, &recvBuf[0], packetSize); } - delete[] recvBuf; - EnterCriticalSection(&s_connectionsLock); for (size_t i = 0; i < s_connections.size(); i++) { if (s_connections[i].smallId == clientSmallId) { s_connections[i].active = false; - closesocket(s_connections[i].tcpSocket); - s_connections[i].tcpSocket = INVALID_SOCKET; + if (s_connections[i].tcpSocket != INVALID_SOCKET) + { + closesocket(s_connections[i].tcpSocket); + s_connections[i].tcpSocket = INVALID_SOCKET; + } break; } } @@ -529,7 +573,7 @@ DWORD WINAPI WinsockNetLayer::RecvThreadProc(LPVOID param) return 0; } -bool WinsockNetLayer::PopDisconnectedSmallId(BYTE *outSmallId) +bool WinsockNetLayer::PopDisconnectedSmallId(BYTE* outSmallId) { bool found = false; EnterCriticalSection(&s_disconnectLock); @@ -550,9 +594,26 @@ void WinsockNetLayer::PushFreeSmallId(BYTE smallId) LeaveCriticalSection(&s_freeSmallIdLock); } +void WinsockNetLayer::CloseConnectionBySmallId(BYTE smallId) +{ + EnterCriticalSection(&s_connectionsLock); + for (size_t i = 0; i < s_connections.size(); i++) + { + if (s_connections[i].smallId == smallId && s_connections[i].active && s_connections[i].tcpSocket != INVALID_SOCKET) + { + closesocket(s_connections[i].tcpSocket); + s_connections[i].tcpSocket = INVALID_SOCKET; + app.DebugPrintf("Win64 LAN: Force-closed TCP connection for smallId=%d\n", smallId); + break; + } + } + LeaveCriticalSection(&s_connectionsLock); +} + DWORD WINAPI WinsockNetLayer::ClientRecvThreadProc(LPVOID param) { - BYTE *recvBuf = new BYTE[WIN64_NET_RECV_BUFFER_SIZE]; + std::vector recvBuf; + recvBuf.resize(WIN64_NET_RECV_BUFFER_SIZE); while (s_active && s_hostConnectionSocket != INVALID_SOCKET) { @@ -565,28 +626,34 @@ DWORD WINAPI WinsockNetLayer::ClientRecvThreadProc(LPVOID param) int packetSize = (header[0] << 24) | (header[1] << 16) | (header[2] << 8) | header[3]; - if (packetSize <= 0 || packetSize > WIN64_NET_RECV_BUFFER_SIZE) + if (packetSize <= 0 || packetSize > WIN64_NET_MAX_PACKET_SIZE) { - app.DebugPrintf("Win64 LAN: Invalid packet size %d from host\n", packetSize); + app.DebugPrintf("Win64 LAN: Invalid packet size %d from host (max=%d)\n", + packetSize, + (int)WIN64_NET_MAX_PACKET_SIZE); break; } - if (!RecvExact(s_hostConnectionSocket, recvBuf, packetSize)) + if ((int)recvBuf.size() < packetSize) + { + recvBuf.resize(packetSize); + app.DebugPrintf("Win64 LAN: Resized client recv buffer to %d bytes\n", packetSize); + } + + if (!RecvExact(s_hostConnectionSocket, &recvBuf[0], packetSize)) { app.DebugPrintf("Win64 LAN: Disconnected from host (body)\n"); break; } - HandleDataReceived(s_hostSmallId, s_localSmallId, recvBuf, packetSize); + HandleDataReceived(s_hostSmallId, s_localSmallId, &recvBuf[0], packetSize); } - delete[] recvBuf; - s_connected = false; return 0; } -bool WinsockNetLayer::StartAdvertising(int gamePort, const wchar_t *hostName, unsigned int gameSettings, unsigned int texPackId, unsigned char subTexId, unsigned short netVer) +bool WinsockNetLayer::StartAdvertising(int gamePort, const wchar_t* hostName, unsigned int gameSettings, unsigned int texPackId, unsigned char subTexId, unsigned short netVer) { if (s_advertising) return true; if (!s_initialized) return false; @@ -614,7 +681,7 @@ bool WinsockNetLayer::StartAdvertising(int gamePort, const wchar_t *hostName, un } BOOL broadcast = TRUE; - setsockopt(s_advertiseSock, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast, sizeof(broadcast)); + setsockopt(s_advertiseSock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); s_advertising = true; s_advertiseThread = CreateThread(NULL, 0, AdvertiseThreadProc, NULL, 0, NULL); @@ -669,8 +736,8 @@ DWORD WINAPI WinsockNetLayer::AdvertiseThreadProc(LPVOID param) Win64LANBroadcast data = s_advertiseData; LeaveCriticalSection(&s_advertiseLock); - int sent = sendto(s_advertiseSock, (const char *)&data, sizeof(data), 0, - (struct sockaddr *)&broadcastAddr, sizeof(broadcastAddr)); + int sent = sendto(s_advertiseSock, (const char*)&data, sizeof(data), 0, + (struct sockaddr*)&broadcastAddr, sizeof(broadcastAddr)); if (sent == SOCKET_ERROR && s_advertising) { @@ -696,7 +763,7 @@ bool WinsockNetLayer::StartDiscovery() } BOOL reuseAddr = TRUE; - setsockopt(s_discoverySock, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseAddr, sizeof(reuseAddr)); + setsockopt(s_discoverySock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseAddr, sizeof(reuseAddr)); struct sockaddr_in bindAddr; memset(&bindAddr, 0, sizeof(bindAddr)); @@ -704,7 +771,7 @@ bool WinsockNetLayer::StartDiscovery() bindAddr.sin_port = htons(WIN64_LAN_DISCOVERY_PORT); bindAddr.sin_addr.s_addr = INADDR_ANY; - if (::bind(s_discoverySock, (struct sockaddr *)&bindAddr, sizeof(bindAddr)) == SOCKET_ERROR) + if (::bind(s_discoverySock, (struct sockaddr*)&bindAddr, sizeof(bindAddr)) == SOCKET_ERROR) { app.DebugPrintf("Win64 LAN: Discovery bind failed: %d\n", WSAGetLastError()); closesocket(s_discoverySock); @@ -713,7 +780,7 @@ bool WinsockNetLayer::StartDiscovery() } DWORD timeout = 500; - setsockopt(s_discoverySock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout)); + setsockopt(s_discoverySock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); s_discovering = true; s_discoveryThread = CreateThread(NULL, 0, DiscoveryThreadProc, NULL, 0, NULL); @@ -763,7 +830,7 @@ DWORD WINAPI WinsockNetLayer::DiscoveryThreadProc(LPVOID param) int senderLen = sizeof(senderAddr); int recvLen = recvfrom(s_discoverySock, recvBuf, sizeof(recvBuf), 0, - (struct sockaddr *)&senderAddr, &senderLen); + (struct sockaddr*)&senderAddr, &senderLen); if (recvLen == SOCKET_ERROR) { @@ -773,7 +840,7 @@ DWORD WINAPI WinsockNetLayer::DiscoveryThreadProc(LPVOID param) if (recvLen < (int)sizeof(Win64LANBroadcast)) continue; - Win64LANBroadcast *broadcast = (Win64LANBroadcast *)recvBuf; + Win64LANBroadcast* broadcast = (Win64LANBroadcast*)recvBuf; if (broadcast->magic != WIN64_LAN_BROADCAST_MAGIC) continue; @@ -841,4 +908,4 @@ DWORD WINAPI WinsockNetLayer::DiscoveryThreadProc(LPVOID param) return 0; } -#endif +#endif \ No newline at end of file diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h index 96b03c9ba..029dd0a73 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h @@ -1,3 +1,5 @@ +// Code implemented by LCEMP, credit if used on other repos +// https://github.com/LCEMP/LCEMP #pragma once #ifdef _WINDOWS64 @@ -12,6 +14,7 @@ #define WIN64_NET_DEFAULT_PORT 25565 #define WIN64_NET_MAX_CLIENTS 7 #define WIN64_NET_RECV_BUFFER_SIZE 65536 +#define WIN64_NET_MAX_PACKET_SIZE (4 * 1024 * 1024) #define WIN64_LAN_DISCOVERY_PORT 25566 #define WIN64_LAN_BROADCAST_MAGIC 0x4D434C4E @@ -63,10 +66,10 @@ public: static void Shutdown(); static bool HostGame(int port); - static bool JoinGame(const char *ip, int port); + static bool JoinGame(const char* ip, int port); - static bool SendToSmallId(BYTE targetSmallId, const void *data, int dataSize); - static bool SendOnSocket(SOCKET sock, const void *data, int dataSize); + static bool SendToSmallId(BYTE targetSmallId, const void* data, int dataSize); + static bool SendOnSocket(SOCKET sock, const void* data, int dataSize); static bool IsHosting() { return s_isHost; } static bool IsConnected() { return s_connected; } @@ -77,12 +80,13 @@ public: static SOCKET GetSocketForSmallId(BYTE smallId); - static void HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char *data, unsigned int dataSize); + static void HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char* data, unsigned int dataSize); - static bool PopDisconnectedSmallId(BYTE *outSmallId); + static bool PopDisconnectedSmallId(BYTE* outSmallId); static void PushFreeSmallId(BYTE smallId); + static void CloseConnectionBySmallId(BYTE smallId); - static bool StartAdvertising(int gamePort, const wchar_t *hostName, unsigned int gameSettings, unsigned int texPackId, unsigned char subTexId, unsigned short netVer); + static bool StartAdvertising(int gamePort, const wchar_t* hostName, unsigned int gameSettings, unsigned int texPackId, unsigned char subTexId, unsigned short netVer); static void StopAdvertising(); static void UpdateAdvertisePlayerCount(BYTE count); static void UpdateAdvertiseJoinable(bool joinable); diff --git a/Minecraft.Client/Windows64/Windows64_App.cpp b/Minecraft.Client/Windows64/Windows64_App.cpp index 461e8c348..ad9e1f24a 100644 --- a/Minecraft.Client/Windows64/Windows64_App.cpp +++ b/Minecraft.Client/Windows64/Windows64_App.cpp @@ -10,46 +10,11 @@ #include "..\..\Minecraft.World\BiomeSource.h" #include "..\..\Minecraft.World\LevelType.h" -wstring g_playerName; - CConsoleMinecraftApp app; -static void LoadPlayerName() -{ - if (!g_playerName.empty()) return; - g_playerName = L"Windows"; - - char exePath[MAX_PATH] = {}; - GetModuleFileNameA(NULL, exePath, MAX_PATH); - char *lastSlash = strrchr(exePath, '\\'); - if (lastSlash) *(lastSlash + 1) = '\0'; - char filePath[MAX_PATH] = {}; - _snprintf_s(filePath, sizeof(filePath), _TRUNCATE, "%susername.txt", exePath); - - FILE *f = NULL; - if (fopen_s(&f, filePath, "r") == 0 && f) - { - char buf[128] = {}; - if (fgets(buf, sizeof(buf), f)) - { - int len = (int)strlen(buf); - while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r' || buf[len-1] == ' ')) - buf[--len] = '\0'; - if (len > 0) - { - wchar_t wbuf[128] = {}; - mbstowcs(wbuf, buf, 127); - g_playerName = wbuf; - } - } - fclose(f); - } -} - CConsoleMinecraftApp::CConsoleMinecraftApp() : CMinecraftApp() { m_bShutdown = false; - LoadPlayerName(); } void CConsoleMinecraftApp::SetRichPresenceContext(int iPad, int contextId) @@ -110,8 +75,8 @@ void CConsoleMinecraftApp::TemporaryCreateGameStart() Minecraft *pMinecraft=Minecraft::GetInstance(); app.ReleaseSaveThumbnail(); ProfileManager.SetLockedProfile(0); - LoadPlayerName(); - pMinecraft->user->name = g_playerName; + extern wchar_t g_Win64UsernameW[17]; + pMinecraft->user->name = g_Win64UsernameW; app.ApplyGameSettingsChanged(0); ////////////////////////////////////////////////////////////////////////////////////////////// From CScene_MultiGameJoinLoad::OnInit diff --git a/Minecraft.Client/Windows64/Windows64_App.h b/Minecraft.Client/Windows64/Windows64_App.h index 32d204ad0..bff916ec7 100644 --- a/Minecraft.Client/Windows64/Windows64_App.h +++ b/Minecraft.Client/Windows64/Windows64_App.h @@ -33,7 +33,6 @@ public: virtual void TemporaryCreateGameStart(); bool m_bShutdown; - wstring g_playerName; }; extern CConsoleMinecraftApp app; diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 78ab76fef..4a3d835ca 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -88,6 +88,9 @@ int g_iScreenHeight = 1080; UINT g_ScreenWidth = 1920; UINT g_ScreenHeight = 1080; +char g_Win64Username[17] = { 0 }; +wchar_t g_Win64UsernameW[17] = { 0 }; + // Fullscreen toggle state static bool g_isFullscreen = false; static WINDOWPLACEMENT g_wpPrev = { sizeof(g_wpPrev) }; @@ -762,8 +765,47 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, //g_iScreenWidth = 960; //g_iScreenHeight = 544; } + + // Default username will be "Windows" + strncpy_s(g_Win64Username, sizeof(g_Win64Username), "Windows", _TRUNCATE); + + char exePath[MAX_PATH] = {}; + GetModuleFileNameA(NULL, exePath, MAX_PATH); + char* lastSlash = strrchr(exePath, '\\'); + if (lastSlash) *(lastSlash + 1) = '\0'; + + char filePath[MAX_PATH] = {}; + _snprintf_s(filePath, sizeof(filePath), _TRUNCATE, "%susername.txt", exePath); + + FILE* f = nullptr; + if (fopen_s(&f, filePath, "r") == 0 && f) + { + char buf[128] = {}; + if (fgets(buf, sizeof(buf), f)) + { + int len = (int)strlen(buf); + while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r' || buf[len - 1] == ' ')) + buf[--len] = '\0'; + + if (len > 0) + { + strncpy_s(g_Win64Username, sizeof(g_Win64Username), buf, _TRUNCATE); + } + } + fclose(f); + } } + if (g_Win64Username[0] == 0) + { + DWORD sz = 17; + if (!GetUserNameA(g_Win64Username, &sz)) + strncpy_s(g_Win64Username, 17, "Player", _TRUNCATE); + g_Win64Username[16] = 0; + } + + MultiByteToWideChar(CP_ACP, 0, g_Win64Username, -1, g_Win64UsernameW, 17); + // Initialize global strings MyRegisterClass(hInstance); @@ -935,6 +977,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, IQNet::m_player[i].m_isHostPlayer = (i == 0); swprintf_s(IQNet::m_player[i].m_gamertag, 32, L"Player%d", i); } + extern wchar_t g_Win64UsernameW[17]; + wcscpy_s(IQNet::m_player[0].m_gamertag, 32, g_Win64UsernameW); WinsockNetLayer::Initialize(); From ac30f09085cacaa9e4afa201b0c9de9c5727054e Mon Sep 17 00:00:00 2001 From: Boom244 Date: Tue, 3 Mar 2026 14:43:06 -0800 Subject: [PATCH 15/26] #221: Fix menu glitch. (#254) --- Minecraft.Client/Windows64/KeyboardMouseInput.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp index fc7633947..df57db442 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp @@ -103,6 +103,7 @@ void KeyboardMouseInput::OnRawMouseInput(LPARAM lParam) void KeyboardMouseInput::OnMouseButton(int button, bool down) { + if (ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) { return; } if (button >= 0 && button < 3) { if (down && !m_mouseButtons[button]) m_mousePressedAccum[button] = true; From f870ef2a1038ac6137e217aab9e3b38ed2848e51 Mon Sep 17 00:00:00 2001 From: Zekken Date: Tue, 3 Mar 2026 18:56:16 -0500 Subject: [PATCH 16/26] Fix Texture Pack images in menu (#335) --- Minecraft.Client/Common/UI/UIController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp index 9e4a32024..6ac2f9ba3 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -1445,7 +1445,7 @@ GDrawTexture * RADLINK UIController::TextureSubstitutionCreateCallback ( void * // 4J Stu - All our flash controls that allow replacing textures use a special 64x64 symbol // Force this size here so that our images don't get scaled wildly - #if (defined __ORBIS__ || defined _DURANGO ) + #if (defined __ORBIS__ || defined _DURANGO || defined _WINDOWS64 ) *width = 96; *height = 96; #else From d31d261ffd707aabfd8715d3bc2cbf2e6fbc3fcd Mon Sep 17 00:00:00 2001 From: Alezito2008 <92759854+Alezito2008@users.noreply.github.com> Date: Wed, 4 Mar 2026 01:29:29 -0300 Subject: [PATCH 17/26] Prevent world input from affecting inventory (#354) --- Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp index 5ad275c39..1a5772bcb 100644 --- a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp @@ -33,6 +33,11 @@ UIScene_AbstractContainerMenu::UIScene_AbstractContainerMenu(int iPad, UILayer * m_bHasMousePosition = false; m_lastMouseX = 0; m_lastMouseY = 0; + + for (int btn = 0; btn < 3; btn++) + { + KMInput.ConsumeMousePress(btn); + } #endif } From f216abca4217b3fe4497d5aea5e4a3d6cf679b46 Mon Sep 17 00:00:00 2001 From: 4win <4winyt@gmail.com> Date: Tue, 3 Mar 2026 22:30:14 -0600 Subject: [PATCH 18/26] fix: properly offset the mouse position in containers (#327) --- .../Common/UI/UIScene_AbstractContainerMenu.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp index 1a5772bcb..bb56c7809 100644 --- a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp @@ -214,8 +214,8 @@ void UIScene_AbstractContainerMenu::tick() scrollDelta = KMInput.ConsumeScrollDelta(); // Convert mouse position to movie coordinates using the movie/client ratio - float mx = (float)mouseX * ((float)m_movieWidth / (float)clientWidth); - float my = (float)mouseY * ((float)m_movieHeight / (float)clientHeight); + float mx = (float)mouseX * ((float)m_movieWidth / (float)clientWidth) - (float)m_controlMainPanel.getXPos(); + float my = (float)mouseY * ((float)m_movieHeight / (float)clientHeight) - (float)m_controlMainPanel.getYPos(); rawMouseMovieX = mx; rawMouseMovieY = my; @@ -306,8 +306,13 @@ void UIScene_AbstractContainerMenu::tick() // Scale mouse client coords to the Iggy display space (which was set to getRenderDimensions()) RECT clientRect; GetClientRect(KMInput.GetHWnd(), &clientRect); - x = (S32)((float)KMInput.GetMouseX() * ((float)width / (float)clientRect.right)); - y = (S32)((float)KMInput.GetMouseY() * ((float)height / (float)clientRect.bottom)); + float mouseMovieX = (float)KMInput.GetMouseX() * ((float)m_movieWidth / (float)clientRect.right); + float mouseMovieY = (float)KMInput.GetMouseY() * ((float)m_movieHeight / (float)clientRect.bottom); + float mouseLocalX = mouseMovieX - (float)m_controlMainPanel.getXPos(); + float mouseLocalY = mouseMovieY - (float)m_controlMainPanel.getYPos(); + + x = (S32)(mouseLocalX * ((float)width / m_movieWidth)); + y = (S32)(mouseLocalY * ((float)height / m_movieHeight)); } else { From b1b622c303a40a5533962fc63559a0346cfbca04 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Wed, 4 Mar 2026 04:31:47 +0000 Subject: [PATCH 19/26] Fix overlapping debug menus and screens (#294) * Fix overlapping debug menus and screens Also resolves a formatting issue with clang-format * Update readme --- .clang-format | 4 +- Minecraft.Client/Minecraft.cpp | 14 ++-- .../Windows64/Windows64_Minecraft.cpp | 67 +++++++++---------- README.md | 1 + 4 files changed, 38 insertions(+), 48 deletions(-) diff --git a/.clang-format b/.clang-format index 383e62104..a4d4a4c2a 100644 --- a/.clang-format +++ b/.clang-format @@ -39,9 +39,7 @@ RemoveSemicolon: false SeparateDefinitionBlocks: Leave ShortNamespaceLines: 1 SkipMacroDefinitionBody: false -SortIncludes: - Enabled: true - IgnoreCase: false +SortIncludes: CaseSensitive SpacesInParens: Never SpacesInParensOptions: ExceptDoubleParentheses: false diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 164417abe..bd75a61a9 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -3618,8 +3618,6 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) if((player->ullButtonsPressed&(1LL<renderDebug = !options->renderDebug; #ifdef _XBOX app.EnableDebugOverlay(options->renderDebug,iPad); #else @@ -3629,13 +3627,11 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) #endif } - if((player->ullButtonsPressed&(1LL< mob = dynamic_pointer_cast(Creeper::_class->newInstance( level )); - //shared_ptr mob = dynamic_pointer_cast(Wolf::_class->newInstance( level )); - shared_ptr mob = dynamic_pointer_cast(shared_ptr(new Spider( level ))); - mob->moveTo(player->x+1, player->y, player->z+1, level->random->nextFloat() * 360, 0); - level->addEntity(mob); + if((player->ullButtonsPressed&(1LL<renderDebug = !options->renderDebug; +#endif } } diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 4a3d835ca..9d9537c58 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -1278,7 +1278,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } } - // F1 toggles the HUD, F3 toggles the debug console overlay, F11 toggles fullscreen + // F1 toggles the HUD if (KMInput.IsKeyPressed(VK_F1)) { int primaryPad = ProfileManager.GetPrimaryPad(); @@ -1286,21 +1286,43 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, app.SetGameSettings(primaryPad, eGameSetting_DisplayHUD, displayHud ? 0 : 1); app.SetGameSettings(primaryPad, eGameSetting_DisplayHand, displayHud ? 0 : 1); } - + + // F3 toggles onscreen debug info if (KMInput.IsKeyPressed(VK_F3)) { - static bool s_debugConsole = false; - s_debugConsole = !s_debugConsole; - ui.ShowUIDebugConsole(s_debugConsole); + if (Minecraft* pMinecraft = Minecraft::GetInstance()) + { + if (pMinecraft->options) + { + pMinecraft->options->renderDebug = !pMinecraft->options->renderDebug; + } + } } #ifdef _DEBUG_MENUS_ENABLED - if (KMInput.IsKeyPressed(VK_F4)) - { - ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_DebugOverlay, NULL, eUILayer_Debug); - } + // F4 Open debug overlay + if (KMInput.IsKeyPressed(VK_F4)) + { + if (Minecraft *pMinecraft = Minecraft::GetInstance()) + { + if (pMinecraft->options && + app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL) + { + ui.NavigateToScene(0, eUIScene_DebugOverlay, NULL, eUILayer_Debug); + } + } + } + + // F6 Open debug console + if (KMInput.IsKeyPressed(VK_F6)) + { + static bool s_debugConsole = false; + s_debugConsole = !s_debugConsole; + ui.ShowUIDebugConsole(s_debugConsole); + } #endif + // F11 Toggle fullscreen if (KMInput.IsKeyPressed(VK_F11)) { ToggleFullscreen(); @@ -1318,33 +1340,6 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } } -#ifdef _DEBUG_MENUS_ENABLED - // F3 toggles onscreen debug info - if (KMInput.IsKeyPressed(VK_F3)) - { - if (Minecraft* pMinecraft = Minecraft::GetInstance()) - { - if (pMinecraft->options && app.DebugSettingsOn()) - { - pMinecraft->options->renderDebug = !pMinecraft->options->renderDebug; - } - } - } - - // F4 opens debug overlay - if (KMInput.IsKeyPressed(VK_F4)) - { - if (Minecraft* pMinecraft = Minecraft::GetInstance()) - { - if (pMinecraft->options && app.DebugSettingsOn() && - app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL) - { - ui.NavigateToScene(0, eUIScene_DebugOverlay, NULL, eUILayer_Debug); - } - } - } -#endif - #if 0 // has the game defined profile data been changed (by a profile load) if(app.uiGameDefinedDataChangedBitmask!=0) diff --git a/README.md b/README.md index bc45ef229..71c067b04 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ This feature is based on [LCEMP](https://github.com/LCEMP/LCEMP/) - **Toggle HUD**: `F1` - **Toggle Debug Info**: `F3` - **Open Debug Overlay**: `F4` +- **Toggle Debug Console**: `F6` ## Build & Run From c8a8f9dd6cc120758ea2501ef5a94320225dd7de Mon Sep 17 00:00:00 2001 From: Mykey Date: Wed, 4 Mar 2026 12:32:51 +0800 Subject: [PATCH 20/26] Update crafting controls description (#359) Clarified crafting controls in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71c067b04..6e9f18aed 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ This feature is based on [LCEMP](https://github.com/LCEMP/LCEMP/) - **Sprint**: `Ctrl` (Hold) or Double-tap `W` - **Inventory**: `E` - **Drop Item**: `Q` -- **Crafting**: `C` +- **Crafting**: `C` Use `Q` and `E` to move through tabs (cycles Left/Right) - **Toggle View (FPS/TPS)**: `F5` - **Fullscreen**: `F11` - **Pause Menu**: `Esc` From 6c842b2854fb8c1d0c87ffcbf0bb5c297b883226 Mon Sep 17 00:00:00 2001 From: Alezito2008 <92759854+Alezito2008@users.noreply.github.com> Date: Wed, 4 Mar 2026 02:21:21 -0300 Subject: [PATCH 21/26] Disable flight state when riding entities (#368) --- Minecraft.World/Player.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Minecraft.World/Player.cpp b/Minecraft.World/Player.cpp index 700935f6e..e0fa26ad4 100644 --- a/Minecraft.World/Player.cpp +++ b/Minecraft.World/Player.cpp @@ -627,6 +627,7 @@ void Player::ride(shared_ptr e) return; } + this->abilities.flying = false; LivingEntity::ride(e); } From bfcc4aa10c7a56efa275652f6c7b59b5cd5e8f8d Mon Sep 17 00:00:00 2001 From: ModMaker101 <119018978+ModMaker101@users.noreply.github.com> Date: Wed, 4 Mar 2026 00:37:04 -0500 Subject: [PATCH 22/26] renderer: frustum test new chunks so newly generated chunks render to full view distance #175 (#344) --- Minecraft.Client/LevelRenderer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp index fc56494f9..222a5c189 100644 --- a/Minecraft.Client/LevelRenderer.cpp +++ b/Minecraft.Client/LevelRenderer.cpp @@ -2557,13 +2557,19 @@ void LevelRenderer::cull(Culler *culler, float a) { unsigned char flags = pClipChunk->globalIdx == -1 ? 0 : globalChunkFlags[ pClipChunk->globalIdx ]; + // Always perform frustum cull test + bool clipres = clip(pClipChunk->aabb, fdraw); + if ( (flags & CHUNK_FLAG_COMPILED ) && ( ( flags & CHUNK_FLAG_EMPTYBOTH ) != CHUNK_FLAG_EMPTYBOTH ) ) { - bool clipres = clip(pClipChunk->aabb, fdraw); pClipChunk->visible = clipres; if( pClipChunk->visible ) vis++; total++; } + else if (clipres) + { + pClipChunk->visible = true; + } else { pClipChunk->visible = false; @@ -2572,6 +2578,7 @@ void LevelRenderer::cull(Culler *culler, float a) } } + void LevelRenderer::playStreamingMusic(const wstring& name, int x, int y, int z) { if (name != L"") From 779cf56f4b9c767df37a7d10569114f8588d9329 Mon Sep 17 00:00:00 2001 From: Tygo de Vries <108730722+TygodeVries@users.noreply.github.com> Date: Wed, 4 Mar 2026 06:39:16 +0100 Subject: [PATCH 23/26] Removed private information (#333) Maybe we should not have this out there to find so easily? --- .../Xbox/Docs/Ratings_Submission_Form.doc | Bin 209408 -> 213504 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Minecraft.Client/Xbox/Docs/Ratings_Submission_Form.doc b/Minecraft.Client/Xbox/Docs/Ratings_Submission_Form.doc index 91d965bd3d1212d9e166c24f8aeb5d9efe7ecd31..2b87aa85425370f0aedffb7a0b001fd6be87bce3 100644 GIT binary patch delta 21638 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 n#>aw>Ckk+WXo@bLHjQ3|E!{qw`#iN*4Qni=voo delta 17434 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 Ti70()Gh$29es5+dVfjA*gIMYR From b1b4435c0101341b1aa196c7b13c939c02a9eaec Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Wed, 4 Mar 2026 14:40:52 +0800 Subject: [PATCH 24/26] fix: fix inverted pitch in the second third person view --- Minecraft.Client/GameRenderer.cpp | 41 ++++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/Minecraft.Client/GameRenderer.cpp b/Minecraft.Client/GameRenderer.cpp index 08c20cfb4..d3c5e8d39 100644 --- a/Minecraft.Client/GameRenderer.cpp +++ b/Minecraft.Client/GameRenderer.cpp @@ -501,23 +501,26 @@ void GameRenderer::moveCameraToPlayer(float a) else { // 4J - corrected bug where this used to just take player->xRot & yRot directly and so wasn't taking into account interpolation, allowing camera to go through walls - float playerYRot = player->yRotO + (player->yRot - player->yRotO) * a; - float playerXRot = player->xRotO + (player->xRot - player->xRotO) * a; - float yRot = playerYRot; - float xRot = playerXRot; + float yRot = player->yRotO + (player->yRot - player->yRotO) * a; + float xRot = player->xRotO + (player->xRot - player->xRotO) * a; // Thirdperson view values are now 0 for disabled, 1 for original mode, 2 for reversed. if( localplayer->ThirdPersonView() == 2 ) { - // Reverse x rotation - note that this is only used in doing collision to calculate our view + // Reverse y rotation - note that this is only used in doing collision to calculate our view // distance, the actual rotation itself is just below this else {} block - xRot += 180.0f; + yRot += 180.0f; } double xd = -Mth::sin(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI) * cameraDist; double zd = Mth::cos(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI) * cameraDist; double yd = -Mth::sin(xRot / 180 * PI) * cameraDist; + if (localplayer->ThirdPersonView() == 2) + { + yd = Mth::sin(xRot / 180 * PI) * cameraDist; + } + for (int i = 0; i < 8; i++) { float xo = (float)((i & 1) * 2 - 1); @@ -538,16 +541,7 @@ void GameRenderer::moveCameraToPlayer(float a) } } - if ( localplayer->ThirdPersonView() == 2) - { - glRotatef(180, 0, 1, 0); - } - - glRotatef(playerXRot - xRot, 1, 0, 0); - glRotatef(playerYRot - yRot, 0, 1, 0); glTranslatef(0, 0, (float) -cameraDist); - glRotatef(yRot - playerYRot, 0, 1, 0); - glRotatef(xRot - playerXRot, 1, 0, 0); } } else @@ -557,8 +551,21 @@ void GameRenderer::moveCameraToPlayer(float a) if (!mc->options->fixedCamera) { - glRotatef(player->xRotO + (player->xRot - player->xRotO) * a, 1, 0, 0); - glRotatef(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, 1, 0); + float pitch = player->xRotO + (player->xRot - player->xRotO) * a; + if (localplayer->ThirdPersonView() == 2) + { + pitch = -pitch; + } + + glRotatef(pitch, 1, 0, 0); + if (localplayer->ThirdPersonView() == 2) + { + glRotatef(player->yRotO + (player->yRot - player->yRotO) * a, 0, 1, 0); + } + else + { + glRotatef(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, 1, 0); + } } glTranslatef(0, heightOffset, 0); From 575cc4ce6ea59ef2acee6f81181bc80f5ed4b7d9 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Tue, 3 Mar 2026 22:12:59 +0800 Subject: [PATCH 25/26] fix: fix horse texture rendering --- Minecraft.Client/Textures.cpp | 101 +++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/Textures.cpp b/Minecraft.Client/Textures.cpp index ab9db75d9..b4817ee32 100644 --- a/Minecraft.Client/Textures.cpp +++ b/Minecraft.Client/Textures.cpp @@ -424,12 +424,109 @@ void Textures::bindTextureLayers(ResourceLocation *resource) { assert(resource->isPreloaded()); + // Hack: 4JLibs on Windows does not currently reproduce Minecraft's layered horse texture path reliably. + // Merge the layers on the CPU and bind the cached result as a normal single texture instead. + wstring cacheKey = L"%layered%"; int layers = resource->getTextureCount(); - for( int i = 0; i < layers; i++ ) { - RenderManager.TextureBind(loadTexture(resource->getTexture(i))); + cacheKey += std::to_wstring(resource->getTexture(i)); + cacheKey += L"/"; } + + int id = -1; + bool inMap = ( idMap.find(cacheKey) != idMap.end() ); + if( inMap ) + { + id = idMap[cacheKey]; + } + else + { + // Cache by layer signature so the merge cost is only paid once per horse texture combination. + intArray mergedPixels; + int mergedWidth = 0; + int mergedHeight = 0; + bool hasMergedPixels = false; + + for( int i = 0; i < layers; i++ ) + { + TEXTURE_NAME textureName = resource->getTexture(i); + if( textureName == (_TEXTURE_NAME)-1 ) + { + continue; + } + + wstring resourceName = wstring(preLoaded[textureName]) + L".png"; + BufferedImage *image = readImage(textureName, resourceName); + if( image == NULL ) + { + continue; + } + + int width = image->getWidth(); + int height = image->getHeight(); + intArray layerPixels = loadTexturePixels(image); + delete image; + + if( !hasMergedPixels ) + { + mergedWidth = width; + mergedHeight = height; + mergedPixels = intArray(width * height); + memcpy(mergedPixels.data, layerPixels.data, width * height * sizeof(int)); + hasMergedPixels = true; + } + else if( width == mergedWidth && height == mergedHeight ) + { + for( int p = 0; p < width * height; p++ ) + { + int dst = mergedPixels[p]; + int src = layerPixels[p]; + + float srcAlpha = ((src >> 24) & 0xff) / 255.0f; + if( srcAlpha <= 0.0f ) + { + continue; + } + + float dstAlpha = ((dst >> 24) & 0xff) / 255.0f; + float outAlpha = srcAlpha + dstAlpha * (1.0f - srcAlpha); + if( outAlpha <= 0.0f ) + { + mergedPixels[p] = 0; + continue; + } + + float srcFactor = srcAlpha / outAlpha; + float dstFactor = (dstAlpha * (1.0f - srcAlpha)) / outAlpha; + + int outA = (int)(outAlpha * 255.0f + 0.5f); + int outR = (int)((((src >> 16) & 0xff) * srcFactor) + (((dst >> 16) & 0xff) * dstFactor) + 0.5f); + int outG = (int)((((src >> 8) & 0xff) * srcFactor) + (((dst >> 8) & 0xff) * dstFactor) + 0.5f); + int outB = (int)(((src & 0xff) * srcFactor) + ((dst & 0xff) * dstFactor) + 0.5f); + mergedPixels[p] = (outA << 24) | (outR << 16) | (outG << 8) | outB; + } + } + + delete[] layerPixels.data; + } + + if( hasMergedPixels ) + { + BufferedImage *mergedImage = new BufferedImage(mergedWidth, mergedHeight, BufferedImage::TYPE_INT_ARGB); + memcpy(mergedImage->getData(), mergedPixels.data, mergedWidth * mergedHeight * sizeof(int)); + delete[] mergedPixels.data; + id = getTexture(mergedImage, C4JRender::TEXTURE_FORMAT_RxGyBzAw, false); + } + else + { + id = 0; + } + + idMap[cacheKey] = id; + } + + RenderManager.TextureBind(id); } void Textures::bind(int id) From 8ecfc525471720012f36a0016d88a4f0f4cfaa1d Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Wed, 4 Mar 2026 15:33:52 +0800 Subject: [PATCH 26/26] feat: add support for username, IP, and port configuration via launch arguments --- .../Windows64/Windows64_Minecraft.cpp | 59 +++++++++++-------- README.md | 14 ++++- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 9d9537c58..3d5eec00f 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -766,33 +766,44 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, //g_iScreenHeight = 544; } - // Default username will be "Windows" - strncpy_s(g_Win64Username, sizeof(g_Win64Username), "Windows", _TRUNCATE); + char cmdLineA[1024]; + strncpy_s(cmdLineA, sizeof(cmdLineA), lpCmdLine, _TRUNCATE); - char exePath[MAX_PATH] = {}; - GetModuleFileNameA(NULL, exePath, MAX_PATH); - char* lastSlash = strrchr(exePath, '\\'); - if (lastSlash) *(lastSlash + 1) = '\0'; - - char filePath[MAX_PATH] = {}; - _snprintf_s(filePath, sizeof(filePath), _TRUNCATE, "%susername.txt", exePath); - - FILE* f = nullptr; - if (fopen_s(&f, filePath, "r") == 0 && f) + char *nameArg = strstr(cmdLineA, "-name "); + if (nameArg) { - char buf[128] = {}; - if (fgets(buf, sizeof(buf), f)) - { - int len = (int)strlen(buf); - while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r' || buf[len - 1] == ' ')) - buf[--len] = '\0'; + nameArg += 6; + while (*nameArg == ' ') nameArg++; + char nameBuf[17]; + int n = 0; + while (nameArg[n] && nameArg[n] != ' ' && n < 16) { nameBuf[n] = nameArg[n]; n++; } + nameBuf[n] = 0; + strncpy_s(g_Win64Username, 17, nameBuf, _TRUNCATE); + } - if (len > 0) - { - strncpy_s(g_Win64Username, sizeof(g_Win64Username), buf, _TRUNCATE); - } - } - fclose(f); + char *ipArg = strstr(cmdLineA, "-ip "); + if (ipArg) + { + ipArg += 4; + while (*ipArg == ' ') ipArg++; + char ipBuf[256]; + int n = 0; + while (ipArg[n] && ipArg[n] != ' ' && n < 255) { ipBuf[n] = ipArg[n]; n++; } + ipBuf[n] = 0; + strncpy_s(g_Win64MultiplayerIP, 256, ipBuf, _TRUNCATE); + g_Win64MultiplayerJoin = true; + } + + char *portArg = strstr(cmdLineA, "-port "); + if (portArg) + { + portArg += 6; + while (*portArg == ' ') portArg++; + char portBuf[16]; + int n = 0; + while (portArg[n] && portArg[n] != ' ' && n < 15) { portBuf[n] = portArg[n]; n++; } + portBuf[n] = 0; + g_Win64MultiplayerPort = atoi(portBuf); } } diff --git a/README.md b/README.md index 6e9f18aed..e6583e768 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,22 @@ Basic LAN multiplayer is available on the Windows build - Other players on the same LAN can discover the session from the in-game Join Game menu - Game connections use TCP port `25565` by default - LAN discovery uses UDP port `25566` -- You can override your in-game username at launch with `username.txt` This feature is based on [LCEMP](https://github.com/LCEMP/LCEMP/) +### Launch Arguments + +| Argument | Description | +|--------------------|----------------------------------------------------------------------------------------------------------------| +| `-name ` | Sets your in-game username | +| `-ip

` | Manually connect to an IP if LAN advertising does not work or if the server cannot be discovered automatically | +| `-port ` | Override the default port if it was changed in the source | + +Example: +``` +Minecraft.Client.exe -name Steve -ip 192.168.0.25 -port 25565 +``` + ## Controls (Keyboard & Mouse) - **Movement**: `W` `A` `S` `D`