From 63e590d783cc3302ec6a5b639dd831e930a557c6 Mon Sep 17 00:00:00 2001 From: dtentiion Date: Mon, 2 Mar 2026 21:13:11 +0000 Subject: [PATCH 01/68] Win64: show actual world names in save list, sort newest-first, preserve level name on load/resave --- .../Common/UI/UIScene_CreateWorldMenu.cpp | 1 + .../Common/UI/UIScene_LoadMenu.cpp | 13 +- Minecraft.Client/Common/UI/UIScene_LoadMenu.h | 1 + .../Common/UI/UIScene_LoadOrJoinMenu.cpp | 150 +++++++++++++++++- Minecraft.Client/MinecraftServer.cpp | 4 +- Minecraft.Client/MinecraftServer.h | 1 + Minecraft.World/ConsoleSaveFileOriginal.cpp | 2 +- README.md | 19 +++ 8 files changed, 183 insertions(+), 8 deletions(-) diff --git a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp index 6be67bed6..a9ff02f5f 100644 --- a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp @@ -1110,6 +1110,7 @@ void UIScene_CreateWorldMenu::CreateGame(UIScene_CreateWorldMenu* pClass, DWORD __int64 seedValue = 0; NetworkGameInitData *param = new NetworkGameInitData(); + param->levelName = wWorldName; if (wSeed.length() != 0) { diff --git a/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp index 1c07e540f..bb399b973 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp @@ -231,12 +231,22 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye #endif m_bShowTimer = true; } -#if defined(_DURANGO) +#if defined(_DURANGO) m_labelGameName.init(params->saveDetails->UTF16SaveName); #else m_labelGameName.init(params->saveDetails->UTF8SaveName); #endif +#endif +#ifdef _WINDOWS64 + if (params->saveDetails != NULL && params->saveDetails->UTF8SaveName[0] != '\0') + { + wchar_t wSaveName[128]; + ZeroMemory(wSaveName, sizeof(wSaveName)); + mbstowcs(wSaveName, params->saveDetails->UTF8SaveName, 127); + m_levelName = wstring(wSaveName); + m_labelGameName.init(m_levelName); + } #endif } @@ -1448,6 +1458,7 @@ void UIScene_LoadMenu::StartGameFromSave(UIScene_LoadMenu* pClass, DWORD dwLocal param->seed = pClass->m_seed; param->saveData = NULL; param->texturePackId = pClass->m_MoreOptionsParams.dwTexturePack; + param->levelName = pClass->m_levelName; Minecraft *pMinecraft = Minecraft::GetInstance(); pMinecraft->skins->selectTexturePackById(pClass->m_MoreOptionsParams.dwTexturePack); diff --git a/Minecraft.Client/Common/UI/UIScene_LoadMenu.h b/Minecraft.Client/Common/UI/UIScene_LoadMenu.h index e45fa09c9..e245e3beb 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_LoadMenu.h @@ -62,6 +62,7 @@ private: bool m_bIsCorrupt; bool m_bThumbnailGetFailed; __int64 m_seed; + wstring m_levelName; #ifdef __PS3__ std::vector*m_pvProductInfo; diff --git a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp index 1d90da775..e3749dcb2 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp @@ -25,6 +25,98 @@ #include "message_dialog.h" #endif +#ifdef _WINDOWS64 +#include "..\..\..\Minecraft.World\NbtIo.h" +#include "..\..\..\Minecraft.World\compression.h" + +static wstring ReadLevelNameFromSaveFile(const wstring& filePath) +{ + HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (hFile == INVALID_HANDLE_VALUE) return L""; + + DWORD fileSize = GetFileSize(hFile, NULL); + if (fileSize < 12 || fileSize == INVALID_FILE_SIZE) { CloseHandle(hFile); return L""; } + + unsigned char *rawData = new unsigned char[fileSize]; + DWORD bytesRead = 0; + if (!ReadFile(hFile, rawData, fileSize, &bytesRead, NULL) || bytesRead != fileSize) + { + CloseHandle(hFile); + delete[] rawData; + return L""; + } + CloseHandle(hFile); + + unsigned char *saveData = NULL; + unsigned int saveSize = 0; + bool freeSaveData = false; + + if (*(unsigned int*)rawData == 0) + { + // Compressed format: bytes 0-3=0, bytes 4-7=decompressed size, bytes 8+=compressed data + unsigned int decompSize = *(unsigned int*)(rawData + 4); + if (decompSize == 0 || decompSize > 128 * 1024 * 1024) + { + delete[] rawData; + return L""; + } + saveData = new unsigned char[decompSize]; + Compression::getCompression()->Decompress(saveData, &decompSize, rawData + 8, fileSize - 8); + saveSize = decompSize; + freeSaveData = true; + } + else + { + saveData = rawData; + saveSize = fileSize; + } + + wstring result = L""; + if (saveSize >= 12) + { + unsigned int headerOffset = *(unsigned int*)saveData; + unsigned int numEntries = *(unsigned int*)(saveData + 4); + const unsigned int entrySize = sizeof(FileEntrySaveData); + + if (headerOffset < saveSize && numEntries > 0 && numEntries < 10000 && + headerOffset + numEntries * entrySize <= saveSize) + { + FileEntrySaveData *table = (FileEntrySaveData *)(saveData + headerOffset); + for (unsigned int i = 0; i < numEntries; i++) + { + if (wcscmp(table[i].filename, L"level.dat") == 0) + { + unsigned int off = table[i].startOffset; + unsigned int len = table[i].length; + if (off >= 12 && off + len <= saveSize && len > 0 && len < 4 * 1024 * 1024) + { + byteArray ba; + ba.data = (byte*)(saveData + off); + ba.length = len; + CompoundTag *root = NbtIo::decompress(ba); + if (root != NULL) + { + CompoundTag *dataTag = root->getCompound(L"Data"); + if (dataTag != NULL) + result = dataTag->getString(L"LevelName"); + delete root; + } + } + break; + } + } + } + } + + if (freeSaveData) delete[] saveData; + delete[] rawData; + // "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""; + return result; +} +#endif + #ifdef SONY_REMOTE_STORAGE_DOWNLOAD unsigned long UIScene_LoadOrJoinMenu::m_ulFileSize=0L; @@ -158,7 +250,7 @@ UIScene_LoadOrJoinMenu::UIScene_LoadOrJoinMenu(int iPad, void *initData, UILayer } #endif -#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) || defined(_DURANGO) +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) || defined(_DURANGO) || defined(_WINDOWS64) // Always clear the saves when we enter this menu StorageManager.ClearSavesInfo(); #endif @@ -603,6 +695,22 @@ void UIScene_LoadOrJoinMenu::tick() m_saveDetails = new SaveListDetails[m_pSaveDetails->iSaveC]; m_iSaveDetailsCount = m_pSaveDetails->iSaveC; +#ifdef _WINDOWS64 + // Build sorted index array (newest-first by filename timestamp YYYYMMDDHHMMSS) + int *sortedIdx = new int[m_pSaveDetails->iSaveC]; + for (int si = 0; si < (int)m_pSaveDetails->iSaveC; ++si) sortedIdx[si] = si; + for (int si = 1; si < (int)m_pSaveDetails->iSaveC; ++si) + { + int key = sortedIdx[si]; + int sj = si - 1; + while (sj >= 0 && strcmp(m_pSaveDetails->SaveInfoA[sortedIdx[sj]].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[key].UTF8SaveFilename) < 0) + { + sortedIdx[sj + 1] = sortedIdx[sj]; + --sj; + } + sortedIdx[sj + 1] = key; + } +#endif for(unsigned int i = 0; i < m_pSaveDetails->iSaveC; ++i) { #if defined(_XBOX_ONE) @@ -616,14 +724,40 @@ void UIScene_LoadOrJoinMenu::tick() m_saveDetails[i].saveId = i; memcpy(m_saveDetails[i].UTF16SaveName, m_pSaveDetails->SaveInfoA[i].UTF16SaveTitle, 128); memcpy(m_saveDetails[i].UTF16SaveFilename, m_pSaveDetails->SaveInfoA[i].UTF16SaveFilename, MAX_SAVEFILENAME_LENGTH); +#else +#ifdef _WINDOWS64 + { + int origIdx = sortedIdx[i]; + 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"); + wstring levelName = ReadLevelNameFromSaveFile(filePath); + if (!levelName.empty()) + { + m_buttonListSaves.addItem(levelName, wstring(L"")); + wcstombs(m_saveDetails[i].UTF8SaveName, levelName.c_str(), 127); + m_saveDetails[i].UTF8SaveName[127] = '\0'; + } + else + { + m_buttonListSaves.addItem(m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveTitle, L""); + memcpy(m_saveDetails[i].UTF8SaveName, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveTitle, 128); + } + m_saveDetails[i].saveId = origIdx; + memcpy(m_saveDetails[i].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH); + } #else m_buttonListSaves.addItem(m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, L""); - - m_saveDetails[i].saveId = i; memcpy(m_saveDetails[i].UTF8SaveName, m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, 128); + m_saveDetails[i].saveId = i; memcpy(m_saveDetails[i].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[i].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH); +#endif #endif } +#ifdef _WINDOWS64 + delete[] sortedIdx; +#endif m_controlSavesTimer.setVisible( false ); // set focus on the first button @@ -639,7 +773,11 @@ void UIScene_LoadOrJoinMenu::tick() app.DebugPrintf("Requesting the first thumbnail\n"); // set the save to load PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); +#ifdef _WINDOWS64 + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[m_saveDetails[m_iRequestingThumbnailId].saveId],&LoadSaveDataThumbnailReturned,this); +#else C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iRequestingThumbnailId],&LoadSaveDataThumbnailReturned,this); +#endif if(eLoadStatus!=C4JStorage::ESaveGame_GetSaveThumbnail) { @@ -702,7 +840,11 @@ void UIScene_LoadOrJoinMenu::tick() app.DebugPrintf("Requesting another thumbnail\n"); // set the save to load PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); +#ifdef _WINDOWS64 + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[m_saveDetails[m_iRequestingThumbnailId].saveId],&LoadSaveDataThumbnailReturned,this); +#else C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iRequestingThumbnailId],&LoadSaveDataThumbnailReturned,this); +#endif if(eLoadStatus!=C4JStorage::ESaveGame_GetSaveThumbnail) { // something went wrong @@ -1310,7 +1452,7 @@ void UIScene_LoadOrJoinMenu::handlePress(F64 controlId, F64 childId) LoadMenuInitData *params = new LoadMenuInitData(); params->iPad = m_iPad; // need to get the iIndex from the list item, since the position in the list doesn't correspond to the GetSaveGameInfo list because of sorting - params->iSaveGameInfoIndex=((int)childId)-m_iDefaultButtonsC; + params->iSaveGameInfoIndex=m_saveDetails[((int)childId)-m_iDefaultButtonsC].saveId; //params->pbSaveRenamed=&m_bSaveRenamed; params->levelGen = NULL; params->saveDetails = &m_saveDetails[ ((int)childId)-m_iDefaultButtonsC ]; diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index ceb9554b2..5e7ce7948 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -149,7 +149,7 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW //localIp = settings->getString(L"server-ip", L""); //onlineMode = settings->getBoolean(L"online-mode", true); //motd = settings->getString(L"motd", L"A Minecraft Server"); - //motd.replace('§', '$'); + //motd.replace('�', '$'); setAnimals(settings->getBoolean(L"spawn-animals", true)); setNpcsEnabled(settings->getBoolean(L"spawn-npcs", true)); @@ -203,7 +203,7 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW __int64 levelNanoTime = System::nanoTime(); - wstring levelName = settings->getString(L"level-name", L"world"); + wstring levelName = (initData && !initData->levelName.empty()) ? initData->levelName : settings->getString(L"level-name", L"world"); wstring levelTypeString; bool gameRuleUseFlatWorld = false; diff --git a/Minecraft.Client/MinecraftServer.h b/Minecraft.Client/MinecraftServer.h index e61001a3d..ac99a4154 100644 --- a/Minecraft.Client/MinecraftServer.h +++ b/Minecraft.Client/MinecraftServer.h @@ -39,6 +39,7 @@ typedef struct _NetworkGameInitData unsigned int xzSize; unsigned char hellScale; ESavePlatform savePlatform; + wstring levelName; _NetworkGameInitData() { diff --git a/Minecraft.World/ConsoleSaveFileOriginal.cpp b/Minecraft.World/ConsoleSaveFileOriginal.cpp index 139d99acf..7a11b5e12 100644 --- a/Minecraft.World/ConsoleSaveFileOriginal.cpp +++ b/Minecraft.World/ConsoleSaveFileOriginal.cpp @@ -740,7 +740,7 @@ void ConsoleSaveFileOriginal::Flush(bool autosave, bool updateThumbnail ) PBYTE pbDataSaveImage=NULL; DWORD dwDataSizeSaveImage=0; -#if ( defined _XBOX || defined _DURANGO ) +#if ( defined _XBOX || defined _DURANGO || defined _WINDOWS64 ) app.GetSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize); #elif ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ ) app.GetSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize,&pbDataSaveImage,&dwDataSizeSaveImage); diff --git a/README.md b/README.md index dd9a7965f..5be0c8fbb 100644 --- a/README.md +++ b/README.md @@ -16,23 +16,32 @@ This project contains the source code of Minecraft Legacy Console Edition v1.3.0 - Disabled V-Sync for better performance - Auto-detect native monitor resolution with DPI awareness, resulting in sharper visuals on high-resolution displays - Full support for keyboard and mouse input +- **Configurable player username/nametag** — edit `username.txt` next to the exe to set your in-game name +- **Persistent game settings** — gamma, music, sound, difficulty, HUD options, debug flags and all other settings now survive restarts (saved to `settings.dat` next to the exe) +- **Correct world save names** — save slots now display the actual world name instead of a raw timestamp; save list is sorted newest-first and refreshes without restarting ## Controls (Keyboard & Mouse) - **Movement**: `W` `A` `S` `D` - **Jump / Fly (Up)**: `Space` - **Sneak / Fly (Down)**: `Shift` (Hold) +- **Toggle Fly**: `F` - **Sprint**: `Ctrl` (Hold) or Double-tap `W` - **Inventory**: `E` - **Drop Item**: `Q` - **Crafting**: `C` - **Toggle View (FPS/TPS)**: `F5` +- **Toggle Debug Info**: `F3` +- **Open Debug Overlay**: `F4` (Debug builds only) - **Fullscreen**: `F11` - **Pause Menu**: `Esc` - **Toggle Mouse Capture**: `Left Alt` (for debugging) - **Attack / Destroy**: `Left Click` - **Use / Place**: `Right Click` - **Select Item**: `Mouse Wheel` or keys `1` to `9` +- **Accept Tutorial Hint**: `Enter` +- **Decline Tutorial Hint**: `B` +- **Host Options / Player List**: `Tab` ## Build & Run @@ -49,7 +58,17 @@ cmake -S . -B build -G "Visual Studio 17 2022" -A x64 cmake --build build --config Debug --target MinecraftClient ``` +## Runtime Files + +Some features require files placed next to the built executable (`x64\Debug\` or `x64\Release\`): + +| File | Purpose | +|------|---------| +| `username.txt` | Plain text file — first line becomes your in-game name and nametag. Created automatically with default value `Windows` on first run if absent. | +| `settings.dat` | Binary save of all game settings. Written automatically whenever you change a setting; loaded on startup. Delete it to reset all settings to defaults. | + ## Known Issues - Builds for other platforms have not been tested and are most likely non-functional - There are some render bugs in the Release mode build +- Changing the resource pack on an existing world while loading it may crash (`reloadAll` called during world load) — use the default resource pack or select it when creating a new world From 2d6a62a0e0c14bd435ef3d760ba413506029eff2 Mon Sep 17 00:00:00 2001 From: dtentiion Date: Mon, 2 Mar 2026 23:00:42 +0000 Subject: [PATCH 02/68] Refactor README for consistent formatting Updated formatting for clarity and consistency in the README. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5be0c8fbb..3b7372993 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ This project contains the source code of Minecraft Legacy Console Edition v1.3.0 - Disabled V-Sync for better performance - Auto-detect native monitor resolution with DPI awareness, resulting in sharper visuals on high-resolution displays - Full support for keyboard and mouse input -- **Configurable player username/nametag** — edit `username.txt` next to the exe to set your in-game name -- **Persistent game settings** — gamma, music, sound, difficulty, HUD options, debug flags and all other settings now survive restarts (saved to `settings.dat` next to the exe) -- **Correct world save names** — save slots now display the actual world name instead of a raw timestamp; save list is sorted newest-first and refreshes without restarting +- **Configurable player username/nametag** - edit `username.txt` next to the exe to set your in-game name +- **Persistent game settings** - gamma, music, sound, difficulty, HUD options, debug flags and all other settings now survive restarts (saved to `settings.dat` next to the exe) +- **Correct world save names** - save slots now display the actual world name instead of a raw timestamp; save list is sorted newest-first and refreshes without restarting ## Controls (Keyboard & Mouse) @@ -64,11 +64,11 @@ Some features require files placed next to the built executable (`x64\Debug\` or | File | Purpose | |------|---------| -| `username.txt` | Plain text file — first line becomes your in-game name and nametag. Created automatically with default value `Windows` on first run if absent. | +| `username.txt` | Plain text file - first line becomes your in-game name and nametag. Created automatically with default value `Windows` on first run if absent. | | `settings.dat` | Binary save of all game settings. Written automatically whenever you change a setting; loaded on startup. Delete it to reset all settings to defaults. | ## Known Issues - Builds for other platforms have not been tested and are most likely non-functional - There are some render bugs in the Release mode build -- Changing the resource pack on an existing world while loading it may crash (`reloadAll` called during world load) — use the default resource pack or select it when creating a new world +- Changing the resource pack on an existing world while loading it may crash (`reloadAll` called during world load), use the default resource pack or select it when creating a new world From a5e3cb04b3fa2dba86fbb3ff84e5354ad530c14c Mon Sep 17 00:00:00 2001 From: void_17 Date: Tue, 3 Mar 2026 08:45:26 +0700 Subject: [PATCH 03/68] Remove #203 core code before a cleaner implementation --- .../Windows64/Windows64_Minecraft.cpp | 88 +------------------ Minecraft.Client/glWrapper.cpp | 3 +- 2 files changed, 3 insertions(+), 88 deletions(-) diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 529399308..48040c664 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -431,90 +431,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return TRUE; } return DefWindowProc(hWnd, message, wParam, lParam); - case WM_SIZE: - { - if (wParam == SIZE_MINIMIZED) - return 0; - - UINT width = LOWORD(lParam); - UINT height = HIWORD(lParam); - - if (width == 0 || height == 0) - return 0; - - g_ScreenWidth = width; - g_ScreenHeight = height; - - if (g_pSwapChain) - { - g_pImmediateContext->OMSetRenderTargets(0, 0, 0); - g_pImmediateContext->ClearState(); - g_pImmediateContext->Flush(); - - if (g_pRenderTargetView) - { - g_pRenderTargetView->Release(); - g_pRenderTargetView = nullptr; - } - - if (g_pDepthStencilView) - { - g_pDepthStencilView->Release(); - g_pDepthStencilView = nullptr; - } - - if (g_pDepthStencilBuffer) - { - g_pDepthStencilBuffer->Release(); - g_pDepthStencilBuffer = nullptr; - } - - HRESULT hr = g_pSwapChain->ResizeBuffers( - 0, - width, - height, - DXGI_FORMAT_UNKNOWN, - 0 - ); - - if (FAILED(hr)) - { - app.DebugPrintf("ResizeBuffers Failed! HRESULT: 0x%X\n", hr); - return 0; - } - - ID3D11Texture2D* pBackBuffer = nullptr; - g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer); - - g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_pRenderTargetView); - pBackBuffer->Release(); - - D3D11_TEXTURE2D_DESC descDepth = {}; - descDepth.Width = width; - descDepth.Height = height; - descDepth.MipLevels = 1; - descDepth.ArraySize = 1; - descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; - descDepth.SampleDesc.Count = 1; - descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL; - - g_pd3dDevice->CreateTexture2D(&descDepth, NULL, &g_pDepthStencilBuffer); - g_pd3dDevice->CreateDepthStencilView(g_pDepthStencilBuffer, NULL, &g_pDepthStencilView); - - g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, g_pDepthStencilView); - - D3D11_VIEWPORT vp = {}; - vp.Width = (FLOAT)width; - vp.Height = (FLOAT)height; - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - vp.TopLeftX = 0; - vp.TopLeftY = 0; - - g_pImmediateContext->RSSetViewports(1, &vp); - } - } - break; default: return DefWindowProc(hWnd, message, wParam, lParam); } @@ -1383,7 +1299,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, { { ui.NavigateToScene(0, eUIScene_InGameInfoMenu); - + } } } @@ -1406,7 +1322,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, { if (Minecraft* pMinecraft = Minecraft::GetInstance()) { - if (pMinecraft->options && app.DebugSettingsOn() && + if (pMinecraft->options && app.DebugSettingsOn() && app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL) { ui.NavigateToScene(0, eUIScene_DebugOverlay, NULL, eUILayer_Debug); diff --git a/Minecraft.Client/glWrapper.cpp b/Minecraft.Client/glWrapper.cpp index 5e0fab2c6..93b13d40e 100644 --- a/Minecraft.Client/glWrapper.cpp +++ b/Minecraft.Client/glWrapper.cpp @@ -53,8 +53,7 @@ extern UINT g_ScreenHeight; void gluPerspective(float fovy, float aspect, float zNear, float zFar) { - float dynamicAspect = (float)g_ScreenWidth / (float)g_ScreenHeight; - RenderManager.MatrixPerspective(fovy, dynamicAspect, zNear, zFar); + RenderManager.MatrixPerspective(fovy,aspect,zNear,zFar); } void glOrtho(float left,float right,float bottom,float top,float zNear,float zFar) From ccebb87ca77329cd744f07df79b3eb88ca0be4d2 Mon Sep 17 00:00:00 2001 From: void_17 Date: Tue, 3 Mar 2026 08:54:08 +0700 Subject: [PATCH 04/68] Enable Whole Program Optimization in Release mode This noticeably improves FPS --- Minecraft.Client/Minecraft.Client.vcxproj | 4 +++- Minecraft.World/Minecraft.World.vcxproj | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/Minecraft.Client.vcxproj b/Minecraft.Client/Minecraft.Client.vcxproj index 99a4ea27e..5135fec9e 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj +++ b/Minecraft.Client/Minecraft.Client.vcxproj @@ -310,6 +310,7 @@ Application MultiByte v143 + true Application @@ -774,7 +775,7 @@ - true + false $(OutDir)$(ProjectName)_D.xex $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) @@ -1776,6 +1777,7 @@ xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\CU $(LayoutDir)Image\Loose\CUlegacy_stdio_definitions.lib;d3d11.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;%(AdditionalDependencies) NotSet false + UseLinkTimeCodeGeneration Run postbuild script diff --git a/Minecraft.World/Minecraft.World.vcxproj b/Minecraft.World/Minecraft.World.vcxproj index 30a871b57..16a93e1af 100644 --- a/Minecraft.World/Minecraft.World.vcxproj +++ b/Minecraft.World/Minecraft.World.vcxproj @@ -307,6 +307,7 @@ StaticLibrary MultiByte v143 + true StaticLibrary From 17a11d7913f971249fa52bcd37fd0cc5b03fdc04 Mon Sep 17 00:00:00 2001 From: MijaeLio <87155057+MijaeLio@users.noreply.github.com> Date: Mon, 2 Mar 2026 21:02:25 -0500 Subject: [PATCH 05/68] FOV option without debug menu (#209) Now located in Graphics section. Based on the FOV thing from discord idk --- .../Common/Media/MediaWindows64.arc | Bin 21409239 -> 21409761 bytes .../UI/UIScene_SettingsGraphicsMenu.cpp | 17 +++++++++++++++++ .../Common/UI/UIScene_SettingsGraphicsMenu.h | 4 +++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/Common/Media/MediaWindows64.arc b/Minecraft.Client/Common/Media/MediaWindows64.arc index 5ea229dca39b46f1f90af6691350f0b1aad37f3b..9498b91d6c8cd0531b384ef2fa6ed5c61ffd7b94 100644 GIT binary patch delta 42193 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 delta 41590 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 @@ -18,6 +21,9 @@ UIScene_SettingsGraphicsMenu::UIScene_SettingsGraphicsMenu(int iPad, void *initD swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_GAMMA ),app.GetGameSettings(m_iPad,eGameSetting_Gamma)); m_sliderGamma.init(TempString,eControl_Gamma,0,100,app.GetGameSettings(m_iPad,eGameSetting_Gamma)); + + swprintf((WCHAR*)TempString, 256, L"FOV: %d%%", (int)pMinecraft->gameRenderer->GetFovVal()); + m_sliderFOV.init(TempString, eControl_FOV, 70, 110, (int)pMinecraft->gameRenderer->GetFovVal()); swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_INTERFACEOPACITY ),app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity)); m_sliderInterfaceOpacity.init(TempString,eControl_InterfaceOpacity,0,100,app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity)); @@ -141,6 +147,17 @@ void UIScene_SettingsGraphicsMenu::handleSliderMove(F64 sliderId, F64 currentVal m_sliderGamma.setLabel(TempString); break; + + case eControl_FOV: + { + Minecraft* pMinecraft = Minecraft::GetInstance(); + pMinecraft->gameRenderer->SetFovVal((float)currentValue); + WCHAR TempString[256]; + swprintf((WCHAR*)TempString, 256, L"FOV: %d%%", (int)currentValue); + m_sliderFOV.setLabel(TempString); + } + break; + case eControl_InterfaceOpacity: m_sliderInterfaceOpacity.handleSliderMove(value); diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.h b/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.h index e9c4905cd..c6e1e394f 100644 --- a/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.h @@ -11,16 +11,18 @@ private: eControl_BedrockFog, eControl_CustomSkinAnim, eControl_Gamma, + eControl_FOV, eControl_InterfaceOpacity }; UIControl_CheckBox m_checkboxClouds, m_checkboxBedrockFog, m_checkboxCustomSkinAnim; // Checkboxes - UIControl_Slider m_sliderGamma, m_sliderInterfaceOpacity; // Sliders + UIControl_Slider m_sliderGamma, m_sliderFOV, m_sliderInterfaceOpacity; // Sliders UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) UI_MAP_ELEMENT( m_checkboxClouds, "Clouds") UI_MAP_ELEMENT( m_checkboxBedrockFog, "BedrockFog") UI_MAP_ELEMENT( m_checkboxCustomSkinAnim, "CustomSkinAnim") UI_MAP_ELEMENT( m_sliderGamma, "Gamma") + UI_MAP_ELEMENT(m_sliderFOV, "FOV") UI_MAP_ELEMENT( m_sliderInterfaceOpacity, "InterfaceOpacity") UI_END_MAP_ELEMENTS_AND_NAMES() From d7882b68a428906a4f063d40432012a560b5921f Mon Sep 17 00:00:00 2001 From: void_17 Date: Tue, 3 Mar 2026 09:27:28 +0700 Subject: [PATCH 06/68] Fix crash on item frame destruction --- Minecraft.World/MapItemSavedData.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Minecraft.World/MapItemSavedData.cpp b/Minecraft.World/MapItemSavedData.cpp index 6304aa624..981bab3e2 100644 --- a/Minecraft.World/MapItemSavedData.cpp +++ b/Minecraft.World/MapItemSavedData.cpp @@ -596,7 +596,14 @@ void MapItemSavedData::mergeInMapData(shared_ptr dataToAdd) void MapItemSavedData::removeItemFrameDecoration(shared_ptr item) { - AUTO_VAR(frameDecoration, nonPlayerDecorations.find( item->getFrame()->entityId ) ); + if ( !item ) + return; + + std::shared_ptr frame = item->getFrame(); + if ( !frame ) + return; + + auto frameDecoration = nonPlayerDecorations.find(frame->entityId); if ( frameDecoration != nonPlayerDecorations.end() ) { delete frameDecoration->second; From acf4a38555b4fd5f90319354178ea66af287f741 Mon Sep 17 00:00:00 2001 From: void_17 Date: Tue, 3 Mar 2026 09:45:52 +0700 Subject: [PATCH 07/68] Enable more aggressive optimizations /O2 /Ob3 --- Minecraft.Client/Minecraft.Client.vcxproj | 6 +++++- Minecraft.World/Minecraft.World.vcxproj | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/Minecraft.Client.vcxproj b/Minecraft.Client/Minecraft.Client.vcxproj index 5135fec9e..549156369 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj +++ b/Minecraft.Client/Minecraft.Client.vcxproj @@ -1757,7 +1757,7 @@ xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\CU $(LayoutDir)Image\Loose\CUUse TurnOffAllWarnings ProgramDatabase - Full + MaxSpeed Sync false $(OutDir)$(ProjectName).pch @@ -1770,6 +1770,10 @@ xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\CU $(LayoutDir)Image\Loose\CUDefault false Speed + true + true + true + /Ob3 true diff --git a/Minecraft.World/Minecraft.World.vcxproj b/Minecraft.World/Minecraft.World.vcxproj index 16a93e1af..58880529c 100644 --- a/Minecraft.World/Minecraft.World.vcxproj +++ b/Minecraft.World/Minecraft.World.vcxproj @@ -1339,7 +1339,7 @@ Use TurnOffAllWarnings ProgramDatabase - Full + MaxSpeed Sync false $(OutDir)$(ProjectName).pch @@ -1352,6 +1352,10 @@ true Default Speed + true + true + true + /Ob3 true From 0b1e51f620317e29666d6f3382a442544d783af6 Mon Sep 17 00:00:00 2001 From: Fayaz Shaikh <61674751+fayaz12g@users.noreply.github.com> Date: Mon, 2 Mar 2026 22:11:16 -0500 Subject: [PATCH 08/68] Cleaner implementation of support dynamic resizing aspect ratio (#228) * Add dynamic resolution * Clean up implementation * Use existing ints instead of new ones * Remove WM_SIZE argument (unecessary now that we directly use g_iScreenWidth and g_iScreenHeight) --- Minecraft.Client/glWrapper.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/glWrapper.cpp b/Minecraft.Client/glWrapper.cpp index 93b13d40e..540271b91 100644 --- a/Minecraft.Client/glWrapper.cpp +++ b/Minecraft.Client/glWrapper.cpp @@ -48,12 +48,13 @@ void glLoadIdentity() RenderManager.MatrixSetIdentity(); } -extern UINT g_ScreenWidth; -extern UINT g_ScreenHeight; +extern int g_iScreenWidth; +extern int g_iScreenHeight; void gluPerspective(float fovy, float aspect, float zNear, float zFar) { - RenderManager.MatrixPerspective(fovy,aspect,zNear,zFar); + float dynamicAspect = (float)g_iScreenWidth / (float)g_iScreenHeight; + RenderManager.MatrixPerspective(fovy, dynamicAspect, zNear, zFar); } void glOrtho(float left,float right,float bottom,float top,float zNear,float zFar) From 77a161e8134f5121020b8e905baf09f226553f46 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Tue, 3 Mar 2026 11:23:15 +0800 Subject: [PATCH 09/68] docs: update Discord invite link in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84d0186d8..ba02cdda0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MinecraftConsoles -[![Discord](https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white)](https://discord.gg/5CSzhc9t) +[![Discord](https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white)](https://discord.gg/jrum7HhegA) ![Tutorial World](.github/TutorialWorld.png) From 7f7d99501cf87fcc234a5d3af453e7e0642f96a3 Mon Sep 17 00:00:00 2001 From: void_17 <61356189+void2012@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:23:28 +0700 Subject: [PATCH 10/68] =?UTF-8?q?Revert=20"Win64:=20configurable=20usernam?= =?UTF-8?q?e=20(username.txt)=20and=20persistent=20game=20setti=E2=80=A6"?= =?UTF-8?q?=20(#234)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b8a7f816b52775fdcfb3503f0000accb8cd65765. --- Minecraft.Client/Common/Consoles_App.cpp | 45 --------------- Minecraft.Client/Extrax64Stubs.cpp | 9 ++- Minecraft.Client/Windows64/Windows64_App.cpp | 57 +------------------ Minecraft.Client/Windows64/Windows64_App.h | 2 - .../Windows64/Windows64_Minecraft.cpp | 27 +-------- 5 files changed, 8 insertions(+), 132 deletions(-) diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index b476ca909..0e02ab403 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -760,43 +760,6 @@ bool CMinecraftApp::LoadBeaconMenu(int iPad ,shared_ptr inventory, sh ////////////////////////////////////////////// // GAME SETTINGS ////////////////////////////////////////////// - -#ifdef _WINDOWS64 -static void Win64_GetSettingsPath(char *outPath, DWORD size) -{ - GetModuleFileNameA(NULL, outPath, size); - char *lastSlash = strrchr(outPath, '\\'); - if (lastSlash) *(lastSlash + 1) = '\0'; - strncat_s(outPath, size, "settings.dat", _TRUNCATE); -} -static void Win64_SaveSettings(GAME_SETTINGS *gs) -{ - if (!gs) return; - char filePath[MAX_PATH] = {}; - Win64_GetSettingsPath(filePath, MAX_PATH); - FILE *f = NULL; - if (fopen_s(&f, filePath, "wb") == 0 && f) - { - fwrite(gs, sizeof(GAME_SETTINGS), 1, f); - fclose(f); - } -} -static void Win64_LoadSettings(GAME_SETTINGS *gs) -{ - if (!gs) return; - char filePath[MAX_PATH] = {}; - Win64_GetSettingsPath(filePath, MAX_PATH); - FILE *f = NULL; - if (fopen_s(&f, filePath, "rb") == 0 && f) - { - GAME_SETTINGS temp = {}; - if (fread(&temp, sizeof(GAME_SETTINGS), 1, f) == 1) - memcpy(gs, &temp, sizeof(GAME_SETTINGS)); - fclose(f); - } -} -#endif - void CMinecraftApp::InitGameSettings() { for(int i=0;ibSettingsChanged=false; } @@ -2427,9 +2385,6 @@ void CMinecraftApp::CheckGameSettingsChanged(bool bOverride5MinuteTimer, int iPa StorageManager.WriteToProfile(iPad,true, bOverride5MinuteTimer); #else ProfileManager.WriteToProfile(iPad,true, bOverride5MinuteTimer); -#ifdef _WINDOWS64 - Win64_SaveSettings(GameSettingsA[iPad]); -#endif #endif GameSettingsA[iPad]->bSettingsChanged=false; } diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index 23b2c7f0e..f368afed0 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; } -PlayerUID IQNetPlayer::GetXuid() { return INVALID_XUID; } -extern wstring g_playerName; -LPCWSTR IQNetPlayer::GetGamertag() { return g_playerName.empty() ? L"Windows" : g_playerName.c_str(); } -int IQNetPlayer::GetSessionIndex() { return 0; } +bool IQNetPlayer::IsLocal() { return !m_isRemote; } +PlayerUID IQNetPlayer::GetXuid() { return (PlayerUID)(0xe000d45248242f2e + m_smallId); } +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; } diff --git a/Minecraft.Client/Windows64/Windows64_App.cpp b/Minecraft.Client/Windows64/Windows64_App.cpp index 461e8c348..dbc1bfc5e 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) @@ -70,27 +35,9 @@ void CConsoleMinecraftApp::FatalLoadError() void CConsoleMinecraftApp::CaptureSaveThumbnail() { - RenderManager.CaptureThumbnail(&m_ThumbnailBuffer); } void CConsoleMinecraftApp::GetSaveThumbnail(PBYTE *pbData,DWORD *pdwSize) { - // On a save caused by a create world, the thumbnail capture won't have happened - if (m_ThumbnailBuffer.Allocated()) - { - if (pbData) - { - *pbData = new BYTE[m_ThumbnailBuffer.GetBufferSize()]; - *pdwSize = m_ThumbnailBuffer.GetBufferSize(); - memcpy(*pbData, m_ThumbnailBuffer.GetBufferPointer(), *pdwSize); - } - m_ThumbnailBuffer.Release(); - } - else - { - // No capture happened (e.g. first save on world creation) leave thumbnail as NULL - if (pbData) *pbData = NULL; - if (pdwSize) *pdwSize = 0; - } } void CConsoleMinecraftApp::ReleaseSaveThumbnail() { @@ -110,8 +57,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 bff916ec7..de8f6d85f 100644 --- a/Minecraft.Client/Windows64/Windows64_App.h +++ b/Minecraft.Client/Windows64/Windows64_App.h @@ -1,9 +1,7 @@ #pragma once -#include "4JLibs\inc\4J_Render.h" class CConsoleMinecraftApp : public CMinecraftApp { - ImageFileBuffer m_ThumbnailBuffer; public: CConsoleMinecraftApp(); diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 48040c664..272b29bfd 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -729,16 +729,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); - // 4J-Win64: set CWD to exe dir so asset paths resolve correctly - { - char szExeDir[MAX_PATH] = {}; - GetModuleFileNameA(NULL, szExeDir, MAX_PATH); - char *pSlash = strrchr(szExeDir, '\\'); - if (pSlash) { *(pSlash + 1) = '\0'; SetCurrentDirectoryA(szExeDir); } - } - - // Declare DPI awareness so GetSystemMetrics returns physical pixels - SetProcessDPIAware(); + dyn_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); g_iScreenWidth = GetSystemMetrics(SM_CXSCREEN); g_iScreenHeight = GetSystemMetrics(SM_CYSCREEN); @@ -1272,21 +1263,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } } - // F3 toggles the debug console overlay, F11 toggles fullscreen - if (KMInput.IsKeyPressed(VK_F3)) - { - static bool s_debugConsole = false; - s_debugConsole = !s_debugConsole; - ui.ShowUIDebugConsole(s_debugConsole); - } - -#ifdef _DEBUG_MENUS_ENABLED - if (KMInput.IsKeyPressed(VK_F4)) - { - ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_DebugOverlay, NULL, eUILayer_Debug); - } -#endif - + // F11 toggles fullscreen if (KMInput.IsKeyPressed(VK_F11)) { ToggleFullscreen(); From b42a4a4e4d72f6dea0a243c77247f7b9e739f5eb Mon Sep 17 00:00:00 2001 From: void_17 <61356189+void2012@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:31:09 +0700 Subject: [PATCH 11/68] =?UTF-8?q?Revert=20"Revert=20"Win64:=20configurable?= =?UTF-8?q?=20username=20(username.txt)=20and=20persistent=20ga=E2=80=A6"?= =?UTF-8?q?=20(#235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 7f7d99501cf87fcc234a5d3af453e7e0642f96a3. --- Minecraft.Client/Common/Consoles_App.cpp | 45 +++++++++++++++ Minecraft.Client/Extrax64Stubs.cpp | 9 +-- Minecraft.Client/Windows64/Windows64_App.cpp | 57 ++++++++++++++++++- Minecraft.Client/Windows64/Windows64_App.h | 2 + .../Windows64/Windows64_Minecraft.cpp | 27 ++++++++- 5 files changed, 132 insertions(+), 8 deletions(-) diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 0e02ab403..b476ca909 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -760,6 +760,43 @@ bool CMinecraftApp::LoadBeaconMenu(int iPad ,shared_ptr inventory, sh ////////////////////////////////////////////// // GAME SETTINGS ////////////////////////////////////////////// + +#ifdef _WINDOWS64 +static void Win64_GetSettingsPath(char *outPath, DWORD size) +{ + GetModuleFileNameA(NULL, outPath, size); + char *lastSlash = strrchr(outPath, '\\'); + if (lastSlash) *(lastSlash + 1) = '\0'; + strncat_s(outPath, size, "settings.dat", _TRUNCATE); +} +static void Win64_SaveSettings(GAME_SETTINGS *gs) +{ + if (!gs) return; + char filePath[MAX_PATH] = {}; + Win64_GetSettingsPath(filePath, MAX_PATH); + FILE *f = NULL; + if (fopen_s(&f, filePath, "wb") == 0 && f) + { + fwrite(gs, sizeof(GAME_SETTINGS), 1, f); + fclose(f); + } +} +static void Win64_LoadSettings(GAME_SETTINGS *gs) +{ + if (!gs) return; + char filePath[MAX_PATH] = {}; + Win64_GetSettingsPath(filePath, MAX_PATH); + FILE *f = NULL; + if (fopen_s(&f, filePath, "rb") == 0 && f) + { + GAME_SETTINGS temp = {}; + if (fread(&temp, sizeof(GAME_SETTINGS), 1, f) == 1) + memcpy(gs, &temp, sizeof(GAME_SETTINGS)); + fclose(f); + } +} +#endif + void CMinecraftApp::InitGameSettings() { for(int i=0;ibSettingsChanged=false; } @@ -2385,6 +2427,9 @@ void CMinecraftApp::CheckGameSettingsChanged(bool bOverride5MinuteTimer, int iPa StorageManager.WriteToProfile(iPad,true, bOverride5MinuteTimer); #else ProfileManager.WriteToProfile(iPad,true, bOverride5MinuteTimer); +#ifdef _WINDOWS64 + Win64_SaveSettings(GameSettingsA[iPad]); +#endif #endif GameSettingsA[iPad]->bSettingsChanged=false; } diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index f368afed0..23b2c7f0e 100644 --- a/Minecraft.Client/Extrax64Stubs.cpp +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -199,10 +199,11 @@ 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 !m_isRemote; } -PlayerUID IQNetPlayer::GetXuid() { return (PlayerUID)(0xe000d45248242f2e + m_smallId); } -LPCWSTR IQNetPlayer::GetGamertag() { return m_gamertag; } -int IQNetPlayer::GetSessionIndex() { return m_smallId; } +bool IQNetPlayer::IsLocal() { return true; } +PlayerUID IQNetPlayer::GetXuid() { return INVALID_XUID; } +extern wstring g_playerName; +LPCWSTR IQNetPlayer::GetGamertag() { return g_playerName.empty() ? L"Windows" : g_playerName.c_str(); } +int IQNetPlayer::GetSessionIndex() { return 0; } bool IQNetPlayer::IsTalking() { return false; } bool IQNetPlayer::IsMutedByLocalUser(DWORD dwUserIndex) { return false; } bool IQNetPlayer::HasVoice() { return false; } diff --git a/Minecraft.Client/Windows64/Windows64_App.cpp b/Minecraft.Client/Windows64/Windows64_App.cpp index dbc1bfc5e..461e8c348 100644 --- a/Minecraft.Client/Windows64/Windows64_App.cpp +++ b/Minecraft.Client/Windows64/Windows64_App.cpp @@ -10,11 +10,46 @@ #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) @@ -35,9 +70,27 @@ void CConsoleMinecraftApp::FatalLoadError() void CConsoleMinecraftApp::CaptureSaveThumbnail() { + RenderManager.CaptureThumbnail(&m_ThumbnailBuffer); } void CConsoleMinecraftApp::GetSaveThumbnail(PBYTE *pbData,DWORD *pdwSize) { + // On a save caused by a create world, the thumbnail capture won't have happened + if (m_ThumbnailBuffer.Allocated()) + { + if (pbData) + { + *pbData = new BYTE[m_ThumbnailBuffer.GetBufferSize()]; + *pdwSize = m_ThumbnailBuffer.GetBufferSize(); + memcpy(*pbData, m_ThumbnailBuffer.GetBufferPointer(), *pdwSize); + } + m_ThumbnailBuffer.Release(); + } + else + { + // No capture happened (e.g. first save on world creation) leave thumbnail as NULL + if (pbData) *pbData = NULL; + if (pdwSize) *pdwSize = 0; + } } void CConsoleMinecraftApp::ReleaseSaveThumbnail() { @@ -57,8 +110,8 @@ void CConsoleMinecraftApp::TemporaryCreateGameStart() Minecraft *pMinecraft=Minecraft::GetInstance(); app.ReleaseSaveThumbnail(); ProfileManager.SetLockedProfile(0); - extern wchar_t g_Win64UsernameW[17]; - pMinecraft->user->name = g_Win64UsernameW; + LoadPlayerName(); + pMinecraft->user->name = g_playerName; app.ApplyGameSettingsChanged(0); ////////////////////////////////////////////////////////////////////////////////////////////// From CScene_MultiGameJoinLoad::OnInit diff --git a/Minecraft.Client/Windows64/Windows64_App.h b/Minecraft.Client/Windows64/Windows64_App.h index de8f6d85f..bff916ec7 100644 --- a/Minecraft.Client/Windows64/Windows64_App.h +++ b/Minecraft.Client/Windows64/Windows64_App.h @@ -1,7 +1,9 @@ #pragma once +#include "4JLibs\inc\4J_Render.h" class CConsoleMinecraftApp : public CMinecraftApp { + ImageFileBuffer m_ThumbnailBuffer; public: CConsoleMinecraftApp(); diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 272b29bfd..48040c664 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -729,7 +729,16 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); - dyn_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + // 4J-Win64: set CWD to exe dir so asset paths resolve correctly + { + char szExeDir[MAX_PATH] = {}; + GetModuleFileNameA(NULL, szExeDir, MAX_PATH); + char *pSlash = strrchr(szExeDir, '\\'); + if (pSlash) { *(pSlash + 1) = '\0'; SetCurrentDirectoryA(szExeDir); } + } + + // Declare DPI awareness so GetSystemMetrics returns physical pixels + SetProcessDPIAware(); g_iScreenWidth = GetSystemMetrics(SM_CXSCREEN); g_iScreenHeight = GetSystemMetrics(SM_CYSCREEN); @@ -1263,7 +1272,21 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } } - // F11 toggles fullscreen + // F3 toggles the debug console overlay, F11 toggles fullscreen + if (KMInput.IsKeyPressed(VK_F3)) + { + static bool s_debugConsole = false; + s_debugConsole = !s_debugConsole; + ui.ShowUIDebugConsole(s_debugConsole); + } + +#ifdef _DEBUG_MENUS_ENABLED + if (KMInput.IsKeyPressed(VK_F4)) + { + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_DebugOverlay, NULL, eUILayer_Debug); + } +#endif + if (KMInput.IsKeyPressed(VK_F11)) { ToggleFullscreen(); From 354a0989eb26879bdc7040ae628ead0b204b1e84 Mon Sep 17 00:00:00 2001 From: Loki Rautio Date: Mon, 2 Mar 2026 23:05:25 -0600 Subject: [PATCH 12/68] Add back x64 stub XUID (temp savedata fix) Fixes savedata loading for existing saves, needs permanent solution --- Minecraft.Client/Extrax64Stubs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index 23b2c7f0e..3a86cbeaf 100644 --- a/Minecraft.Client/Extrax64Stubs.cpp +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -200,7 +200,7 @@ DWORD IQNetPlayer::GetCurrentRtt() { return 0; } bool IQNetPlayer::IsHost() { return m_isHostPlayer; } bool IQNetPlayer::IsGuest() { return false; } bool IQNetPlayer::IsLocal() { return true; } -PlayerUID IQNetPlayer::GetXuid() { return INVALID_XUID; } +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; } From af5d62a81e03cead55b00f6757d547dd2ae6959b Mon Sep 17 00:00:00 2001 From: Loki Rautio Date: Mon, 2 Mar 2026 23:19:29 -0600 Subject: [PATCH 13/68] Add back missing filters --- .../Minecraft.Client.vcxproj.filters | 8124 +++++++++++++---- 1 file changed, 6277 insertions(+), 1847 deletions(-) diff --git a/Minecraft.Client/Minecraft.Client.vcxproj.filters b/Minecraft.Client/Minecraft.Client.vcxproj.filters index 708fe0b0a..2be7710a9 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj.filters +++ b/Minecraft.Client/Minecraft.Client.vcxproj.filters @@ -1,1868 +1,6298 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {e23474e2-447c-41a9-82be-e32747f5b196} + + + {d7b60dd5-624a-46b3-b81d-f5f74550f613} + + + {68105641-375c-4565-9945-7890df6d82d9} + + + {8be4617d-3699-46a4-8769-28edb23c89f0} + + + {0b94741f-653f-48c2-874f-6aa69e7e9622} + + + {20606602-63d3-460c-b33e-d3e747a3d8db} + + + {4afb96fe-3fcb-4bd1-89a1-adfea86c73fb} + + + {304b5ee1-bfdb-489f-8e24-0a4e61177ca1} + + + {225f9542-472d-45c1-9046-eb2a46ab029c} + + + {14e20ee1-fe3b-481b-acce-7a634ee9c1d6} + + + {486b537f-d140-4a23-8409-fe3bc4184009} + + + {91ef92f9-432b-4b8f-9f16-4efd211003a1} + + + {66656f96-a5da-48c6-a7f9-79ab343dbd2f} + + + {3423fd63-b0d7-4f50-b0ca-549386c6cf57} + + + {d097d6ee-2ae4-48d8-8b5a-9b48882bdb2c} + + + {1d0a6eec-14cd-4e6d-8a0a-f5f8f0ab5240} + + + {269fab49-d870-4358-baef-32ed6ac9eca7} + + + {fef42379-3d37-4ef3-aa73-b19aaa77e3cc} + + + {bce4041a-9336-45e7-bd40-ed057ed96ee8} + + + {9756cb73-3f40-4fcb-9bab-5a3ce3c4d2f6} + + + {bb820acc-a8eb-4e36-8b4e-9517263ed51b} + + + {3c3aca1d-0e3e-43f1-b4cd-f8dfce2d29e7} + + + {9f1bf1ed-5366-4a29-b3f3-296725a7b01c} + + + {3e905494-b5dc-4084-a1fe-cbd91b9af667} + + + {afb98298-0033-42ec-98a5-93f8d347ee0d} + + + {1953b4f7-41ea-430b-ad2d-e3d7c352b647} + + + {39ab4d1f-8199-4ec7-948e-3d42ad8c8573} + + + {73bbdc5b-04f3-42f8-bf3b-769335e19178} + + + {385338b7-fa77-4c46-a7f2-89c82dc6e192} + + + {0f94b57d-88f8-4a20-b4e4-d1fa95d8f439} + + + {abe2942f-f984-4930-9e2d-9c9c2b35ac74} + + + {a2be9911-8785-4f6a-932e-e03321ee466b} + + + {7cb56f76-52cf-4303-8631-e1471fdc09a0} + + + {098e2985-9c15-450f-baa2-78604e7c1f54} + + + {6c286ad1-f871-408a-be6e-db44e7edcd2c} + + + {7c254dd0-f36f-4001-83cc-1634a2c792c2} + + + {2a26afce-4160-4fb0-8d01-e394a669dae6} + + + {9229f78c-152c-47d5-858a-fd054b856a1c} + + + {81ab078c-fc67-460c-befd-616dbe4bc3bc} + + + {db324829-af2c-428d-9710-8ad20ecc3fd0} + + + {758ac0be-6bd7-42c0-9b09-fdd452c0e134} + + + {c2dcdce8-b00f-4094-b0de-dad838d49525} + + + {8fd2f4e7-b93a-4067-93b8-a7ebac6d4a9c} + + + {93a41380-e12e-4f7a-bb7b-459f7169faed} + + + {4eb1ba28-620f-4136-979c-4dc91c44b666} + + + {5ac21685-36c0-4cd1-8861-e6a0e4a37c62} + + + {1b4710ff-c513-4a11-9d34-ff36fe1b4246} + + + {f4877497-fdf4-48a8-ada4-e6042f632e7a} + + + {45f40847-5b95-4dca-82f2-7616d7a35e54} + + + {1a98ef4c-6c9d-4a22-93d7-89f0fc3320fd} + + + {3be02e3c-c628-4315-a507-a9fe7733af01} + + + {c6dffb6d-2cf6-4c3e-89a3-fb05229b98aa} + + + {7d088a48-eeda-4783-94f7-c0d09b06f347} + + + {a466219c-afde-4184-8b84-91df32e5b892} + + + {40d6ff43-3d13-42ab-99ae-ebc9d585110f} + + + {047a3693-2040-404d-a386-2e5795b231d3} + + + {2509ddc5-330c-45da-a6f9-d37b858acd34} + + + {b2935b29-33d3-4d57-a145-753a646e5de4} + + + {26661545-d0a0-438a-a775-31cec1fb7849} + + + {5f5f5678-57b0-4f7f-b7dc-1ddd01ea2774} + + + {a19d2d41-9a2f-4631-941b-c3bfa7c2fdfd} + + + {bcdb8322-b7e6-482b-a3da-eb3f84dac713} + + + {e2959475-d5c8-4874-a782-fb5266e4441c} + + + {794dfcdb-98c6-4939-b04d-86b9657d4ff6} + + + {775f3088-bc52-43a3-b9ec-7f3f58508240} + + + {ebe1835b-76a8-408d-b3ee-70ffa4db7907} + + + {45bddf1c-e6d6-4a78-9b7d-73d7511e070d} + + + {16186163-4c73-4fa8-85c7-57d2b34e3fe9} + + + {b33b6793-e585-487e-8626-0096242f8e04} + + + {1264d92e-fa06-40ef-846c-4ce2a99e8ccc} + + + {b28c2ec8-a257-41ca-aad2-cf2ced04e4fa} + + + {7369fca1-3096-4b7d-a93e-924587f23108} + + + {e0eabf73-2721-46f9-bc46-4e0292bb53d3} + + + {bf450dfd-c9e8-4120-8a4c-3860b606637e} + + + {4412cd12-307d-407c-8d4a-34df3274c892} + + + {91fbb0f7-3d94-4786-aa07-c9c57a6db9a9} + + + {afb9404f-f23d-46b1-b4d5-4b1096d5bf40} + + + {716a30f7-f9dd-43bf-9228-646dff4c58b1} + + + {3531c304-b08e-48ae-860c-773f6702ec4d} + + + {924f367a-618c-429e-9866-f60821f21d4a} + + + {e3e43b8f-e455-4222-a92e-f6567a41e326} + + + {61ac879d-17b0-402b-b29f-88c60a1161c7} + + + {4f5c7e99-5cbc-4db4-99c4-37db45537198} + + + {33341824-5702-4a56-b75c-9dac57e49349} + + + {4dbeff57-70bc-4b4c-b5d0-4c6834968d85} + + + {4be5c8d2-8944-4e8f-9d79-b1abc4b66f8f} + + + {ba24985e-3b16-45af-963e-9f2edca20b1a} + + + {77957a66-a869-4b9b-bbda-e7f43e01096f} + + + {fa09ab64-0a3f-429b-93cb-149ee490767b} + + + {dec59bc5-d9d3-4be5-b449-3df3b430eb39} + + + {0bcca89e-0d2d-407b-b1e4-878465404901} + + + {395a09e4-1ff9-458c-8fb8-a4cb28aa4881} + + + {056ec81c-c93f-4c56-9bcb-697cda24a612} + + + {d3d4cc74-edfa-4bbb-8e66-7252dbbc131b} + + + {1511a94f-13bc-49e0-bf75-7cdf98f1e77f} + + + {f0b2e12a-e042-49bc-a5fa-78d1cf79e5d3} + + + {d2020762-d261-4c89-bbb9-0c7113012882} + + + {88ebd63d-2bbc-438a-a810-9b26fcfdd908} + + + {2cf98618-28c5-46df-9ff7-3d331ee4a275} + + + {3c643f18-092d-4870-a206-8dc906748a64} + + + {11cc2598-d569-47ad-8843-7a8296878be9} + + + {33371180-d4ec-4439-8a95-059babcc1db9} + + + {c6d264ea-d4ac-4f3f-81f7-0d91fdc27713} + + + {bde45e25-7dce-4a39-a2bf-dad234708b07} + + + {9685dbaa-ed65-453c-ba57-ec01e59022ae} + + + {92ead381-f2b8-4c6d-a3ca-c6fbc7753361} + + + {2031e778-56ff-4126-b09d-4ec59453b21c} + + + {98e39923-fe62-42d5-8650-746c2d61efd2} + + + {094cddb4-1ac5-424b-80e3-e3b0e9bb3b05} + + + {914f66a5-b1a7-4615-9adc-287d28158eee} + + + {36ba326b-c3a1-473e-8cb4-054e34c276a8} + + + {f9dae5df-fabf-41f9-9b13-8d32e5b5baa5} + + + {e634a43c-ee4c-4adc-8847-c667fdc73c5f} + + + {71d6ccac-7a6e-4399-987b-06b606056f59} + + + {05765c7e-26d6-4760-b0f6-7aa9f374d163} + + + {02363026-02fd-4efc-a115-6ae3dc652546} + + + {d71c6707-d6ba-4ab5-a505-a916e007e60d} + + + {eb5eb5f3-0ea7-4658-a8fb-634eb289941d} + + + {46d5754b-1818-4685-a16d-f7415f61868c} + + + {541f67ae-2627-40af-8316-d76ee9bb6985} + + + {ccfdb851-7965-4551-88bb-4312ddbf830a} + + + {2b9abc76-798a-4aae-ba50-2dfc8f78ae81} + + + {35491a01-dd6f-4313-b857-5e3eb323b44f} + + + {bcac2142-c160-4a73-96c5-cbdf681a16f0} + + + {290b2f1c-dcd8-4ebc-9d6d-fa6de190117e} + + + {a7ec80a7-ea10-438c-a10f-7eeef759c32d} + + + {9a2c49f6-2f9d-4e9d-a4ea-a0a04ecba75f} + + + {24e96065-3dd4-4150-bde2-128d133fd2c4} + + + {10961b95-cb43-4a00-b999-04b66a1a0b43} + + + {6aaa8af3-3df6-43f4-9346-9adfe45ca3a7} + + + {aba0f713-fcfb-417e-9616-c8474225de71} + + + {94298ae6-25e0-4cc9-8c5a-efd53e156baa} + + + {6ec99327-b465-4e61-b064-023a09bdf907} + + + {2095b7df-1779-4788-b004-3479d5ab59d8} + + + {4c9eb137-a48c-44a4-be08-ef1745834ece} + + + {2bae7445-385f-4b0e-a3ec-11c1c584f930} + + + {7c655cf2-f74e-4e6a-9114-405f5bc28a56} + + + {a392080f-8e8b-42be-832a-a35869dba580} + + + {42dca5dc-e462-4537-9929-847a044eb116} + + + {0da3a534-f8c9-4d0c-a73f-dfeb402b27c1} + + + {2e1858a4-a24b-49d8-b19c-c24b45f75a4f} + + + {096eb9da-ee6c-46ba-a0f4-dd8d1748b6a1} + + + {50dc7509-93df-4e0a-8a9a-cea040e92180} + + + {67544d93-633f-46a8-9cdf-8ae646a745d1} + + + {08da2d2a-3276-4109-b190-05fbc4709398} + + + {cef89641-7631-4c30-855f-603163446077} + + + {a15076ff-0dbe-4fb5-8b58-4ceb4b189c8f} + + + {a36a05f3-bc99-4097-b7a8-f81c37eec6e3} + + + {c9fd57aa-ede6-46f3-b968-0f4a7c64f7f1} + + + {bcd2eaff-60b9-41f4-8e1a-258639b27f99} + + + {ebc154be-8d55-478b-9038-856d445aaf15} + + + {0749340b-e216-450a-a02e-001917097ba5} + + + {6b6c31a6-0b8d-4dc0-8d6e-38ab6de709ff} + + + {d7537fdd-877b-461c-9c86-3235843fcfc0} + + + {61e77fc3-d018-4e08-985c-9871eca81fe2} + + + {2c983999-feb8-40db-885b-abf061e2ab58} + + + {093a811c-5f90-4c0e-b260-4b637079730a} + + + {c2fdb165-80e4-4ce0-9bf1-12e5c58f83a5} + + + {ad68d69a-99d0-4eea-9bb4-58cb7083a7a1} + + + {a04f2d63-3e47-470f-b4ac-c1d5caf8ce56} + + + {a0aa2098-142e-4688-8d73-00ec7e5e9361} + + + {f7fc551a-1d1a-4584-af3b-2eadb712b0f7} + + + {7155e1ba-d9b6-473b-8c59-77dd883b766f} + + + {017984f1-6659-4a44-96fd-7dbb8f9b2654} + + + {5d6f34a3-c647-479d-a1a9-89a9ffca4ab9} + + + {6f049254-6585-4a90-be74-70d3878d864f} + + + {e4051e75-f566-41ce-b86a-46c838872963} + + + {18d3c9bc-132e-4770-a665-fc030eb86394} + + + {8a2156f5-3462-447b-b04d-e555a917fbf2} + + + {e0cb4d67-dd35-43ab-88cc-63173cc31125} + + + {090821e7-2a93-44de-bf5e-d5dbbcb41621} + + + {11f70fef-83b4-4fb9-85ab-51109fbb6a56} + + + {7b594635-988d-40aa-8a00-0d60b1f49a5a} + + + {e7df083d-5b13-46bc-a5b9-610c3ffb33bc} + + + {2ef42e03-cbaa-4077-a7f4-008150037f01} + + + {4d1da71a-dd84-4073-be6d-1e534eca98f3} + + + {acb27adb-45a3-45cf-85f5-3ae00cf3357d} + + + {3a9d8989-ff64-411c-84ad-b7dfb2520d5a} + + + {de5f0642-c9ab-431b-a255-a936076ffed2} + + + {76ac5981-4824-487a-992f-273bfa73fb68} + + + {b1794e73-9397-4e45-8a0d-a4f6dc72c321} + + + {ff6b8d80-d0ed-4225-b56c-1d0a19824e2f} + + + {4d0806f8-ae38-4bac-8469-0a82fc61eecd} + + + {67f51112-db23-4c8a-af1b-f748f7bbce8f} + + + {a47c9da7-bf36-42ae-aedf-c00c071c0582} + + + {017967fb-353e-448b-ae2c-639a182f3ee0} + + + {f4d6c5f9-40d6-4e52-bc03-fef06e9f0221} + + + {122ac1f3-113d-4f91-8676-bbe16e236f4f} + + + {1d28fadf-f748-4616-830b-ec2faa1b5f8e} + + + {bf865c6c-8bf4-4bd6-aaed-ff2a7c92706a} + + + {3eefa342-44e2-493a-9165-40f85bcef557} + + + {f88c0f6a-8051-41e7-9bf6-b9d3c7bb2937} + + + {a6b9803b-8dc2-4552-856e-470f78757533} + + + {21ba77e3-ca31-4dbb-b85d-48ddf892e1da} + + + {06443c48-8447-447b-895f-da725cc13c0c} + + + {ba60dadb-f607-49b7-ab07-0da3a6e06138} + + + {abc41045-2c80-41e8-a8e5-80383e3331b7} + + + {6e66e638-15af-47a6-83de-93bb0cb8ae3d} + + + {b2a3a14e-806c-4ebf-9413-0bbca21b6699} + + + {57a41953-69e1-408c-94ca-5a0fc35bee3d} + + + {afe55d4b-8cbd-4fc0-b4b5-e823d35ac9f6} + + + {ff3c3e8d-02aa-446f-912b-876aad8bb71a} + + + {5ce05bd9-a7f6-47cf-81c3-8c95d3627c5c} + + + {f90e55f2-d904-4421-8284-db37fe80c549} + + + {4c8bf8d5-d6d9-4b6b-96dd-00d64f476027} + + + {90c63e2f-0b47-4aca-a1df-26c436af7c69} + + + {ad3528e0-0c39-42d5-b756-fdf691df5f17} + + + {262a14ae-51b7-4d11-be00-2bf7840dc67d} + + + {40ad6aa5-e972-4aaf-bbb0-c783e72fb341} + + + {d705167f-d99e-49b5-a667-24c0c2fe7bcc} + + + {d52b4de1-b2d7-4c80-afb4-7c6edae1efcb} + + + {61ac299e-6446-4df9-b5cc-9b2c0890b47c} + + + {147837b5-da79-4938-abcf-f8926a72b25c} + + + {34edb787-189e-49c7-8412-f5def16b6f99} + + + {1f029554-0246-45da-8bfd-8d4bc8d4cffc} + + + {15633337-4260-4618-bffa-df945dba2b1a} + + + {81d283e0-15b7-4dcf-a85d-961169a993cd} + + + {dea799c3-4584-461c-a788-9766f61cea56} + + + {619bbb82-dfbc-499e-b078-048ad7e26222} + + + {f5065760-0ad8-4fb3-b6a9-f3ba06be0e51} + + + {360a336e-01e3-4a34-8608-efd2c7c72ef7} + + + {1d9e76bb-7f51-487f-b0b4-de3419fd1925} + + + {177ed754-f97c-4e53-9e75-1f548ae2a0b4} + + + {4b317e13-b7e6-4468-8a2e-bfbbe3bb272b} + + + {acc4e8ae-a1f1-4f2b-9bf2-e12b74fa3a1a} + + + {893769f2-22f7-4c41-ad2b-cb8668fb3b66} + + + {c1441371-f323-4549-90a0-53c6f743b4b1} + + + {b043e348-607a-4ac2-95de-f573db5dd04f} + + + {af98fe8e-ce25-437a-8ab9-efa9d8f0a5b0} + + + {9a61fbe5-f9a2-4c83-b407-5a295808664e} + + + {f55d07b2-80f2-4a01-8fb8-0b09545bf916} + + + {829b148f-b0d9-4a70-87ea-22f57281ac1f} + + + {08832b8f-5370-4c06-95ab-b5b285eb5fc5} + + + {918450ce-de83-4daf-8f25-7aaa8afcb856} + + + {5d807c82-39b9-4651-ab8a-14244deff851} + + + {9dee27ed-5aaf-4fad-b219-faebcebbe450} + + + {22d0b2d5-3279-4144-a23c-8eafb9d90e63} + + + {0061db22-43de-4b54-a161-c43958cdcd7e} + + + {889a84db-3009-4a7c-8234-4bf93d412690} + + + {e5d7fb24-25b8-413c-84ec-974bf0d4a3d1} + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + Xbox\GameConfig + + + Xbox\GameConfig + + + Xbox\res\audio + + + Xbox\res\audio + + + Xbox\res\audio + + + Xbox\4JLibs\Media + + + Xbox\res + + + Xbox\res + + + Xbox\xexxml + + + Xbox\xexxml + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\Sentient\DynamicConf + + + + Windows64\GameConfig + + + Windows64\GameConfig + + + Durango + + + Durango + + + Durango + + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\Miles Sound System\lib + + + PS3\Miles Sound System\lib + + + PS3\Miles Sound System\lib + + + PS3\Miles Sound System\lib + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + Windows64\Iggy\gdraw + + + Windows64\Iggy\gdraw + + + Windows64\Iggy\gdraw + + + Windows64\Iggy\gdraw + + + Durango\Iggy\gdraw + + + Durango\Iggy\gdraw + + + Durango\Iggy\gdraw + + + PS3\Iggy\gdraw + + + PS3\Iggy\gdraw + + + Windows64\Iggy\gdraw + + + Orbis\Iggy\gdraw + + + Orbis\Iggy\gdraw + + + Common\Source Files\Network + + + PSVita\GameConfig + + + PSVita\GameConfig + + + Orbis\4JLibs\libs + + + PSVita\Iggy\gdraw + + + PSVita\Iggy\gdraw + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Header Files + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + Header Files + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\player + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer + + + net\minecraft\client\skins + + + net\minecraft\client\skins + + + net\minecraft\client\skins + + + net\minecraft\client\skins + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client + + + net\minecraft\client\player + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\client\player + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client\level + + + net\minecraft\client + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui\particle + + + net\minecraft\client\gui\particle + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\title + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\achievement + + + net\minecraft\client\gui\achievement + + + net\minecraft\client\gui\achievement + + + Xbox\4JLibs\inc + + + Xbox\4JLibs\inc + + + Xbox\4JLibs\inc + + + Xbox\4JLibs\inc + + + Xbox\GameConfig + + + Xbox\Source Files + + + net\minecraft\server\network + + + net\minecraft\server\network + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\server\level + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Help & Options + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Controls + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Credits + + + Xbox\Source Files\XUI\Menu screens\Help & Options\How To Play + + + Xbox\Source Files\XUI\Menu screens\Help & Options\How To Play + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Tutorial + + + Xbox\Source Files\XUI\Menu screens\Leaderboards + + + Xbox\Source Files\XUI\Menu screens\Pause + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Menu screens\Social + + + Header Files + + + Header Files + + + Header Files + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\XML + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\Sentient\DynamicConf + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\Font + + + Xbox\Source Files\Font + + + Xbox\Source Files\Font + + + Xbox\Source Files\XUI\Menu screens\Debug + + + net\minecraft\server\network + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Controls + + + net\minecraft\client\renderer\tileentity + + + Xbox\Source Files\Sentient + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\XML + + + net\minecraft\server\level + + + net\minecraft\server\network + + + net\minecraft\client + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\multiplayer + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\server + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\dragon + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\model\geom + + + net\minecraft\client\particle + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\dragon + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model\geom + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model\geom + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Header Files + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\Social + + + Header Files + + + Windows + + + Windows + + + Durango\4JLibs\inc + + + Durango\4JLibs\inc + + + Durango\4JLibs\inc + + + Durango\4JLibs\inc + + + Common\Source Files\Trial + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Durango\Source Files + + + Common\Source Files\Tutorial\Hints + + + Durango\Source Files\Sentient + + + Durango\Source Files\Sentient + + + Durango\Source Files\Sentient + + + Durango\Source Files\Sentient + + + Durango\Source Files\Sentient + + + Durango\XML + + + Durango\Source Files\Sentient + + + Durango\Source Files\Social + + + Durango + + + Common + + + PS3\4JLibs\inc + + + PS3\4JLibs\inc + + + PS3\4JLibs\inc + + + PS3\4JLibs\inc + + + PS3\Source Files\Social + + + PS3\Source Files\Sentient + + + PS3\Source Files\Sentient + + + PS3\Source Files\Sentient + + + PS3\Source Files\Sentient + + + PS3\Source Files\Sentient + + + PS3\Source Files\Sentient + + + PS3\Source Files + + + PS3\PS3Extras + + + PS3\PS3Extras + + + Durango + + + Common\Source Files + + + Common\Source Files + + + Common\Source Files + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules + + + Common\Source Files\GameRules + + + PS3 + + + Xbox\Source Files\XUI + + + Xbox\Source Files\XUI + + + Xbox\Source Files\XUI + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + net\minecraft\client\skins + + + net\minecraft\client\particle + + + net\minecraft\client\skins + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture\custom + + + net\minecraft\client\renderer\texture\custom + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI + + + PS3\PS3Extras + + + PS3\PS3Extras + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\skins + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Header Files + + + Windows64\4JLibs\inc + + + Windows64\4JLibs\inc + + + Windows64\4JLibs\inc + + + Windows64\4JLibs\inc + + + Windows64\GameConfig + + + Windows64\XML + + + Windows64\Source Files + + + Windows64\Source Files\Social + + + Windows64\Source Files\Sentient + + + Windows64\Source Files\Sentient + + + Windows64\Source Files\Sentient + + + Windows64\Source Files\Sentient + + + Windows64\Source Files\Sentient + + + Windows64\Source Files\Sentient + + + Windows64 + + + Windows64\Source Files + + + Windows64 + + + Durango\Source Files + + + Orbis\OrbisExtras + + + Orbis\4JLibs\inc + + + Orbis\4JLibs\inc + + + Orbis\4JLibs\inc + + + Orbis\4JLibs\inc + + + Orbis\OrbisExtras + + + Orbis\OrbisExtras + + + Xbox\Source Files\XUI\Base Scene + + + Header Files + + + Common\Source Files\DLC + + + Orbis + + + Orbis\OrbisExtras + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Social + + + Orbis\XML + + + Orbis\Source Files + + + Orbis\OrbisExtras + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + Common + + + Common + + + Common + + + Common + + + net\minecraft\client\model + + + net\minecraft\client\renderer\entity + + + Windows64\Miles Sound System\Include + + + Windows64\Miles Sound System\Include + + + Orbis\Miles Sound System\include + + + Orbis\Miles Sound System\include + + + Durango\Miles Sound System\include + + + Durango\Miles Sound System\include + + + PS3\Miles Sound System\include + + + PS3\Miles Sound System\include + + + Common\Source Files\Audio + + + Xbox\Source Files\Audio + + + Common\Source Files\Audio + + + PS3\PS3Extras + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\CompressedTile_SPU + + + Common\Source Files\Localisation + + + Common\Source Files\DLC + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\Rules + + + Common\Source Files\GameRules\LevelRules + + + Common\Source Files\DLC + + + PS3 + + + Common\Source Files\GameRules\LevelRules\Rules + + + Common\Source Files\GameRules + + + Common\Source Files\GameRules + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\UI + + + Windows64\Iggy\include + + + Windows64\Iggy\include + + + Windows64\Iggy\include + + + Windows64\Iggy\include + + + Windows64\Iggy\include + + + Windows64\Iggy\gdraw + + + Windows64 + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\GameRules\LevelGeneration + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\model + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Common\Source Files\DLC + + + Common\Source Files\Colours + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Durango\DurangoExtras + + + Durango\Iggy\include + + + Durango\Iggy\include + + + Durango\Iggy\include + + + Durango\Iggy\include + + + Durango\Iggy\include + + + Durango\Iggy\gdraw + + + Durango + + + PS3 + + + PS3\Iggy\gdraw + + + PS3\Iggy\include + + + PS3\Iggy\include + + + PS3\Iggy\include + + + PS3\Iggy\include + + + PS3\Iggy\include + + + PS3\Iggy\include + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Orbis\Iggy\gdraw + + + Orbis\Iggy\include + + + Orbis\Iggy\include + + + Orbis\Iggy\include + + + Orbis\Iggy\include + + + Orbis\Iggy\include + + + Orbis\Iggy\include + + + Common\Source Files\Network + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\UI\Scenes\Debug + + + Xbox\Source Files + + + Common\Source Files\Network + + + Common\Source Files\Network + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\Network + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\Debug + + + Common\Source Files\UI\Components + + + Xbox\Source Files\Network + + + Common\Source Files\Network + + + Xbox\Source Files\Network + + + Orbis + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\BuildVer + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + PS3 + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Controls + + + PS3\Source Files\Network + + + PS3\Source Files\Leaderboards + + + Common\Source Files\Leaderboards + + + Xbox\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Controls + + + Windows64\Source Files\Leaderboards + + + Orbis\Source Files\Leaderboards + + + Durango\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Components + + + PS3\4JLibs + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + PS3\Source Files + + + Common\Source Files\UI\Controls + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\renderer\tileentity + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Durango\Source Files\Achievements + + + Common\Source Files\UI\Scenes\Debug + + + Xbox\Source Files\XUI\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\All Platforms + + + Xbox\Source Files\XUI\Containers + + + net\minecraft\client\model + + + net\minecraft\client\renderer\entity + + + Orbis + + + Orbis\Source Files + + + Orbis\Network + + + net\minecraft\server\commands + + + net\minecraft\server\commands + + + Durango\Network + + + Durango\Network + + + Durango\Network + + + Common\Source Files\Network\Sony + + + Common\Source Files\Network\Sony + + + Orbis\Network + + + PS3\Source Files\Network + + + Common\Source Files\Network\Sony + + + Common\Source Files\Network\Sony + + + Orbis\Network + + + Common\Source Files\Network\Sony + + + Common\Source Files\Network\Sony + + + PS3\Source Files\Network + + + PS3\Source Files\Network + + + Orbis\Network + + + Common\Source Files\GameRules\LevelGeneration + + + Durango\Network + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Xbox\Source Files\XUI\Menu screens + + + Durango\Network + + + PSVita\4JLibs\inc + + + PSVita\4JLibs\inc + + + PSVita\4JLibs\inc + + + PSVita\4JLibs\inc + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Social + + + PSVita\XML + + + PSVita + + + PSVita\GameConfig + + + Orbis\Network + + + Common\Source Files\UI\Scenes\Debug + + + Durango\Source Files + + + Durango\Network + + + Durango\Source Files\Leaderboards + + + Common\Source Files\Telemetry + + + Durango\Source Files\Sentient + + + Durango\ServiceConfig + + + Common\Source Files\UI + + + Common\Source Files\Network\Sony + + + Orbis\Network + + + PS3\Source Files\Network + + + Common\Source Files\UI\Components + + + Durango\Network + + + Durango\XML + + + PSVita\Iggy\gdraw + + + PSVita\Iggy\include + + + PSVita\Iggy\include + + + PSVita\Iggy\include + + + PSVita\Iggy\include + + + PSVita\Iggy\include + + + PSVita\Iggy\include + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + Common\Source Files\UI\Controls + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + PSVita\Miles Sound System\Include + + + PSVita\Miles Sound System\Include + + + Durango\Source Files\Leaderboards + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + Xbox\4JLibs\inc + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + Orbis\Network + + + Xbox\Source Files\Network + + + Common\Source Files\UI\Scenes + + + Common\Source Files\Leaderboards + + + Common\Source Files\Leaderboards + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\particle + + + net\minecraft\client\resources + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + net\minecraft\client\model + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Controls + + + Common\Source Files\UI\All Platforms + + + Xbox\Source Files\XUI\Containers + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI + + + Common\Source Files\UI\Controls + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\Leaderboards + + + Windows64\Source Files\Network + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Source Files + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + Source Files + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer + + + net\minecraft\client\skins + + + net\minecraft\client\skins + + + net\minecraft\client\skins + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client + + + net\minecraft\client\player + + + net\minecraft\client\player + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\client\player + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client\level + + + net\minecraft\client + + + net\minecraft\client\gui + + + net\minecraft\client + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui\particle + + + net\minecraft\client\gui\particle + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\title + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\achievement + + + net\minecraft\client\gui\achievement + + + net\minecraft\client\gui\achievement + + + Source Files + + + Source Files + + + Xbox\Source Files + + + Xbox\Source Files + + + net\minecraft\server\network + + + net\minecraft\server\network + + + net\minecraft\server\network + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server + + + net\minecraft\server\level + + + net\minecraft\server + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Help & Options + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Controls + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Credits + + + Xbox\Source Files\XUI\Menu screens\Help & Options\How To Play + + + Xbox\Source Files\XUI\Menu screens\Help & Options\How To Play + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Tutorial + + + Xbox\Source Files\XUI\Menu screens\Leaderboards + + + Xbox\Source Files\XUI\Menu screens\Pause + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Menu screens\Social + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\XML + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\Sentient\DynamicConf + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\Font + + + Xbox\Source Files\Font + + + Xbox\Source Files\Font + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\Sentient + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Controls + + + net\minecraft\client\renderer\tileentity + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + net\minecraft\server\level + + + net\minecraft\client + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\dragon + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\dragon + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model\geom + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\Social + + + Source Files + + + Common\Source Files\Trial + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial\Hints + + + PS3\Source Files + + + PS3\PS3Extras + + + Durango + + + Durango\Source Files + + + Common\Source Files + + + Common\Source Files + + + Common\Source Files\GameRules\LevelGeneration + + + PS3 + + + Xbox\Source Files\XUI + + + Xbox\Source Files\XUI + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Controls + + + net\minecraft\client\skins + + + net\minecraft\client\particle + + + net\minecraft\client\skins + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture\custom + + + net\minecraft\client\renderer\texture\custom + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + PS3\PS3Extras + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\skins + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\skins + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Windows64\Source Files + + + Windows64\Source Files + + + Windows64 + + + Durango\Source Files + + + Orbis\OrbisExtras + + + Xbox\Source Files\XUI\Base Scene + + + Common\Source Files\DLC + + + Orbis\OrbisExtras + + + Orbis + + + Orbis\Source Files + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + Common + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\model + + + Xbox\Source Files\Audio + + + Common\Source Files\Audio + + + Common\Source Files\Audio + + + PS3\PS3Extras + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\CompressedTile_SPU + + + Common\Source Files\Localisation + + + Common\Source Files\DLC + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\Rules + + + Common\Source Files\GameRules\LevelRules + + + Common\Source Files\DLC + + + PS3 + + + Common\Source Files\GameRules + + + Common\Source Files\GameRules + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\UI + + + Windows64\Iggy\gdraw + + + Windows64 + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\GameRules\LevelGeneration + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\model + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Common\Source Files\DLC + + + Common\Source Files\Colours + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Durango\DurangoExtras + + + Durango\Iggy\gdraw + + + Durango + + + PS3 + + + PS3\Iggy\gdraw + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + PS3\Source Files\Audio + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Orbis\Iggy\gdraw + + + Common\Source Files\UI\Scenes\Debug + + + Xbox\Source Files + + + Common\Source Files\Network + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\Debug + + + Common\Source Files\UI\Components + + + Xbox\Source Files\Network + + + Common\Source Files\Network + + + Xbox\Source Files\Network + + + Orbis + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Controls + + + PS3\Source Files\Network + + + PS3\Source Files\Leaderboards + + + Common\Source Files\Leaderboards + + + Xbox\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Controls + + + Windows64\Source Files\Leaderboards + + + Orbis\Source Files\Leaderboards + + + Durango\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Components + + + PS3\4JLibs + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\Audio + + + PS3\Source Files + + + Common\Source Files\UI\Controls + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\renderer\tileentity + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Durango\Source Files\Achievements + + + Common\Source Files\UI\Scenes\Debug + + + Xbox\Source Files\XUI\Containers + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Xbox\Source Files\XUI\Containers + + + net\minecraft\client\model + + + net\minecraft\client\renderer\entity + + + Orbis + + + Orbis\Network + + + net\minecraft\server\commands + + + net\minecraft\server\commands + + + Durango\Network + + + Durango\Network + + + Durango\Network + + + Common\Source Files\Network\Sony + + + Common\Source Files\Network\Sony + + + Common\Source Files\Network\Sony + + + PS3\Source Files\Network + + + Orbis\Network + + + Orbis\Network + + + Common\Source Files\Network\Sony + + + PS3\Source Files\Network + + + PS3\Source Files\Network + + + Orbis\Network + + + Common\Source Files\GameRules\LevelGeneration + + + Durango\Network + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Xbox\Source Files\XUI\Menu screens + + + Durango\Network + + + PSVita + + + PSVita + + + PSVita\Source Files + + + PSVita\PSVitaExtras + + + Orbis\Network + + + Common\Source Files\Network\Sony + + + Common\Source Files\UI\Scenes\Debug + + + Durango\Network + + + Durango\Source Files\Leaderboards + + + Common\Source Files\Telemetry + + + Durango\Source Files\Sentient + + + Common\Source Files\UI + + + Orbis\Network + + + PS3\Source Files\Network + + + Common\Source Files\Network\Sony + + + Orbis + + + Orbis + + + Orbis + + + Common\Source Files\UI\Components + + + Durango\Network + + + Durango\Network + + + Durango\Network + + + Durango\Network + + + Durango\Network + + + Durango\XML + + + PSVita\Iggy\gdraw + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + Common\Source Files\UI\Controls + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Durango\Source Files\Leaderboards + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + Orbis\Network + + + Common\Source Files\UI\Scenes + + + Common\Source Files\Leaderboards + + + Common\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\particle + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + net\minecraft\client\model + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Controls + + + Common\Source Files\UI\All Platforms + + + Xbox\Source Files\XUI\Containers + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Controls + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\Leaderboards + + + Source Files + + + Windows64\Source Files\Network + - + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Durango\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\Miles Sound System\lib + + + Windows64\Iggy\lib + + + Windows64\Iggy\lib + + + Windows64\Iggy\lib + + + Durango\Iggy\lib + + + Durango\Iggy\lib + + + Durango\Iggy\lib + + + Durango\Iggy\lib + + + PS3\Iggy\lib + + + PS3\Iggy\lib + + + PS3\Iggy\lib + + + Orbis\Iggy\lib + + + Orbis\Iggy\lib + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + Durango\Miles Sound System\lib + + + Durango\Miles Sound System\lib + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\Iggy\Lib + + + PSVita\Iggy\Lib + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + + + + Xbox\SentientLibs + + + + + Windows + + + Windows + + + Durango + + + + + Windows + + + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\ContentPackage + - - - - - - + + Source Files + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 3093ca37d3724fd2dc788b2fcabc746bf33f523f Mon Sep 17 00:00:00 2001 From: void_17 <61356189+void2012@users.noreply.github.com> Date: Tue, 3 Mar 2026 12:21:41 +0700 Subject: [PATCH 14/68] Implement smooth scrolling in Creative Mode menu (#240) --- Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp index c3348e584..973020db2 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -845,8 +845,9 @@ IUIScene_CreativeMenu::TabSpec::TabSpec(LPCWSTR icon, int descriptionId, int sta } } - m_staticPerPage = MAX_SIZE - dynamicItems; - m_pages = (int)ceil((float)m_staticItems / m_staticPerPage); + m_staticPerPage = columns; + const int totalRows = (m_staticItems + columns - 1) / columns; + m_pages = std::max(1, totalRows - 5 + 1); } IUIScene_CreativeMenu::TabSpec::~TabSpec() From fad108aaee9178b65f74ac7ab716599dbfb3a14b Mon Sep 17 00:00:00 2001 From: Loki Rautio Date: Mon, 2 Mar 2026 23:27:20 -0600 Subject: [PATCH 15/68] Use Xbox One command buffer limit - fixes #238 --- Minecraft.Client/LevelRenderer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/LevelRenderer.h b/Minecraft.Client/LevelRenderer.h index 46a4c31fe..88280b1e4 100644 --- a/Minecraft.Client/LevelRenderer.h +++ b/Minecraft.Client/LevelRenderer.h @@ -52,7 +52,7 @@ public: static const int CHUNK_SIZE = 16; #endif static const int CHUNK_Y_COUNT = Level::maxBuildHeight / CHUNK_SIZE; -#if defined _XBOX_ONE +#if (defined _XBOX_ONE || defined _WINDOWS64) static const int MAX_COMMANDBUFFER_ALLOCATIONS = 2047 * 1024 * 1024; // Changed to 2047. 4J had set to 512. #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) From 7ce1fa3452a25980fb40311b031fbc67145899b9 Mon Sep 17 00:00:00 2001 From: 4win <4winyt@gmail.com> Date: Mon, 2 Mar 2026 23:32:26 -0600 Subject: [PATCH 16/68] feat: bind F1 to toggle the HUD settings (#244) --- Minecraft.Client/Windows64/Windows64_Minecraft.cpp | 10 +++++++++- README.md | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 48040c664..b49af8535 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -1272,7 +1272,15 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } } - // F3 toggles the debug console overlay, F11 toggles fullscreen + // F1 toggles the HUD, F3 toggles the debug console overlay, F11 toggles fullscreen + if (KMInput.IsKeyPressed(VK_F1)) + { + int primaryPad = ProfileManager.GetPrimaryPad(); + unsigned char displayHud = app.GetGameSettings(primaryPad, eGameSetting_DisplayHUD); + app.SetGameSettings(primaryPad, eGameSetting_DisplayHUD, displayHud ? 0 : 1); + app.SetGameSettings(primaryPad, eGameSetting_DisplayHand, displayHud ? 0 : 1); + } + if (KMInput.IsKeyPressed(VK_F3)) { static bool s_debugConsole = false; diff --git a/README.md b/README.md index ba02cdda0..999626e9a 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ This feature is based on [LCEMP](https://github.com/LCEMP/LCEMP/) - **Select Item**: `Mouse Wheel` or keys `1` to `9` - **Accept or Decline Tutorial hints**: `Enter` to accept and `B` to decline - **Game Info (Player list and Host Options)**: `TAB` +- **Toggle HUD**: `F1` - **Toggle Debug Info**: `F3` - **Open Debug Overlay**: `F4` From 8f17df635179fc085d4a6513211fbf6b543d1c41 Mon Sep 17 00:00:00 2001 From: Loki Rautio Date: Mon, 2 Mar 2026 23:46:39 -0600 Subject: [PATCH 17/68] Disable blank changelog popup for now Partially addresses issue in #190 --- Minecraft.Client/Xbox/Xbox_App.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/Xbox/Xbox_App.cpp b/Minecraft.Client/Xbox/Xbox_App.cpp index b252035e6..a342d8c2e 100644 --- a/Minecraft.Client/Xbox/Xbox_App.cpp +++ b/Minecraft.Client/Xbox/Xbox_App.cpp @@ -1658,7 +1658,8 @@ HRESULT CConsoleMinecraftApp::NavigateToScene(int iPad,EUIScene eScene, void *in // If you're navigating to the multigamejoinload, and the player hasn't seen the updates message yet, display it now // display this message the first 3 times - if((eScene==eUIScene_LoadOrJoinMenu) && (bSeenUpdateTextThisSession==false) && ( app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_DisplayUpdateMessage)!=0)) + // todo: re-enable if we fix this menu, for now its just blank! + if(false && (eScene==eUIScene_LoadOrJoinMenu) && (bSeenUpdateTextThisSession==false) && ( app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_DisplayUpdateMessage)!=0)) { eScene=eUIScene_NewUpdateMessage; bSeenUpdateTextThisSession=true; From c1ec83aedc9b946b60a7c250983af876b2dccb2b Mon Sep 17 00:00:00 2001 From: void_17 <61356189+void2012@users.noreply.github.com> Date: Tue, 3 Mar 2026 12:59:47 +0700 Subject: [PATCH 18/68] Add nightly.yml release description Compiler information mostly --- .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 0affa845e..df01b57af 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -35,4 +35,5 @@ jobs: with: tag_name: nightly name: Nightly Release - files: LCEWindows64.zip \ No newline at end of file + 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 From cd03a390b74c2452f26909a860227353cbd46ab6 Mon Sep 17 00:00:00 2001 From: Loki Rautio Date: Tue, 3 Mar 2026 00:07:31 -0600 Subject: [PATCH 19/68] Move Tutorial.pck to the correct Dec2014 location Fixes #190 --- .../Windows64Media/{Media => Tutorial}/Tutorial.pck | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename Minecraft.Client/Windows64Media/{Media => Tutorial}/Tutorial.pck (100%) diff --git a/Minecraft.Client/Windows64Media/Media/Tutorial.pck b/Minecraft.Client/Windows64Media/Tutorial/Tutorial.pck similarity index 100% rename from Minecraft.Client/Windows64Media/Media/Tutorial.pck rename to Minecraft.Client/Windows64Media/Tutorial/Tutorial.pck From ca7615d77d61e02c5fe895577f0ca353c6369887 Mon Sep 17 00:00:00 2001 From: 4win <4winyt@gmail.com> Date: Tue, 3 Mar 2026 01:13:20 -0600 Subject: [PATCH 20/68] feat: make the game sensitivity slider affect mouse sensitivity (#255) --- Minecraft.Client/Input.cpp | 2 +- Minecraft.Client/Minecraft.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/Input.cpp b/Minecraft.Client/Input.cpp index 6933a2d70..1639d09b8 100644 --- a/Minecraft.Client/Input.cpp +++ b/Minecraft.Client/Input.cpp @@ -145,7 +145,7 @@ void Input::tick(LocalPlayer *player) // Delta should normally be 0 since applyFrameMouseLook() already consumed it if (rawDx != 0.0f || rawDy != 0.0f) { - float mouseSensitivity = 0.5f; + float mouseSensitivity = ((float)app.GetGameSettings(iPad, eGameSetting_Sensitivity_InGame)) / 100.0f; float mdx = rawDx * mouseSensitivity; float mdy = -rawDy * mouseSensitivity; if (app.GetGameSettings(iPad, eGameSetting_ControlInvertLook)) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 87f1b8bee..bc9aec3b9 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -1191,7 +1191,7 @@ void Minecraft::applyFrameMouseLook() KMInput.ConsumeMouseDelta(rawDx, rawDy); if (rawDx == 0.0f && rawDy == 0.0f) continue; - float mouseSensitivity = 0.5f; + float mouseSensitivity = ((float)app.GetGameSettings(iPad, eGameSetting_Sensitivity_InGame)) / 100.0f; float mdx = rawDx * mouseSensitivity; float mdy = -rawDy * mouseSensitivity; if (app.GetGameSettings(iPad, eGameSetting_ControlInvertLook)) From 6d4ce5136cccb994883473871db03e9d50e6b683 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Tue, 3 Mar 2026 17:33:58 +0800 Subject: [PATCH 21/68] fix: fix executable icon when using cmake --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c3ae11260..f14cf6272 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.24) -project(MinecraftConsoles LANGUAGES C CXX ASM_MASM) +project(MinecraftConsoles LANGUAGES C CXX RC ASM_MASM) if(NOT WIN32) message(FATAL_ERROR "This CMake build currently supports Windows only.") @@ -17,6 +17,9 @@ include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/ClientSources.cmake") list(TRANSFORM MINECRAFT_WORLD_SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/") list(TRANSFORM MINECRAFT_CLIENT_SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/") +list(APPEND MINECRAFT_CLIENT_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Xbox/MinecraftWindows.rc" +) add_library(MinecraftWorld STATIC ${MINECRAFT_WORLD_SOURCES}) target_include_directories(MinecraftWorld PRIVATE From 2915044f953bc6cd3a28291fbb5661080d58bb30 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Tue, 3 Mar 2026 17:40:03 +0800 Subject: [PATCH 22/68] chore: sync VS release optimization flags into CMake build --- CMakeLists.txt | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f14cf6272..8e40e5db3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,15 @@ endif() set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +function(configure_msvc_target target) + target_compile_options(${target} PRIVATE + $<$:/W3> + $<$:/MP> + $<$:/EHsc> + $<$,$>:/GL /O2 /Ob3 /Oi /GT /GF> + ) +endfunction() + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/WorldSources.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/ClientSources.cmake") @@ -31,11 +40,7 @@ target_compile_definitions(MinecraftWorld PRIVATE $<$>:_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_LIB;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64> ) if(MSVC) - target_compile_options(MinecraftWorld PRIVATE - $<$:/W3> - $<$:/MP> - $<$:/EHsc> - ) + configure_msvc_target(MinecraftWorld) endif() add_executable(MinecraftClient WIN32 ${MINECRAFT_CLIENT_SOURCES}) @@ -50,10 +55,9 @@ target_compile_definitions(MinecraftClient PRIVATE $<$>:_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64> ) if(MSVC) - target_compile_options(MinecraftClient PRIVATE - $<$:/W3> - $<$:/MP> - $<$:/EHsc> + configure_msvc_target(MinecraftClient) + target_link_options(MinecraftClient PRIVATE + $<$:/LTCG /INCREMENTAL:NO> ) endif() From a97ee4aab16cd46c8466ea89c99a437d27be6491 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Tue, 3 Mar 2026 17:45:25 +0800 Subject: [PATCH 23/68] chore: sync VS Release warning-disable setting into CMake --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e40e5db3..977bce2d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,8 @@ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") function(configure_msvc_target target) target_compile_options(${target} PRIVATE - $<$:/W3> + $<$>,$>:/W3> + $<$,$>:/W0> $<$:/MP> $<$:/EHsc> $<$,$>:/GL /O2 /Ob3 /Oi /GT /GF> 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 24/68] 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 25/68] 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 26/68] 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 27/68] 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 28/68] 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 29/68] 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 30/68] 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 31/68] 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 32/68] 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 33/68] 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 34/68] 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 35/68] 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 36/68] 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 37/68] 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 38/68] #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 39/68] 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 40/68] 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 41/68] 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 42/68] 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 43/68] 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 44/68] 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 45/68] 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 46/68] 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 c9d58eeac7c72f0b3038e084667b4d89a6249fce Mon Sep 17 00:00:00 2001 From: dtentiion Date: Wed, 4 Mar 2026 06:05:24 +0000 Subject: [PATCH 47/68] Delete README.md Unnecessary --- README.md | 74 ------------------------------------------------------- 1 file changed, 74 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 3b7372993..000000000 --- a/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# MinecraftConsoles - -[![Discord](https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white)](https://discord.gg/5CSzhc9t) - -![img.png](.github/IMG_8725.png) - -## Introduction - -This project contains the source code of Minecraft Legacy Console Edition v1.3.0494.0, with some fixes and improvements applied. - -## Features - -- Fixed compilation and execution in both Debug and Release mode on Windows using Visual Studio 2022 -- Added support for keyboard and mouse input -- Added fullscreen mode support (toggle using F11) -- Disabled V-Sync for better performance -- Auto-detect native monitor resolution with DPI awareness, resulting in sharper visuals on high-resolution displays -- Full support for keyboard and mouse input -- **Configurable player username/nametag** - edit `username.txt` next to the exe to set your in-game name -- **Persistent game settings** - gamma, music, sound, difficulty, HUD options, debug flags and all other settings now survive restarts (saved to `settings.dat` next to the exe) -- **Correct world save names** - save slots now display the actual world name instead of a raw timestamp; save list is sorted newest-first and refreshes without restarting - -## Controls (Keyboard & Mouse) - -- **Movement**: `W` `A` `S` `D` -- **Jump / Fly (Up)**: `Space` -- **Sneak / Fly (Down)**: `Shift` (Hold) -- **Toggle Fly**: `F` -- **Sprint**: `Ctrl` (Hold) or Double-tap `W` -- **Inventory**: `E` -- **Drop Item**: `Q` -- **Crafting**: `C` -- **Toggle View (FPS/TPS)**: `F5` -- **Toggle Debug Info**: `F3` -- **Open Debug Overlay**: `F4` (Debug builds only) -- **Fullscreen**: `F11` -- **Pause Menu**: `Esc` -- **Toggle Mouse Capture**: `Left Alt` (for debugging) -- **Attack / Destroy**: `Left Click` -- **Use / Place**: `Right Click` -- **Select Item**: `Mouse Wheel` or keys `1` to `9` -- **Accept Tutorial Hint**: `Enter` -- **Decline Tutorial Hint**: `B` -- **Host Options / Player List**: `Tab` - -## Build & Run - -1. Install Visual Studio 2022 -2. Clone the repository -3. Open the project by double-clicking `MinecraftConsoles.sln` -4. Make sure `Minecraft.Client` is set as the Startup Project -5. Set the build configuration to **Debug** (Release is also OK but has some bugs) and the target platform to **Windows64**, then build and run - -### CMake (Windows x64) - -```powershell -cmake -S . -B build -G "Visual Studio 17 2022" -A x64 -cmake --build build --config Debug --target MinecraftClient -``` - -## Runtime Files - -Some features require files placed next to the built executable (`x64\Debug\` or `x64\Release\`): - -| File | Purpose | -|------|---------| -| `username.txt` | Plain text file - first line becomes your in-game name and nametag. Created automatically with default value `Windows` on first run if absent. | -| `settings.dat` | Binary save of all game settings. Written automatically whenever you change a setting; loaded on startup. Delete it to reset all settings to defaults. | - -## Known Issues - -- Builds for other platforms have not been tested and are most likely non-functional -- There are some render bugs in the Release mode build -- Changing the resource pack on an existing world while loading it may crash (`reloadAll` called during world load), use the default resource pack or select it when creating a new world 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 48/68] 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 49/68] 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 50/68] 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` From d112090fde200c545a70ec5dc33fe91cca0f26ec Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Wed, 4 Mar 2026 16:18:47 +0800 Subject: [PATCH 51/68] feat: headless server --- CMakeLists.txt | 1 + .../Network/PlatformNetworkManagerStub.cpp | 19 +- Minecraft.Client/Minecraft.Client.vcxproj | 5 +- Minecraft.Client/MinecraftServer.cpp | 470 ++++++++++++- Minecraft.Client/MinecraftServer.h | 1 + Minecraft.Client/Settings.cpp | 80 ++- Minecraft.Client/Settings.h | 6 +- .../Windows64/Network/WinsockNetLayer.cpp | 21 +- .../Windows64/Network/WinsockNetLayer.h | 5 +- .../Windows64/Windows64_Minecraft.cpp | 639 +++++++++++------- Minecraft.World/Minecraft.World.vcxproj | 5 +- README.md | 12 +- 12 files changed, 973 insertions(+), 291 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74851754d..2d83c5c8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ function(configure_msvc_target target) $<$>,$>:/W3> $<$,$>:/W0> $<$:/MP> + $<$:/FS> $<$:/EHsc> $<$,$>:/GL /O2 /Oi /GT /GF> ) diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp index 10d1a6a53..c2466fe39 100644 --- a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp +++ b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp @@ -362,12 +362,23 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame, #ifdef _WINDOWS64 int port = WIN64_NET_DEFAULT_PORT; + const char* bindIp = NULL; + if (g_Win64DedicatedServer) + { + if (g_Win64DedicatedServerPort > 0) + port = g_Win64DedicatedServerPort; + if (g_Win64DedicatedServerBindIP[0] != 0) + bindIp = g_Win64DedicatedServerBindIP; + } if (!WinsockNetLayer::IsActive()) - WinsockNetLayer::HostGame(port); + WinsockNetLayer::HostGame(port, bindIp); - const wchar_t* hostName = IQNet::m_player[0].m_gamertag; - unsigned int settings = app.GetGameHostOption(eGameHostOption_All); - WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION); + if (WinsockNetLayer::IsActive()) + { + const wchar_t* hostName = IQNet::m_player[0].m_gamertag; + unsigned int settings = app.GetGameHostOption(eGameHostOption_All); + WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION); + } #endif //#endif } diff --git a/Minecraft.Client/Minecraft.Client.vcxproj b/Minecraft.Client/Minecraft.Client.vcxproj index 549156369..d7c49acfc 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj +++ b/Minecraft.Client/Minecraft.Client.vcxproj @@ -1545,6 +1545,7 @@ if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata" true Default false + /FS %(AdditionalOptions) true @@ -1773,7 +1774,7 @@ xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\CU $(LayoutDir)Image\Loose\CUtrue true true - /Ob3 + /FS /Ob3 %(AdditionalOptions) true @@ -48677,4 +48678,4 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU - \ No newline at end of file + diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index 4206a3995..974c74051 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -27,6 +27,11 @@ #include "..\Minecraft.World\Pos.h" #include "..\Minecraft.World\System.h" #include "..\Minecraft.World\StringHelpers.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.item.enchantment.h" +#include "..\Minecraft.World\net.minecraft.world.damagesource.h" +#include #ifdef SPLIT_SAVES #include "..\Minecraft.World\ConsoleSaveFileSplit.h" #endif @@ -78,6 +83,438 @@ bool MinecraftServer::s_slowQueuePacketSent = false; unordered_map MinecraftServer::ironTimers; +static void PrintConsoleLine(const wchar_t *prefix, const wstring &message) +{ + wprintf(L"%ls%ls\n", prefix, message.c_str()); + fflush(stdout); +} + +static bool TryParseIntValue(const wstring &text, int &value) +{ + std::wistringstream stream(text); + stream >> value; + return !stream.fail() && stream.eof(); +} + +static vector SplitConsoleCommand(const wstring &command) +{ + vector tokens; + std::wistringstream stream(command); + wstring token; + while (stream >> token) + { + tokens.push_back(token); + } + return tokens; +} + +static wstring JoinConsoleCommandTokens(const vector &tokens, size_t startIndex) +{ + wstring joined; + for (size_t i = startIndex; i < tokens.size(); ++i) + { + if (!joined.empty()) joined += L" "; + joined += tokens[i]; + } + return joined; +} + +static shared_ptr FindPlayerByName(PlayerList *playerList, const wstring &name) +{ + if (playerList == NULL) return nullptr; + + for (size_t i = 0; i < playerList->players.size(); ++i) + { + shared_ptr player = playerList->players[i]; + if (player != NULL && equalsIgnoreCase(player->getName(), name)) + { + return player; + } + } + + return nullptr; +} + +static void SetAllLevelTimes(MinecraftServer *server, int value) +{ + for (unsigned int i = 0; i < server->levels.length; ++i) + { + if (server->levels[i] != NULL) + { + server->levels[i]->setDayTime(value); + } + } +} + +static bool ExecuteConsoleCommand(MinecraftServer *server, const wstring &rawCommand) +{ + if (server == NULL) + return false; + + wstring command = trimString(rawCommand); + if (command.empty()) + return true; + + if (command[0] == L'/') + { + command = trimString(command.substr(1)); + } + + vector tokens = SplitConsoleCommand(command); + if (tokens.empty()) + return true; + + const wstring action = toLower(tokens[0]); + PlayerList *playerList = server->getPlayers(); + + if (action == L"help" || action == L"?") + { + server->info(L"Commands: help, stop, list, say , save-all, time , weather [seconds], tp , give [amount] [aux], enchant [level], kill "); + return true; + } + + if (action == L"stop") + { + server->info(L"Stopping server..."); + MinecraftServer::HaltServer(); + return true; + } + + if (action == L"list") + { + wstring playerNames = (playerList != NULL) ? playerList->getPlayerNames() : L""; + if (playerNames.empty()) playerNames = L"(none)"; + server->info(L"Players (" + _toString((playerList != NULL) ? playerList->getPlayerCount() : 0) + L"): " + playerNames); + return true; + } + + if (action == L"say") + { + if (tokens.size() < 2) + { + server->warn(L"Usage: say "); + return false; + } + + wstring message = L"[Server] " + JoinConsoleCommandTokens(tokens, 1); + if (playerList != NULL) + { + playerList->broadcastAll(shared_ptr(new ChatPacket(message))); + } + server->info(message); + return true; + } + + if (action == L"save-all") + { + if (playerList != NULL) + { + playerList->saveAll(NULL, false); + } + server->info(L"World saved."); + return true; + } + + if (action == L"time") + { + if (tokens.size() < 2) + { + server->warn(L"Usage: time set | time add "); + return false; + } + + if (toLower(tokens[1]) == L"add") + { + if (tokens.size() < 3) + { + server->warn(L"Usage: time add "); + return false; + } + + int delta = 0; + if (!TryParseIntValue(tokens[2], delta)) + { + server->warn(L"Invalid tick value: " + tokens[2]); + return false; + } + + for (unsigned int i = 0; i < server->levels.length; ++i) + { + if (server->levels[i] != NULL) + { + server->levels[i]->setDayTime(server->levels[i]->getDayTime() + delta); + } + } + + server->info(L"Added " + _toString(delta) + L" ticks."); + return true; + } + + wstring timeValue = toLower(tokens[1]); + if (timeValue == L"set") + { + if (tokens.size() < 3) + { + server->warn(L"Usage: time set "); + return false; + } + timeValue = toLower(tokens[2]); + } + + int targetTime = 0; + if (timeValue == L"day") + { + targetTime = 0; + } + else if (timeValue == L"night") + { + targetTime = 12500; + } + else if (!TryParseIntValue(timeValue, targetTime)) + { + server->warn(L"Invalid time value: " + timeValue); + return false; + } + + SetAllLevelTimes(server, targetTime); + server->info(L"Time set to " + _toString(targetTime) + L"."); + return true; + } + + if (action == L"weather") + { + if (tokens.size() < 2) + { + server->warn(L"Usage: weather [seconds]"); + return false; + } + + int durationSeconds = 600; + if (tokens.size() >= 3 && !TryParseIntValue(tokens[2], durationSeconds)) + { + server->warn(L"Invalid duration: " + tokens[2]); + return false; + } + + if (server->levels[0] == NULL) + { + server->warn(L"The overworld is not loaded."); + return false; + } + + LevelData *levelData = server->levels[0]->getLevelData(); + int duration = durationSeconds * SharedConstants::TICKS_PER_SECOND; + levelData->setRainTime(duration); + levelData->setThunderTime(duration); + + wstring weather = toLower(tokens[1]); + if (weather == L"clear") + { + levelData->setRaining(false); + levelData->setThundering(false); + } + else if (weather == L"rain") + { + levelData->setRaining(true); + levelData->setThundering(false); + } + else if (weather == L"thunder") + { + levelData->setRaining(true); + levelData->setThundering(true); + } + else + { + server->warn(L"Usage: weather [seconds]"); + return false; + } + + server->info(L"Weather set to " + weather + L"."); + return true; + } + + if (action == L"tp" || action == L"teleport") + { + if (tokens.size() < 3) + { + server->warn(L"Usage: tp "); + return false; + } + + shared_ptr subject = FindPlayerByName(playerList, tokens[1]); + shared_ptr destination = FindPlayerByName(playerList, tokens[2]); + if (subject == NULL) + { + server->warn(L"Unknown player: " + tokens[1]); + return false; + } + if (destination == NULL) + { + server->warn(L"Unknown player: " + tokens[2]); + return false; + } + if (subject->level->dimension->id != destination->level->dimension->id || !subject->isAlive()) + { + server->warn(L"Teleport failed because the players are not in the same dimension or the source player is dead."); + return false; + } + + subject->ride(nullptr); + subject->connection->teleport(destination->x, destination->y, destination->z, destination->yRot, destination->xRot); + server->info(L"Teleported " + subject->getName() + L" to " + destination->getName() + L"."); + return true; + } + + if (action == L"give") + { + if (tokens.size() < 3) + { + server->warn(L"Usage: give [amount] [aux]"); + return false; + } + + shared_ptr player = FindPlayerByName(playerList, tokens[1]); + if (player == NULL) + { + server->warn(L"Unknown player: " + tokens[1]); + return false; + } + + int itemId = 0; + int amount = 1; + int aux = 0; + if (!TryParseIntValue(tokens[2], itemId)) + { + server->warn(L"Invalid item id: " + tokens[2]); + return false; + } + if (tokens.size() >= 4 && !TryParseIntValue(tokens[3], amount)) + { + server->warn(L"Invalid amount: " + tokens[3]); + return false; + } + if (tokens.size() >= 5 && !TryParseIntValue(tokens[4], aux)) + { + server->warn(L"Invalid aux value: " + tokens[4]); + return false; + } + if (itemId <= 0 || Item::items[itemId] == NULL) + { + server->warn(L"Unknown item id: " + _toString(itemId)); + return false; + } + if (amount <= 0) + { + server->warn(L"Amount must be positive."); + return false; + } + + shared_ptr itemInstance(new ItemInstance(itemId, amount, aux)); + shared_ptr drop = player->drop(itemInstance); + if (drop != NULL) + { + drop->throwTime = 0; + } + server->info(L"Gave item " + _toString(itemId) + L" x" + _toString(amount) + L" to " + player->getName() + L"."); + return true; + } + + if (action == L"enchant") + { + if (tokens.size() < 3) + { + server->warn(L"Usage: enchant [level]"); + return false; + } + + shared_ptr player = FindPlayerByName(playerList, tokens[1]); + if (player == NULL) + { + server->warn(L"Unknown player: " + tokens[1]); + return false; + } + + int enchantmentId = 0; + int enchantmentLevel = 1; + if (!TryParseIntValue(tokens[2], enchantmentId)) + { + server->warn(L"Invalid enchantment id: " + tokens[2]); + return false; + } + if (tokens.size() >= 4 && !TryParseIntValue(tokens[3], enchantmentLevel)) + { + server->warn(L"Invalid enchantment level: " + tokens[3]); + return false; + } + + shared_ptr selectedItem = player->getSelectedItem(); + if (selectedItem == NULL) + { + server->warn(L"The player is not holding an item."); + return false; + } + + Enchantment *enchantment = Enchantment::enchantments[enchantmentId]; + if (enchantment == NULL) + { + server->warn(L"Unknown enchantment id: " + _toString(enchantmentId)); + return false; + } + if (!enchantment->canEnchant(selectedItem)) + { + server->warn(L"That enchantment cannot be applied to the selected item."); + return false; + } + + if (enchantmentLevel < enchantment->getMinLevel()) enchantmentLevel = enchantment->getMinLevel(); + if (enchantmentLevel > enchantment->getMaxLevel()) enchantmentLevel = enchantment->getMaxLevel(); + + if (selectedItem->hasTag()) + { + ListTag *enchantmentTags = selectedItem->getEnchantmentTags(); + if (enchantmentTags != NULL) + { + for (int i = 0; i < enchantmentTags->size(); i++) + { + int type = enchantmentTags->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_ID); + if (Enchantment::enchantments[type] != NULL && !Enchantment::enchantments[type]->isCompatibleWith(enchantment)) + { + server->warn(L"That enchantment conflicts with an existing enchantment on the selected item."); + return false; + } + } + } + } + + selectedItem->enchant(enchantment, enchantmentLevel); + server->info(L"Enchanted " + player->getName() + L"'s held item with " + _toString(enchantmentId) + L" " + _toString(enchantmentLevel) + L"."); + return true; + } + + if (action == L"kill") + { + if (tokens.size() < 2) + { + server->warn(L"Usage: kill "); + return false; + } + + shared_ptr player = FindPlayerByName(playerList, tokens[1]); + if (player == NULL) + { + server->warn(L"Unknown player: " + tokens[1]); + return false; + } + + player->hurt(DamageSource::outOfWorld, 3.4e38f); + server->info(L"Killed " + player->getName() + L"."); + return true; + } + + server->warn(L"Unknown command: " + command); + return false; +} + MinecraftServer::MinecraftServer() { // 4J - added initialisers @@ -107,12 +544,14 @@ MinecraftServer::MinecraftServer() forceGameType = false; commandDispatcher = new ServerCommandDispatcher(); + InitializeCriticalSection(&m_consoleInputCS); DispenserBootstrap::bootStrap(); } MinecraftServer::~MinecraftServer() { + DeleteCriticalSection(&m_consoleInputCS); } bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DWORD initSettings, bool findSeed) @@ -150,6 +589,15 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW #endif settings = new Settings(new File(L"server.properties")); + app.SetGameHostOption(eGameHostOption_Difficulty, settings->getInt(L"difficulty", app.GetGameHostOption(eGameHostOption_Difficulty))); + app.SetGameHostOption(eGameHostOption_GameType, settings->getInt(L"gamemode", app.GetGameHostOption(eGameHostOption_GameType))); + app.SetGameHostOption(eGameHostOption_Structures, settings->getBoolean(L"generate-structures", app.GetGameHostOption(eGameHostOption_Structures) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_BonusChest, settings->getBoolean(L"bonus-chest", app.GetGameHostOption(eGameHostOption_BonusChest) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_PvP, settings->getBoolean(L"pvp", app.GetGameHostOption(eGameHostOption_PvP) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_TrustPlayers, settings->getBoolean(L"trust-players", app.GetGameHostOption(eGameHostOption_TrustPlayers) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_FireSpreads, settings->getBoolean(L"fire-spreads", app.GetGameHostOption(eGameHostOption_FireSpreads) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_TNT, settings->getBoolean(L"tnt", app.GetGameHostOption(eGameHostOption_TNT) > 0) ? 1 : 0); + app.DebugPrintf("\n*** SERVER SETTINGS ***\n"); app.DebugPrintf("ServerSettings: host-friends-only is %s\n",(app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0)?"on":"off"); app.DebugPrintf("ServerSettings: game-type is %s\n",(app.GetGameHostOption(eGameHostOption_GameType)==0)?"Survival Mode":"Creative Mode"); @@ -169,11 +617,11 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW setAnimals(settings->getBoolean(L"spawn-animals", true)); setNpcsEnabled(settings->getBoolean(L"spawn-npcs", true)); - setPvpAllowed(app.GetGameHostOption( eGameHostOption_PvP )>0?true:false); // settings->getBoolean(L"pvp", true); + setPvpAllowed(app.GetGameHostOption( eGameHostOption_PvP )>0?true:false); // 4J Stu - We should never have hacked clients flying when they shouldn't be like the PC version, so enable flying always // Fix for #46612 - TU5: Code: Multiplayer: A client can be banned for flying when accidentaly being blown by dynamite - setFlightAllowed(true); //settings->getBoolean(L"allow-flight", false); + setFlightAllowed(settings->getBoolean(L"allow-flight", true)); // 4J Stu - Enabling flight to stop it kicking us when we use it #ifdef _DEBUG_MENUS_ENABLED @@ -1707,17 +2155,23 @@ void MinecraftServer::tick() void MinecraftServer::handleConsoleInput(const wstring& msg, ConsoleInputSource *source) { + EnterCriticalSection(&m_consoleInputCS); consoleInput.push_back(new ConsoleInput(msg, source)); + LeaveCriticalSection(&m_consoleInputCS); } void MinecraftServer::handleConsoleInputs() { - while (consoleInput.size() > 0) + vector pendingInputs; + EnterCriticalSection(&m_consoleInputCS); + pendingInputs.swap(consoleInput); + LeaveCriticalSection(&m_consoleInputCS); + + for (size_t i = 0; i < pendingInputs.size(); ++i) { - AUTO_VAR(it, consoleInput.begin()); - ConsoleInput *input = *it; - consoleInput.erase(it); - // commands->handleCommand(input); // 4J - removed - TODO - do we want equivalent of console commands? + ConsoleInput *input = pendingInputs[i]; + ExecuteConsoleCommand(this, input->msg); + delete input; } } @@ -1750,10 +2204,12 @@ File *MinecraftServer::getFile(const wstring& name) void MinecraftServer::info(const wstring& string) { + PrintConsoleLine(L"[INFO] ", string); } void MinecraftServer::warn(const wstring& string) { + PrintConsoleLine(L"[WARN] ", string); } wstring MinecraftServer::getConsoleName() diff --git a/Minecraft.Client/MinecraftServer.h b/Minecraft.Client/MinecraftServer.h index 5f33fa853..ec0218e01 100644 --- a/Minecraft.Client/MinecraftServer.h +++ b/Minecraft.Client/MinecraftServer.h @@ -103,6 +103,7 @@ private: // vector tickables = new ArrayList(); // 4J - removed CommandDispatcher *commandDispatcher; vector consoleInput; // 4J - was synchronizedList - TODO - investigate + CRITICAL_SECTION m_consoleInputCS; public: bool onlineMode; bool animals; diff --git a/Minecraft.Client/Settings.cpp b/Minecraft.Client/Settings.cpp index 89946773b..4223b0016 100644 --- a/Minecraft.Client/Settings.cpp +++ b/Minecraft.Client/Settings.cpp @@ -1,18 +1,90 @@ #include "stdafx.h" #include "Settings.h" +#include "..\Minecraft.World\File.h" #include "..\Minecraft.World\StringHelpers.h" +#include + +static wstring ParsePropertyText(const string &text) +{ + return trimString(convStringToWstring(text)); +} + +static bool TryParseBoolean(const wstring &text, bool defaultValue) +{ + wstring lowered = toLower(trimString(text)); + if (lowered == L"true" || lowered == L"1" || lowered == L"yes" || lowered == L"on") + return true; + if (lowered == L"false" || lowered == L"0" || lowered == L"no" || lowered == L"off") + return false; + return defaultValue; +} -// 4J - TODO - serialise/deserialise from file Settings::Settings(File *file) { + if (file != NULL) + { + filePath = file->getPath(); + } + + if (filePath.empty()) + return; + + std::ifstream stream(wstringtofilename(filePath), std::ios::in | std::ios::binary); + if (!stream.is_open()) + return; + + string line; + while (std::getline(stream, line)) + { + if (!line.empty() && line[line.size() - 1] == '\r') + line.erase(line.size() - 1); + + if (line.size() >= 3 && + (unsigned char)line[0] == 0xEF && + (unsigned char)line[1] == 0xBB && + (unsigned char)line[2] == 0xBF) + { + line.erase(0, 3); + } + + size_t commentPos = line.find_first_of("#;"); + if (commentPos != string::npos && line.find_first_not_of(" \t") == commentPos) + continue; + + size_t separatorPos = line.find('='); + if (separatorPos == string::npos) + continue; + + wstring key = ParsePropertyText(line.substr(0, separatorPos)); + if (key.empty()) + continue; + + wstring value = ParsePropertyText(line.substr(separatorPos + 1)); + properties[key] = value; + } } void Settings::generateNewProperties() { + saveProperties(); } void Settings::saveProperties() { + if (filePath.empty()) + return; + + std::ofstream stream(wstringtofilename(filePath), std::ios::out | std::ios::binary | std::ios::trunc); + if (!stream.is_open()) + return; + + stream << "# MinecraftConsoles dedicated server properties\r\n"; + for (unordered_map::const_iterator it = properties.begin(); it != properties.end(); ++it) + { + string key = string(wstringtochararray(it->first)); + string value = string(wstringtochararray(it->second)); + stream << key << "=" << value << "\r\n"; + } } wstring Settings::getString(const wstring& key, const wstring& defaultValue) @@ -39,17 +111,17 @@ bool Settings::getBoolean(const wstring& key, bool defaultValue) { if(properties.find(key) == properties.end()) { - properties[key] = _toString(defaultValue); + properties[key] = defaultValue ? L"true" : L"false"; saveProperties(); } MemSect(35); - bool retval = _fromString(properties[key]); + bool retval = TryParseBoolean(properties[key], defaultValue); MemSect(0); return retval; } void Settings::setBooleanAndSave(const wstring& key, bool value) { - properties[key] = _toString(value); + properties[key] = value ? L"true" : L"false"; saveProperties(); } \ No newline at end of file diff --git a/Minecraft.Client/Settings.h b/Minecraft.Client/Settings.h index b6a2c0181..4a3c130be 100644 --- a/Minecraft.Client/Settings.h +++ b/Minecraft.Client/Settings.h @@ -7,8 +7,8 @@ class Settings // public static Logger logger = Logger.getLogger("Minecraft"); // private Properties properties = new Properties(); private: - unordered_map properties; // 4J - TODO was Properties type, will need to implement something we can serialise/deserialise too - //File *file; + unordered_map properties; + wstring filePath; public: Settings(File *file); @@ -18,4 +18,4 @@ public: int getInt(const wstring& key, int defaultValue); bool getBoolean(const wstring& key, bool defaultValue); void setBooleanAndSave(const wstring& key, bool value); -}; +}; \ No newline at end of file diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp index d3ea1c3aa..ca1d62af7 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp @@ -51,6 +51,9 @@ bool g_Win64MultiplayerHost = false; bool g_Win64MultiplayerJoin = false; int g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT; char g_Win64MultiplayerIP[256] = "127.0.0.1"; +bool g_Win64DedicatedServer = false; +int g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT; +char g_Win64DedicatedServerBindIP[256] = ""; bool WinsockNetLayer::Initialize() { @@ -139,7 +142,7 @@ void WinsockNetLayer::Shutdown() } } -bool WinsockNetLayer::HostGame(int port) +bool WinsockNetLayer::HostGame(int port, const char* bindIp) { if (!s_initialized && !Initialize()) return false; @@ -159,15 +162,19 @@ bool WinsockNetLayer::HostGame(int port) hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; + hints.ai_flags = (bindIp == NULL || bindIp[0] == 0) ? AI_PASSIVE : 0; char portStr[16]; sprintf_s(portStr, "%d", port); - int iResult = getaddrinfo(NULL, portStr, &hints, &result); + const char* resolvedBindIp = (bindIp != NULL && bindIp[0] != 0) ? bindIp : NULL; + int iResult = getaddrinfo(resolvedBindIp, portStr, &hints, &result); if (iResult != 0) { - app.DebugPrintf("getaddrinfo failed: %d\n", iResult); + app.DebugPrintf("getaddrinfo failed for %s:%d - %d\n", + resolvedBindIp != NULL ? resolvedBindIp : "*", + port, + iResult); return false; } @@ -206,7 +213,9 @@ bool WinsockNetLayer::HostGame(int port) s_acceptThread = CreateThread(NULL, 0, AcceptThreadProc, NULL, 0, NULL); - app.DebugPrintf("Win64 LAN: Hosting on port %d\n", port); + app.DebugPrintf("Win64 LAN: Hosting on %s:%d\n", + resolvedBindIp != NULL ? resolvedBindIp : "*", + port); return true; } @@ -908,4 +917,4 @@ DWORD WINAPI WinsockNetLayer::DiscoveryThreadProc(LPVOID param) return 0; } -#endif \ No newline at end of file +#endif diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h index 029dd0a73..fd1280f7d 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h @@ -65,7 +65,7 @@ public: static bool Initialize(); static void Shutdown(); - static bool HostGame(int port); + static bool HostGame(int port, const char* bindIp = NULL); static bool JoinGame(const char* ip, int port); static bool SendToSmallId(BYTE targetSmallId, const void* data, int dataSize); @@ -147,5 +147,8 @@ extern bool g_Win64MultiplayerHost; extern bool g_Win64MultiplayerJoin; extern int g_Win64MultiplayerPort; extern char g_Win64MultiplayerIP[256]; +extern bool g_Win64DedicatedServer; +extern int g_Win64DedicatedServerPort; +extern char g_Win64DedicatedServerBindIP[256]; #endif diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 3d5eec00f..ed6781a31 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -4,7 +4,9 @@ #include "stdafx.h" #include +#include #include +#include #include "GameConfig\Minecraft.spa.h" #include "..\MinecraftServer.h" #include "..\LocalPlayer.h" @@ -34,6 +36,7 @@ #include "Sentient\SentientManager.h" #include "..\..\Minecraft.World\IntCache.h" #include "..\Textures.h" +#include "..\Settings.h" #include "Resource.h" #include "..\..\Minecraft.World\compression.h" #include "..\..\Minecraft.World\OldChunkStorage.h" @@ -95,6 +98,148 @@ wchar_t g_Win64UsernameW[17] = { 0 }; static bool g_isFullscreen = false; static WINDOWPLACEMENT g_wpPrev = { sizeof(g_wpPrev) }; +struct Win64LaunchOptions +{ + int screenMode; + bool serverMode; +}; + +static void CopyWideArgToAnsi(LPCWSTR source, char* dest, size_t destSize) +{ + if (destSize == 0) + return; + + dest[0] = 0; + if (source == NULL) + return; + + WideCharToMultiByte(CP_ACP, 0, source, -1, dest, (int)destSize, NULL, NULL); + dest[destSize - 1] = 0; +} + +static void ApplyScreenMode(int screenMode) +{ + switch (screenMode) + { + case 1: + g_iScreenWidth = 1280; + g_iScreenHeight = 720; + break; + case 2: + g_iScreenWidth = 640; + g_iScreenHeight = 480; + break; + case 3: + g_iScreenWidth = 720; + g_iScreenHeight = 408; + break; + default: + break; + } +} + +static Win64LaunchOptions ParseLaunchOptions() +{ + Win64LaunchOptions options = {}; + options.screenMode = 0; + options.serverMode = false; + + g_Win64MultiplayerJoin = false; + g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT; + g_Win64DedicatedServer = false; + g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT; + g_Win64DedicatedServerBindIP[0] = 0; + + int argc = 0; + LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); + if (argv == NULL) + return options; + + if (argc > 1 && lstrlenW(argv[1]) == 1) + { + if (argv[1][0] >= L'1' && argv[1][0] <= L'3') + options.screenMode = argv[1][0] - L'0'; + } + + for (int i = 1; i < argc; ++i) + { + if (_wcsicmp(argv[i], L"-server") == 0) + { + options.serverMode = true; + break; + } + } + + g_Win64DedicatedServer = options.serverMode; + + for (int i = 1; i < argc; ++i) + { + if (_wcsicmp(argv[i], L"-name") == 0 && (i + 1) < argc) + { + CopyWideArgToAnsi(argv[++i], g_Win64Username, sizeof(g_Win64Username)); + } + else if (_wcsicmp(argv[i], L"-ip") == 0 && (i + 1) < argc) + { + char ipBuf[256]; + CopyWideArgToAnsi(argv[++i], ipBuf, sizeof(ipBuf)); + if (options.serverMode) + { + strncpy_s(g_Win64DedicatedServerBindIP, sizeof(g_Win64DedicatedServerBindIP), ipBuf, _TRUNCATE); + } + else + { + strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), ipBuf, _TRUNCATE); + g_Win64MultiplayerJoin = true; + } + } + else if (_wcsicmp(argv[i], L"-port") == 0 && (i + 1) < argc) + { + wchar_t* endPtr = NULL; + long port = wcstol(argv[++i], &endPtr, 10); + if (endPtr != argv[i] && *endPtr == 0 && port > 0 && port <= 65535) + { + if (options.serverMode) + g_Win64DedicatedServerPort = (int)port; + else + g_Win64MultiplayerPort = (int)port; + } + } + } + + LocalFree(argv); + return options; +} + +static BOOL WINAPI HeadlessServerCtrlHandler(DWORD ctrlType) +{ + switch (ctrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_SHUTDOWN_EVENT: + app.m_bShutdown = true; + MinecraftServer::HaltServer(); + return TRUE; + default: + return FALSE; + } +} + +static void SetupHeadlessServerConsole() +{ + if (AllocConsole()) + { + FILE* stream = NULL; + freopen_s(&stream, "CONIN$", "r", stdin); + freopen_s(&stream, "CONOUT$", "w", stdout); + freopen_s(&stream, "CONOUT$", "w", stderr); + SetConsoleTitleA("Minecraft Server"); + } + + SetConsoleCtrlHandler(HeadlessServerCtrlHandler, TRUE); +} + void DefineActions(void) { // The app needs to define the actions required, and the possible mappings for these @@ -722,6 +867,225 @@ void CleanupDevice() if( g_pd3dDevice ) g_pd3dDevice->Release(); } +static Minecraft* InitialiseMinecraftRuntime() +{ + app.loadMediaArchive(); + + RenderManager.Initialise(g_pd3dDevice, g_pSwapChain); + + app.loadStringTable(); + ui.init(g_pd3dDevice, g_pImmediateContext, g_pRenderTargetView, g_pDepthStencilView, g_iScreenWidth, g_iScreenHeight); + + InputManager.Initialise(1, 3, MINECRAFT_ACTION_MAX, ACTION_MAX_MENU); + KMInput.Init(g_hWnd); + DefineActions(); + InputManager.SetJoypadMapVal(0, 0); + InputManager.SetKeyRepeatRate(0.3f, 0.2f); + + ProfileManager.Initialise(TITLEID_MINECRAFT, + app.m_dwOfferID, + PROFILE_VERSION_10, + NUM_PROFILE_VALUES, + NUM_PROFILE_SETTINGS, + dwProfileSettingsA, + app.GAME_DEFINED_PROFILE_DATA_BYTES * XUSER_MAX_COUNT, + &app.uiGameDefinedDataChangedBitmask + ); + ProfileManager.SetDefaultOptionsCallback(&CConsoleMinecraftApp::DefaultOptionsCallback, (LPVOID)&app); + + g_NetworkManager.Initialise(); + + for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++) + { + IQNet::m_player[i].m_smallId = (BYTE)i; + IQNet::m_player[i].m_isRemote = false; + IQNet::m_player[i].m_isHostPlayer = (i == 0); + swprintf_s(IQNet::m_player[i].m_gamertag, 32, L"Player%d", i); + } + wcscpy_s(IQNet::m_player[0].m_gamertag, 32, g_Win64UsernameW); + + WinsockNetLayer::Initialize(); + + ProfileManager.SetDebugFullOverride(true); + + Tesselator::CreateNewThreadStorage(1024 * 1024); + AABB::CreateNewThreadStorage(); + Vec3::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + Compression::CreateNewThreadStorage(); + OldChunkStorage::CreateNewThreadStorage(); + Level::enableLightingCache(); + Tile::CreateNewThreadStorage(); + + Minecraft::main(); + Minecraft* pMinecraft = Minecraft::GetInstance(); + if (pMinecraft == NULL) + return NULL; + + app.InitGameSettings(); + app.InitialiseTips(); + + pMinecraft->options->set(Options::Option::MUSIC, 1.0f); + pMinecraft->options->set(Options::Option::SOUND, 1.0f); + + return pMinecraft; +} + +static int HeadlessServerConsoleThreadProc(void* lpParameter) +{ + UNREFERENCED_PARAMETER(lpParameter); + + std::string line; + while (!app.m_bShutdown) + { + if (!std::getline(std::cin, line)) + { + if (std::cin.eof()) + { + break; + } + + std::cin.clear(); + Sleep(50); + continue; + } + + wstring command = trimString(convStringToWstring(line)); + if (command.empty()) + continue; + + MinecraftServer* server = MinecraftServer::getInstance(); + if (server != NULL) + { + server->handleConsoleInput(command, server); + } + } + + return 0; +} + +static int RunHeadlessServer() +{ + SetupHeadlessServerConsole(); + + Settings serverSettings(new File(L"server.properties")); + wstring configuredBindIp = serverSettings.getString(L"server-ip", L""); + + const char* bindIp = "*"; + if (g_Win64DedicatedServerBindIP[0] != 0) + { + bindIp = g_Win64DedicatedServerBindIP; + } + else if (!configuredBindIp.empty()) + { + bindIp = wstringtochararray(configuredBindIp); + } + + const int port = g_Win64DedicatedServerPort > 0 ? g_Win64DedicatedServerPort : serverSettings.getInt(L"server-port", WIN64_NET_DEFAULT_PORT); + + printf("Starting headless server on %s:%d\n", bindIp, port); + fflush(stdout); + + Minecraft* pMinecraft = InitialiseMinecraftRuntime(); + if (pMinecraft == NULL) + { + fprintf(stderr, "Failed to initialise the Minecraft runtime.\n"); + return 1; + } + + app.SetGameHostOption(eGameHostOption_Difficulty, serverSettings.getInt(L"difficulty", 1)); + app.SetGameHostOption(eGameHostOption_Gamertags, 1); + app.SetGameHostOption(eGameHostOption_GameType, serverSettings.getInt(L"gamemode", 0)); + app.SetGameHostOption(eGameHostOption_LevelType, 0); + app.SetGameHostOption(eGameHostOption_Structures, serverSettings.getBoolean(L"generate-structures", true) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_BonusChest, serverSettings.getBoolean(L"bonus-chest", false) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_PvP, serverSettings.getBoolean(L"pvp", true) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_TrustPlayers, serverSettings.getBoolean(L"trust-players", true) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_FireSpreads, serverSettings.getBoolean(L"fire-spreads", true) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_TNT, serverSettings.getBoolean(L"tnt", true) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_HostCanFly, 1); + app.SetGameHostOption(eGameHostOption_HostCanChangeHunger, 1); + app.SetGameHostOption(eGameHostOption_HostCanBeInvisible, 1); + app.SetGameHostOption(eGameHostOption_MobGriefing, 1); + app.SetGameHostOption(eGameHostOption_KeepInventory, 0); + app.SetGameHostOption(eGameHostOption_DoMobSpawning, 1); + app.SetGameHostOption(eGameHostOption_DoMobLoot, 1); + app.SetGameHostOption(eGameHostOption_DoTileDrops, 1); + app.SetGameHostOption(eGameHostOption_NaturalRegeneration, 1); + app.SetGameHostOption(eGameHostOption_DoDaylightCycle, 1); + + MinecraftServer::resetFlags(); + g_NetworkManager.HostGame(0, false, true, MINECRAFT_NET_MAX_PLAYERS, 0); + + if (!WinsockNetLayer::IsActive()) + { + fprintf(stderr, "Failed to bind the server socket on %s:%d.\n", bindIp, port); + return 1; + } + + g_NetworkManager.FakeLocalPlayerJoined(); + + NetworkGameInitData* param = new NetworkGameInitData(); + param->seed = 0; + param->settings = app.GetGameHostOption(eGameHostOption_All); + + g_NetworkManager.ServerStoppedCreate(true); + g_NetworkManager.ServerReadyCreate(true); + + C4JThread* thread = new C4JThread(&CGameNetworkManager::ServerThreadProc, param, "Server", 256 * 1024); + thread->SetProcessor(CPU_CORE_SERVER); + thread->Run(); + + g_NetworkManager.ServerReadyWait(); + g_NetworkManager.ServerReadyDestroy(); + + if (MinecraftServer::serverHalted()) + { + fprintf(stderr, "The server halted during startup.\n"); + g_NetworkManager.LeaveGame(false); + return 1; + } + + app.SetGameStarted(true); + g_NetworkManager.DoWork(); + + printf("Server ready on %s:%d\n", bindIp, port); + printf("Type 'help' for server commands.\n"); + fflush(stdout); + + C4JThread* consoleThread = new C4JThread(&HeadlessServerConsoleThreadProc, NULL, "Server console", 128 * 1024); + consoleThread->Run(); + + MSG msg = { 0 }; + while (WM_QUIT != msg.message && !app.m_bShutdown && !MinecraftServer::serverHalted()) + { + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + continue; + } + + app.UpdateTime(); + ProfileManager.Tick(); + StorageManager.Tick(); + RenderManager.Tick(); + ui.tick(); + g_NetworkManager.DoWork(); + app.HandleXuiActions(); + + Sleep(10); + } + + printf("Stopping server...\n"); + fflush(stdout); + + app.m_bShutdown = true; + MinecraftServer::HaltServer(); + g_NetworkManager.LeaveGame(false); + return 0; +} + int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, @@ -742,70 +1106,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, SetProcessDPIAware(); g_iScreenWidth = GetSystemMetrics(SM_CXSCREEN); g_iScreenHeight = GetSystemMetrics(SM_CYSCREEN); - - if(lpCmdLine) - { - if(lpCmdLine[0] == '1') - { - g_iScreenWidth = 1280; - g_iScreenHeight = 720; - } - else if(lpCmdLine[0] == '2') - { - g_iScreenWidth = 640; - g_iScreenHeight = 480; - } - else if(lpCmdLine[0] == '3') - { - // Vita - g_iScreenWidth = 720; - g_iScreenHeight = 408; - - // Vita native - //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); - } - - 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); - } - } + Win64LaunchOptions launchOptions = ParseLaunchOptions(); + ApplyScreenMode(launchOptions.screenMode); if (g_Win64Username[0] == 0) { @@ -821,7 +1123,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, MyRegisterClass(hInstance); // Perform application initialization: - if (!InitInstance (hInstance, nCmdShow)) + if (!InitInstance (hInstance, launchOptions.serverMode ? SW_HIDE : nCmdShow)) { return FALSE; } @@ -834,6 +1136,13 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, return 0; } + if (launchOptions.serverMode) + { + int serverResult = RunHeadlessServer(); + CleanupDevice(); + return serverResult; + } + #if 0 // Main message loop MSG msg = {0}; @@ -886,202 +1195,12 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } #endif - app.loadMediaArchive(); - - RenderManager.Initialise(g_pd3dDevice, g_pSwapChain); - - app.loadStringTable(); - ui.init(g_pd3dDevice,g_pImmediateContext,g_pRenderTargetView,g_pDepthStencilView,g_iScreenWidth,g_iScreenHeight); - - //////////////// - // Initialise // - //////////////// - - // Set the number of possible joypad layouts that the user can switch between, and the number of actions - InputManager.Initialise(1,3,MINECRAFT_ACTION_MAX, ACTION_MAX_MENU); - - // Initialize keyboard/mouse input - KMInput.Init(g_hWnd); - - // Set the default joypad action mappings for Minecraft - DefineActions(); - InputManager.SetJoypadMapVal(0,0); - InputManager.SetKeyRepeatRate(0.3f,0.2f); - - // Initialise the profile manager with the game Title ID, Offer ID, a profile version number, and the number of profile values and settings - ProfileManager.Initialise(TITLEID_MINECRAFT, - app.m_dwOfferID, - PROFILE_VERSION_10, - NUM_PROFILE_VALUES, - NUM_PROFILE_SETTINGS, - dwProfileSettingsA, - app.GAME_DEFINED_PROFILE_DATA_BYTES*XUSER_MAX_COUNT, - &app.uiGameDefinedDataChangedBitmask - ); -#if 0 - // register the awards - ProfileManager.RegisterAward(eAward_TakingInventory, ACHIEVEMENT_01, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_GettingWood, ACHIEVEMENT_02, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_Benchmarking, ACHIEVEMENT_03, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_TimeToMine, ACHIEVEMENT_04, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_HotTopic, ACHIEVEMENT_05, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_AquireHardware, ACHIEVEMENT_06, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_TimeToFarm, ACHIEVEMENT_07, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_BakeBread, ACHIEVEMENT_08, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_TheLie, ACHIEVEMENT_09, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_GettingAnUpgrade, ACHIEVEMENT_10, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_DeliciousFish, ACHIEVEMENT_11, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_OnARail, ACHIEVEMENT_12, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_TimeToStrike, ACHIEVEMENT_13, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_MonsterHunter, ACHIEVEMENT_14, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_CowTipper, ACHIEVEMENT_15, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_WhenPigsFly, ACHIEVEMENT_16, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_LeaderOfThePack, ACHIEVEMENT_17, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_MOARTools, ACHIEVEMENT_18, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_DispenseWithThis, ACHIEVEMENT_19, eAwardType_Achievement); - ProfileManager.RegisterAward(eAward_InToTheNether, ACHIEVEMENT_20, eAwardType_Achievement); - - ProfileManager.RegisterAward(eAward_mine100Blocks, GAMER_PICTURE_GAMERPIC1, eAwardType_GamerPic,false,app.GetStringTable(),IDS_AWARD_TITLE,IDS_AWARD_GAMERPIC1,IDS_CONFIRM_OK); - ProfileManager.RegisterAward(eAward_kill10Creepers, GAMER_PICTURE_GAMERPIC2, eAwardType_GamerPic,false,app.GetStringTable(),IDS_AWARD_TITLE,IDS_AWARD_GAMERPIC2,IDS_CONFIRM_OK); - - ProfileManager.RegisterAward(eAward_eatPorkChop, AVATARASSETAWARD_PORKCHOP_TSHIRT, eAwardType_AvatarItem,false,app.GetStringTable(),IDS_AWARD_TITLE,IDS_AWARD_AVATAR1,IDS_CONFIRM_OK); - ProfileManager.RegisterAward(eAward_play100Days, AVATARASSETAWARD_WATCH, eAwardType_AvatarItem,false,app.GetStringTable(),IDS_AWARD_TITLE,IDS_AWARD_AVATAR2,IDS_CONFIRM_OK); - ProfileManager.RegisterAward(eAward_arrowKillCreeper, AVATARASSETAWARD_CAP, eAwardType_AvatarItem,false,app.GetStringTable(),IDS_AWARD_TITLE,IDS_AWARD_AVATAR3,IDS_CONFIRM_OK); - - ProfileManager.RegisterAward(eAward_socialPost, 0, eAwardType_Theme,false,app.GetStringTable(),IDS_AWARD_TITLE,IDS_AWARD_THEME,IDS_CONFIRM_OK,THEME_NAME,THEME_FILESIZE); - - // Rich Presence init - number of presences, number of contexts - ProfileManager.RichPresenceInit(4,1); - ProfileManager.RegisterRichPresenceContext(CONTEXT_GAME_STATE); - - // initialise the storage manager with a default save display name, a Minimum save size, and a callback for displaying the saving message - StorageManager.Init(app.GetString(IDS_DEFAULT_SAVENAME),"savegame.dat",FIFTY_ONE_MB,&CConsoleMinecraftApp::DisplaySavingMessage,(LPVOID)&app); - // Set up the global title storage path - StorageManager.StoreTMSPathName(); - - // set a function to be called when there's a sign in change, so we can exit a level if the primary player signs out - ProfileManager.SetSignInChangeCallback(&CConsoleMinecraftApp::SignInChangeCallback,(LPVOID)&app); - - // set a function to be called when the ethernet is disconnected, so we can back out if required - ProfileManager.SetNotificationsCallback(&CConsoleMinecraftApp::NotificationsCallback,(LPVOID)&app); - -#endif - // Set a callback for the default player options to be set - when there is no profile data for the player - ProfileManager.SetDefaultOptionsCallback(&CConsoleMinecraftApp::DefaultOptionsCallback,(LPVOID)&app); -#if 0 - // Set a callback to deal with old profile versions needing updated to new versions - ProfileManager.SetOldProfileVersionCallback(&CConsoleMinecraftApp::OldProfileVersionCallback,(LPVOID)&app); - - // Set a callback for when there is a read error on profile data - ProfileManager.SetProfileReadErrorCallback(&CConsoleMinecraftApp::ProfileReadErrorCallback,(LPVOID)&app); - -#endif - // QNet needs to be setup after profile manager, as we do not want its Notify listener to handle - // XN_SYS_SIGNINCHANGED notifications. This does mean that we need to have a callback in the - // ProfileManager for XN_LIVE_INVITE_ACCEPTED for QNet. - g_NetworkManager.Initialise(); - - for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++) + Minecraft *pMinecraft = InitialiseMinecraftRuntime(); + if (pMinecraft == NULL) { - IQNet::m_player[i].m_smallId = (BYTE)i; - IQNet::m_player[i].m_isRemote = false; - IQNet::m_player[i].m_isHostPlayer = (i == 0); - swprintf_s(IQNet::m_player[i].m_gamertag, 32, L"Player%d", i); + CleanupDevice(); + return 1; } - extern wchar_t g_Win64UsernameW[17]; - wcscpy_s(IQNet::m_player[0].m_gamertag, 32, g_Win64UsernameW); - - WinsockNetLayer::Initialize(); - - // 4J-PB moved further down - //app.InitGameSettings(); - - // debug switch to trial version - ProfileManager.SetDebugFullOverride(true); - -#if 0 - //ProfileManager.AddDLC(2); - StorageManager.SetDLCPackageRoot("DLCDrive"); - StorageManager.RegisterMarketplaceCountsCallback(&CConsoleMinecraftApp::MarketplaceCountsCallback,(LPVOID)&app); - // Kinect ! - - if(XNuiGetHardwareStatus()!=0) - { - // If the Kinect Sensor is not physically connected, this function returns 0. - NuiInitialize(NUI_INITIALIZE_FLAG_USES_HIGH_QUALITY_COLOR | NUI_INITIALIZE_FLAG_USES_DEPTH | - NUI_INITIALIZE_FLAG_EXTRAPOLATE_FLOOR_PLANE | NUI_INITIALIZE_FLAG_USES_FITNESS | NUI_INITIALIZE_FLAG_NUI_GUIDE_DISABLED | NUI_INITIALIZE_FLAG_SUPPRESS_AUTOMATIC_UI,NUI_INITIALIZE_DEFAULT_HARDWARE_THREAD ); - } - - // Sentient ! - hr = TelemetryManager->Init(); - -#endif - // Initialise TLS for tesselator, for this main thread - Tesselator::CreateNewThreadStorage(1024*1024); - // Initialise TLS for AABB and Vec3 pools, for this main thread - AABB::CreateNewThreadStorage(); - Vec3::CreateNewThreadStorage(); - IntCache::CreateNewThreadStorage(); - Compression::CreateNewThreadStorage(); - OldChunkStorage::CreateNewThreadStorage(); - Level::enableLightingCache(); - Tile::CreateNewThreadStorage(); - - Minecraft::main(); - Minecraft *pMinecraft=Minecraft::GetInstance(); - - app.InitGameSettings(); - -#if 0 - //bool bDisplayPauseMenu=false; - - // set the default gamma level - float fVal=50.0f*327.68f; - RenderManager.UpdateGamma((unsigned short)fVal); - - // load any skins - //app.AddSkinsToMemoryTextureFiles(); - - // set the achievement text for a trial achievement, now we have the string table loaded - ProfileManager.SetTrialTextStringTable(app.GetStringTable(),IDS_CONFIRM_OK, IDS_CONFIRM_CANCEL); - ProfileManager.SetTrialAwardText(eAwardType_Achievement,IDS_UNLOCK_TITLE,IDS_UNLOCK_ACHIEVEMENT_TEXT); - ProfileManager.SetTrialAwardText(eAwardType_GamerPic,IDS_UNLOCK_TITLE,IDS_UNLOCK_GAMERPIC_TEXT); - ProfileManager.SetTrialAwardText(eAwardType_AvatarItem,IDS_UNLOCK_TITLE,IDS_UNLOCK_AVATAR_TEXT); - ProfileManager.SetTrialAwardText(eAwardType_Theme,IDS_UNLOCK_TITLE,IDS_UNLOCK_THEME_TEXT); - ProfileManager.SetUpsellCallback(&app.UpsellReturnedCallback,&app); - - // Set up a debug character press sequence -#ifndef _FINAL_BUILD - app.SetDebugSequence("LRLRYYY"); -#endif - - // Initialise the social networking manager. - CSocialManager::Instance()->Initialise(); - - // Update the base scene quick selects now that the minecraft class exists - //CXuiSceneBase::UpdateScreenSettings(0); -#endif - app.InitialiseTips(); -#if 0 - - DWORD initData=0; - -#ifndef _FINAL_BUILD -#ifndef _DEBUG -#pragma message(__LOC__"Need to define the _FINAL_BUILD before submission") -#endif -#endif - - // Set the default sound levels - pMinecraft->options->set(Options::Option::MUSIC,1.0f); - pMinecraft->options->set(Options::Option::SOUND,1.0f); - - app.NavigateToScene(XUSER_INDEX_ANY,eUIScene_Intro,&initData); -#endif - - // Set the default sound levels - pMinecraft->options->set(Options::Option::MUSIC,1.0f); - pMinecraft->options->set(Options::Option::SOUND,1.0f); //app.TemporaryCreateGameStart(); @@ -1637,4 +1756,4 @@ void MemPixStuff() PIXAddNamedCounter(((float)allSectsTotal)/(4096.0f),"MemSect total pages"); } -#endif \ No newline at end of file +#endif diff --git a/Minecraft.World/Minecraft.World.vcxproj b/Minecraft.World/Minecraft.World.vcxproj index 58880529c..7ab7c4ce5 100644 --- a/Minecraft.World/Minecraft.World.vcxproj +++ b/Minecraft.World/Minecraft.World.vcxproj @@ -1257,6 +1257,7 @@ false true Default + /FS %(AdditionalOptions) true @@ -1355,7 +1356,7 @@ true true true - /Ob3 + /FS /Ob3 %(AdditionalOptions) true @@ -4957,4 +4958,4 @@ - \ No newline at end of file + diff --git a/README.md b/README.md index e6583e768..a84ee508c 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,22 @@ This feature is based on [LCEMP](https://github.com/LCEMP/LCEMP/) | 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 | +| `-server` | Launches a headless server instead of the client | +| `-ip
` | Client mode: manually connect to an IP. Server mode: override the bind IP from `server.properties` | +| `-port ` | Client mode: override the join port. Server mode: override the listen port from `server.properties` | Example: ``` Minecraft.Client.exe -name Steve -ip 192.168.0.25 -port 25565 ``` +Headless server example: +``` +Minecraft.Client.exe -server -ip 0.0.0.0 -port 25565 +``` + +The headless server also reads and writes `server.properties` in the working directory. If `-ip` / `-port` are omitted in `-server` mode, it falls back to `server-ip` / `server-port` from that file. Dedicated-server host options such as `trust-players`, `pvp`, `fire-spreads`, `tnt`, `difficulty`, `gamemode`, `spawn-animals`, and `spawn-npcs` are persisted there as well. + ## Controls (Keyboard & Mouse) - **Movement**: `W` `A` `S` `D` From 5d9f6f6347c3bf21a0049f33401ec2a1c0cd6cbf Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Wed, 4 Mar 2026 17:47:16 +0800 Subject: [PATCH 52/68] docs: mark V-Sync note as WIP in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a84ee508c..c14b59132 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ This project contains the source code of Minecraft Legacy Console Edition v1.6.0 - Fixed compilation and execution in both Debug and Release mode on Windows using Visual Studio 2022 - Added support for keyboard and mouse input - Added fullscreen mode support (toggle using F11) -- Disabled V-Sync for better performance +- (WIP) Disabled V-Sync for better performance - Added a high-resolution timer path on Windows for smoother high-FPS gameplay timing - Device's screen resolution will be used as the game resolution instead of using a fixed resolution (1920x1080) - LAN Multiplayer & Discovery From ef9b6fd500dfabd9463267b0dd9e29577eea8a2b Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Wed, 4 Mar 2026 17:48:19 +0800 Subject: [PATCH 53/68] docs: fix table formatting in README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c14b59132..23cfc2530 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,12 @@ This feature is based on [LCEMP](https://github.com/LCEMP/LCEMP/) ### Launch Arguments -| Argument | Description | -|--------------------|----------------------------------------------------------------------------------------------------------------| -| `-name ` | Sets your in-game username | -| `-server` | Launches a headless server instead of the client | -| `-ip
` | Client mode: manually connect to an IP. Server mode: override the bind IP from `server.properties` | -| `-port ` | Client mode: override the join port. Server mode: override the listen port from `server.properties` | +| Argument | Description | +|--------------------|-----------------------------------------------------------------------------------------------------| +| `-name ` | Sets your in-game username | +| `-server` | Launches a headless server instead of the client | +| `-ip
` | Client mode: manually connect to an IP. Server mode: override the bind IP from `server.properties` | +| `-port ` | Client mode: override the join port. Server mode: override the listen port from `server.properties` | Example: ``` From 0993e628abc392821cb9049f4730cbb8f3d70522 Mon Sep 17 00:00:00 2001 From: Loki Rautio Date: Wed, 4 Mar 2026 05:44:16 -0600 Subject: [PATCH 54/68] Disable automatic Windows account username This could reveal someone's private information on a livestream or video. We need a long-term username implementation --- Minecraft.Client/Windows64/Windows64_Minecraft.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index ed6781a31..c36ceee5c 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -1112,8 +1112,9 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, if (g_Win64Username[0] == 0) { DWORD sz = 17; - if (!GetUserNameA(g_Win64Username, &sz)) - strncpy_s(g_Win64Username, 17, "Player", _TRUNCATE); + //if (!GetUserNameA(g_Win64Username, &sz)) + // todo: SET USERNAMES PROPERLY + strncpy_s(g_Win64Username, 17, "Player", _TRUNCATE); g_Win64Username[16] = 0; } From ea17b152b70122d8c58783fd9a67f45ecd8330cf Mon Sep 17 00:00:00 2001 From: Loki Rautio Date: Wed, 4 Mar 2026 05:57:56 -0600 Subject: [PATCH 55/68] Restore username.txt loading without conflict Still allows for -name launch argument, but restores old expected behavior --- .../Windows64/Windows64_Minecraft.cpp | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index c36ceee5c..ec89feb18 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -1106,16 +1106,48 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, SetProcessDPIAware(); g_iScreenWidth = GetSystemMetrics(SM_CXSCREEN); g_iScreenHeight = GetSystemMetrics(SM_CYSCREEN); + + // Load username from username.txt + 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); + } + + // Load stuff from launch options, including username Win64LaunchOptions launchOptions = ParseLaunchOptions(); ApplyScreenMode(launchOptions.screenMode); + // If no username, let's fall back if (g_Win64Username[0] == 0) { - DWORD sz = 17; - //if (!GetUserNameA(g_Win64Username, &sz)) - // todo: SET USERNAMES PROPERLY - strncpy_s(g_Win64Username, 17, "Player", _TRUNCATE); - g_Win64Username[16] = 0; + // Default username will be "Player" + strncpy_s(g_Win64Username, sizeof(g_Win64Username), "Player", _TRUNCATE); } MultiByteToWideChar(CP_ACP, 0, g_Win64Username, -1, g_Win64UsernameW, 17); From 0e9d8629f1211e25f650f62e5c58aebd3caea031 Mon Sep 17 00:00:00 2001 From: Loki Rautio Date: Wed, 4 Mar 2026 05:59:48 -0600 Subject: [PATCH 56/68] Update README with info about username.txt --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 23cfc2530..7578f159f 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ This project contains the source code of Minecraft Legacy Console Edition v1.6.0 - Added a high-resolution timer path on Windows for smoother high-FPS gameplay timing - Device's screen resolution will be used as the game resolution instead of using a fixed resolution (1920x1080) - LAN Multiplayer & Discovery +- Added persistent username system via "username.txt" ## Multiplayer From 52d9bcc9a9fbccd995ab59847c005f98751ebe0a Mon Sep 17 00:00:00 2001 From: Loki Rautio Date: Wed, 4 Mar 2026 06:14:06 -0600 Subject: [PATCH 57/68] Enable Stained Glass in Creative Menu --- Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp index 973020db2..bda2228e3 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -210,7 +210,6 @@ void IUIScene_CreativeMenu::staticCtor() ITEM_AUX(Tile::woolCarpet_Id,13) // Green ITEM_AUX(Tile::woolCarpet_Id,12) // Brown -#if 0 ITEM_AUX(Tile::stained_glass_Id,14) // Red ITEM_AUX(Tile::stained_glass_Id,1) // Orange ITEM_AUX(Tile::stained_glass_Id,4) // Yellow @@ -244,7 +243,6 @@ void IUIScene_CreativeMenu::staticCtor() ITEM_AUX(Tile::stained_glass_pane_Id,15) // Black ITEM_AUX(Tile::stained_glass_pane_Id,13) // Green ITEM_AUX(Tile::stained_glass_pane_Id,12) // Brown -#endif #ifndef _CONTENT_PACKAGE DEF(eCreativeInventory_ArtToolsDecorations) From 2d430798a56b00f321ef92a416797d6ce069d7b0 Mon Sep 17 00:00:00 2001 From: Marlian <84173858+MCbabel@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:47:43 +0100 Subject: [PATCH 58/68] Fix creative inventory crash with Art Tools debug option (#399) Fix vector out-of-bounds crash when scrolling the potions tab in the creative inventory with Art Tools debug enabled. - Fix getPageCount() returning total rows instead of scrollable pages in Art Tools mode - Fix off-by-one boundary check in populateMenu() for both static and debug group loops (< should be <=) Fixes #386 --- Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp index bda2228e3..0099cea67 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -880,7 +880,7 @@ void IUIScene_CreativeMenu::TabSpec::populateMenu(AbstractContainerMenu *menu, i for(; currentGroup < m_staticGroupsCount; ++currentGroup) { int size = categoryGroups[m_staticGroupsA[currentGroup]].size(); - if( currentIndex + size < startIndex) + if( currentIndex + size <= startIndex) { currentIndex += size; continue; @@ -930,7 +930,7 @@ void IUIScene_CreativeMenu::TabSpec::populateMenu(AbstractContainerMenu *menu, i for(; currentGroup < m_debugGroupsCount; ++currentGroup) { int size = categoryGroups[m_debugGroupsA[currentGroup]].size(); - if( currentIndex + size < startIndex) + if( currentIndex + size <= startIndex) { currentIndex += size; continue; @@ -971,7 +971,9 @@ unsigned int IUIScene_CreativeMenu::TabSpec::getPageCount() #ifndef _CONTENT_PACKAGE if(app.DebugArtToolsOn()) { - return (int)ceil((float)(m_staticItems + m_debugItems) / m_staticPerPage); + int totalItems = m_staticItems + m_debugItems; + const int totalRows = (totalItems + columns - 1) / columns; + return std::max(1, totalRows - rows + 1); } else #endif From ca5fde56fed613a8f45767868636e2321b03d3f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davi=20Eler=20Magalh=C3=A3es?= Date: Wed, 4 Mar 2026 12:07:37 -0300 Subject: [PATCH 59/68] Fix: Sorted the item list in the debug overlay (#340) * Fix: Sorted the item list in the debug overlay * revert show all files to false * Revert ShowAllFiles by removing it * removed extra line * Adressed PR review changes * Replaced push_back with emplace_back * Removed redundant emplace_back --- .../Common/UI/UIScene_DebugOverlay.cpp | 38 ++++++++++++++----- .../Minecraft.Client.vcxproj.user | 2 +- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Minecraft.Client/Common/UI/UIScene_DebugOverlay.cpp b/Minecraft.Client/Common/UI/UIScene_DebugOverlay.cpp index 8e45c3246..6514e6ece 100644 --- a/Minecraft.Client/Common/UI/UIScene_DebugOverlay.cpp +++ b/Minecraft.Client/Common/UI/UIScene_DebugOverlay.cpp @@ -39,17 +39,37 @@ UIScene_DebugOverlay::UIScene_DebugOverlay(int iPad, void *initData, UILayer *pa m_buttonSetNight.init(L"Set Night", eControl_SetNight); m_buttonListItems.init(eControl_Items); + + // Sort items alphabetically + std::vector> sortedItems; + for (size_t i = 0; i < Item::items.length; ++i) + { + if (Item::items[i] != NULL) + { + sortedItems.emplace_back(std::wstring(app.GetString(Item::items[i]->getDescriptionId())), i); + } + } + for (size_t i = 1; i < sortedItems.size(); ++i) + { + auto key = sortedItems[i]; + int j = i - 1; + while (j >= 0 && sortedItems[j].first > key.first) + { + sortedItems[j + 1] = sortedItems[j]; + --j; + } + sortedItems[j + 1] = key; + } + + // Populate the list in sorted order int listId = 0; - for(unsigned int i = 0; i < Item::items.length; ++i) - { - if(Item::items[i] != NULL) - { - m_itemIds.push_back(i); - m_buttonListItems.addItem(app.GetString(Item::items[i]->getDescriptionId()), listId); - ++listId; - } - } + for (const auto& entry : sortedItems) + { + m_itemIds.push_back(entry.second); + m_buttonListItems.addItem(entry.first.c_str(), listId); + ++listId; + } m_buttonListEnchantments.init(eControl_Enchantments); diff --git a/Minecraft.Client/Minecraft.Client.vcxproj.user b/Minecraft.Client/Minecraft.Client.vcxproj.user index 09a7ebc4a..24ca62f80 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj.user +++ b/Minecraft.Client/Minecraft.Client.vcxproj.user @@ -19,4 +19,4 @@ $(SolutionDir)$(Platform)\$(Configuration)\ WindowsLocalDebugger - \ No newline at end of file + From 2be856a2d447ab758e02bfeaaa9cac5e1114dbfc Mon Sep 17 00:00:00 2001 From: ModMaker101 <119018978+ModMaker101@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:43:29 -0500 Subject: [PATCH 60/68] Fix Chunk destructor segfault using smart pointers #112 (#414) --- Minecraft.Client/Chunk.cpp | 20 +++++++++----------- Minecraft.Client/Chunk.h | 6 +++--- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Minecraft.Client/Chunk.cpp b/Minecraft.Client/Chunk.cpp index d039227bf..63cd05017 100644 --- a/Minecraft.Client/Chunk.cpp +++ b/Minecraft.Client/Chunk.cpp @@ -52,7 +52,7 @@ Chunk::Chunk(Level *level, LevelRenderer::rteMap &globalRenderableTileEntities, : globalRenderableTileEntities( &globalRenderableTileEntities ), globalRenderableTileEntities_cs(&globalRenderableTileEntities_cs) { clipChunk->visible = false; - bb = NULL; + bb = nullptr; id = 0; this->level = level; @@ -101,15 +101,15 @@ void Chunk::setPos(int x, int y, int z) float g = 6.0f; // 4J - changed to just set the value rather than make a new one, if we've already created storage - if( bb == NULL ) + if( !bb ) { - bb = AABB::newPermanent(-g, -g, -g, XZSIZE+g, SIZE+g, XZSIZE+g); + bb = shared_ptr(AABB::newPermanent(-g, -g, -g, XZSIZE+g, SIZE+g, XZSIZE+g)); } - else - { + else + { // 4J MGH - bounds are relative to the position now, so the AABB will be setup already, either above, or from the tesselator bounds. // bb->set(-g, -g, -g, SIZE+g, SIZE+g, SIZE+g); - } + } clipChunk->aabb[0] = bb->x0 + x; clipChunk->aabb[1] = bb->y0 + y; clipChunk->aabb[2] = bb->z0 + z; @@ -154,6 +154,7 @@ void Chunk::translateToPos() Chunk::Chunk() { + bb = nullptr; } void Chunk::makeCopyForRebuild(Chunk *source) @@ -998,7 +999,7 @@ int Chunk::getList(int layer) void Chunk::cull(Culler *culler) { - clipChunk->visible = culler->isVisible(bb); + clipChunk->visible = culler->isVisible(bb.get()); } void Chunk::renderBB() @@ -1027,10 +1028,7 @@ void Chunk::clearDirty() #endif } -Chunk::~Chunk() -{ - delete bb; -} +Chunk::~Chunk() = default; bool Chunk::emptyFlagSet(int layer) { diff --git a/Minecraft.Client/Chunk.h b/Minecraft.Client/Chunk.h index f7947156f..e0ae016ef 100644 --- a/Minecraft.Client/Chunk.h +++ b/Minecraft.Client/Chunk.h @@ -46,11 +46,11 @@ public: int xRender, yRender, zRender; int xRenderOffs, yRenderOffs, zRenderOffs; - int xm, ym, zm; - AABB *bb; + int xm, ym, zm; + shared_ptr bb; ClipChunk *clipChunk; - int id; + int id; //public: // vector > renderableTileEntities; // 4J - removed From cea1084978bbf525c983fba67dc44f3718f667e4 Mon Sep 17 00:00:00 2001 From: Alezito2008 <92759854+Alezito2008@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:28:20 -0300 Subject: [PATCH 61/68] Fix duplicated stained glass when art tools enabled (#426) --- .../Common/UI/IUIScene_CreativeMenu.cpp | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp index 0099cea67..544fedc0f 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -263,40 +263,6 @@ void IUIScene_CreativeMenu::staticCtor() BuildFirework(list, FireworksItem::TYPE_CREEPER, DyePowderItem::BLUE, 1, true, false); BuildFirework(list, FireworksItem::TYPE_STAR, DyePowderItem::YELLOW, 1, false, false); BuildFirework(list, FireworksItem::TYPE_BIG, DyePowderItem::WHITE, 1, true, true); - - ITEM_AUX(Tile::stained_glass_Id,14) // Red - ITEM_AUX(Tile::stained_glass_Id,1) // Orange - ITEM_AUX(Tile::stained_glass_Id,4) // Yellow - ITEM_AUX(Tile::stained_glass_Id,5) // Lime - ITEM_AUX(Tile::stained_glass_Id,3) // Light Blue - ITEM_AUX(Tile::stained_glass_Id,9) // Cyan - ITEM_AUX(Tile::stained_glass_Id,11) // Blue - ITEM_AUX(Tile::stained_glass_Id,10) // Purple - ITEM_AUX(Tile::stained_glass_Id,2) // Magenta - ITEM_AUX(Tile::stained_glass_Id,6) // Pink - ITEM_AUX(Tile::stained_glass_Id,0) // White - ITEM_AUX(Tile::stained_glass_Id,8) // Light Gray - ITEM_AUX(Tile::stained_glass_Id,7) // Gray - ITEM_AUX(Tile::stained_glass_Id,15) // Black - ITEM_AUX(Tile::stained_glass_Id,13) // Green - ITEM_AUX(Tile::stained_glass_Id,12) // Brown - - ITEM_AUX(Tile::stained_glass_pane_Id,14) // Red - ITEM_AUX(Tile::stained_glass_pane_Id,1) // Orange - ITEM_AUX(Tile::stained_glass_pane_Id,4) // Yellow - ITEM_AUX(Tile::stained_glass_pane_Id,5) // Lime - ITEM_AUX(Tile::stained_glass_pane_Id,3) // Light Blue - ITEM_AUX(Tile::stained_glass_pane_Id,9) // Cyan - ITEM_AUX(Tile::stained_glass_pane_Id,11) // Blue - ITEM_AUX(Tile::stained_glass_pane_Id,10) // Purple - ITEM_AUX(Tile::stained_glass_pane_Id,2) // Magenta - ITEM_AUX(Tile::stained_glass_pane_Id,6) // Pink - ITEM_AUX(Tile::stained_glass_pane_Id,0) // White - ITEM_AUX(Tile::stained_glass_pane_Id,8) // Light Gray - ITEM_AUX(Tile::stained_glass_pane_Id,7) // Gray - ITEM_AUX(Tile::stained_glass_pane_Id,15) // Black - ITEM_AUX(Tile::stained_glass_pane_Id,13) // Green - ITEM_AUX(Tile::stained_glass_pane_Id,12) // Brown } #endif From 464cf91f4c88b9df7921e0e730ac638b356c7138 Mon Sep 17 00:00:00 2001 From: ModMaker101 <119018978+ModMaker101@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:28:37 -0500 Subject: [PATCH 62/68] Prevent door sounds from playing twice #392 (#425) --- Minecraft.Client/MultiPlayerGameMode.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/MultiPlayerGameMode.cpp b/Minecraft.Client/MultiPlayerGameMode.cpp index e7611b37c..9470a49af 100644 --- a/Minecraft.Client/MultiPlayerGameMode.cpp +++ b/Minecraft.Client/MultiPlayerGameMode.cpp @@ -370,7 +370,9 @@ bool MultiPlayerGameMode::useItemOn(shared_ptr player, Level *level, sha // are meant to be directly caused by this. If we don't do this, then the sounds never happen as the tile's use method is only called on the // server, and that won't allow any sounds that are directly made, or broadcast back level events to us that would make the sound, since we are // the source of the event. - if( ( t > 0 ) && ( !bTestUseOnly ) && player->isAllowedToUse(Tile::tiles[t]) ) + // --------------------------------------------------------------------------------- + // Only call soundOnly version if we didn't already call the tile's use method above + if( !didSomething && ( t > 0 ) && ( !bTestUseOnly ) && player->isAllowedToUse(Tile::tiles[t]) ) { Tile::tiles[t]->use(level, x, y, z, player, face, clickX, clickY, clickZ, true); } From 206c6800f29ac608eef00775ee6a52c12f6f86d6 Mon Sep 17 00:00:00 2001 From: ModMaker101 <119018978+ModMaker101@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:41:59 -0500 Subject: [PATCH 63/68] Fix incorrect distance comparison return value (#432) --- Minecraft.World/NearestAttackableTargetGoal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.World/NearestAttackableTargetGoal.cpp b/Minecraft.World/NearestAttackableTargetGoal.cpp index 1dbf587ed..7ddd88950 100644 --- a/Minecraft.World/NearestAttackableTargetGoal.cpp +++ b/Minecraft.World/NearestAttackableTargetGoal.cpp @@ -34,7 +34,7 @@ bool NearestAttackableTargetGoal::DistComp::operator() (shared_ptr e1, s double distSqr2 = source->distanceToSqr(e2); if (distSqr1 < distSqr2) return true; if (distSqr1 > distSqr2) return false; - return true; + return false; } NearestAttackableTargetGoal::NearestAttackableTargetGoal(PathfinderMob *mob, const type_info& targetType, int randomInterval, bool mustSee, bool mustReach /*= false*/, EntitySelector *entitySelector /* =NULL */) From ac03b88a907bb49f5159f08de07398f3fce32991 Mon Sep 17 00:00:00 2001 From: Alezito2008 <92759854+Alezito2008@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:42:32 -0300 Subject: [PATCH 64/68] Fix:Prevent horse spawner crash (#433) --- Minecraft.World/EntityHorse.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.World/EntityHorse.cpp b/Minecraft.World/EntityHorse.cpp index 2d67d21bd..d72500873 100644 --- a/Minecraft.World/EntityHorse.cpp +++ b/Minecraft.World/EntityHorse.cpp @@ -479,7 +479,7 @@ void EntityHorse::createInventory() void EntityHorse::updateEquipment() { - if (!level->isClientSide) + if (level && !level->isClientSide) { setSaddled(inventory->getItem(INV_SLOT_SADDLE) != NULL); if (canWearArmor()) From 1dc8a005ed111463c22c17b487e5ec8a3e2d30f3 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Wed, 4 Mar 2026 21:19:40 +0800 Subject: [PATCH 65/68] refactor: refactor KBM input code --- .../Common/Tutorial/ChoiceTask.cpp | 4 +- Minecraft.Client/Common/Tutorial/InfoTask.cpp | 4 +- .../UI/IUIScene_AbstractContainerMenu.cpp | 42 +- .../UI/IUIScene_AbstractContainerMenu.h | 12 + Minecraft.Client/Common/UI/UIControl.cpp | 2 - Minecraft.Client/Common/UI/UIControl.h | 2 +- Minecraft.Client/Common/UI/UIController.cpp | 254 +++++++++- Minecraft.Client/Common/UI/UIController.h | 4 + Minecraft.Client/Common/UI/UIGroup.h | 2 - Minecraft.Client/Common/UI/UIScene.h | 4 +- .../UI/UIScene_AbstractContainerMenu.cpp | 166 ++----- .../Common/UI/UIScene_AbstractContainerMenu.h | 8 +- .../Common/UI/UIScene_LoadOrJoinMenu.cpp | 8 + .../UI/UIScene_SettingsGraphicsMenu.cpp | 38 +- Minecraft.Client/Input.cpp | 147 +++--- Minecraft.Client/Input.h | 9 +- Minecraft.Client/LocalPlayer.cpp | 33 +- Minecraft.Client/Minecraft.cpp | 148 +++--- Minecraft.Client/Screen.cpp | 8 +- .../Windows64/KeyboardMouseInput.cpp | 454 +++++++++++------- .../Windows64/KeyboardMouseInput.h | 170 ++++--- .../Windows64/Windows64_Minecraft.cpp | 220 +++++---- Minecraft.Client/stubs.cpp | 103 +++- Minecraft.Client/stubs.h | 33 +- README.md | 1 - 25 files changed, 1187 insertions(+), 689 deletions(-) diff --git a/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp b/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp index f42b3ee02..1ea34ace6 100644 --- a/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp +++ b/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp @@ -58,7 +58,7 @@ bool ChoiceTask::isCompleted() #ifdef _WINDOWS64 if (!m_bConfirmMappingComplete && (InputManager.GetValue(xboxPad, m_iConfirmMapping) > 0 - || KMInput.IsKeyDown(VK_RETURN))) + || g_KBMInput.IsKeyDown(VK_RETURN))) #else if (!m_bConfirmMappingComplete && InputManager.GetValue(xboxPad, m_iConfirmMapping) > 0) @@ -70,7 +70,7 @@ bool ChoiceTask::isCompleted() #ifdef _WINDOWS64 if (!m_bCancelMappingComplete && (InputManager.GetValue(xboxPad, m_iCancelMapping) > 0 - || KMInput.IsKeyDown('B'))) + || g_KBMInput.IsKeyDown('B'))) #else if (!m_bCancelMappingComplete && InputManager.GetValue(xboxPad, m_iCancelMapping) > 0) diff --git a/Minecraft.Client/Common/Tutorial/InfoTask.cpp b/Minecraft.Client/Common/Tutorial/InfoTask.cpp index 43a10357c..748093e5e 100644 --- a/Minecraft.Client/Common/Tutorial/InfoTask.cpp +++ b/Minecraft.Client/Common/Tutorial/InfoTask.cpp @@ -7,7 +7,7 @@ #include "TutorialConstraints.h" #include "InfoTask.h" #include "..\..\..\Minecraft.World\Material.h" -#include "..\..\KeyboardMouseInput.h" +#include "..\..\Windows64\KeyboardMouseInput.h" InfoTask::InfoTask(Tutorial *tutorial, int descriptionId, int promptId /*= -1*/, bool requiresUserInput /*= false*/, int iMapping /*= 0*/, ETelemetryChallenges telemetryEvent /*= eTelemetryTutorial_NoEvent*/) @@ -67,7 +67,7 @@ bool InfoTask::isCompleted() if(!current) { #ifdef _WINDOWS64 - if (InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0 || KMInput.IsKeyDown(VK_SPACE)) + if (InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0 || g_KBMInput.IsKeyDown(VK_SPACE)) #else if( InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0) #endif diff --git a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp index 667431b2b..882584211 100644 --- a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp @@ -13,6 +13,12 @@ #include #endif +#ifdef _WINDOWS64 +#include "..\..\Windows64\KeyboardMouseInput.h" + +SavedInventoryCursorPos g_savedInventoryCursorPos = { 0.0f, 0.0f, false }; +#endif + IUIScene_AbstractContainerMenu::IUIScene_AbstractContainerMenu() { m_menu = NULL; @@ -474,6 +480,34 @@ void IUIScene_AbstractContainerMenu::onMouseTick() } #endif +#ifdef _WINDOWS64 + if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) + { + int deltaX = g_KBMInput.GetMouseDeltaX(); + int deltaY = g_KBMInput.GetMouseDeltaY(); + + extern HWND g_hWnd; + RECT rc; + GetClientRect(g_hWnd, &rc); + int winW = rc.right - rc.left; + int winH = rc.bottom - rc.top; + + if (winW > 0 && winH > 0) + { + float scaleX = (float)getMovieWidth() / (float)winW; + float scaleY = (float)getMovieHeight() / (float)winH; + + vPointerPos.x += (float)deltaX * scaleX; + vPointerPos.y += (float)deltaY * scaleY; + } + + if (deltaX != 0 || deltaY != 0) + { + bStickInput = true; + } + } +#endif + // Determine which slot the pointer is currently over. ESceneSection eSectionUnderPointer = eSectionNone; int iNewSlotX = -1; @@ -694,7 +728,11 @@ void IUIScene_AbstractContainerMenu::onMouseTick() // If there is no stick input, and we are over a slot, then snap pointer to slot centre. // 4J - TomK - only if this particular component allows so! - if(!m_bPointerDrivenByMouse && CanHaveFocus(eSectionUnderPointer)) +#ifdef _WINDOWS64 + if((g_KBMInput.IsMouseGrabbed() || !g_KBMInput.IsKBMActive()) && CanHaveFocus(eSectionUnderPointer)) +#else + if(CanHaveFocus(eSectionUnderPointer)) +#endif { vPointerPos.x = vSnapPos.x; vPointerPos.y = vSnapPos.y; @@ -1329,7 +1367,7 @@ bool IUIScene_AbstractContainerMenu::handleKeyDown(int iPad, int iAction, bool b // Standard left click buttonNum = 0; - if (KMInput.IsKeyDown(VK_SHIFT)) + if (g_KBMInput.IsKeyDown(VK_LSHIFT)) { { validKeyPress = TRUE; diff --git a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h index fe79bf190..4877cfce8 100644 --- a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h +++ b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h @@ -1,5 +1,15 @@ #pragma once +#ifdef _WINDOWS64 +struct SavedInventoryCursorPos +{ + float x; + float y; + bool hasSavedPos; +}; +extern SavedInventoryCursorPos g_savedInventoryCursorPos; +#endif + // Uncomment to enable tap input detection to jump 1 slot. Doesn't work particularly well yet, and I feel the system does not need it. // Would probably be required if we decide to slow down the pointer movement. // 4J Stu - There was a request to be able to navigate the scenes with the dpad, so I have used much of the TAP_DETECTION @@ -265,4 +275,6 @@ protected: public: virtual int getPad() = 0; + virtual int getMovieWidth() = 0; + virtual int getMovieHeight() = 0; }; diff --git a/Minecraft.Client/Common/UI/UIControl.cpp b/Minecraft.Client/Common/UI/UIControl.cpp index ec2e13d8e..be267ada6 100644 --- a/Minecraft.Client/Common/UI/UIControl.cpp +++ b/Minecraft.Client/Common/UI/UIControl.cpp @@ -42,7 +42,6 @@ bool UIControl::setupControl(UIScene *scene, IggyValuePath *parent, const string return res; } -#ifdef __PSVITA__ void UIControl::UpdateControl() { F64 fx, fy, fwidth, fheight; @@ -55,7 +54,6 @@ void UIControl::UpdateControl() m_width = (S32)Math::round(fwidth); m_height = (S32)Math::round(fheight); } -#endif // __PSVITA__ void UIControl::ReInit() { diff --git a/Minecraft.Client/Common/UI/UIControl.h b/Minecraft.Client/Common/UI/UIControl.h index e37f04de5..29770df28 100644 --- a/Minecraft.Client/Common/UI/UIControl.h +++ b/Minecraft.Client/Common/UI/UIControl.h @@ -61,8 +61,8 @@ public: UIControl(); virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); -#ifdef __PSVITA__ void UpdateControl(); +#ifdef __PSVITA__ void setHidden(bool bHidden) {m_bHidden=bHidden;} bool getHidden(void) {return m_bHidden;} #endif diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp index 6ac2f9ba3..01ab49ba4 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -2,6 +2,7 @@ #include "UIController.h" #include "UI.h" #include "UIScene.h" +#include "UIControl_Slider.h" #include "..\..\..\Minecraft.World\StringHelpers.h" #include "..\..\LocalPlayer.h" #include "..\..\DLCTexturePack.h" @@ -11,6 +12,9 @@ #include "..\..\EnderDragonRenderer.h" #include "..\..\MultiPlayerLocalPlayer.h" #include "UIFontData.h" +#ifdef _WINDOWS64 +#include "..\..\Windows64\KeyboardMouseInput.h" +#endif #ifdef __PSVITA__ #include #endif @@ -52,6 +56,21 @@ bool UIController::ms_bReloadSkinCSInitialised = false; DWORD UIController::m_dwTrialTimerLimitSecs=DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME; +#ifdef _WINDOWS64 +static UIControl_Slider *FindSliderById(UIScene *pScene, int sliderId) +{ + vector *controls = pScene->GetControls(); + if (!controls) return NULL; + for (size_t i = 0; i < controls->size(); ++i) + { + UIControl *ctrl = (*controls)[i]; + if (ctrl && ctrl->getControlType() == UIControl::eSlider && ctrl->getId() == sliderId) + return (UIControl_Slider *)ctrl; + } + return NULL; +} +#endif + static void RADLINK WarningCallback(void *user_callback_data, Iggy *player, IggyResult code, const char *message) { //enum IggyResult{ IGGY_RESULT_SUCCESS = 0, IGGY_RESULT_Warning_None = 0, @@ -216,6 +235,10 @@ UIController::UIController() m_currentRenderViewport = C4JRender::VIEWPORT_TYPE_FULLSCREEN; m_bCustomRenderPosition = false; m_winUserIndex = 0; + m_mouseDraggingSliderScene = eUIScene_COUNT; + m_mouseDraggingSliderId = -1; + m_lastHoverMouseX = -1; + m_lastHoverMouseY = -1; m_accumulatedTicks = 0; m_lastUiSfx = 0; @@ -761,6 +784,168 @@ void UIController::tickInput() else #endif { +#ifdef _WINDOWS64 + if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) + { + UIScene *pScene = NULL; + for (int grp = 0; grp < eUIGroup_COUNT && !pScene; ++grp) + { + pScene = m_groups[grp]->GetTopScene(eUILayer_Debug); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Tooltips); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Error); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Alert); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Popup); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Fullscreen); + if (!pScene) pScene = m_groups[grp]->GetTopScene(eUILayer_Scene); + } + if (pScene && pScene->getMovie()) + { + Iggy *movie = pScene->getMovie(); + int rawMouseX = g_KBMInput.GetMouseX(); + int rawMouseY = g_KBMInput.GetMouseY(); + F32 mouseX = (F32)rawMouseX; + F32 mouseY = (F32)rawMouseY; + + extern HWND g_hWnd; + if (g_hWnd) + { + RECT rc; + GetClientRect(g_hWnd, &rc); + int winW = rc.right - rc.left; + int winH = rc.bottom - rc.top; + if (winW > 0 && winH > 0) + { + mouseX = mouseX * (m_fScreenWidth / (F32)winW); + mouseY = mouseY * (m_fScreenHeight / (F32)winH); + } + } + + // Only update hover focus when the mouse has actually moved, + // so that mouse-wheel scrolling can change list selection + // without the hover immediately snapping focus back. + bool mouseMoved = (rawMouseX != m_lastHoverMouseX || rawMouseY != m_lastHoverMouseY); + m_lastHoverMouseX = rawMouseX; + m_lastHoverMouseY = rawMouseY; + + if (mouseMoved) + { + IggyFocusHandle currentFocus = IGGY_FOCUS_NULL; + IggyFocusableObject focusables[64]; + S32 numFocusables = 0; + IggyPlayerGetFocusableObjects(movie, ¤tFocus, focusables, 64, &numFocusables); + + if (numFocusables > 0 && numFocusables <= 64) + { + IggyFocusHandle hitObject = IGGY_FOCUS_NULL; + for (S32 i = 0; i < numFocusables; ++i) + { + if (mouseX >= focusables[i].x0 && mouseX <= focusables[i].x1 && + mouseY >= focusables[i].y0 && mouseY <= focusables[i].y1) + { + hitObject = focusables[i].object; + break; + } + } + + if (hitObject != currentFocus) + { + IggyPlayerSetFocusRS(movie, hitObject, 0); + } + } + } + + // Convert mouse to scene/movie coordinates for slider hit testing + F32 sceneMouseX = mouseX; + F32 sceneMouseY = mouseY; + { + S32 displayWidth = 0, displayHeight = 0; + pScene->GetParentLayer()->getRenderDimensions(displayWidth, displayHeight); + if (displayWidth > 0 && displayHeight > 0) + { + sceneMouseX = mouseX * ((F32)pScene->getRenderWidth() / (F32)displayWidth); + sceneMouseY = mouseY * ((F32)pScene->getRenderHeight() / (F32)displayHeight); + } + } + + // Get main panel offset (controls are positioned relative to it) + S32 panelOffsetX = 0, panelOffsetY = 0; + UIControl *pMainPanel = pScene->GetMainPanel(); + if (pMainPanel) + { + pMainPanel->UpdateControl(); + panelOffsetX = pMainPanel->getXPos(); + panelOffsetY = pMainPanel->getYPos(); + } + + bool leftPressed = g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT); + bool leftDown = leftPressed || g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT); + + if (m_mouseDraggingSliderScene != eUIScene_COUNT && m_mouseDraggingSliderScene != pScene->getSceneType()) + { + m_mouseDraggingSliderScene = eUIScene_COUNT; + m_mouseDraggingSliderId = -1; + } + + if (leftPressed) + { + vector *controls = pScene->GetControls(); + if (controls) + { + for (size_t i = 0; i < controls->size(); ++i) + { + UIControl *ctrl = (*controls)[i]; + if (!ctrl || ctrl->getControlType() != UIControl::eSlider || !ctrl->getVisible()) + continue; + + UIControl_Slider *pSlider = (UIControl_Slider *)ctrl; + pSlider->UpdateControl(); + S32 cx = pSlider->getXPos() + panelOffsetX; + S32 cy = pSlider->getYPos() + panelOffsetY; + S32 cw = pSlider->GetRealWidth(); + S32 ch = pSlider->getHeight(); + if (cw <= 0 || ch <= 0) + continue; + + if (sceneMouseX >= cx && sceneMouseX <= cx + cw && sceneMouseY >= cy && sceneMouseY <= cy + ch) + { + m_mouseDraggingSliderScene = pScene->getSceneType(); + m_mouseDraggingSliderId = pSlider->getId(); + break; + } + } + } + } + + if (leftDown && m_mouseDraggingSliderScene == pScene->getSceneType() && m_mouseDraggingSliderId >= 0) + { + UIControl_Slider *pSlider = FindSliderById(pScene, m_mouseDraggingSliderId); + if (pSlider && pSlider->getVisible()) + { + pSlider->UpdateControl(); + S32 sliderX = pSlider->getXPos() + panelOffsetX; + S32 sliderWidth = pSlider->GetRealWidth(); + if (sliderWidth > 0) + { + float fNewSliderPos = (sceneMouseX - (float)sliderX) / (float)sliderWidth; + if (fNewSliderPos < 0.0f) fNewSliderPos = 0.0f; + if (fNewSliderPos > 1.0f) fNewSliderPos = 1.0f; + pSlider->SetSliderTouchPos(fNewSliderPos); + } + } + else + { + m_mouseDraggingSliderScene = eUIScene_COUNT; + m_mouseDraggingSliderId = -1; + } + } + else if (!leftDown) + { + m_mouseDraggingSliderScene = eUIScene_COUNT; + m_mouseDraggingSliderId = -1; + } + } + } +#endif handleInput(); ++m_accumulatedTicks; } @@ -995,28 +1180,59 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key) released = InputManager.ButtonReleased(iPad,key); // Toggle #ifdef _WINDOWS64 - // Keyboard menu input for player 0 if (iPad == 0) { - bool kbDown = false, kbPressed = false, kbReleased = false; - switch(key) + int vk = 0; + switch (key) { - case ACTION_MENU_UP: kbDown = KMInput.IsKeyDown(VK_UP); kbPressed = KMInput.IsKeyPressed(VK_UP); kbReleased = KMInput.IsKeyReleased(VK_UP); break; - case ACTION_MENU_DOWN: kbDown = KMInput.IsKeyDown(VK_DOWN); kbPressed = KMInput.IsKeyPressed(VK_DOWN); kbReleased = KMInput.IsKeyReleased(VK_DOWN); break; - case ACTION_MENU_LEFT: kbDown = KMInput.IsKeyDown(VK_LEFT); kbPressed = KMInput.IsKeyPressed(VK_LEFT); kbReleased = KMInput.IsKeyReleased(VK_LEFT); break; - case ACTION_MENU_RIGHT: kbDown = KMInput.IsKeyDown(VK_RIGHT); kbPressed = KMInput.IsKeyPressed(VK_RIGHT); kbReleased = KMInput.IsKeyReleased(VK_RIGHT); break; - case ACTION_MENU_OK: kbDown = KMInput.IsKeyDown(VK_RETURN); kbPressed = KMInput.IsKeyPressed(VK_RETURN); kbReleased = KMInput.IsKeyReleased(VK_RETURN); break; - case ACTION_MENU_A: kbDown = KMInput.IsKeyDown(VK_RETURN); kbPressed = KMInput.IsKeyPressed(VK_RETURN); kbReleased = KMInput.IsKeyReleased(VK_RETURN); break; - case ACTION_MENU_CANCEL: kbDown = KMInput.IsKeyDown(VK_ESCAPE); kbPressed = KMInput.IsKeyPressed(VK_ESCAPE); kbReleased = KMInput.IsKeyReleased(VK_ESCAPE); break; - case ACTION_MENU_B: kbDown = KMInput.IsKeyDown(VK_ESCAPE); kbPressed = KMInput.IsKeyPressed(VK_ESCAPE); kbReleased = KMInput.IsKeyReleased(VK_ESCAPE); break; - 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; + case ACTION_MENU_OK: case ACTION_MENU_A: vk = VK_RETURN; break; + case ACTION_MENU_CANCEL: case ACTION_MENU_B: vk = VK_ESCAPE; break; + case ACTION_MENU_UP: vk = VK_UP; break; + case ACTION_MENU_DOWN: vk = VK_DOWN; break; + case ACTION_MENU_LEFT: vk = VK_LEFT; break; + case ACTION_MENU_RIGHT: vk = VK_RIGHT; break; + case ACTION_MENU_X: vk = 'R'; break; + case ACTION_MENU_Y: vk = VK_TAB; break; + case ACTION_MENU_LEFT_SCROLL: vk = 'Q'; break; + case ACTION_MENU_RIGHT_SCROLL: vk = 'E'; break; + case ACTION_MENU_PAGEUP: vk = VK_PRIOR; break; + case ACTION_MENU_PAGEDOWN: vk = VK_NEXT; break; + } + if (vk != 0) + { + if (g_KBMInput.IsKeyPressed(vk)) { pressed = true; down = true; } + if (g_KBMInput.IsKeyReleased(vk)) { released = true; down = false; } + if (!pressed && !released && g_KBMInput.IsKeyDown(vk)) { down = true; } + } + + if ((key == ACTION_MENU_OK || key == ACTION_MENU_A) && !g_KBMInput.IsMouseGrabbed()) + { + if (m_mouseDraggingSliderId < 0) + { + if (g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT)) { pressed = true; down = true; } + if (g_KBMInput.IsMouseButtonReleased(KeyboardMouseInput::MOUSE_LEFT)) { released = true; down = false; } + if (!pressed && !released && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT)) { down = true; } + } + } + + // Scroll wheel for list scrolling — only consume the wheel value when the + // action key actually matches, so the other direction isn't lost. + if (!g_KBMInput.IsMouseGrabbed() && (key == ACTION_MENU_OTHER_STICK_UP || key == ACTION_MENU_OTHER_STICK_DOWN)) + { + int wheel = g_KBMInput.PeekMouseWheel(); + if (key == ACTION_MENU_OTHER_STICK_UP && wheel > 0) + { + g_KBMInput.ConsumeMouseWheel(); + pressed = true; + down = true; + } + else if (key == ACTION_MENU_OTHER_STICK_DOWN && wheel < 0) + { + g_KBMInput.ConsumeMouseWheel(); + pressed = true; + down = true; + } } - pressed = pressed || kbPressed; - released = released || kbReleased; - down = down || kbDown; } #endif @@ -3138,4 +3354,4 @@ void UIController::SendTouchInput(unsigned int iPad, unsigned int key, bool bPre } -#endif \ No newline at end of file +#endif diff --git a/Minecraft.Client/Common/UI/UIController.h b/Minecraft.Client/Common/UI/UIController.h index 49c780320..373d67b2c 100644 --- a/Minecraft.Client/Common/UI/UIController.h +++ b/Minecraft.Client/Common/UI/UIController.h @@ -158,6 +158,10 @@ private: vector m_queuedMessageBoxData; unsigned int m_winUserIndex; + EUIScene m_mouseDraggingSliderScene; + int m_mouseDraggingSliderId; + int m_lastHoverMouseX; + int m_lastHoverMouseY; //bool m_bSysUIShowing; bool m_bSystemUIShowing; C4JThread *m_reloadSkinThread; diff --git a/Minecraft.Client/Common/UI/UIGroup.h b/Minecraft.Client/Common/UI/UIGroup.h index 0ffee0ca2..28369f276 100644 --- a/Minecraft.Client/Common/UI/UIGroup.h +++ b/Minecraft.Client/Common/UI/UIGroup.h @@ -37,9 +37,7 @@ private: public: UIGroup(EUIGroup group, int iPad); -#ifdef __PSVITA__ EUIGroup GetGroup() {return m_group;} -#endif UIComponent_Tooltips *getTooltips() { return m_tooltips; } UIComponent_TutorialPopup *getTutorialPopup() { return m_tutorialPopup; } UIScene_HUD *getHUD() { return m_hud; } diff --git a/Minecraft.Client/Common/UI/UIScene.h b/Minecraft.Client/Common/UI/UIScene.h index 8c20aaae3..b4008fa01 100644 --- a/Minecraft.Client/Common/UI/UIScene.h +++ b/Minecraft.Client/Common/UI/UIScene.h @@ -107,8 +107,10 @@ public: int getRenderHeight() { return m_renderHeight; } #ifdef __PSVITA__ - UILayer *GetParentLayer() {return m_parentLayer;} EUIGroup GetParentLayerGroup() {return m_parentLayer->m_parentGroup->GetGroup();} +#endif +#if defined(__PSVITA__) || defined(_WINDOWS64) + UILayer *GetParentLayer() {return m_parentLayer;} vector *GetControls() {return &m_controls;} #endif diff --git a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp index bb56c7809..6b196c1b8 100644 --- a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp @@ -28,17 +28,6 @@ UIScene_AbstractContainerMenu::UIScene_AbstractContainerMenu(int iPad, UILayer * ui.OverrideSFX(m_iPad,ACTION_MENU_DOWN,true); m_bIgnoreInput=false; -#ifdef _WINDOWS64 - m_bMouseDragSlider=false; - m_bHasMousePosition = false; - m_lastMouseX = 0; - m_lastMouseY = 0; - - for (int btn = 0; btn < 3; btn++) - { - KMInput.ConsumeMousePress(btn); - } -#endif } UIScene_AbstractContainerMenu::~UIScene_AbstractContainerMenu() @@ -49,6 +38,16 @@ UIScene_AbstractContainerMenu::~UIScene_AbstractContainerMenu() void UIScene_AbstractContainerMenu::handleDestroy() { app.DebugPrintf("UIScene_AbstractContainerMenu::handleDestroy\n"); + +#ifdef _WINDOWS64 + g_savedInventoryCursorPos.x = m_pointerPos.x; + g_savedInventoryCursorPos.y = m_pointerPos.y; + g_savedInventoryCursorPos.hasSavedPos = true; + + g_KBMInput.SetScreenCursorHidden(false); + g_KBMInput.SetCursorHiddenForUI(false); +#endif + Minecraft *pMinecraft = Minecraft::GetInstance(); if( pMinecraft->localgameModes[m_iPad] != NULL ) { @@ -84,6 +83,10 @@ void UIScene_AbstractContainerMenu::InitDataAssociations(int iPad, AbstractConta void UIScene_AbstractContainerMenu::PlatformInitialize(int iPad, int startIndex) { +#ifdef _WINDOWS64 + g_KBMInput.SetScreenCursorHidden(true); + g_KBMInput.SetCursorHiddenForUI(true); +#endif m_labelInventory.init( app.GetString(IDS_INVENTORY) ); @@ -168,6 +171,19 @@ void UIScene_AbstractContainerMenu::PlatformInitialize(int iPad, int startIndex) //m_pointerControl->SetPosition( &vPointerPos ); m_pointerPos = vPointerPos; +#ifdef _WINDOWS64 + if (g_savedInventoryCursorPos.hasSavedPos) + { + m_pointerPos.x = g_savedInventoryCursorPos.x; + m_pointerPos.y = g_savedInventoryCursorPos.y; + + if (m_pointerPos.x < m_fPointerMinX) m_pointerPos.x = m_fPointerMinX; + if (m_pointerPos.x > m_fPointerMaxX) m_pointerPos.x = m_fPointerMaxX; + if (m_pointerPos.y < m_fPointerMinY) m_pointerPos.y = m_fPointerMinY; + if (m_pointerPos.y > m_fPointerMaxY) m_pointerPos.y = m_fPointerMaxY; + } +#endif + IggyEvent mouseEvent; S32 width, height; m_parentLayer->getRenderDimensions(width, height); @@ -190,139 +206,15 @@ void UIScene_AbstractContainerMenu::tick() { UIScene::tick(); -#ifdef _WINDOWS64 - bool mouseActive = (m_iPad == 0 && !KMInput.IsCaptured()); - bool drivePointerFromMouse = false; - float rawMouseMovieX = 0, rawMouseMovieY = 0; - int scrollDelta = 0; - // Map Windows mouse position to the virtual pointer in movie coordinates - if (mouseActive) - { - RECT clientRect; - GetClientRect(KMInput.GetHWnd(), &clientRect); - int clientWidth = clientRect.right; - int clientHeight = clientRect.bottom; - if (clientWidth > 0 && clientHeight > 0) - { - int mouseX = KMInput.GetMouseX(); - int mouseY = KMInput.GetMouseY(); - bool mouseMoved = !m_bHasMousePosition || mouseX != m_lastMouseX || mouseY != m_lastMouseY; - - m_bHasMousePosition = true; - m_lastMouseX = mouseX; - m_lastMouseY = mouseY; - scrollDelta = KMInput.ConsumeScrollDelta(); - - // Convert mouse position to movie coordinates using the movie/client ratio - 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; - - // Once the mouse has taken over the container cursor, keep following the OS cursor - // until explicit controller input takes ownership back. - drivePointerFromMouse = m_bPointerDrivenByMouse || mouseMoved || KMInput.IsMouseDown(0) || KMInput.IsMouseDown(1) || KMInput.IsMouseDown(2) || scrollDelta != 0; - if (drivePointerFromMouse) - { - m_bPointerDrivenByMouse = true; - m_eCurrTapState = eTapStateNoInput; - m_pointerPos.x = mx; - m_pointerPos.y = my; - } - } - } -#endif - onMouseTick(); -#ifdef _WINDOWS64 - // Dispatch mouse clicks AFTER onMouseTick() has updated m_eCurrSection from the new pointer position - if (mouseActive) - { - if (KMInput.ConsumeMousePress(0)) - { - if (m_eCurrSection == eSectionInventoryCreativeSlider) - { - // Scrollbar click: use raw mouse position (onMouseTick may have snapped m_pointerPos) - m_bMouseDragSlider = true; - m_pointerPos.x = rawMouseMovieX; - m_pointerPos.y = rawMouseMovieY; - handleOtherClicked(m_iPad, eSectionInventoryCreativeSlider, 0, false); - } - else - { - handleKeyDown(m_iPad, ACTION_MENU_A, false); - } - } - else if (m_bMouseDragSlider && KMInput.IsMouseDown(0)) - { - // Continue scrollbar drag: update scroll position from current mouse Y - m_pointerPos.x = rawMouseMovieX; - m_pointerPos.y = rawMouseMovieY; - handleOtherClicked(m_iPad, eSectionInventoryCreativeSlider, 0, false); - } - - if (!KMInput.IsMouseDown(0)) - m_bMouseDragSlider = false; - - if (KMInput.ConsumeMousePress(1)) - { - handleKeyDown(m_iPad, ACTION_MENU_X, false); - } - if (KMInput.ConsumeMousePress(2)) - { - handleKeyDown(m_iPad, ACTION_MENU_Y, false); - } - - // Mouse scroll wheel for page scrolling - if (scrollDelta > 0) - { - handleKeyDown(m_iPad, ACTION_MENU_OTHER_STICK_UP, false); - } - else if (scrollDelta < 0) - { - handleKeyDown(m_iPad, ACTION_MENU_OTHER_STICK_DOWN, false); - } - - // ESC to close — must be last since it may destroy this scene - if (KMInput.ConsumeKeyPress(VK_ESCAPE)) - { - handleKeyDown(m_iPad, ACTION_MENU_B, false); - return; - } - } -#endif - IggyEvent mouseEvent; S32 width, height; m_parentLayer->getRenderDimensions(width, height); -#ifdef _WINDOWS64 - S32 x, y; - if (mouseActive && m_bPointerDrivenByMouse) - { - // Send raw mouse position directly as Iggy event to avoid coordinate round-trip errors - // Scale mouse client coords to the Iggy display space (which was set to getRenderDimensions()) - RECT clientRect; - GetClientRect(KMInput.GetHWnd(), &clientRect); - 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(); + S32 x = (S32)(m_pointerPos.x * ((float)width / m_movieWidth)); + S32 y = (S32)(m_pointerPos.y * ((float)height / m_movieHeight)); - x = (S32)(mouseLocalX * ((float)width / m_movieWidth)); - y = (S32)(mouseLocalY * ((float)height / m_movieHeight)); - } - else - { - x = (S32)(m_pointerPos.x * ((float)width / m_movieWidth)); - y = (S32)(m_pointerPos.y * ((float)height / m_movieHeight)); - } -#else - S32 x = m_pointerPos.x*((float)width/m_movieWidth); - S32 y = m_pointerPos.y*((float)height/m_movieHeight); -#endif IggyMakeEventMouseMove( &mouseEvent, x, y); // 4J Stu - This seems to be broken on Durango, so do it ourself diff --git a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h index 812c5b41c..605f5dbdf 100644 --- a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h @@ -10,12 +10,6 @@ class UIScene_AbstractContainerMenu : public UIScene, public virtual IUIScene_Ab private: ESceneSection m_focusSection; bool m_bIgnoreInput; -#ifdef _WINDOWS64 - bool m_bMouseDragSlider; - bool m_bHasMousePosition; - int m_lastMouseX; - int m_lastMouseY; -#endif protected: UIControl m_controlMainPanel; @@ -42,6 +36,8 @@ public: virtual void handleDestroy(); int getPad() { return m_iPad; } + int getMovieWidth() { return m_movieWidth; } + int getMovieHeight() { return m_movieHeight; } bool getIgnoreInput() { return m_bIgnoreInput; } void setIgnoreInput(bool bVal) { m_bIgnoreInput=bVal; } diff --git a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp index a5cb9aa25..d3cbe4c30 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp @@ -1317,6 +1317,14 @@ void UIScene_LoadOrJoinMenu::handleInput(int iPad, int key, bool repeat, bool pr sendInputToMovie(key, repeat, pressed, released); handled = true; break; + case ACTION_MENU_OTHER_STICK_UP: + sendInputToMovie(ACTION_MENU_UP, repeat, pressed, released); + handled = true; + break; + case ACTION_MENU_OTHER_STICK_DOWN: + sendInputToMovie(ACTION_MENU_DOWN, repeat, pressed, released); + handled = true; + break; } } diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp index 9bf3b983b..0a76a5e5d 100644 --- a/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp @@ -4,6 +4,33 @@ #include "..\..\Minecraft.h" #include "..\..\GameRenderer.h" +namespace +{ + const int FOV_MIN = 70; + const int FOV_MAX = 110; + const int FOV_SLIDER_MAX = 100; + + int clampFov(int value) + { + if (value < FOV_MIN) return FOV_MIN; + if (value > FOV_MAX) return FOV_MAX; + return value; + } + + int fovToSliderValue(float fov) + { + int clampedFov = clampFov((int)(fov + 0.5f)); + return ((clampedFov - FOV_MIN) * FOV_SLIDER_MAX) / (FOV_MAX - FOV_MIN); + } + + int sliderValueToFov(int sliderValue) + { + if (sliderValue < 0) sliderValue = 0; + if (sliderValue > FOV_SLIDER_MAX) sliderValue = FOV_SLIDER_MAX; + return FOV_MIN + ((sliderValue * (FOV_MAX - FOV_MIN)) / FOV_SLIDER_MAX); + } +} + UIScene_SettingsGraphicsMenu::UIScene_SettingsGraphicsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) { // Setup all the Iggy references we need for this scene @@ -22,8 +49,9 @@ UIScene_SettingsGraphicsMenu::UIScene_SettingsGraphicsMenu(int iPad, void *initD swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_GAMMA ),app.GetGameSettings(m_iPad,eGameSetting_Gamma)); m_sliderGamma.init(TempString,eControl_Gamma,0,100,app.GetGameSettings(m_iPad,eGameSetting_Gamma)); - swprintf((WCHAR*)TempString, 256, L"FOV: %d%%", (int)pMinecraft->gameRenderer->GetFovVal()); - m_sliderFOV.init(TempString, eControl_FOV, 70, 110, (int)pMinecraft->gameRenderer->GetFovVal()); + int initialFov = clampFov((int)(pMinecraft->gameRenderer->GetFovVal() + 0.5f)); + swprintf((WCHAR*)TempString, 256, L"FOV: %d", initialFov); + m_sliderFOV.init(TempString, eControl_FOV, 0, FOV_SLIDER_MAX, fovToSliderValue((float)initialFov)); swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_INTERFACEOPACITY ),app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity)); m_sliderInterfaceOpacity.init(TempString,eControl_InterfaceOpacity,0,100,app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity)); @@ -150,10 +178,12 @@ void UIScene_SettingsGraphicsMenu::handleSliderMove(F64 sliderId, F64 currentVal case eControl_FOV: { + m_sliderFOV.handleSliderMove(value); Minecraft* pMinecraft = Minecraft::GetInstance(); - pMinecraft->gameRenderer->SetFovVal((float)currentValue); + int fovValue = sliderValueToFov(value); + pMinecraft->gameRenderer->SetFovVal((float)fovValue); WCHAR TempString[256]; - swprintf((WCHAR*)TempString, 256, L"FOV: %d%%", (int)currentValue); + swprintf((WCHAR*)TempString, 256, L"FOV: %d", fovValue); m_sliderFOV.setLabel(TempString); } break; diff --git a/Minecraft.Client/Input.cpp b/Minecraft.Client/Input.cpp index 1639d09b8..5e41da299 100644 --- a/Minecraft.Client/Input.cpp +++ b/Minecraft.Client/Input.cpp @@ -7,20 +7,21 @@ #include "Input.h" #include "..\Minecraft.Client\LocalPlayer.h" #include "Options.h" +#ifdef _WINDOWS64 +#include "Windows64\KeyboardMouseInput.h" +#endif Input::Input() { xa = 0; ya = 0; - sprintForward = 0; wasJumping = false; jumping = false; sneaking = false; - usingKeyboardMovement = false; + sprinting = false; lReset = false; rReset = false; - m_gamepadSneaking = false; } void Input::tick(LocalPlayer *player) @@ -32,43 +33,43 @@ void Input::tick(LocalPlayer *player) Minecraft *pMinecraft=Minecraft::GetInstance(); int iPad=player->GetXboxPad(); + float controllerXA = 0.0f; + float controllerYA = 0.0f; + // 4J-PB minecraft movement seems to be the wrong way round, so invert x! if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT) ) - xa = -InputManager.GetJoypadStick_LX(iPad); - else - xa = 0.0f; + controllerXA = -InputManager.GetJoypadStick_LX(iPad); if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD) ) - ya = InputManager.GetJoypadStick_LY(iPad); - else - ya = 0.0f; - sprintForward = ya; - usingKeyboardMovement = false; + controllerYA = InputManager.GetJoypadStick_LY(iPad); + float kbXA = 0.0f; + float kbYA = 0.0f; #ifdef _WINDOWS64 - // WASD movement (combine with gamepad) - if (iPad == 0 && KMInput.IsCaptured()) + if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) { - float kbX = 0.0f, kbY = 0.0f; - if (KMInput.IsKeyDown('W')) { kbY += 1.0f; sprintForward += 1.0f; usingKeyboardMovement = true; } - if (KMInput.IsKeyDown('S')) { kbY -= 1.0f; sprintForward -= 1.0f; usingKeyboardMovement = true; } - if (KMInput.IsKeyDown('A')) { kbX += 1.0f; usingKeyboardMovement = true; } // inverted like gamepad - if (KMInput.IsKeyDown('D')) { kbX -= 1.0f; usingKeyboardMovement = true; } - // Normalize diagonal - if (kbX != 0.0f && kbY != 0.0f) { kbX *= 0.707f; kbY *= 0.707f; } - if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT)) - xa = max(min(xa + kbX, 1.0f), -1.0f); - if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD)) - ya = max(min(ya + kbY, 1.0f), -1.0f); + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT) ) + kbXA = g_KBMInput.GetMoveX(); + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD) ) + kbYA = g_KBMInput.GetMoveY(); } #endif - sprintForward = max(min(sprintForward, 1.0f), -1.0f); + + if (kbXA != 0.0f || kbYA != 0.0f) + { + xa = kbXA; + ya = kbYA; + } + else + { + xa = controllerXA; + ya = controllerYA; + } #ifndef _CONTENT_PACKAGE if (app.GetFreezePlayers()) { xa = ya = 0.0f; - sprintForward = 0.0f; player->abilities.flying = true; } #endif @@ -80,7 +81,6 @@ void Input::tick(LocalPlayer *player) lReset = true; } xa = ya = 0.0f; - sprintForward = 0.0f; } // 4J: In flying mode, don't actually toggle sneaking (unless we're riding in which case we need to sneak to dismount) @@ -88,15 +88,46 @@ void Input::tick(LocalPlayer *player) { if((player->ullButtonsPressed&(1LL<localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_SNEAK_TOGGLE)) { - m_gamepadSneaking=!m_gamepadSneaking; + sneaking=!sneaking; } } - sneaking = m_gamepadSneaking; #ifdef _WINDOWS64 - // Keyboard hold-to-sneak (overrides gamepad toggle) - if (iPad == 0 && KMInput.IsCaptured() && KMInput.IsKeyDown(VK_SHIFT) && !player->abilities.flying) - sneaking = true; + if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) + { + // Left Shift = sneak (hold to crouch) + if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_SNEAK_TOGGLE)) + { + if (!player->abilities.flying) + { + sneaking = g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SNEAK); + } + } + + // Left Ctrl + forward = sprint (hold to sprint) + if (!player->abilities.flying) + { + bool ctrlHeld = g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SPRINT); + bool movingForward = (kbYA > 0.0f); + + if (ctrlHeld && movingForward) + { + sprinting = true; + } + else + { + sprinting = false; + } + } + else + { + sprinting = false; + } + } + else if (iPad == 0) + { + sprinting = false; + } #endif if(sneaking) @@ -109,6 +140,7 @@ void Input::tick(LocalPlayer *player) float tx = 0.0f; float ty = 0.0f; + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_RIGHT) ) tx = InputManager.GetJoypadStick_RX(iPad)*(((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame))/100.0f); // apply sensitivity to look if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_UP) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_DOWN) ) @@ -132,47 +164,52 @@ void Input::tick(LocalPlayer *player) } tx = ty = 0.0f; } - player->interpolateTurn(tx * abs(tx) * turnSpeed, ty * abs(ty) * turnSpeed); + + float turnX = tx * abs(tx) * turnSpeed; + float turnY = ty * abs(ty) * turnSpeed; #ifdef _WINDOWS64 - // Mouse look is now handled per-frame in Minecraft::applyFrameMouseLook() - // to eliminate the 20Hz tick delay. Only flush any remaining delta here - // as a safety measure. - if (iPad == 0 && KMInput.IsCaptured()) + if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) { - float rawDx, rawDy; - KMInput.ConsumeMouseDelta(rawDx, rawDy); - // Delta should normally be 0 since applyFrameMouseLook() already consumed it - if (rawDx != 0.0f || rawDy != 0.0f) + float mouseSensitivity = ((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame)) / 100.0f; + float mouseLookScale = 5.0f; + float mx = g_KBMInput.GetLookX(mouseSensitivity * mouseLookScale); + float my = g_KBMInput.GetLookY(mouseSensitivity * mouseLookScale); + + if ( app.GetGameSettings(iPad,eGameSetting_ControlInvertLook) ) { - float mouseSensitivity = ((float)app.GetGameSettings(iPad, eGameSetting_Sensitivity_InGame)) / 100.0f; - float mdx = rawDx * mouseSensitivity; - float mdy = -rawDy * mouseSensitivity; - if (app.GetGameSettings(iPad, eGameSetting_ControlInvertLook)) - mdy = -mdy; - player->interpolateTurn(mdx, mdy); + my = -my; } + + turnX += mx; + turnY += my; } #endif + player->interpolateTurn(turnX, turnY); + //jumping = controller.isButtonPressed(0); - unsigned int jump = InputManager.GetValue(iPad, MINECRAFT_ACTION_JUMP); - if( jump > 0 && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP) ) + bool kbJump = false; +#ifdef _WINDOWS64 + kbJump = (iPad == 0) && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_JUMP); +#endif + if( (jump > 0 || kbJump) && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP) ) jumping = true; else jumping = false; -#ifdef _WINDOWS64 - // Keyboard jump (Space) - if (iPad == 0 && KMInput.IsCaptured() && KMInput.IsKeyDown(VK_SPACE) && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP)) - jumping = true; -#endif - #ifndef _CONTENT_PACKAGE if (app.GetFreezePlayers()) jumping = false; #endif +#ifdef _WINDOWS64 + if (iPad == 0 && g_KBMInput.IsKeyPressed(VK_ESCAPE) && g_KBMInput.IsMouseGrabbed()) + { + g_KBMInput.SetMouseGrabbed(false); + } +#endif + //OutputDebugString("INPUT: End input tick\n"); } diff --git a/Minecraft.Client/Input.h b/Minecraft.Client/Input.h index d8dedd57e..c7e1eec47 100644 --- a/Minecraft.Client/Input.h +++ b/Minecraft.Client/Input.h @@ -6,20 +6,17 @@ class Input public: float xa; float ya; - float sprintForward; bool wasJumping; bool jumping; bool sneaking; - bool usingKeyboardMovement; - - Input(); // 4J - added + bool sprinting; + + Input(); virtual void tick(LocalPlayer *player); private: - bool lReset; bool rReset; - bool m_gamepadSneaking; }; diff --git a/Minecraft.Client/LocalPlayer.cpp b/Minecraft.Client/LocalPlayer.cpp index bb25105f7..77306621f 100644 --- a/Minecraft.Client/LocalPlayer.cpp +++ b/Minecraft.Client/LocalPlayer.cpp @@ -251,13 +251,10 @@ void LocalPlayer::aiStep() if (changingDimensionDelay > 0) changingDimensionDelay--; bool wasJumping = input->jumping; float runTreshold = 0.8f; - float sprintForward = input->sprintForward; - - bool wasRunning = sprintForward >= runTreshold; + bool wasRunning = input->ya >= runTreshold; //input->tick( dynamic_pointer_cast( shared_from_this() ) ); // 4J-PB - make it a localplayer input->tick( this ); - sprintForward = input->sprintForward; if (isUsingItem() && !isRiding()) { input->xa *= 0.2f; @@ -281,25 +278,9 @@ void LocalPlayer::aiStep() // world with low food, then reload it in creative. if(abilities.mayfly || isAllowedToFly() ) enoughFoodToSprint = true; - bool forwardEnoughToTriggerSprint = sprintForward >= runTreshold; - bool forwardReturnedToDeadzone = sprintForward == 0.0f; - bool forwardEnoughToContinueSprint = sprintForward >= runTreshold; - -#ifdef _WINDOWS64 - if (GetXboxPad() == 0 && input->usingKeyboardMovement) - { - forwardEnoughToContinueSprint = sprintForward > 0.0f; - } -#endif - -#ifdef _WINDOWS64 - // Keyboard sprint: Ctrl held while moving forward - if (GetXboxPad() == 0 && input->usingKeyboardMovement && KMInput.IsKeyDown(VK_CONTROL) && sprintForward > 0.0f && - enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness) && onGround) - { - if (!isSprinting()) setSprinting(true); - } -#endif + bool forwardEnoughToTriggerSprint = input->ya >= runTreshold; + bool forwardReturnedToDeadzone = input->ya == 0.0f; + bool forwardEnoughToContinueSprint = input->ya >= runTreshold; // 4J - altered this slightly to make sure that the joypad returns to below returnTreshold in between registering two movements up to runThreshold if (onGround && !isSprinting() && enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness)) @@ -327,6 +308,12 @@ void LocalPlayer::aiStep() } } if (isSneaking()) sprintTriggerTime = 0; +#ifdef _WINDOWS64 + if (input->sprinting && onGround && enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness) && !isSneaking()) + { + setSprinting(true); + } +#endif // 4J-PB - try not stopping sprint on collision //if (isSprinting() && (input->ya < runTreshold || horizontalCollision || !enoughFoodToSprint)) if (isSprinting() && (!forwardEnoughToContinueSprint || !enoughFoodToSprint || isSneaking() || isUsingItem())) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index bd75a61a9..19ee79bdb 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -502,6 +502,13 @@ void Minecraft::setScreen(Screen *screen) this->screen->removed(); } +#ifdef _WINDOWS64 + if (screen != NULL && g_KBMInput.IsMouseGrabbed()) + { + g_KBMInput.SetMouseGrabbed(false); + } +#endif + //4J Gordon: Do not force a stats save here /*if (dynamic_cast(screen)!=NULL) { @@ -1184,11 +1191,11 @@ void Minecraft::applyFrameMouseLook() int iPad = localplayers[i]->GetXboxPad(); if (iPad != 0) continue; // Mouse only applies to pad 0 - if (!KMInput.IsCaptured()) continue; + if (!g_KBMInput.IsMouseGrabbed()) continue; if (localgameModes[iPad] == NULL) continue; float rawDx, rawDy; - KMInput.ConsumeMouseDelta(rawDx, rawDy); + g_KBMInput.ConsumeMouseDelta(rawDx, rawDy); if (rawDx == 0.0f && rawDy == 0.0f) continue; float mouseSensitivity = ((float)app.GetGameSettings(iPad, eGameSetting_Sensitivity_InGame)) / 100.0f; @@ -1450,14 +1457,54 @@ void Minecraft::run_middle() // Keyboard/mouse button presses for player 0 if (i == 0) { - if (KMInput.ConsumeKeyPress(VK_ESCAPE)) localplayers[i]->ullButtonsPressed |= 1LL<ullButtonsPressed |= 1LL<ullButtonsPressed |= 1LL<ullButtonsPressed |= 1LL<ullButtonsPressed |= 1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<inventory) + localplayers[i]->inventory->selected = slot; + } + } + } + + // Utility keys always work regardless of KBM active state + if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_PAUSE) && !ui.IsTutorialVisible(i)) + { + localplayers[i]->ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<abilities.flying && KMInput.IsKeyDown(VK_SHIFT) && !ui.GetMenuDisplayed(i)) - localplayers[i]->ullButtonsPressed |= 1LL<abilities.flying && !ui.GetMenuDisplayed(i)) + localplayers[i]->ullButtonsPressed|=1LL< 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL)) wheel += 1; - else if (kbWheel < 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_RIGHT_SCROLL)) wheel -= 1; - - // 1-9 keys for direct hotbar selection - if (gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL)) - { - for (int k = '1'; k <= '9'; k++) - { - if (KMInput.ConsumeKeyPress(k)) - { - player->inventory->selected = k - '1'; - app.SetOpacityTimer(iPad); - break; - } - } - } + wheel = g_KBMInput.GetMouseWheel(); } #endif if (wheel != 0) @@ -3485,33 +3528,20 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) player->handleMouseClick(0); player->lastClickTick[0] = ticks; } -#ifdef _WINDOWS64 - else if (iPad == 0 && KMInput.IsCaptured() && KMInput.ConsumeMousePress(0)) - { - player->handleMouseClick(0); - player->lastClickTick[0] = ticks; - } -#endif - if (InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) && ticks - player->lastClickTick[0] >= timer->ticksPerSecond / 4) +#ifdef _WINDOWS64 + bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) || (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT)); +#else + bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION); +#endif + if (actionHeld && ticks - player->lastClickTick[0] >= timer->ticksPerSecond / 4) { //printf("MINECRAFT_ACTION_ACTION ButtonDown"); player->handleMouseClick(0); player->lastClickTick[0] = ticks; } -#ifdef _WINDOWS64 - else if (iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(0) && ticks - player->lastClickTick[0] >= timer->ticksPerSecond / 4) - { - player->handleMouseClick(0); - player->lastClickTick[0] = ticks; - } -#endif - if(InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) -#ifdef _WINDOWS64 - || (iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(0)) -#endif - ) + if(actionHeld) { player->handleMouseDown(0, true ); } @@ -3530,25 +3560,21 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) lastClickTick = ticks; } */ +#ifdef _WINDOWS64 + bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT)); +#else + bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE); +#endif if( player->isUsingItem() ) { - if(!InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) -#ifdef _WINDOWS64 - && !(iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(1)) -#endif - ) gameMode->releaseUsingItem(player); + if(!useHeld) gameMode->releaseUsingItem(player); } else if( gameMode->isInputAllowed(MINECRAFT_ACTION_USE) ) { -#ifdef _WINDOWS64 - bool useButtonDown = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && KMInput.IsCaptured() && KMInput.IsMouseDown(1)); -#else - bool useButtonDown = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE); -#endif if( player->abilities.instabuild ) { // 4J - attempt to handle click in special creative mode fashion if possible (used for placing blocks at regular intervals) - bool didClick = player->creativeModeHandleMouseClick(1, useButtonDown ); + bool didClick = player->creativeModeHandleMouseClick(1, useHeld ); // If this handler has put us in lastClick_oldRepeat mode then it is because we aren't placing blocks - behave largely as the code used to if( player->lastClickState == LocalPlayer::lastClick_oldRepeat ) { @@ -3560,7 +3586,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) else { // Otherwise just the original game code for handling autorepeat - if (useButtonDown && ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4) + if (useHeld && ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4) { player->handleMouseClick(1); player->lastClickTick[1] = ticks; @@ -3576,7 +3602,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) bool firstClick = ( player->lastClickTick[1] == 0 ); bool autoRepeat = ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4; if ( player->isRiding() || player->isSprinting() || player->isSleeping() ) autoRepeat = false; - if (useButtonDown ) + if (useHeld ) { // If the player has just exited a bed, then delay the time before a repeat key is allowed without releasing if(player->isSleeping() ) player->lastClickTick[1] = ticks + (timer->ticksPerSecond * 2); diff --git a/Minecraft.Client/Screen.cpp b/Minecraft.Client/Screen.cpp index 6a402b04c..e69b73736 100644 --- a/Minecraft.Client/Screen.cpp +++ b/Minecraft.Client/Screen.cpp @@ -110,13 +110,13 @@ void Screen::updateEvents() // Poll mouse button state and dispatch click/release events for (int btn = 0; btn < 3; btn++) { - if (KMInput.ConsumeMousePress(btn)) + if (g_KBMInput.IsMouseButtonPressed(btn)) { int xm = Mouse::getX() * width / minecraft->width; int ym = height - Mouse::getY() * height / minecraft->height - 1; mouseClicked(xm, ym, btn); } - if (KMInput.ConsumeMouseRelease(btn)) + if (g_KBMInput.IsMouseButtonReleased(btn)) { int xm = Mouse::getX() * width / minecraft->width; int ym = height - Mouse::getY() * height / minecraft->height - 1; @@ -127,7 +127,7 @@ void Screen::updateEvents() // Poll keyboard events for (int vk = 0; vk < 256; vk++) { - if (KMInput.ConsumeKeyPress(vk)) + if (g_KBMInput.IsKeyPressed(vk)) { // Map Windows virtual key to the Keyboard constants used by Screen::keyPressed int mappedKey = -1; @@ -144,7 +144,7 @@ void Screen::updateEvents() else if (vk >= 'A' && vk <= 'Z') { ch = (wchar_t)(vk - 'A' + L'a'); - if (KMInput.IsKeyDown(VK_SHIFT)) ch = (wchar_t)vk; + if (g_KBMInput.IsKeyDown(VK_LSHIFT) || g_KBMInput.IsKeyDown(VK_RSHIFT)) ch = (wchar_t)vk; } else if (vk >= '0' && vk <= '9') ch = (wchar_t)vk; else if (vk == VK_SPACE) ch = L' '; diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp index df57db442..7ab99a719 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp @@ -3,118 +3,158 @@ #ifdef _WINDOWS64 #include "KeyboardMouseInput.h" +#include -KeyboardMouseInput KMInput; +KeyboardMouseInput g_KBMInput; -KeyboardMouseInput::KeyboardMouseInput() - : m_mouseDeltaXAccum(0.0f) - , m_mouseDeltaYAccum(0.0f) - , m_scrollDeltaAccum(0) - , m_captured(false) - , m_hWnd(NULL) - , m_initialized(false) - , m_mouseX(0) - , m_mouseY(0) +extern HWND g_hWnd; + +// Forward declaration +static void ClipCursorToWindow(HWND hWnd); + +void KeyboardMouseInput::Init() { - memset(m_keyState, 0, sizeof(m_keyState)); - memset(m_keyStatePrev, 0, sizeof(m_keyStatePrev)); - memset(m_mouseButtons, 0, sizeof(m_mouseButtons)); - memset(m_mouseButtonsPrev, 0, sizeof(m_mouseButtonsPrev)); + memset(m_keyDown, 0, sizeof(m_keyDown)); + memset(m_keyDownPrev, 0, sizeof(m_keyDownPrev)); memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); - memset(m_mousePressedAccum, 0, sizeof(m_mousePressedAccum)); - memset(m_mouseReleasedAccum, 0, sizeof(m_mouseReleasedAccum)); -} + memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum)); + memset(m_keyPressed, 0, sizeof(m_keyPressed)); + memset(m_keyReleased, 0, sizeof(m_keyReleased)); + memset(m_mouseButtonDown, 0, sizeof(m_mouseButtonDown)); + memset(m_mouseButtonDownPrev, 0, sizeof(m_mouseButtonDownPrev)); + memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum)); + memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum)); + memset(m_mouseBtnPressed, 0, sizeof(m_mouseBtnPressed)); + memset(m_mouseBtnReleased, 0, sizeof(m_mouseBtnReleased)); + m_mouseX = 0; + m_mouseY = 0; + m_mouseDeltaX = 0; + m_mouseDeltaY = 0; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + m_mouseWheelAccum = 0; + m_mouseGrabbed = false; + m_cursorHiddenForUI = false; + m_windowFocused = true; + m_hasInput = false; + m_kbmActive = true; + m_screenWantsCursorHidden = false; -KeyboardMouseInput::~KeyboardMouseInput() -{ - if (m_captured) - { - SetCapture(false); - } -} - -void KeyboardMouseInput::Init(HWND hWnd) -{ - m_hWnd = hWnd; - m_initialized = true; - - // Register for raw mouse input RAWINPUTDEVICE rid; - rid.usUsagePage = HID_USAGE_PAGE_GENERIC; - rid.usUsage = HID_USAGE_GENERIC_MOUSE; + rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC + rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE rid.dwFlags = 0; - rid.hwndTarget = hWnd; + rid.hwndTarget = g_hWnd; RegisterRawInputDevices(&rid, 1, sizeof(rid)); } +void KeyboardMouseInput::ClearAllState() +{ + memset(m_keyDown, 0, sizeof(m_keyDown)); + memset(m_keyDownPrev, 0, sizeof(m_keyDownPrev)); + memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); + memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum)); + memset(m_keyPressed, 0, sizeof(m_keyPressed)); + memset(m_keyReleased, 0, sizeof(m_keyReleased)); + memset(m_mouseButtonDown, 0, sizeof(m_mouseButtonDown)); + memset(m_mouseButtonDownPrev, 0, sizeof(m_mouseButtonDownPrev)); + memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum)); + memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum)); + memset(m_mouseBtnPressed, 0, sizeof(m_mouseBtnPressed)); + memset(m_mouseBtnReleased, 0, sizeof(m_mouseBtnReleased)); + m_mouseDeltaX = 0; + m_mouseDeltaY = 0; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + m_mouseWheelAccum = 0; +} + void KeyboardMouseInput::Tick() { - // Keep cursor pinned to center while captured - if (m_captured) - CenterCursor(); -} + memcpy(m_keyDownPrev, m_keyDown, sizeof(m_keyDown)); + memcpy(m_mouseButtonDownPrev, m_mouseButtonDown, sizeof(m_mouseButtonDown)); -void KeyboardMouseInput::EndFrame() -{ - // Advance previous state for next frame's edge detection. - // Must be called AFTER all per-frame consumers have read IsKeyPressed/Released etc. - memcpy(m_keyStatePrev, m_keyState, sizeof(m_keyState)); - memcpy(m_mouseButtonsPrev, m_mouseButtons, sizeof(m_mouseButtons)); -} + memcpy(m_keyPressed, m_keyPressedAccum, sizeof(m_keyPressedAccum)); + memcpy(m_keyReleased, m_keyReleasedAccum, sizeof(m_keyReleasedAccum)); + memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); + memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum)); -void KeyboardMouseInput::OnKeyDown(WPARAM vk) -{ - if (vk < 256) + memcpy(m_mouseBtnPressed, m_mouseBtnPressedAccum, sizeof(m_mouseBtnPressedAccum)); + memcpy(m_mouseBtnReleased, m_mouseBtnReleasedAccum, sizeof(m_mouseBtnReleasedAccum)); + memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum)); + memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum)); + + m_mouseDeltaX = m_mouseDeltaAccumX; + m_mouseDeltaY = m_mouseDeltaAccumY; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + + m_hasInput = (m_mouseDeltaX != 0 || m_mouseDeltaY != 0 || m_mouseWheelAccum != 0); + if (!m_hasInput) { - if (!m_keyState[vk]) m_keyPressedAccum[vk] = true; - m_keyState[vk] = true; - } -} - -void KeyboardMouseInput::OnKeyUp(WPARAM vk) -{ - if (vk < 256) - { - m_keyState[vk] = false; - } -} - -void KeyboardMouseInput::OnRawMouseInput(LPARAM lParam) -{ - if (!m_captured) return; - - UINT dwSize = 0; - GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); - - BYTE* lpb = (BYTE*)alloca(dwSize); - if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) - return; - - RAWINPUT* raw = (RAWINPUT*)lpb; - if (raw->header.dwType == RIM_TYPEMOUSE) - { - if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) + for (int i = 0; i < MAX_KEYS; i++) { - m_mouseDeltaXAccum += (float)raw->data.mouse.lLastX; - m_mouseDeltaYAccum += (float)raw->data.mouse.lLastY; + if (m_keyDown[i]) { m_hasInput = true; break; } } } -} - -void KeyboardMouseInput::OnMouseButton(int button, bool down) -{ - if (ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) { return; } - if (button >= 0 && button < 3) + if (!m_hasInput) { - if (down && !m_mouseButtons[button]) m_mousePressedAccum[button] = true; - if (!down && m_mouseButtons[button]) m_mouseReleasedAccum[button] = true; - m_mouseButtons[button] = down; + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) + { + if (m_mouseButtonDown[i]) { m_hasInput = true; break; } + } + } + + if ((m_mouseGrabbed || m_cursorHiddenForUI) && g_hWnd) + { + RECT rc; + GetClientRect(g_hWnd, &rc); + POINT center; + center.x = (rc.right - rc.left) / 2; + center.y = (rc.bottom - rc.top) / 2; + ClientToScreen(g_hWnd, ¢er); + SetCursorPos(center.x, center.y); } } -void KeyboardMouseInput::OnMouseWheel(int delta) +void KeyboardMouseInput::OnKeyDown(int vkCode) { - m_scrollDeltaAccum += delta; + if (vkCode >= 0 && vkCode < MAX_KEYS) + { + if (!m_keyDown[vkCode]) + m_keyPressedAccum[vkCode] = true; + m_keyDown[vkCode] = true; + } +} + +void KeyboardMouseInput::OnKeyUp(int vkCode) +{ + if (vkCode >= 0 && vkCode < MAX_KEYS) + { + if (m_keyDown[vkCode]) + m_keyReleasedAccum[vkCode] = true; + m_keyDown[vkCode] = false; + } +} + +void KeyboardMouseInput::OnMouseButtonDown(int button) +{ + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + { + if (!m_mouseButtonDown[button]) + m_mouseBtnPressedAccum[button] = true; + m_mouseButtonDown[button] = true; + } +} + +void KeyboardMouseInput::OnMouseButtonUp(int button) +{ + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + { + if (m_mouseButtonDown[button]) + m_mouseBtnReleasedAccum[button] = true; + m_mouseButtonDown[button] = false; + } } void KeyboardMouseInput::OnMouseMove(int x, int y) @@ -123,139 +163,193 @@ void KeyboardMouseInput::OnMouseMove(int x, int y) m_mouseY = y; } -int KeyboardMouseInput::GetMouseX() const { return m_mouseX; } -int KeyboardMouseInput::GetMouseY() const { return m_mouseY; } -HWND KeyboardMouseInput::GetHWnd() const { return m_hWnd; } - -void KeyboardMouseInput::ClearAllState() +void KeyboardMouseInput::OnMouseWheel(int delta) { - memset(m_keyState, 0, sizeof(m_keyState)); - memset(m_mouseButtons, 0, sizeof(m_mouseButtons)); - memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); - memset(m_mousePressedAccum, 0, sizeof(m_mousePressedAccum)); - memset(m_mouseReleasedAccum, 0, sizeof(m_mouseReleasedAccum)); - m_mouseDeltaXAccum = 0.0f; - m_mouseDeltaYAccum = 0.0f; - m_scrollDeltaAccum = 0; + // Normalize from raw Windows delta (multiples of WHEEL_DELTA=120) to discrete notch counts + m_mouseWheelAccum += delta / WHEEL_DELTA; } -// Per-frame key queries -bool KeyboardMouseInput::IsKeyDown(int vk) const +int KeyboardMouseInput::GetMouseWheel() { - if (vk < 0 || vk >= 256) return false; - return m_keyState[vk]; + int val = m_mouseWheelAccum; + m_mouseWheelAccum = 0; + return val; } -bool KeyboardMouseInput::IsKeyPressed(int vk) const +void KeyboardMouseInput::OnRawMouseDelta(int dx, int dy) { - if (vk < 0 || vk >= 256) return false; - return m_keyState[vk] && !m_keyStatePrev[vk]; + m_mouseDeltaAccumX += dx; + m_mouseDeltaAccumY += dy; } -bool KeyboardMouseInput::IsKeyReleased(int vk) const +bool KeyboardMouseInput::IsKeyDown(int vkCode) const { - if (vk < 0 || vk >= 256) return false; - return !m_keyState[vk] && m_keyStatePrev[vk]; + if (vkCode >= 0 && vkCode < MAX_KEYS) + return m_keyDown[vkCode]; + return false; } -// Per-frame mouse button queries -bool KeyboardMouseInput::IsMouseDown(int btn) const +bool KeyboardMouseInput::IsKeyPressed(int vkCode) const { - if (btn < 0 || btn >= 3) return false; - return m_mouseButtons[btn]; + if (vkCode >= 0 && vkCode < MAX_KEYS) + return m_keyPressed[vkCode]; + return false; } -bool KeyboardMouseInput::IsMousePressed(int btn) const +bool KeyboardMouseInput::IsKeyReleased(int vkCode) const { - if (btn < 0 || btn >= 3) return false; - return m_mouseButtons[btn] && !m_mouseButtonsPrev[btn]; + if (vkCode >= 0 && vkCode < MAX_KEYS) + return m_keyReleased[vkCode]; + return false; } -bool KeyboardMouseInput::IsMouseReleased(int btn) const +bool KeyboardMouseInput::IsMouseButtonDown(int button) const { - if (btn < 0 || btn >= 3) return false; - return !m_mouseButtons[btn] && m_mouseButtonsPrev[btn]; + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + return m_mouseButtonDown[button]; + return false; } -// Game-tick consume methods -bool KeyboardMouseInput::ConsumeKeyPress(int vk) +bool KeyboardMouseInput::IsMouseButtonPressed(int button) const { - if (vk < 0 || vk >= 256) return false; - bool pressed = m_keyPressedAccum[vk]; - m_keyPressedAccum[vk] = false; - return pressed; + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + return m_mouseBtnPressed[button]; + return false; } -bool KeyboardMouseInput::ConsumeMousePress(int btn) +bool KeyboardMouseInput::IsMouseButtonReleased(int button) const { - if (btn < 0 || btn >= 3) return false; - bool pressed = m_mousePressedAccum[btn]; - m_mousePressedAccum[btn] = false; - return pressed; -} - -bool KeyboardMouseInput::ConsumeMouseRelease(int btn) -{ - if (btn < 0 || btn >= 3) return false; - bool released = m_mouseReleasedAccum[btn]; - m_mouseReleasedAccum[btn] = false; - return released; + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + return m_mouseBtnReleased[button]; + return false; } void KeyboardMouseInput::ConsumeMouseDelta(float &dx, float &dy) { - dx = m_mouseDeltaXAccum; - dy = m_mouseDeltaYAccum; - m_mouseDeltaXAccum = 0.0f; - m_mouseDeltaYAccum = 0.0f; + dx = (float)m_mouseDeltaAccumX; + dy = (float)m_mouseDeltaAccumY; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; } -int KeyboardMouseInput::ConsumeScrollDelta() +void KeyboardMouseInput::SetMouseGrabbed(bool grabbed) { - int delta = m_scrollDeltaAccum; - m_scrollDeltaAccum = 0; - return delta; -} + if (m_mouseGrabbed == grabbed) + return; -// Mouse capture -void KeyboardMouseInput::SetCapture(bool capture) -{ - if (capture == m_captured) return; - m_captured = capture; - - if (capture) + m_mouseGrabbed = grabbed; + if (grabbed && g_hWnd) { - ShowCursor(FALSE); - RECT rect; - GetClientRect(m_hWnd, &rect); - POINT topLeft = { rect.left, rect.top }; - POINT bottomRight = { rect.right, rect.bottom }; - ClientToScreen(m_hWnd, &topLeft); - ClientToScreen(m_hWnd, &bottomRight); - RECT screenRect = { topLeft.x, topLeft.y, bottomRight.x, bottomRight.y }; - ClipCursor(&screenRect); - CenterCursor(); + while (ShowCursor(FALSE) >= 0) {} + ClipCursorToWindow(g_hWnd); - // Flush accumulated deltas so the snap-to-center doesn't cause a jump - m_mouseDeltaXAccum = 0.0f; - m_mouseDeltaYAccum = 0.0f; + RECT rc; + GetClientRect(g_hWnd, &rc); + POINT center; + center.x = (rc.right - rc.left) / 2; + center.y = (rc.bottom - rc.top) / 2; + ClientToScreen(g_hWnd, ¢er); + SetCursorPos(center.x, center.y); + + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; } - else + else if (!grabbed && !m_cursorHiddenForUI && g_hWnd) { - ShowCursor(TRUE); + while (ShowCursor(TRUE) < 0) {} ClipCursor(NULL); } } -bool KeyboardMouseInput::IsCaptured() const { return m_captured; } - -void KeyboardMouseInput::CenterCursor() +void KeyboardMouseInput::SetCursorHiddenForUI(bool hidden) { - RECT rect; - GetClientRect(m_hWnd, &rect); - POINT center = { (rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2 }; - ClientToScreen(m_hWnd, ¢er); - SetCursorPos(center.x, center.y); + if (m_cursorHiddenForUI == hidden) + return; + + m_cursorHiddenForUI = hidden; + if (hidden && g_hWnd) + { + while (ShowCursor(FALSE) >= 0) {} + ClipCursorToWindow(g_hWnd); + + RECT rc; + GetClientRect(g_hWnd, &rc); + POINT center; + center.x = (rc.right - rc.left) / 2; + center.y = (rc.bottom - rc.top) / 2; + ClientToScreen(g_hWnd, ¢er); + SetCursorPos(center.x, center.y); + + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + } + else if (!hidden && !m_mouseGrabbed && g_hWnd) + { + while (ShowCursor(TRUE) < 0) {} + ClipCursor(NULL); + } +} + +static void ClipCursorToWindow(HWND hWnd) +{ + if (!hWnd) return; + RECT rc; + GetClientRect(hWnd, &rc); + POINT topLeft = { rc.left, rc.top }; + POINT bottomRight = { rc.right, rc.bottom }; + ClientToScreen(hWnd, &topLeft); + ClientToScreen(hWnd, &bottomRight); + RECT clipRect = { topLeft.x, topLeft.y, bottomRight.x, bottomRight.y }; + ClipCursor(&clipRect); +} + +void KeyboardMouseInput::SetWindowFocused(bool focused) +{ + m_windowFocused = focused; + if (focused) + { + if (m_mouseGrabbed || m_cursorHiddenForUI) + { + while (ShowCursor(FALSE) >= 0) {} + ClipCursorToWindow(g_hWnd); + } + else + { + while (ShowCursor(TRUE) < 0) {} + ClipCursor(NULL); + } + } + else + { + while (ShowCursor(TRUE) < 0) {} + ClipCursor(NULL); + } +} + +float KeyboardMouseInput::GetMoveX() const +{ + float x = 0.0f; + if (m_keyDown[KEY_LEFT]) x += 1.0f; + if (m_keyDown[KEY_RIGHT]) x -= 1.0f; + return x; +} + +float KeyboardMouseInput::GetMoveY() const +{ + float y = 0.0f; + if (m_keyDown[KEY_FORWARD]) y += 1.0f; + if (m_keyDown[KEY_BACKWARD]) y -= 1.0f; + return y; +} + +float KeyboardMouseInput::GetLookX(float sensitivity) const +{ + return (float)m_mouseDeltaX * sensitivity; +} + +float KeyboardMouseInput::GetLookY(float sensitivity) const +{ + return (float)(-m_mouseDeltaY) * sensitivity; } #endif // _WINDOWS64 diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.h b/Minecraft.Client/Windows64/KeyboardMouseInput.h index a09843f90..f098ccabd 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.h +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.h @@ -4,88 +4,130 @@ #include -// HID usage page and usage for raw input registration -#ifndef HID_USAGE_PAGE_GENERIC -#define HID_USAGE_PAGE_GENERIC ((USHORT)0x01) -#endif -#ifndef HID_USAGE_GENERIC_MOUSE -#define HID_USAGE_GENERIC_MOUSE ((USHORT)0x02) -#endif - class KeyboardMouseInput { public: - KeyboardMouseInput(); - ~KeyboardMouseInput(); + static const int MAX_KEYS = 256; - void Init(HWND hWnd); + static const int MOUSE_LEFT = 0; + static const int MOUSE_RIGHT = 1; + static const int MOUSE_MIDDLE = 2; + static const int MAX_MOUSE_BUTTONS = 3; + + static const int KEY_FORWARD = 'W'; + static const int KEY_BACKWARD = 'S'; + static const int KEY_LEFT = 'A'; + static const int KEY_RIGHT = 'D'; + static const int KEY_JUMP = VK_SPACE; + static const int KEY_SNEAK = VK_LSHIFT; + static const int KEY_SPRINT = VK_LCONTROL; + static const int KEY_INVENTORY = 'E'; + static const int KEY_DROP = 'Q'; + static const int KEY_CRAFTING = 'C'; + static const int KEY_CRAFTING_ALT = 'R'; + static const int KEY_CONFIRM = VK_RETURN; + static const int KEY_CANCEL = VK_ESCAPE; + static const int KEY_PAUSE = VK_ESCAPE; + static const int KEY_THIRD_PERSON = VK_F5; + static const int KEY_DEBUG_INFO = VK_F3; + + void Init(); void Tick(); - void EndFrame(); - - // Called from WndProc - void OnKeyDown(WPARAM vk); - void OnKeyUp(WPARAM vk); - void OnRawMouseInput(LPARAM lParam); - void OnMouseButton(int button, bool down); - void OnMouseWheel(int delta); void ClearAllState(); - // Per-frame edge detection (for UI / per-frame logic like Alt toggle) - bool IsKeyDown(int vk) const; - bool IsKeyPressed(int vk) const; - bool IsKeyReleased(int vk) const; - bool IsMouseDown(int btn) const; - bool IsMousePressed(int btn) const; - bool IsMouseReleased(int btn) const; - - // Game-tick consume methods: accumulate across frames, clear on read. - // Use these from code that runs at game tick rate (20Hz). - bool ConsumeKeyPress(int vk); - bool ConsumeMousePress(int btn); - bool ConsumeMouseRelease(int btn); - void ConsumeMouseDelta(float &dx, float &dy); - int ConsumeScrollDelta(); - - // Absolute cursor position (client-area coordinates, for GUI when not captured) + void OnKeyDown(int vkCode); + void OnKeyUp(int vkCode); + void OnMouseButtonDown(int button); + void OnMouseButtonUp(int button); void OnMouseMove(int x, int y); - int GetMouseX() const; - int GetMouseY() const; - HWND GetHWnd() const; + void OnMouseWheel(int delta); + void OnRawMouseDelta(int dx, int dy); - // Mouse capture for FPS look - void SetCapture(bool capture); - bool IsCaptured() const; + bool IsKeyDown(int vkCode) const; + bool IsKeyPressed(int vkCode) const; + bool IsKeyReleased(int vkCode) const; + + bool IsMouseButtonDown(int button) const; + bool IsMouseButtonPressed(int button) const; + bool IsMouseButtonReleased(int button) const; + + int GetMouseX() const { return m_mouseX; } + int GetMouseY() const { return m_mouseY; } + + int GetMouseDeltaX() const { return m_mouseDeltaX; } + int GetMouseDeltaY() const { return m_mouseDeltaY; } + + int GetMouseWheel(); + int PeekMouseWheel() const { return m_mouseWheelAccum; } + void ConsumeMouseWheel() { m_mouseWheelAccum = 0; } + + // Per-frame delta consumption for low-latency mouse look. + // Reads and clears the raw accumulators (not the per-tick snapshot). + void ConsumeMouseDelta(float &dx, float &dy); + + void SetMouseGrabbed(bool grabbed); + bool IsMouseGrabbed() const { return m_mouseGrabbed; } + + void SetCursorHiddenForUI(bool hidden); + bool IsCursorHiddenForUI() const { return m_cursorHiddenForUI; } + + void SetWindowFocused(bool focused); + bool IsWindowFocused() const { return m_windowFocused; } + + bool HasAnyInput() const { return m_hasInput; } + + void SetKBMActive(bool active) { m_kbmActive = active; } + bool IsKBMActive() const { return m_kbmActive; } + + void SetScreenCursorHidden(bool hidden) { m_screenWantsCursorHidden = hidden; } + bool IsScreenCursorHidden() const { return m_screenWantsCursorHidden; } + + float GetMoveX() const; + float GetMoveY() const; + + float GetLookX(float sensitivity) const; + float GetLookY(float sensitivity) const; private: - void CenterCursor(); + bool m_keyDown[MAX_KEYS]; + bool m_keyDownPrev[MAX_KEYS]; - // Per-frame double-buffered state (for IsKeyPressed/Released per-frame edge detection) - bool m_keyState[256]; - bool m_keyStatePrev[256]; - bool m_mouseButtons[3]; - bool m_mouseButtonsPrev[3]; + bool m_keyPressedAccum[MAX_KEYS]; + bool m_keyReleasedAccum[MAX_KEYS]; + bool m_keyPressed[MAX_KEYS]; + bool m_keyReleased[MAX_KEYS]; - // Sticky press accumulators (persist until consumed by game tick) - bool m_keyPressedAccum[256]; - bool m_mousePressedAccum[3]; - bool m_mouseReleasedAccum[3]; + bool m_mouseButtonDown[MAX_MOUSE_BUTTONS]; + bool m_mouseButtonDownPrev[MAX_MOUSE_BUTTONS]; - // Mouse delta accumulators (persist until consumed by game tick) - float m_mouseDeltaXAccum; - float m_mouseDeltaYAccum; + bool m_mouseBtnPressedAccum[MAX_MOUSE_BUTTONS]; + bool m_mouseBtnReleasedAccum[MAX_MOUSE_BUTTONS]; + bool m_mouseBtnPressed[MAX_MOUSE_BUTTONS]; + bool m_mouseBtnReleased[MAX_MOUSE_BUTTONS]; - // Scroll accumulator (persists until consumed by game tick) - int m_scrollDeltaAccum; - - bool m_captured; - HWND m_hWnd; - bool m_initialized; - - // Absolute cursor position in client coordinates int m_mouseX; int m_mouseY; + + int m_mouseDeltaX; + int m_mouseDeltaY; + int m_mouseDeltaAccumX; + int m_mouseDeltaAccumY; + + int m_mouseWheelAccum; + + bool m_mouseGrabbed; + + bool m_cursorHiddenForUI; + + bool m_windowFocused; + + bool m_hasInput; + + bool m_kbmActive; + + bool m_screenWantsCursorHidden; }; -extern KeyboardMouseInput KMInput; +extern KeyboardMouseInput g_KBMInput; #endif // _WINDOWS64 diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index ec89feb18..0f83fae57 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -501,82 +501,90 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) PostQuitMessage(0); break; - // Keyboard/Mouse input handling - case WM_KEYDOWN: - if (!(lParam & 0x40000000)) // ignore auto-repeat - KMInput.OnKeyDown(wParam); - break; - case WM_KEYUP: - KMInput.OnKeyUp(wParam); - break; - case WM_SYSKEYDOWN: - if (wParam == VK_MENU) // Alt key - { - if (!(lParam & 0x40000000)) - KMInput.OnKeyDown(wParam); - return 0; // prevent default Alt behavior - } - return DefWindowProc(hWnd, message, wParam, lParam); - case WM_SYSKEYUP: - if (wParam == VK_MENU) - { - KMInput.OnKeyUp(wParam); - return 0; - } - return DefWindowProc(hWnd, message, wParam, lParam); - case WM_INPUT: - KMInput.OnRawMouseInput(lParam); - break; - case WM_LBUTTONDOWN: - KMInput.OnMouseButton(0, true); - break; - case WM_LBUTTONUP: - KMInput.OnMouseButton(0, false); - break; - case WM_RBUTTONDOWN: - KMInput.OnMouseButton(1, true); - break; - case WM_RBUTTONUP: - KMInput.OnMouseButton(1, false); - break; - case WM_MBUTTONDOWN: - KMInput.OnMouseButton(2, true); - break; - case WM_MBUTTONUP: - KMInput.OnMouseButton(2, false); - break; - case WM_MOUSEWHEEL: - KMInput.OnMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); - break; - case WM_MOUSEMOVE: - KMInput.OnMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - break; - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) - KMInput.SetCapture(false); - break; - case WM_SETFOCUS: - { - // Re-capture when window receives focus (e.g., after clicking on it) - Minecraft *pMinecraft = Minecraft::GetInstance(); - bool shouldCapture = pMinecraft && app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL; - if (shouldCapture) - KMInput.SetCapture(true); - } - break; case WM_KILLFOCUS: - KMInput.SetCapture(false); - KMInput.ClearAllState(); + g_KBMInput.ClearAllState(); + g_KBMInput.SetWindowFocused(false); + if (g_KBMInput.IsMouseGrabbed()) + g_KBMInput.SetMouseGrabbed(false); break; - case WM_SETCURSOR: - // Hide the OS cursor when an Iggy/Flash menu is displayed (it has its own Flash cursor) - if (LOWORD(lParam) == HTCLIENT && !KMInput.IsCaptured() && ui.GetMenuDisplayed(0)) + case WM_SETFOCUS: + g_KBMInput.SetWindowFocused(true); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + int vk = (int)wParam; + if (lParam & 0x40000000) break; // ignore auto-repeat + if (vk == VK_SHIFT) + vk = (MapVirtualKey((lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX) == VK_RSHIFT) ? VK_RSHIFT : VK_LSHIFT; + else if (vk == VK_CONTROL) + vk = (lParam & (1 << 24)) ? VK_RCONTROL : VK_LCONTROL; + else if (vk == VK_MENU) + vk = (lParam & (1 << 24)) ? VK_RMENU : VK_LMENU; + g_KBMInput.OnKeyDown(vk); + break; + } + case WM_KEYUP: + case WM_SYSKEYUP: + { + int vk = (int)wParam; + if (vk == VK_SHIFT) + vk = (MapVirtualKey((lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK_EX) == VK_RSHIFT) ? VK_RSHIFT : VK_LSHIFT; + else if (vk == VK_CONTROL) + vk = (lParam & (1 << 24)) ? VK_RCONTROL : VK_LCONTROL; + else if (vk == VK_MENU) + vk = (lParam & (1 << 24)) ? VK_RMENU : VK_LMENU; + g_KBMInput.OnKeyUp(vk); + break; + } + + case WM_LBUTTONDOWN: + g_KBMInput.OnMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT); + break; + case WM_LBUTTONUP: + g_KBMInput.OnMouseButtonUp(KeyboardMouseInput::MOUSE_LEFT); + break; + case WM_RBUTTONDOWN: + g_KBMInput.OnMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT); + break; + case WM_RBUTTONUP: + g_KBMInput.OnMouseButtonUp(KeyboardMouseInput::MOUSE_RIGHT); + break; + case WM_MBUTTONDOWN: + g_KBMInput.OnMouseButtonDown(KeyboardMouseInput::MOUSE_MIDDLE); + break; + case WM_MBUTTONUP: + g_KBMInput.OnMouseButtonUp(KeyboardMouseInput::MOUSE_MIDDLE); + break; + + case WM_MOUSEMOVE: + g_KBMInput.OnMouseMove(LOWORD(lParam), HIWORD(lParam)); + break; + + case WM_MOUSEWHEEL: + g_KBMInput.OnMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); + break; + + case WM_INPUT: { - SetCursor(NULL); - return TRUE; + UINT dwSize = 0; + GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); + if (dwSize > 0 && dwSize <= 256) + { + BYTE rawBuffer[256]; + if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, rawBuffer, &dwSize, sizeof(RAWINPUTHEADER)) == dwSize) + { + RAWINPUT* raw = (RAWINPUT*)rawBuffer; + if (raw->header.dwType == RIM_TYPEMOUSE) + { + g_KBMInput.OnRawMouseDelta(raw->data.mouse.lLastX, raw->data.mouse.lLastY); + } + } + } } - return DefWindowProc(hWnd, message, wParam, lParam); + break; default: return DefWindowProc(hWnd, message, wParam, lParam); } @@ -852,6 +860,9 @@ void ToggleFullscreen() SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); } g_isFullscreen = !g_isFullscreen; + + if (g_KBMInput.IsWindowFocused()) + g_KBMInput.SetWindowFocused(true); } //-------------------------------------------------------------------------------------- @@ -877,7 +888,7 @@ static Minecraft* InitialiseMinecraftRuntime() ui.init(g_pd3dDevice, g_pImmediateContext, g_pRenderTargetView, g_pDepthStencilView, g_iScreenWidth, g_iScreenHeight); InputManager.Initialise(1, 3, MINECRAFT_ACTION_MAX, ACTION_MAX_MENU); - KMInput.Init(g_hWnd); + g_KBMInput.Init(); DefineActions(); InputManager.SetJoypadMapVal(0, 0); InputManager.SetKeyRepeatRate(0.3f, 0.2f); @@ -1263,12 +1274,16 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, MSG msg = {0}; while( WM_QUIT != msg.message && !app.m_bShutdown) { - if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) + g_KBMInput.Tick(); + + while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); - continue; + if (msg.message == WM_QUIT) break; } + if (msg.message == WM_QUIT) break; + RenderManager.StartFrame(); #if 0 if(pMinecraft->soundEngine->isStreamingWavebankReady() && @@ -1290,7 +1305,34 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, app.UpdateTime(); PIXBeginNamedEvent(0,"Input manager tick"); InputManager.Tick(); - KMInput.Tick(); + + // Detect KBM vs controller input mode + if (InputManager.IsPadConnected(0)) + { + bool controllerUsed = InputManager.ButtonPressed(0) || + InputManager.GetJoypadStick_LX(0, false) != 0.0f || + InputManager.GetJoypadStick_LY(0, false) != 0.0f || + InputManager.GetJoypadStick_RX(0, false) != 0.0f || + InputManager.GetJoypadStick_RY(0, false) != 0.0f; + + if (controllerUsed) + g_KBMInput.SetKBMActive(false); + else if (g_KBMInput.HasAnyInput()) + g_KBMInput.SetKBMActive(true); + } + else + { + g_KBMInput.SetKBMActive(true); + } + + if (!g_KBMInput.IsMouseGrabbed()) + { + if (!g_KBMInput.IsKBMActive()) + g_KBMInput.SetCursorHiddenForUI(true); + else if (!g_KBMInput.IsScreenCursorHidden()) + g_KBMInput.SetCursorHiddenForUI(false); + } + PIXEndNamedEvent(); PIXBeginNamedEvent(0,"Profile manager tick"); // ProfileManager.Tick(); @@ -1420,29 +1462,29 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, ui.CheckMenuDisplayed(); - // Update mouse capture: capture when in-game and no menu is open + // Update mouse grab: grab when in-game and no menu is open { static bool altToggleSuppressCapture = false; bool shouldCapture = app.GetGameStarted() && !ui.GetMenuDisplayed(0) && pMinecraft->screen == NULL; // Left Alt key toggles capture on/off for debugging - if (KMInput.IsKeyPressed(VK_MENU)) + if (g_KBMInput.IsKeyPressed(VK_LMENU) || g_KBMInput.IsKeyPressed(VK_RMENU)) { - if (KMInput.IsCaptured()) { KMInput.SetCapture(false); altToggleSuppressCapture = true; } - else if (shouldCapture) { KMInput.SetCapture(true); altToggleSuppressCapture = false; } + if (g_KBMInput.IsMouseGrabbed()) { g_KBMInput.SetMouseGrabbed(false); altToggleSuppressCapture = true; } + else if (shouldCapture) { g_KBMInput.SetMouseGrabbed(true); altToggleSuppressCapture = false; } } else if (!shouldCapture) { - if (KMInput.IsCaptured()) KMInput.SetCapture(false); + if (g_KBMInput.IsMouseGrabbed()) g_KBMInput.SetMouseGrabbed(false); altToggleSuppressCapture = false; } - else if (shouldCapture && !KMInput.IsCaptured() && GetFocus() == g_hWnd && !altToggleSuppressCapture) + else if (shouldCapture && !g_KBMInput.IsMouseGrabbed() && GetFocus() == g_hWnd && !altToggleSuppressCapture) { - KMInput.SetCapture(true); + g_KBMInput.SetMouseGrabbed(true); } } // F1 toggles the HUD - if (KMInput.IsKeyPressed(VK_F1)) + if (g_KBMInput.IsKeyPressed(VK_F1)) { int primaryPad = ProfileManager.GetPrimaryPad(); unsigned char displayHud = app.GetGameSettings(primaryPad, eGameSetting_DisplayHUD); @@ -1451,7 +1493,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } // F3 toggles onscreen debug info - if (KMInput.IsKeyPressed(VK_F3)) + if (g_KBMInput.IsKeyPressed(VK_F3)) { if (Minecraft* pMinecraft = Minecraft::GetInstance()) { @@ -1464,7 +1506,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, #ifdef _DEBUG_MENUS_ENABLED // F4 Open debug overlay - if (KMInput.IsKeyPressed(VK_F4)) + if (g_KBMInput.IsKeyPressed(VK_F4)) { if (Minecraft *pMinecraft = Minecraft::GetInstance()) { @@ -1477,7 +1519,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, } // F6 Open debug console - if (KMInput.IsKeyPressed(VK_F6)) + if (g_KBMInput.IsKeyPressed(VK_F6)) { static bool s_debugConsole = false; s_debugConsole = !s_debugConsole; @@ -1486,13 +1528,13 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, #endif // F11 Toggle fullscreen - if (KMInput.IsKeyPressed(VK_F11)) + if (g_KBMInput.IsKeyPressed(VK_F11)) { ToggleFullscreen(); } // TAB opens game info menu. - Vvis :3 - Updated by detectiveren - if (KMInput.IsKeyPressed(VK_TAB) && !ui.GetMenuDisplayed(0)) + if (g_KBMInput.IsKeyPressed(VK_TAB) && !ui.GetMenuDisplayed(0)) { if (Minecraft* pMinecraft = Minecraft::GetInstance()) { @@ -1593,8 +1635,6 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, // Fix for #7318 - Title crashes after short soak in the leaderboards menu // A memory leak was caused because the icon renderer kept creating new Vec3's because the pool wasn't reset Vec3::resetPool(); - - KMInput.EndFrame(); } // Free resources, unregister custom classes, and exit. diff --git a/Minecraft.Client/stubs.cpp b/Minecraft.Client/stubs.cpp index 16215ba2f..af65eb75e 100644 --- a/Minecraft.Client/stubs.cpp +++ b/Minecraft.Client/stubs.cpp @@ -3,41 +3,98 @@ #ifdef _WINDOWS64 #include "Windows64\KeyboardMouseInput.h" +static const int s_keyToVK[] = { + 'A', // KEY_A = 0 + 'B', // KEY_B = 1 + 'C', // KEY_C = 2 + 'D', // KEY_D = 3 + 'E', // KEY_E = 4 + 'F', // KEY_F = 5 + 'G', // KEY_G = 6 + 'H', // KEY_H = 7 + 'I', // KEY_I = 8 + 'J', // KEY_J = 9 + 'K', // KEY_K = 10 + 'L', // KEY_L = 11 + 'M', // KEY_M = 12 + 'N', // KEY_N = 13 + 'O', // KEY_O = 14 + 'P', // KEY_P = 15 + 'Q', // KEY_Q = 16 + 'R', // KEY_R = 17 + 'S', // KEY_S = 18 + 'T', // KEY_T = 19 + 'U', // KEY_U = 20 + 'V', // KEY_V = 21 + 'W', // KEY_W = 22 + 'X', // KEY_X = 23 + 'Y', // KEY_Y = 24 + 'Z', // KEY_Z = 25 + VK_SPACE, // KEY_SPACE = 26 + VK_LSHIFT, // KEY_LSHIFT = 27 + VK_ESCAPE, // KEY_ESCAPE = 28 + VK_BACK, // KEY_BACK = 29 + VK_RETURN, // KEY_RETURN = 30 + VK_RSHIFT, // KEY_RSHIFT = 31 + VK_UP, // KEY_UP = 32 + VK_DOWN, // KEY_DOWN = 33 + VK_TAB, // KEY_TAB = 34 + '1', // KEY_1 = 35 + '2', // KEY_2 = 36 + '3', // KEY_3 = 37 + '4', // KEY_4 = 38 + '5', // KEY_5 = 39 + '6', // KEY_6 = 40 + '7', // KEY_7 = 41 + '8', // KEY_8 = 42 + '9', // KEY_9 = 43 + VK_F1, // KEY_F1 = 44 + VK_F3, // KEY_F3 = 45 + VK_F4, // KEY_F4 = 46 + VK_F5, // KEY_F5 = 47 + VK_F6, // KEY_F6 = 48 + VK_F8, // KEY_F8 = 49 + VK_F9, // KEY_F9 = 50 + VK_F11, // KEY_F11 = 51 + VK_ADD, // KEY_ADD = 52 + VK_SUBTRACT,// KEY_SUBTRACT = 53 + VK_LEFT, // KEY_LEFT = 54 + VK_RIGHT, // KEY_RIGHT = 55 +}; +static const int s_keyToVKCount = sizeof(s_keyToVK) / sizeof(s_keyToVK[0]); + +int Keyboard::toVK(int keyConst) +{ + if (keyConst >= 0 && keyConst < s_keyToVKCount) + return s_keyToVK[keyConst]; + return 0; +} + +bool Keyboard::isKeyDown(int keyCode) +{ + int vk = toVK(keyCode); + if (vk > 0) + return g_KBMInput.IsKeyDown(vk); + return false; +} + int Mouse::getX() { - return KMInput.GetMouseX(); + return g_KBMInput.GetMouseX(); } int Mouse::getY() { // Return Y in bottom-up coordinates (OpenGL convention, matching original Java LWJGL Mouse) + extern HWND g_hWnd; RECT rect; - GetClientRect(KMInput.GetHWnd(), &rect); - return (rect.bottom - 1) - KMInput.GetMouseY(); + GetClientRect(g_hWnd, &rect); + return (rect.bottom - 1) - g_KBMInput.GetMouseY(); } bool Mouse::isButtonDown(int button) { - return KMInput.IsMouseDown(button); -} - -bool Keyboard::isKeyDown(int key) -{ - // Map Keyboard constants to Windows virtual key codes - if (key == Keyboard::KEY_LSHIFT) return KMInput.IsKeyDown(VK_LSHIFT); - if (key == Keyboard::KEY_RSHIFT) return KMInput.IsKeyDown(VK_RSHIFT); - if (key == Keyboard::KEY_ESCAPE) return KMInput.IsKeyDown(VK_ESCAPE); - if (key == Keyboard::KEY_RETURN) return KMInput.IsKeyDown(VK_RETURN); - if (key == Keyboard::KEY_BACK) return KMInput.IsKeyDown(VK_BACK); - if (key == Keyboard::KEY_SPACE) return KMInput.IsKeyDown(VK_SPACE); - if (key == Keyboard::KEY_TAB) return KMInput.IsKeyDown(VK_TAB); - if (key == Keyboard::KEY_UP) return KMInput.IsKeyDown(VK_UP); - if (key == Keyboard::KEY_DOWN) return KMInput.IsKeyDown(VK_DOWN); - if (key == Keyboard::KEY_LEFT) return KMInput.IsKeyDown(VK_LEFT); - if (key == Keyboard::KEY_RIGHT) return KMInput.IsKeyDown(VK_RIGHT); - if (key >= Keyboard::KEY_A && key <= Keyboard::KEY_Z) - return KMInput.IsKeyDown('A' + (key - Keyboard::KEY_A)); - return false; + return g_KBMInput.IsMouseButtonDown(button); } #endif diff --git a/Minecraft.Client/stubs.h b/Minecraft.Client/stubs.h index cc7888672..f4ae056f2 100644 --- a/Minecraft.Client/stubs.h +++ b/Minecraft.Client/stubs.h @@ -187,12 +187,13 @@ public: static void create() {} static void destroy() {} #ifdef _WINDOWS64 - static bool isKeyDown(int key); + static bool isKeyDown(int keyCode); #else - static bool isKeyDown(int) {return false;} + static bool isKeyDown(int) { return false; } #endif static wstring getKeyName(int) { return L"KEYNAME"; } static void enableRepeatEvents(bool) {} + static const int KEY_A = 0; static const int KEY_B = 1; static const int KEY_C = 2; @@ -228,8 +229,32 @@ public: static const int KEY_UP = 32; static const int KEY_DOWN = 33; static const int KEY_TAB = 34; - static const int KEY_LEFT = 35; - static const int KEY_RIGHT = 36; + static const int KEY_1 = 35; + static const int KEY_2 = 36; + static const int KEY_3 = 37; + static const int KEY_4 = 38; + static const int KEY_5 = 39; + static const int KEY_6 = 40; + static const int KEY_7 = 41; + static const int KEY_8 = 42; + static const int KEY_9 = 43; + static const int KEY_F1 = 44; + static const int KEY_F3 = 45; + static const int KEY_F4 = 46; + static const int KEY_F5 = 47; + static const int KEY_F6 = 48; + static const int KEY_F8 = 49; + static const int KEY_F9 = 50; + static const int KEY_F11 = 51; + static const int KEY_ADD = 52; + static const int KEY_SUBTRACT = 53; + static const int KEY_LEFT = 54; + static const int KEY_RIGHT = 55; + +#ifdef _WINDOWS64 + // Map LWJGL-style key constant to Windows VK code + static int toVK(int keyConst); +#endif }; class Mouse diff --git a/README.md b/README.md index 7578f159f..960deff38 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,6 @@ The headless server also reads and writes `server.properties` in the working dir - **Toggle View (FPS/TPS)**: `F5` - **Fullscreen**: `F11` - **Pause Menu**: `Esc` -- **Toggle Mouse Capture**: `Left Alt` (for debugging) - **Attack / Destroy**: `Left Click` - **Use / Place**: `Right Click` - **Select Item**: `Mouse Wheel` or keys `1` to `9` From 870d3e4b6854215ff4c0e2aa449e3835e1e37327 Mon Sep 17 00:00:00 2001 From: ModMaker101 <119018978+ModMaker101@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:24:42 -0500 Subject: [PATCH 66/68] Fix mob nametag position to match correct height offset #422 (#440) --- Minecraft.Client/LivingEntityRenderer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/LivingEntityRenderer.cpp b/Minecraft.Client/LivingEntityRenderer.cpp index 89d656142..757948a37 100644 --- a/Minecraft.Client/LivingEntityRenderer.cpp +++ b/Minecraft.Client/LivingEntityRenderer.cpp @@ -487,11 +487,11 @@ void LivingEntityRenderer::renderNameTag(shared_ptr mob, const wst Font *font = getFont(); - float size = 1.60f; - float s = 1 / 60.0f * size; + constexpr float size = 1.60f; + constexpr float s = 1 / 60.0f * size; glPushMatrix(); - glTranslatef((float) x + 0, (float) y + 2.3f, (float) z); + glTranslatef(static_cast(x) + 0, static_cast(y) + mob->bbHeight + 0.5f, static_cast(z)); glNormal3f(0, 1, 0); glRotatef(-this->entityRenderDispatcher->playerRotY, 0, 1, 0); From ef66f6736dc0150c680afba4992b67970dbab992 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Thu, 5 Mar 2026 01:23:29 +0800 Subject: [PATCH 67/68] fix: ignore dedicated server properties in normal world startup --- Minecraft.Client/MinecraftServer.cpp | 65 ++++++++++++++++++++-------- Minecraft.Client/PlayerList.cpp | 4 +- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index a81bce592..af9e226bc 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -31,6 +31,9 @@ #include "..\Minecraft.World\net.minecraft.world.item.h" #include "..\Minecraft.World\net.minecraft.world.item.enchantment.h" #include "..\Minecraft.World\net.minecraft.world.damagesource.h" +#ifdef _WINDOWS64 +#include "Windows64\Network\WinsockNetLayer.h" +#endif #include #ifdef SPLIT_SAVES #include "..\Minecraft.World\ConsoleSaveFileSplit.h" @@ -83,6 +86,30 @@ bool MinecraftServer::s_slowQueuePacketSent = false; unordered_map MinecraftServer::ironTimers; +static bool ShouldUseDedicatedServerProperties() +{ +#ifdef _WINDOWS64 + return g_Win64DedicatedServer; +#else + return false; +#endif +} + +static int GetDedicatedServerInt(Settings *settings, const wchar_t *key, int defaultValue) +{ + return (ShouldUseDedicatedServerProperties() && settings != NULL) ? settings->getInt(key, defaultValue) : defaultValue; +} + +static bool GetDedicatedServerBool(Settings *settings, const wchar_t *key, bool defaultValue) +{ + return (ShouldUseDedicatedServerProperties() && settings != NULL) ? settings->getBoolean(key, defaultValue) : defaultValue; +} + +static wstring GetDedicatedServerString(Settings *settings, const wchar_t *key, const wstring &defaultValue) +{ + return (ShouldUseDedicatedServerProperties() && settings != NULL) ? settings->getString(key, defaultValue) : defaultValue; +} + static void PrintConsoleLine(const wchar_t *prefix, const wstring &message) { wprintf(L"%ls%ls\n", prefix, message.c_str()); @@ -589,14 +616,14 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW #endif settings = new Settings(new File(L"server.properties")); - app.SetGameHostOption(eGameHostOption_Difficulty, settings->getInt(L"difficulty", app.GetGameHostOption(eGameHostOption_Difficulty))); - app.SetGameHostOption(eGameHostOption_GameType, settings->getInt(L"gamemode", app.GetGameHostOption(eGameHostOption_GameType))); - app.SetGameHostOption(eGameHostOption_Structures, settings->getBoolean(L"generate-structures", app.GetGameHostOption(eGameHostOption_Structures) > 0) ? 1 : 0); - app.SetGameHostOption(eGameHostOption_BonusChest, settings->getBoolean(L"bonus-chest", app.GetGameHostOption(eGameHostOption_BonusChest) > 0) ? 1 : 0); - app.SetGameHostOption(eGameHostOption_PvP, settings->getBoolean(L"pvp", app.GetGameHostOption(eGameHostOption_PvP) > 0) ? 1 : 0); - app.SetGameHostOption(eGameHostOption_TrustPlayers, settings->getBoolean(L"trust-players", app.GetGameHostOption(eGameHostOption_TrustPlayers) > 0) ? 1 : 0); - app.SetGameHostOption(eGameHostOption_FireSpreads, settings->getBoolean(L"fire-spreads", app.GetGameHostOption(eGameHostOption_FireSpreads) > 0) ? 1 : 0); - app.SetGameHostOption(eGameHostOption_TNT, settings->getBoolean(L"tnt", app.GetGameHostOption(eGameHostOption_TNT) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_Difficulty, GetDedicatedServerInt(settings, L"difficulty", app.GetGameHostOption(eGameHostOption_Difficulty))); + app.SetGameHostOption(eGameHostOption_GameType, GetDedicatedServerInt(settings, L"gamemode", app.GetGameHostOption(eGameHostOption_GameType))); + app.SetGameHostOption(eGameHostOption_Structures, GetDedicatedServerBool(settings, L"generate-structures", app.GetGameHostOption(eGameHostOption_Structures) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_BonusChest, GetDedicatedServerBool(settings, L"bonus-chest", app.GetGameHostOption(eGameHostOption_BonusChest) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_PvP, GetDedicatedServerBool(settings, L"pvp", app.GetGameHostOption(eGameHostOption_PvP) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_TrustPlayers, GetDedicatedServerBool(settings, L"trust-players", app.GetGameHostOption(eGameHostOption_TrustPlayers) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_FireSpreads, GetDedicatedServerBool(settings, L"fire-spreads", app.GetGameHostOption(eGameHostOption_FireSpreads) > 0) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_TNT, GetDedicatedServerBool(settings, L"tnt", app.GetGameHostOption(eGameHostOption_TNT) > 0) ? 1 : 0); app.DebugPrintf("\n*** SERVER SETTINGS ***\n"); app.DebugPrintf("ServerSettings: host-friends-only is %s\n",(app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0)?"on":"off"); @@ -615,13 +642,13 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW //motd = settings->getString(L"motd", L"A Minecraft Server"); //motd.replace('�', '$'); - setAnimals(settings->getBoolean(L"spawn-animals", true)); - setNpcsEnabled(settings->getBoolean(L"spawn-npcs", true)); + setAnimals(GetDedicatedServerBool(settings, L"spawn-animals", true)); + setNpcsEnabled(GetDedicatedServerBool(settings, L"spawn-npcs", true)); setPvpAllowed(app.GetGameHostOption( eGameHostOption_PvP )>0?true:false); // 4J Stu - We should never have hacked clients flying when they shouldn't be like the PC version, so enable flying always // Fix for #46612 - TU5: Code: Multiplayer: A client can be banned for flying when accidentaly being blown by dynamite - setFlightAllowed(settings->getBoolean(L"allow-flight", true)); + setFlightAllowed(GetDedicatedServerBool(settings, L"allow-flight", true)); // 4J Stu - Enabling flight to stop it kicking us when we use it #ifdef _DEBUG_MENUS_ENABLED @@ -667,7 +694,7 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW __int64 levelNanoTime = System::nanoTime(); - wstring levelName = (initData && !initData->levelName.empty()) ? initData->levelName : settings->getString(L"level-name", L"world"); + wstring levelName = (initData && !initData->levelName.empty()) ? initData->levelName : GetDedicatedServerString(settings, L"level-name", L"world"); wstring levelTypeString; bool gameRuleUseFlatWorld = false; @@ -677,11 +704,11 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW } if(gameRuleUseFlatWorld || app.GetGameHostOption(eGameHostOption_LevelType)>0) { - levelTypeString = settings->getString(L"level-type", L"flat"); + levelTypeString = GetDedicatedServerString(settings, L"level-type", L"flat"); } else { - levelTypeString = settings->getString(L"level-type",L"default"); + levelTypeString = GetDedicatedServerString(settings, L"level-type",L"default"); } LevelType *pLevelType = LevelType::getLevelType(levelTypeString); @@ -702,7 +729,7 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW #endif } - setMaxBuildHeight(settings->getInt(L"max-build-height", Level::maxBuildHeight)); + setMaxBuildHeight(GetDedicatedServerInt(settings, L"max-build-height", Level::maxBuildHeight)); setMaxBuildHeight(((getMaxBuildHeight() + 8) / 16) * 16); setMaxBuildHeight(Mth::clamp(getMaxBuildHeight(), 64, Level::maxBuildHeight)); //settings->setProperty(L"max-build-height", maxBuildHeight); @@ -851,7 +878,7 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring // 4J TODO - free levels here if there are already some? levels = ServerLevelArray(3); - int gameTypeId = settings->getInt(L"gamemode", app.GetGameHostOption(eGameHostOption_GameType));//LevelSettings::GAMETYPE_SURVIVAL); + int gameTypeId = GetDedicatedServerInt(settings, L"gamemode", app.GetGameHostOption(eGameHostOption_GameType));//LevelSettings::GAMETYPE_SURVIVAL); GameType *gameType = LevelSettings::validateGameType(gameTypeId); app.DebugPrintf("Default game type: %d\n" , gameTypeId); @@ -950,7 +977,7 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring #if DEBUG_SERVER_DONT_SPAWN_MOBS levels[i]->setSpawnSettings(false, false); #else - levels[i]->setSpawnSettings(settings->getBoolean(L"spawn-monsters", true), animals); + levels[i]->setSpawnSettings(GetDedicatedServerBool(settings, L"spawn-monsters", true), animals); #endif levels[i]->getLevelData()->setGameType(gameType); @@ -1038,7 +1065,7 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring for (int i = 0; i < levels.length ; i++) { // logger.info("Preparing start region for level " + i); - if (i == 0 || settings->getBoolean(L"allow-nether", true)) + if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true)) { ServerLevel *level = levels[i]; if(levelChunksNeedConverted) @@ -1780,7 +1807,7 @@ void MinecraftServer::run(__int64 seed, void *lpParameter) MinecraftServer::setTimeOfDayAtEndOfTick = false; for (unsigned int i = 0; i < levels.length; i++) { - if (i == 0 || settings->getBoolean(L"allow-nether", true)) + if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true)) { ServerLevel *level = levels[i]; level->setDayTime( MinecraftServer::setTimeOfDay ); diff --git a/Minecraft.Client/PlayerList.cpp b/Minecraft.Client/PlayerList.cpp index 3cf47a1cf..0de0c36be 100644 --- a/Minecraft.Client/PlayerList.cpp +++ b/Minecraft.Client/PlayerList.cpp @@ -53,14 +53,12 @@ PlayerList::PlayerList(MinecraftServer *server) //int viewDistance = server->settings->getInt(L"view-distance", 10); - maxPlayers = server->settings->getInt(L"max-players", 20); - doWhiteList = false; - #ifdef _WINDOWS64 maxPlayers = MINECRAFT_NET_MAX_PLAYERS; #else maxPlayers = server->settings->getInt(L"max-players", 20); #endif + doWhiteList = false; InitializeCriticalSection(&m_kickPlayersCS); InitializeCriticalSection(&m_closePlayersCS); } From 7b35df871444842976e3b8389825f39a4267a270 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Thu, 5 Mar 2026 01:38:34 +0800 Subject: [PATCH 68/68] Fix controller paging regression in creative menu Preserve smooth row-by-row scrolling for mouse wheel input, but restore full-page movement for controller/menu scroll actions in the creative inventory. Commit 3093ca3 changed page indexing to support smooth scrolling, which caused ACTION_MENU_OTHER_STICK_UP/DOWN to advance by one row instead of one page. Track whether the scroll action originated from the mouse wheel and only use single-row steps in that case. Fixes #253 --- .../Common/UI/IUIScene_CreativeMenu.cpp | 22 +++++++++++++++++-- .../Windows64/KeyboardMouseInput.cpp | 5 +++++ .../Windows64/KeyboardMouseInput.h | 4 +++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp index 544fedc0f..04852e97d 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -1098,7 +1098,15 @@ void IUIScene_CreativeMenu::handleAdditionalKeyPress(int iAction) } break; case ACTION_MENU_OTHER_STICK_DOWN: - ++m_tabPage[m_curTab]; + { + int pageStep = TabSpec::rows; +#ifdef _WINDOWS64 + if (g_KBMInput.WasMouseWheelConsumed()) + { + pageStep = 1; + } +#endif + m_tabPage[m_curTab] += pageStep; if(m_tabPage[m_curTab] >= specs[m_curTab]->getPageCount()) { m_tabPage[m_curTab] = specs[m_curTab]->getPageCount() - 1; @@ -1107,9 +1115,18 @@ void IUIScene_CreativeMenu::handleAdditionalKeyPress(int iAction) { switchTab(m_curTab); } + } break; case ACTION_MENU_OTHER_STICK_UP: - --m_tabPage[m_curTab]; + { + int pageStep = TabSpec::rows; +#ifdef _WINDOWS64 + if (g_KBMInput.WasMouseWheelConsumed()) + { + pageStep = 1; + } +#endif + m_tabPage[m_curTab] -= pageStep; if(m_tabPage[m_curTab] < 0) { m_tabPage[m_curTab] = 0; @@ -1118,6 +1135,7 @@ void IUIScene_CreativeMenu::handleAdditionalKeyPress(int iAction) { switchTab(m_curTab); } + } break; } } diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp index 7ab99a719..95c3f4fc2 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp @@ -33,6 +33,7 @@ void KeyboardMouseInput::Init() m_mouseDeltaAccumX = 0; m_mouseDeltaAccumY = 0; m_mouseWheelAccum = 0; + m_mouseWheelConsumed = false; m_mouseGrabbed = false; m_cursorHiddenForUI = false; m_windowFocused = true; @@ -67,6 +68,7 @@ void KeyboardMouseInput::ClearAllState() m_mouseDeltaAccumX = 0; m_mouseDeltaAccumY = 0; m_mouseWheelAccum = 0; + m_mouseWheelConsumed = false; } void KeyboardMouseInput::Tick() @@ -88,6 +90,7 @@ void KeyboardMouseInput::Tick() m_mouseDeltaY = m_mouseDeltaAccumY; m_mouseDeltaAccumX = 0; m_mouseDeltaAccumY = 0; + m_mouseWheelConsumed = false; m_hasInput = (m_mouseDeltaX != 0 || m_mouseDeltaY != 0 || m_mouseWheelAccum != 0); if (!m_hasInput) @@ -172,6 +175,8 @@ void KeyboardMouseInput::OnMouseWheel(int delta) int KeyboardMouseInput::GetMouseWheel() { int val = m_mouseWheelAccum; + if (val != 0) + m_mouseWheelConsumed = true; m_mouseWheelAccum = 0; return val; } diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.h b/Minecraft.Client/Windows64/KeyboardMouseInput.h index f098ccabd..050670550 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.h +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.h @@ -59,7 +59,8 @@ public: int GetMouseWheel(); int PeekMouseWheel() const { return m_mouseWheelAccum; } - void ConsumeMouseWheel() { m_mouseWheelAccum = 0; } + void ConsumeMouseWheel() { if (m_mouseWheelAccum != 0) m_mouseWheelConsumed = true; m_mouseWheelAccum = 0; } + bool WasMouseWheelConsumed() const { return m_mouseWheelConsumed; } // Per-frame delta consumption for low-latency mouse look. // Reads and clears the raw accumulators (not the per-tick snapshot). @@ -114,6 +115,7 @@ private: int m_mouseDeltaAccumY; int m_mouseWheelAccum; + bool m_mouseWheelConsumed; bool m_mouseGrabbed;