From 60189f6ab61b87ba348387d895b29ea6a9da2399 Mon Sep 17 00:00:00 2001 From: pieeebot <274605694+pieeebot@users.noreply.github.com> Date: Wed, 13 May 2026 13:41:16 +0300 Subject: [PATCH 01/32] chore: build v1.0.2 --- BUMP | 2 +- NOTES.md | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/BUMP b/BUMP index a60b8fce..d665b886 100644 --- a/BUMP +++ b/BUMP @@ -1 +1 @@ -1.0.1b +1.0.2b diff --git a/NOTES.md b/NOTES.md index f690ecd1..09f5b5cd 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,13 +1,7 @@ -![Banner](https://github.com/pieeebot/neoLegacy/raw/main/.github/banner.png) +# neoLegacy v1.0.2b -# neoLegacy v1.0.1b - -- Classic Crafting -- Commands support! - - /give - - /tp - /teleport - - /gamemode - - .... +- Tabs on the Load/Create/Join UI are now clickable. +- Fixed a crash when switching game modes using `/gamemode` on Windows. image From f2193765d0bbf45f801dbd9f902cf3c5b303202a Mon Sep 17 00:00:00 2001 From: Fireblade <72758695+Firebladedoge229@users.noreply.github.com> Date: Thu, 14 May 2026 06:27:56 -0400 Subject: [PATCH 02/32] fix: dedicated server (#67) * fix: dedicated server * prioritize item.h when building item mappings --- CMakeLists.txt | 6 +++++- Minecraft.Client/Common/Consoles_App.cpp | 2 ++ Minecraft.Client/Common/UI/UIScene_InventoryMenu.cpp | 2 +- Minecraft.Client/GameRenderer.cpp | 4 ++++ Minecraft.Client/MinecraftServer.cpp | 4 ++++ build-linux.sh | 10 +++++++--- 6 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5623ed4a..52391571 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -235,10 +235,14 @@ if(TARGET Minecraft.Server) endif() set(_item_map_inputs - "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/Tile.h" "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/Item.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/Tile.h" ) +if(CMAKE_CROSSCOMPILING AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + string(REPLACE ";" "\\;" _item_map_inputs "${_item_map_inputs}") +endif() + #neo: added ItemNameMap generation add_custom_command( OUTPUT "${CMAKE_BINARY_DIR}/generated/ItemNameMap.h" diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 89ed29e3..7612806d 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -946,7 +946,9 @@ void CMinecraftApp::InitGameSettings() memset(pProfileSettings,0,sizeof(C_4JProfile::PROFILESETTINGS)); SetDefaultOptions(pProfileSettings,i); Win64_LoadSettings(GameSettingsA[i]); +#ifndef MINECRAFT_SERVER_BUILD ApplyGameSettingsChanged(i); +#endif #elif defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__ C4JStorage::PROFILESETTINGS *pProfileSettings=StorageManager.GetDashboardProfileSettings(i); // 4J-PB - don't cause an options write to happen here diff --git a/Minecraft.Client/Common/UI/UIScene_InventoryMenu.cpp b/Minecraft.Client/Common/UI/UIScene_InventoryMenu.cpp index fe0add7d..9031cb92 100644 --- a/Minecraft.Client/Common/UI/UIScene_InventoryMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_InventoryMenu.cpp @@ -6,7 +6,7 @@ #include "..\..\..\Minecraft.World\net.minecraft.world.item.h" #include "..\..\..\Minecraft.World\net.minecraft.stats.h" #include "..\..\..\Minecraft.World\net.minecraft.world.effect.h" -#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\MultiPlayerLocalPlayer.h" #include "..\..\Minecraft.h" #include "..\..\Options.h" #include "..\..\EntityRenderDispatcher.h" diff --git a/Minecraft.Client/GameRenderer.cpp b/Minecraft.Client/GameRenderer.cpp index d57d2c75..c584b0b9 100644 --- a/Minecraft.Client/GameRenderer.cpp +++ b/Minecraft.Client/GameRenderer.cpp @@ -148,6 +148,7 @@ GameRenderer::GameRenderer(Minecraft *mc) itemInHandRenderer = nullptr; // 4J-PB - set up the local players iteminhand renderers here - needs to be done with lighting enabled so that the render geometry gets compiled correctly +#ifndef MINECRAFT_SERVER_BUILD glEnable(GL_LIGHTING); mc->localitemInHandRenderers[0] = new ItemInHandRenderer(mc);//itemInHandRenderer; mc->localitemInHandRenderers[1] = new ItemInHandRenderer(mc); @@ -170,7 +171,9 @@ GameRenderer::GameRenderer(Minecraft *mc) for(int i=0;iSet(eUpdateEventIsFinished); @@ -183,6 +186,7 @@ GameRenderer::GameRenderer(Minecraft *mc) m_updateThread->SetProcessor(CPU_CORE_CHUNK_UPDATE); m_updateThread->Run(); #endif +#endif } // 4J Stu Added to go with 1.8.2 change diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index db87c954..621af1c5 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -2017,7 +2017,11 @@ void MinecraftServer::run(int64_t seed, void *lpParameter) QueryPerformanceCounter(&asAfterRules); #endif +#ifdef MINECRAFT_SERVER_BUILD + levels[0]->saveToDisc(nullptr, true); +#else levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, true); +#endif #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) QueryPerformanceCounter(&asAfterFlush); diff --git a/build-linux.sh b/build-linux.sh index d5357932..8e35335e 100755 --- a/build-linux.sh +++ b/build-linux.sh @@ -84,6 +84,10 @@ do_cmake_configure() { -imsvc $winsdk/sdk/include/um \ -imsvc $winsdk/sdk/include/shared" + if [[ "$BUILD_TYPE" == "Debug" ]]; then + c_flags="$c_flags -w" + fi + local linker_flags="\ -libpath:$winsdk/crt/lib/x86_64 \ -libpath:$winsdk/sdk/lib/um/x86_64 \ @@ -155,7 +159,7 @@ write_client_launcher() { #!/usr/bin/env bash set -euo pipefail GAME_DIR="$INSTALL_DIR/client" -PERSIST_DIR="\${MC_DATA_DIR:-\$HOME/.local/share/minecraft-lce-client}" +PERSIST_DIR="\${MC_DATA_DIR:-\$HOME/.local/share/neoLegacy/client}" export WINEARCH=win64 export WINEPREFIX="\${WINEPREFIX:-\$HOME/.local/share/minecraft-lce-client-prefix}" export WINEDLLOVERRIDES="winemenubuilder.exe=d" @@ -183,7 +187,7 @@ set -euo pipefail GAME_DIR="$INSTALL_DIR/server" SERVER_PORT="\${MC_PORT:-25565}" SERVER_BIND_IP="\${MC_BIND:-0.0.0.0}" -PERSIST_DIR="\${MC_DATA_DIR:-\$HOME/.local/share/minecraft-lce-server}" +PERSIST_DIR="\${MC_DATA_DIR:-\$HOME/.local/share/neoLegacy/server}" export WINEARCH=win64 export WINEPREFIX="\${WINEPREFIX:-\$HOME/.local/share/minecraft-lce-server-prefix}" export WINEDLLOVERRIDES="winemenubuilder.exe=d;winedbg.exe=d" @@ -226,7 +230,7 @@ set -euo pipefail GAME_DIR="$INSTALL_DIR/fourkit" SERVER_PORT="\${MC_PORT:-25565}" SERVER_BIND_IP="\${MC_BIND:-0.0.0.0}" -PERSIST_DIR="\${MC_DATA_DIR:-\$HOME/.local/share/minecraft-lce-fourkit}" +PERSIST_DIR="\${MC_DATA_DIR:-\$HOME/.local/share/neoLegacy/fourkit}" export WINEARCH=win64 export WINEPREFIX="\${WINEPREFIX:-\$HOME/.local/share/minecraft-lce-fourkit-prefix}" export WINEDLLOVERRIDES="winemenubuilder.exe=d;winedbg.exe=d" From 9db673dc8d3a2bed254efe25457499b00a6ef424 Mon Sep 17 00:00:00 2001 From: pieeebot <274605694+pieeebot@users.noreply.github.com> Date: Thu, 14 May 2026 15:46:32 +0300 Subject: [PATCH 03/32] chore: release v1.0.3b --- BUMP | 2 +- NOTES.md | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/BUMP b/BUMP index d665b886..75363ac2 100644 --- a/BUMP +++ b/BUMP @@ -1 +1 @@ -1.0.2b +1.0.3b diff --git a/NOTES.md b/NOTES.md index 09f5b5cd..aa034806 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,7 +1,6 @@ -# neoLegacy v1.0.2b +# neoLegacy v1.0.3b -- Tabs on the Load/Create/Join UI are now clickable. -- Fixed a crash when switching game modes using `/gamemode` on Windows. +- Fixed a crash when running the server software on Linux via Wine. image From 9b1e25ac01086fa5aa8efcee12042bf36c304d71 Mon Sep 17 00:00:00 2001 From: PUFF_MON <90284792+PUFFMON@users.noreply.github.com> Date: Thu, 14 May 2026 20:58:05 +0200 Subject: [PATCH 04/32] feat: Change cursor icon (#69) --- Minecraft.Client/Common/UI/UIController.cpp | 29 +++++++++++++++++++ Minecraft.Client/Common/UI/UIController.h | 1 + .../Windows64/KeyboardMouseInput.cpp | 14 +++++++++ .../Windows64/KeyboardMouseInput.h | 2 ++ 4 files changed, 46 insertions(+) diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp index 86161e97..04976b5b 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -5,6 +5,7 @@ #include "UIScene.h" #include "UIControl_Slider.h" #include "UIControl_TexturePackList.h" +#include "UIControl_CheckBox.h" #include "UIControl_AchievementsList.h" #include "UIScene_AchievementsMenu.h" #include "../../../Minecraft.World/StringHelpers.h" @@ -836,6 +837,7 @@ void UIController::tickInput() { #ifdef _WINDOWS64 m_mouseClickConsumedByScene = false; + UIControl* currHitCtrl = NULL; if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) { UIScene *pScene = nullptr; @@ -1010,6 +1012,7 @@ void UIController::tickInput() hitControlId = -1; hitArea = INT_MAX; hitCtrl = NULL; + hitCtrl = ctrl; break; // ButtonList takes priority } if (type == UIControl::eAchievementList) @@ -1022,6 +1025,7 @@ void UIController::tickInput() hitControlId = -1; hitArea = INT_MAX; hitCtrl = NULL; + hitCtrl = ctrl; break; } if (type == UIControl::eTexturePackList) @@ -1070,6 +1074,8 @@ void UIController::tickInput() } } } + currHitCtrl = hitCtrl; + UpdateCursorIcon(currHitCtrl); } } @@ -1196,6 +1202,27 @@ void UIController::tickInput() } } +void UIController::UpdateCursorIcon(UIControl *hitCtrl) +{ + // from WinUser.h + if (hitCtrl && (hitCtrl->getControlType() == UIControl::eButton || hitCtrl->getControlType() == UIControl::eButtonList)) + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_HAND)); + else if (hitCtrl && (hitCtrl->getControlType() == UIControl::eSlider || hitCtrl->getControlType() == UIControl::eTexturePackList)) + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_SIZEWE)); + else if (hitCtrl && hitCtrl->getControlType() == UIControl::eTextInput) + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_IBEAM)); + else if (hitCtrl && hitCtrl->getControlType() == UIControl::eCheckBox) // Show the cross sign shaped cursor only when the checkbox is disabled/grayed out + { + UIControl_CheckBox *pCheck = static_cast(hitCtrl); + if (pCheck && !pCheck->IsEnabled()) + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_NO)); + else + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_HAND)); + } + else + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_ARROW)); +} + void UIController::handleInput() { // For each user, loop over each key type and send messages based on the state @@ -2078,6 +2105,7 @@ bool UIController::NavigateToScene(int iPad, EUIScene scene, void *initData, EUI SetMenuDisplayed(menuDisplayedPad,true); bool success = m_groups[static_cast(group)]->NavigateToScene(iPad, scene, initData, layer); if(success && group == eUIGroup_Fullscreen) setFullscreenMenuDisplayed(true); + UpdateCursorIcon(nullptr); LeaveCriticalSection(&m_navigationLock); timer.PrintElapsedTime(L"Navigate to scene"); @@ -2117,6 +2145,7 @@ bool UIController::NavigateBack(int iPad, bool forceUsePad, EUIScene eScene, EUI navComplete = m_groups[static_cast(eUIGroup_Fullscreen)]->NavigateBack(iPad, eScene, eLayer); if(!m_groups[static_cast(eUIGroup_Fullscreen)]->GetMenuDisplayed()) SetMenuDisplayed(XUSER_INDEX_ANY,false); } + UpdateCursorIcon(nullptr); return navComplete; } diff --git a/Minecraft.Client/Common/UI/UIController.h b/Minecraft.Client/Common/UI/UIController.h index 776de903..27c5c61d 100644 --- a/Minecraft.Client/Common/UI/UIController.h +++ b/Minecraft.Client/Common/UI/UIController.h @@ -262,6 +262,7 @@ public: // INPUT private: void tickInput(); + void UpdateCursorIcon(UIControl *hitCtrl); void handleInput(); void handleKeyPress(unsigned int iPad, unsigned int key); diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp index be6efe90..3b6f5aec 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp @@ -390,6 +390,20 @@ float KeyboardMouseInput::GetLookY(float sensitivity) const return static_cast(-m_mouseDeltaY) * sensitivity; } +void KeyboardMouseInput::SetCursorIcon(LPCWSTR cursorName) +{ + HCURSOR hCursor = LoadCursorW(nullptr, cursorName); + if (hCursor) + { + SetCursor(hCursor); + + if (g_hWnd) + { + SetClassLongPtrW(g_hWnd, GCLP_HCURSOR, (LONG_PTR)hCursor); + } + } +} + void KeyboardMouseInput::OnChar(wchar_t c) { int next = (m_charBufferHead + 1) % CHAR_BUFFER_SIZE; diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.h b/Minecraft.Client/Windows64/KeyboardMouseInput.h index 23158ed2..ed98a03c 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.h +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.h @@ -104,6 +104,8 @@ public: float GetLookX(float sensitivity) const; float GetLookY(float sensitivity) const; + void SetCursorIcon(LPCWSTR cursorName); + private: bool m_keyDown[MAX_KEYS]; bool m_keyDownPrev[MAX_KEYS]; From e176a9f5eca5674509f4ef267585931de925b391 Mon Sep 17 00:00:00 2001 From: NSDeathman <104826306+NSDeathman@users.noreply.github.com> Date: Fri, 15 May 2026 16:23:17 +0400 Subject: [PATCH 05/32] fix(TU31): fix podzol down face texture (#71) --- Minecraft.World/DirtTile.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Minecraft.World/DirtTile.cpp b/Minecraft.World/DirtTile.cpp index eb199945..00515129 100644 --- a/Minecraft.World/DirtTile.cpp +++ b/Minecraft.World/DirtTile.cpp @@ -42,8 +42,20 @@ Icon* DirtTile::getTexture(int face, int data) if (data < 0 || data >= DIRT_NAMES_LENGTH) data = 0; - if (TEXTURE_NAMES[data] == L"dirt_podzol") { - return (face == Facing::UP) ? podzolTop : podzolSide; + if (TEXTURE_NAMES[data] == L"dirt_podzol") + { + switch(face) + { + case Facing::UP: + return podzolTop; + break; + case Facing::DOWN: + return Tile::dirt->getTexture(face); + break; + default: + return podzolSide; + break; + } } return icons[data]; From 8b9058b13f39d557c0c193be4fa8fb3474179197 Mon Sep 17 00:00:00 2001 From: NSDeathman <104826306+NSDeathman@users.noreply.github.com> Date: Fri, 15 May 2026 21:13:02 +0400 Subject: [PATCH 06/32] feat(TU31): Poppy & Dandelion now yield 1 dye instead of 2 like TU31 (#76) --- Minecraft.World/ClothDyeRecipes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Minecraft.World/ClothDyeRecipes.cpp b/Minecraft.World/ClothDyeRecipes.cpp index 4662d604..a779b177 100644 --- a/Minecraft.World/ClothDyeRecipes.cpp +++ b/Minecraft.World/ClothDyeRecipes.cpp @@ -38,11 +38,11 @@ void ClothDyeRecipes::addRecipes(Recipes *r) } // some dye recipes - r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::YELLOW), + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 1, DyePowderItem::YELLOW), L"tg", Tile::flower,L'D'); - r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::RED), + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 1, DyePowderItem::RED), L"tg", Tile::rose,L'D'); From faea0e7caab9ac644c7eb9e1fad59dfff7603521 Mon Sep 17 00:00:00 2001 From: Fireblade <72758695+Firebladedoge229@users.noreply.github.com> Date: Fri, 15 May 2026 17:25:26 -0400 Subject: [PATCH 07/32] fix: skin geometry crash on multiplayer servers fixes #72 --- Minecraft.Client/Common/Consoles_App.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 7612806d..82043249 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -9613,8 +9613,9 @@ bool CMinecraftApp::DLCContentRetrieved(eDLCMarketplaceType eType) void CMinecraftApp::SetAdditionalSkinBoxes(DWORD dwSkinID, SKIN_BOX *SkinBoxA, DWORD dwSkinBoxC) { - EntityRenderer *renderer = EntityRenderDispatcher::instance->getRenderer(eTYPE_PLAYER); - Model *pModel = renderer->getModel(); + EntityRenderDispatcher *dispatcher = EntityRenderDispatcher::instance; + EntityRenderer *renderer = dispatcher ? dispatcher->getRenderer(eTYPE_PLAYER) : nullptr; + Model *pModel = renderer ? renderer->getModel() : nullptr; vector *pvModelPart = new vector; vector *pvSkinBoxes = new vector; @@ -9645,8 +9646,9 @@ void CMinecraftApp::SetAdditionalSkinBoxes(DWORD dwSkinID, SKIN_BOX *SkinBoxA, D vector * CMinecraftApp::SetAdditionalSkinBoxes(DWORD dwSkinID, vector *pvSkinBoxA) { - EntityRenderer *renderer = EntityRenderDispatcher::instance->getRenderer(eTYPE_PLAYER); - Model *pModel = renderer->getModel(); + EntityRenderDispatcher *dispatcher = EntityRenderDispatcher::instance; + EntityRenderer *renderer = dispatcher ? dispatcher->getRenderer(eTYPE_PLAYER) : nullptr; + Model *pModel = renderer ? renderer->getModel() : nullptr; vector *pvModelPart = new vector; EnterCriticalSection( &csAdditionalModelParts ); From fd43009ab689a4a665e2f97a6df296f4ab640890 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit <116128211+DrPerkyLegit@users.noreply.github.com> Date: Sat, 16 May 2026 05:17:52 -0400 Subject: [PATCH 08/32] chore: bump network version to 570 (#78) Updated the build number from 560 to 570. All clients will be prompted with version miss-matches when joining smartcmd servers and cannot connect to clients older than this point --- cmake/GenerateBuildVer.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/GenerateBuildVer.cmake b/cmake/GenerateBuildVer.cmake index 5db7f0d0..aa4e52bd 100644 --- a/cmake/GenerateBuildVer.cmake +++ b/cmake/GenerateBuildVer.cmake @@ -7,7 +7,7 @@ if(NOT OUTPUT_FILE) message(FATAL_ERROR "OUTPUT_FILE must be set.") endif() -set(BUILD_NUMBER 560) # Note: Build/network has to stay static for now, as without it builds wont be able to play together. We can change it later when we have a better versioning scheme in place. +set(BUILD_NUMBER 570) # Note: Build/network has to stay static for now, as without it builds wont be able to play together. We can change it later when we have a better versioning scheme in place. set(SUFFIX "") # Get short SHA @@ -88,4 +88,4 @@ if(_changed) file(RENAME "${_tmp}" "${OUTPUT_FILE}") else() file(REMOVE "${_tmp}") -endif() \ No newline at end of file +endif() From ce9ee06d41510ff90021be83d72a31053b3c44f9 Mon Sep 17 00:00:00 2001 From: NSDeathman <104826306+NSDeathman@users.noreply.github.com> Date: Sat, 16 May 2026 13:32:47 +0400 Subject: [PATCH 09/32] feat(TU31): Village roads have base block underneath like in TU31 (#73) https://minecraft.wiki/w/Xbox_360_Edition_TU31 "World generation: Gravel roads in villages have cobblestone underneath, to prevent them from collapsing into caves." --- Minecraft.World/VillagePieces.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Minecraft.World/VillagePieces.cpp b/Minecraft.World/VillagePieces.cpp index dcd765e0..7ab2c66b 100644 --- a/Minecraft.World/VillagePieces.cpp +++ b/Minecraft.World/VillagePieces.cpp @@ -709,7 +709,8 @@ BoundingBox *VillagePieces::StraightRoad::findPieceBox(StartPiece *startPiece, l bool VillagePieces::StraightRoad::postProcess(Level *level, Random *random, BoundingBox *chunkBB) { - int tile = biomeBlock(Tile::gravel_Id, 0); + int roadTile = biomeBlock(Tile::gravel_Id, 0); + int baseTile = biomeBlock(Tile::cobblestone_Id, 0); for (int x = boundingBox->x0; x <= boundingBox->x1; x++) { for (int z = boundingBox->z0; z <= boundingBox->z1; z++) @@ -717,7 +718,8 @@ bool VillagePieces::StraightRoad::postProcess(Level *level, Random *random, Boun if (chunkBB->isInside(x, 64, z)) { int y = level->getTopSolidBlock(x, z) - 1; - level->setTileAndData(x, y, z,tile, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y, z, roadTile, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y - 1, z, baseTile, 0, Tile::UPDATE_CLIENTS); } } } From 3bc06a62a2475493e5b0e3af3f5640a8ef05be46 Mon Sep 17 00:00:00 2001 From: NSDeathman <104826306+NSDeathman@users.noreply.github.com> Date: Sun, 17 May 2026 02:41:02 +0400 Subject: [PATCH 10/32] Bug fix: now fences and glass planes do not turn grass to dirt (#80) --- Minecraft.World/GrassTile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.World/GrassTile.cpp b/Minecraft.World/GrassTile.cpp index c42ea940..c822fcfd 100644 --- a/Minecraft.World/GrassTile.cpp +++ b/Minecraft.World/GrassTile.cpp @@ -122,7 +122,7 @@ void GrassTile::tick(Level *level, int x, int y, int z, Random *random) } Material* above = level->getMaterial(x, y + 1, z); - if (above->isSolid() || above->isLiquid()) level->setTileAndUpdate(x, y, z, Tile::dirt_Id); + if (above->isLiquid()) level->setTileAndUpdate(x, y, z, Tile::dirt_Id); } int GrassTile::getResource(int data, Random *random, int playerBonusLevel) From 4cb96bcb44d48b3fb1c18f4828b8e831f132bf15 Mon Sep 17 00:00:00 2001 From: /home/neo <158327205+neoapps-dev@users.noreply.github.com> Date: Sun, 17 May 2026 02:12:16 +0300 Subject: [PATCH 11/32] revert last commit --- Minecraft.World/GrassTile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.World/GrassTile.cpp b/Minecraft.World/GrassTile.cpp index c822fcfd..c42ea940 100644 --- a/Minecraft.World/GrassTile.cpp +++ b/Minecraft.World/GrassTile.cpp @@ -122,7 +122,7 @@ void GrassTile::tick(Level *level, int x, int y, int z, Random *random) } Material* above = level->getMaterial(x, y + 1, z); - if (above->isLiquid()) level->setTileAndUpdate(x, y, z, Tile::dirt_Id); + if (above->isSolid() || above->isLiquid()) level->setTileAndUpdate(x, y, z, Tile::dirt_Id); } int GrassTile::getResource(int data, Random *random, int playerBonusLevel) From d36a027e00bf25545b2025d1f44bbfd9ab37faa8 Mon Sep 17 00:00:00 2001 From: NSDeathman <104826306+NSDeathman@users.noreply.github.com> Date: Mon, 18 May 2026 22:13:26 +0400 Subject: [PATCH 12/32] feat(TU31): Villagers turn into witches when struck by lightning (#83) * Villagers turn into witches when struck by lightning * fix: include statement for if a villager has a custom nametag * fix: fix unneccesary line * Build fix --------- Co-authored-by: piebot <274605694+pieeebot@users.noreply.github.com> --- Minecraft.World/Villager.cpp | 18 +++++++++++++++++- Minecraft.World/Villager.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Minecraft.World/Villager.cpp b/Minecraft.World/Villager.cpp index e20a9924..a181df84 100644 --- a/Minecraft.World/Villager.cpp +++ b/Minecraft.World/Villager.cpp @@ -156,7 +156,7 @@ bool Villager::mobInteract(shared_ptr player) shared_ptr item = player->inventory->getSelected(); bool holdingSpawnEgg = item != nullptr && item->id == Item::spawnEgg_Id; - if (!holdingSpawnEgg && isAlive() && !isTrading() && !isBaby()) + if (!player->isSneaking() && !holdingSpawnEgg && isAlive() && !isTrading() && !isBaby()) { if (!level->isClientSide) { @@ -776,3 +776,19 @@ wstring Villager::getDisplayName() }; return app.GetString(name); } + +void Villager::thunderHit(const LightningBolt *lightningBolt) +{ + if (level->isClientSide) return; + shared_ptr witch = std::make_shared(level); + witch->moveTo(x, y, z, yRot, xRot); + + if (this->hasCustomName()) + witch->setCustomName(this->getCustomName()); + + if (this->isPersistenceRequired()) + witch->setPersistenceRequired(); + + level->addEntity(witch); + remove(); +} diff --git a/Minecraft.World/Villager.h b/Minecraft.World/Villager.h index d8aeb15a..f08a028b 100644 --- a/Minecraft.World/Villager.h +++ b/Minecraft.World/Villager.h @@ -147,4 +147,5 @@ public: virtual shared_ptr getBreedOffspring(shared_ptr target); virtual bool canBeLeashed(); virtual wstring getDisplayName(); + virtual void thunderHit(const LightningBolt *lightningBolt); }; \ No newline at end of file From 42ee0b519ebf13abbebeb590ca8c9f681b080d55 Mon Sep 17 00:00:00 2001 From: NSDeathman <104826306+NSDeathman@users.noreply.github.com> Date: Mon, 18 May 2026 23:02:31 +0400 Subject: [PATCH 13/32] feat(TU31): Creepers can be ignited with flint and steel (#84) * TU31 feature: Creepers can be ignited with flint and steel * make mobInteract more accurate to decomp --------- Co-authored-by: piebot <274605694+pieeebot@users.noreply.github.com> --- Minecraft.World/Creeper.cpp | 36 +++++++++++++++++++++++++++++++++++ Minecraft.World/Creeper.h | 10 +++++++++- Minecraft.World/SwellGoal.cpp | 6 ++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/Minecraft.World/Creeper.cpp b/Minecraft.World/Creeper.cpp index dde58894..e5250fdb 100644 --- a/Minecraft.World/Creeper.cpp +++ b/Minecraft.World/Creeper.cpp @@ -26,6 +26,7 @@ void Creeper::_init() oldSwell = 0; maxSwell = 30; explosionRadius = 3; + ignited = false; } Creeper::Creeper(Level *level) : Monster( level ) @@ -190,3 +191,38 @@ void Creeper::thunderHit(const LightningBolt *lightningBolt) Monster::thunderHit(lightningBolt); entityData->set(DATA_IS_POWERED, static_cast(1)); } + +void Creeper::Ignite() +{ + setSwellDir(1); + ignited = true; +} + +bool Creeper::isIgnited() +{ + return ignited; +} + +bool Creeper::mobInteract(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + + if (item == nullptr || item->id != Item::flintAndSteel_Id) + return Mob::mobInteract(player); + + playSound(eSoundType_FIRE_NEWIGNITE, 1, random->nextFloat() * 0.4f + 0.8f); + player->swing(); + + if (!level->isClientSide) + { + if (!isIgnited()) + { + Ignite(); + item->hurtAndBreak(1, player); + return true; + } + return Mob::mobInteract(player); + } + + return true; +} diff --git a/Minecraft.World/Creeper.h b/Minecraft.World/Creeper.h index d5cd4cff..ed9ff59d 100644 --- a/Minecraft.World/Creeper.h +++ b/Minecraft.World/Creeper.h @@ -21,6 +21,8 @@ private: int maxSwell; int explosionRadius; + bool ignited; + void _init(); public: @@ -34,6 +36,8 @@ public: virtual int getMaxFallDistance(); + virtual bool mobInteract(shared_ptr player); + protected: virtual void causeFallDamage(float distance); virtual void defineSynchedData(); @@ -61,5 +65,9 @@ protected: public: int getSwellDir(); void setSwellDir(int dir); - void thunderHit(const LightningBolt *lightningBolt) ; + void thunderHit(const LightningBolt *lightningBolt); + +public: + void Ignite(); + bool isIgnited(); }; diff --git a/Minecraft.World/SwellGoal.cpp b/Minecraft.World/SwellGoal.cpp index 0fd3c34a..a77037a4 100644 --- a/Minecraft.World/SwellGoal.cpp +++ b/Minecraft.World/SwellGoal.cpp @@ -32,6 +32,12 @@ void SwellGoal::stop() void SwellGoal::tick() { + if(creeper->isIgnited()) + { + creeper->setSwellDir(1); + return; + } + if (target.lock() == nullptr) { creeper->setSwellDir(-1); From db6d5a76c24461cdbcc72ed74e24c28df72b34ed Mon Sep 17 00:00:00 2001 From: pieeebot <274605694+pieeebot@users.noreply.github.com> Date: Tue, 19 May 2026 12:18:34 +0300 Subject: [PATCH 14/32] chore: release v1.0.4b --- BUMP | 2 +- NOTES.md | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/BUMP b/BUMP index 75363ac2..d99a1985 100644 --- a/BUMP +++ b/BUMP @@ -1 +1 @@ -1.0.3b +1.0.4b diff --git a/NOTES.md b/NOTES.md index aa034806..966c46f4 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,8 +1,16 @@ -# neoLegacy v1.0.3b +# neoLegacy v1.0.4b -- Fixed a crash when running the server software on Linux via Wine. +### Bug Fixes +- Fixed Podzol bottom face displaying incorrect texture (was using side texture instead of dirt) +- Fixed cursor icon not updating when hovering over UI elements -image +### Changes +- Added TU31 parity changes which include: + - Creepers can now be ignited with Flint and Steel + - Village gravel roads now have Cobblestone underneath + - Villagers now transform into Witches when struck by lightning + +roadmap # Download Get the latest build from [LCE Emerald Launcher](https://github.com/LCE-Hub/LCE-Emerald-Launcher/releases) or the upcoming LC Launcher. From b91b0c9810f2c22dc254cb090ff2e0ba00afbc2e Mon Sep 17 00:00:00 2001 From: pieeebot <274605694+pieeebot@users.noreply.github.com> Date: Tue, 19 May 2026 12:19:42 +0300 Subject: [PATCH 15/32] chore: fix patch note --- NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NOTES.md b/NOTES.md index 966c46f4..933ebe47 100644 --- a/NOTES.md +++ b/NOTES.md @@ -2,9 +2,9 @@ ### Bug Fixes - Fixed Podzol bottom face displaying incorrect texture (was using side texture instead of dirt) -- Fixed cursor icon not updating when hovering over UI elements ### Changes +- Cursor icon now changes when hovering over different UI elements - Added TU31 parity changes which include: - Creepers can now be ignited with Flint and Steel - Village gravel roads now have Cobblestone underneath From 092d9481dcd3ad1a9221642db08aac0cf7ae6935 Mon Sep 17 00:00:00 2001 From: car keys <109132519+meowica@users.noreply.github.com> Date: Tue, 19 May 2026 20:12:34 +0800 Subject: [PATCH 16/32] chore: fix broken link (#92) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c6124e69..88f7bb9d 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Huge thanks to the following projects: - [Patoke/LCERenewed](https://github.com/Patoke/LCERenewed) - for some of the patches that required deep decompilation - [itsRevela/LCE-Revelations](https://github.com/itsRevela/LCE-Revelations) - for providing a stable project for neoLegacy to continue with -- [GabsPuNs/MinecraftConsoles](https://github.com/GabsPuNs/MinecraftConsoles) - for providing us with their implemention of the Classic Crafting Feature. +- [GabsPuNs/Project-Zenith](https://github.com/GabsPuNs/Project-Zenith-Main) - for providing us with their implemention of the Classic Crafting Feature. # Build & Run From c326eabbb430302cd6d6261364ab416df53b9498 Mon Sep 17 00:00:00 2001 From: piebot <274605694+pieeebot@users.noreply.github.com> Date: Tue, 19 May 2026 15:15:01 +0300 Subject: [PATCH 17/32] chore: update revelations link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88f7bb9d..008a0bee 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Users can download our [Nightly Build](https://github.com/pieeebot/neoLegacy/rel Huge thanks to the following projects: - [Patoke/LCERenewed](https://github.com/Patoke/LCERenewed) - for some of the patches that required deep decompilation -- [itsRevela/LCE-Revelations](https://github.com/itsRevela/LCE-Revelations) - for providing a stable project for neoLegacy to continue with +- [itsRevela/LCE-Revelations](https://git.revela.dev/itsRevela/LCE-Revelations) - for providing a stable project for neoLegacy to continue with - [GabsPuNs/Project-Zenith](https://github.com/GabsPuNs/Project-Zenith-Main) - for providing us with their implemention of the Classic Crafting Feature. # Build & Run From dc5ad7aa6e68a56c07adb4e510784193e87c2471 Mon Sep 17 00:00:00 2001 From: NSDeathman <104826306+NSDeathman@users.noreply.github.com> Date: Wed, 20 May 2026 14:05:12 +0400 Subject: [PATCH 18/32] feat(TU31): added craft recipes for andesite, diorite and granite (and polished) blocks (#97) --- Minecraft.World/StructureRecipies.cpp | 48 +++++++++++++++++++++++++-- Minecraft.World/Tile.cpp | 2 +- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/Minecraft.World/StructureRecipies.cpp b/Minecraft.World/StructureRecipies.cpp index 150c0848..78454512 100644 --- a/Minecraft.World/StructureRecipies.cpp +++ b/Minecraft.World/StructureRecipies.cpp @@ -76,8 +76,52 @@ void StructureRecipies::addRecipes(Recipes *r) L'#', new ItemInstance(Tile::quartzBlock, 1, QuartzBlockTile::TYPE_DEFAULT), L'S'); - - + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 2, StoneTile::DIORITE), // + L"ssctcig", + L"#Q", // + L"Q#", // + + L'#', Tile::cobblestone, L'Q', Item::netherQuartz, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 1, StoneTile::GRANITE), // + L"sczcig", + L"#Q", // + + L'#', new ItemInstance(Tile::stone_Id, 1, StoneTile::DIORITE), L'Q', Item::netherQuartz, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 2, StoneTile::ANDESITE), // + L"sczctg", + L"#-", // + + L'#', new ItemInstance(Tile::stone_Id, 1, StoneTile::DIORITE), L'-', Tile::cobblestone, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 4, StoneTile::POLISHED_DIORITE), // + L"ssczg", + L"##", // + L"##", // + + L'#', new ItemInstance(Tile::stone_Id, 1, StoneTile::DIORITE), + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 4, StoneTile::POLISHED_GRANITE), // + L"ssczg", + L"##", // + L"##", // + + L'#', new ItemInstance(Tile::stone_Id, 1, StoneTile::GRANITE), + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 4, StoneTile::POLISHED_ANDESITE), // + L"ssczg", + L"##", // + L"##", // + + L'#', new ItemInstance(Tile::stone_Id, 1, StoneTile::ANDESITE), + L'S'); // 4J Stu - Changed the order, as the blocks that go with sandstone cause a 3-icon scroll // that touches the text "Structures" in the title in 720 fullscreen. diff --git a/Minecraft.World/Tile.cpp b/Minecraft.World/Tile.cpp index b25c6fc5..28bdd939 100644 --- a/Minecraft.World/Tile.cpp +++ b/Minecraft.World/Tile.cpp @@ -324,7 +324,7 @@ void Tile::staticCtor() Tile::tiles = new Tile *[TILE_NUM_COUNT]; memset( tiles, 0, sizeof( Tile *)*TILE_NUM_COUNT ); - Tile::stone = (new StoneTile(1)) ->setDestroyTime(1.5f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setIconName(L"stone")->setDescriptionId(IDS_TILE_STONE)->setUseDescriptionId(IDS_DESC_STONE); + Tile::stone = (new StoneTile(1))->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_stone)->setDestroyTime(1.5f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setIconName(L"stone")->setDescriptionId(IDS_TILE_STONE)->setUseDescriptionId(IDS_DESC_STONE); Tile::grass = static_cast((new GrassTile(2))->setDestroyTime(0.6f)->setSoundType(Tile::SOUND_GRASS)->setIconName(L"grass")->setDescriptionId(IDS_TILE_GRASS)->setUseDescriptionId(IDS_DESC_GRASS)); Tile::dirt = (new DirtTile(3)) ->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_GRAVEL)->setIconName(L"dirt")->setDescriptionId(IDS_TILE_DIRT)->setUseDescriptionId(IDS_DESC_DIRT); Tile::cobblestone = (new Tile(4, Material::stone)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_stone)->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setIconName(L"cobblestone")->setDescriptionId(IDS_TILE_STONE_BRICK)->setUseDescriptionId(IDS_DESC_STONE_BRICK); From d55a38eb84480dc698ab5564b6078940ae48f085 Mon Sep 17 00:00:00 2001 From: Huckle <142506495+thehuckledev@users.noreply.github.com> Date: Wed, 20 May 2026 15:45:57 +0100 Subject: [PATCH 19/32] feat: Make -ip and -port args join server on game launch (#96) feat: Add -quitondisconnect arg Co-authored-by: TheHuckle --- Minecraft.Client/Common/Consoles_App.cpp | 12 ++ Minecraft.Client/Common/UI/UIScene_Intro.cpp | 158 ++++++++++++++++++ .../Windows64/Network/WinsockNetLayer.cpp | 1 + .../Windows64/Network/WinsockNetLayer.h | 1 + .../Windows64/Windows64_Minecraft.cpp | 7 +- 5 files changed, 178 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 82043249..7b4cdfaa 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -27,6 +27,9 @@ #include "../GameMode.h" #include "../Xbox/Social/SocialManager.h" #include "Tutorial/TutorialMode.h" +#ifdef _WINDOWS64 +#include "../Windows64/Network/WinsockNetLayer.h" // HUCKLE - added for quit on disconnect +#endif #if defined _XBOX || defined _WINDOWS64 #include "../Xbox/XML/ATGXmlParser.h" #include "../Xbox/XML/xmlFilesCallback.h" @@ -3388,6 +3391,15 @@ void CMinecraftApp::HandleXuiActions(void) SetAction(i,eAppAction_Idle); + // HUCKLE - added for quit game on disconnect +#ifdef _WINDOWS64 + if(g_Win64MultiplayerQuitOnDisconnect == true) + { + app.ExitGame(); + return; + } +#endif + // If we're already leaving don't exit if (g_NetworkManager.IsLeavingGame()) { diff --git a/Minecraft.Client/Common/UI/UIScene_Intro.cpp b/Minecraft.Client/Common/UI/UIScene_Intro.cpp index f4aefc40..975df407 100644 --- a/Minecraft.Client/Common/UI/UIScene_Intro.cpp +++ b/Minecraft.Client/Common/UI/UIScene_Intro.cpp @@ -2,6 +2,12 @@ #include "UI.h" #include "UIScene_Intro.h" +// HUCKLE - added below for joining game on launch +#ifdef _WINDOWS64 +#include "../../Windows64/Network/WinsockNetLayer.h" +#include "../../User.h" +#endif + UIScene_Intro::UIScene_Intro(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) { @@ -104,6 +110,82 @@ void UIScene_Intro::handleInput(int iPad, int key, bool repeat, bool pressed, bo } #elif defined _XBOX_ONE ui.NavigateToScene(0,eUIScene_MainMenu); +#elif defined _WINDOWS64 + // HUCKLE - added this for auto joining servers on game launch + // THANKS so much to DrPerky and GeorgeV22 for helping with this bit, honestly got stuck for 4 hours :sob: + if(g_Win64MultiplayerJoin == true) + { + int primaryPad = ProfileManager.GetPrimaryPad(); + + if (!ProfileManager.IsSignedIn(primaryPad) || ProfileManager.IsGuest(primaryPad)) + { + UINT uiIDA[1] = { IDS_OK }; + ui.RequestErrorMessage(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 1); + ui.NavigateToScene(0, eUIScene_MainMenu); + return; + } + + app.ClearSignInChangeUsersMask(); + app.ReleaseSaveThumbnail(); + ProfileManager.SetLockedProfile(primaryPad); + ProfileManager.QuerySigninStatus(); + + if (!app.DLCInstallProcessCompleted()) + app.StartInstallDLCProcess(primaryPad); + + Minecraft* pMinecraft = Minecraft::GetInstance(); + pMinecraft->user->name = convStringToWstring(ProfileManager.GetGamertag(primaryPad)); + app.ApplyGameSettingsChanged(primaryPad); + + auto sessionInfo = std::make_unique(); + + // label and name + const wchar_t* defaultName = L""; + size_t nameLen = wcslen(defaultName); + + // ip and port + strncpy_s(sessionInfo->data.hostIP, g_Win64MultiplayerIP, sizeof(sessionInfo->data.hostIP) - 1); + sessionInfo->data.hostPort = g_Win64MultiplayerPort; + + // display label + sessionInfo->displayLabel = new wchar_t[nameLen + 1]; + wcscpy_s(sessionInfo->displayLabel, nameLen + 1, defaultName); + sessionInfo->displayLabelLength = static_cast(nameLen); + sessionInfo->displayLabelViewableStartIndex = 0; + + // name + wcsncpy_s(sessionInfo->data.hostName, XUSER_NAME_SIZE, defaultName, _TRUNCATE); + + // session ids + sessionInfo->sessionId = static_cast(inet_addr(g_Win64MultiplayerIP)) | + static_cast(g_Win64MultiplayerPort) << 32; + + // random props + sessionInfo->data.isReadyToJoin = true; + sessionInfo->data.isJoinable = true; + + DWORD dwLocalUsersMask = 0; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad()); + + CGameNetworkManager::eJoinGameResult result = g_NetworkManager.JoinGame( sessionInfo.get(), dwLocalUsersMask ); + + if (result == CGameNetworkManager::JOINGAME_PENDING) + { + ConnectionProgressParams *param = new ConnectionProgressParams(); + param->iPad = ProfileManager.GetPrimaryPad(); + param->stringId = IDS_PROGRESS_CONNECTING; + param->showTooltips = true; + param->setFailTimer = false; + param->timerTime = 0; + param->cancelFunc = nullptr; + param->cancelFuncParam = nullptr; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_ConnectingProgress, param); + } + } + else + { + ui.NavigateToScene(0,eUIScene_SaveMessage); + } #else ui.NavigateToScene(0,eUIScene_SaveMessage); #endif @@ -169,6 +251,82 @@ void UIScene_Intro::handleAnimationEnd() { m_bAnimationEnded = true; } +#elif defined _WINDOWS64 + // HUCKLE - added this for auto joining servers on game launch + // THANKS so much to DrPerky and GeorgeV22 for helping with this bit, honestly got stuck for 4 hours :sob: + if(g_Win64MultiplayerJoin == true) + { + int primaryPad = ProfileManager.GetPrimaryPad(); + + if (!ProfileManager.IsSignedIn(primaryPad) || ProfileManager.IsGuest(primaryPad)) + { + UINT uiIDA[1] = { IDS_OK }; + ui.RequestErrorMessage(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 1); + ui.NavigateToScene(0, eUIScene_MainMenu); + return; + } + + app.ClearSignInChangeUsersMask(); + app.ReleaseSaveThumbnail(); + ProfileManager.SetLockedProfile(primaryPad); + ProfileManager.QuerySigninStatus(); + + if (!app.DLCInstallProcessCompleted()) + app.StartInstallDLCProcess(primaryPad); + + Minecraft* pMinecraft = Minecraft::GetInstance(); + pMinecraft->user->name = convStringToWstring(ProfileManager.GetGamertag(primaryPad)); + app.ApplyGameSettingsChanged(primaryPad); + + auto sessionInfo = std::make_unique(); + + // label and name + const wchar_t* defaultName = L""; + size_t nameLen = wcslen(defaultName); + + // ip and port + strncpy_s(sessionInfo->data.hostIP, g_Win64MultiplayerIP, sizeof(sessionInfo->data.hostIP) - 1); + sessionInfo->data.hostPort = g_Win64MultiplayerPort; + + // display label + sessionInfo->displayLabel = new wchar_t[nameLen + 1]; + wcscpy_s(sessionInfo->displayLabel, nameLen + 1, defaultName); + sessionInfo->displayLabelLength = static_cast(nameLen); + sessionInfo->displayLabelViewableStartIndex = 0; + + // name + wcsncpy_s(sessionInfo->data.hostName, XUSER_NAME_SIZE, defaultName, _TRUNCATE); + + // session ids + sessionInfo->sessionId = static_cast(inet_addr(g_Win64MultiplayerIP)) | + static_cast(g_Win64MultiplayerPort) << 32; + + // random props + sessionInfo->data.isReadyToJoin = true; + sessionInfo->data.isJoinable = true; + + DWORD dwLocalUsersMask = 0; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad()); + + CGameNetworkManager::eJoinGameResult result = g_NetworkManager.JoinGame( sessionInfo.get(), dwLocalUsersMask ); + + if (result == CGameNetworkManager::JOINGAME_PENDING) + { + ConnectionProgressParams *param = new ConnectionProgressParams(); + param->iPad = ProfileManager.GetPrimaryPad(); + param->stringId = IDS_PROGRESS_CONNECTING; + param->showTooltips = true; + param->setFailTimer = false; + param->timerTime = 0; + param->cancelFunc = nullptr; + param->cancelFuncParam = nullptr; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_ConnectingProgress, param); + } + } + else + { + ui.NavigateToScene(0,eUIScene_SaveMessage); + } #else ui.NavigateToScene(0,eUIScene_SaveMessage); #endif diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp index e763e3d5..904103b7 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp @@ -115,6 +115,7 @@ bool WinsockNetLayer::s_clientKeyStored = false; bool g_Win64MultiplayerHost = false; bool g_Win64MultiplayerJoin = false; +bool g_Win64MultiplayerQuitOnDisconnect = false; int g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT; char g_Win64MultiplayerIP[256] = "127.0.0.1"; bool g_Win64DedicatedServer = false; diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h index a4eb2fae..dc830c84 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h @@ -227,6 +227,7 @@ public: extern bool g_Win64MultiplayerHost; extern bool g_Win64MultiplayerJoin; +extern bool g_Win64MultiplayerQuitOnDisconnect; extern int g_Win64MultiplayerPort; extern char g_Win64MultiplayerIP[256]; extern bool g_Win64DedicatedServer; diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 48af6508..03467061 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -219,6 +219,7 @@ static Win64LaunchOptions ParseLaunchOptions() Win64LaunchOptions options = {}; options.screenMode = 0; + g_Win64MultiplayerQuitOnDisconnect = false; g_Win64MultiplayerJoin = false; g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT; @@ -239,6 +240,10 @@ static Win64LaunchOptions ParseLaunchOptions() { CopyWideArgToAnsi(argv[++i], g_Win64Username, sizeof(g_Win64Username)); } + else if (_wcsicmp(argv[i], L"-quitondisconnect") == 0) + { + g_Win64MultiplayerQuitOnDisconnect = true; + } else if (_wcsicmp(argv[i], L"-ip") == 0 && (i + 1) < argc) { char ipBuf[256]; @@ -1759,7 +1764,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, return 1; } g_bResizeReady = true; - + //app.TemporaryCreateGameStart(); //Sleep(10000); From a2f200fd983aafb89e63b41f2403fa56237e3086 Mon Sep 17 00:00:00 2001 From: "Riley M." <69326171+ryleu@users.noreply.github.com> Date: Thu, 21 May 2026 11:49:56 -0500 Subject: [PATCH 20/32] fix: nix flake (#101) * fix: unbroke import paths in EnchantmentMenu.cpp * fix: patch nix flakes for new 4JLibs * style: format nix flake * refactor(nix): rely on build-linux.sh functions for nix builds * fix(nix): patch dev shell for build-linux.sh * feat(nix): derive package version from network version --- Minecraft.World/EnchantmentMenu.cpp | 14 +- build-linux.sh | 33 +-- flake.lock | 17 ++ flake.nix | 300 +++++++++++++--------------- 4 files changed, 188 insertions(+), 176 deletions(-) diff --git a/Minecraft.World/EnchantmentMenu.cpp b/Minecraft.World/EnchantmentMenu.cpp index 1536bc31..eb94b11e 100644 --- a/Minecraft.World/EnchantmentMenu.cpp +++ b/Minecraft.World/EnchantmentMenu.cpp @@ -6,13 +6,13 @@ #include "net.minecraft.world.item.h" #include "net.minecraft.world.item.enchantment.h" #include "EnchantmentMenu.h" -#include "../../../Minecraft.Client/ServerPlayer.h" -#include "../../../Minecraft.Client/MinecraftServer.h" -#include "../../../Minecraft.Client/PlayerList.h" -#include "../../../Minecraft.Client/MultiPlayerLocalPlayer.h" -#include "../../../Minecraft.Client/PlayerConnection.h" -#include "../../../Minecraft.World/CustomPayloadPacket.h" -#include "../../../Minecraft.Client/Minecraft.h" +#include "../Minecraft.Client/ServerPlayer.h" +#include "../Minecraft.Client/MinecraftServer.h" +#include "../Minecraft.Client/PlayerList.h" +#include "../Minecraft.Client/MultiPlayerLocalPlayer.h" +#include "../Minecraft.Client/PlayerConnection.h" +#include "../Minecraft.World/CustomPayloadPacket.h" +#include "../Minecraft.Client/Minecraft.h" EnchantmentMenu::EnchantmentMenu(shared_ptr inventory, Level *level, int xt, int yt, int zt) { diff --git a/build-linux.sh b/build-linux.sh index 8e35335e..c0b71037 100755 --- a/build-linux.sh +++ b/build-linux.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash set -euo pipefail VERSION="0.0.0" # man we're using nightly :sob: -SOURCE_DIR="${1:-.}" +SOURCE_DIR="${SOURCE_DIR:-${1:-.}}" BUILD_CI="${BUILD_CI:-0}" -BUILD_TYPE="${2:-Release}" +BUILD_TYPE="${BUILD_TYPE:-${2:-Release}}" XWIN_CACHE="${XWIN_CACHE:-$PWD/.xwin}" INSTALL_DIR="${INSTALL_PREFIX:-$HOME/.local/share/neoLegacy}" RED='\033[0;31m' @@ -263,15 +263,22 @@ LAUNCHER chmod +x "$INSTALL_DIR/minecraft-lce-fourkit" } -BUILD_DIR="$SOURCE_DIR/build/windows64-clang" -mkdir -p "$BUILD_DIR" -info "LegacyEvolved LCE v$VERSION build script" -info "Source: $SOURCE_DIR | Type: $BUILD_TYPE" -echo "" -check_deps -fetch_winsdk -patch_winsdk_symlinks -do_cmake_configure -do_build -do_install +main() { + BUILD_DIR="$SOURCE_DIR/build/windows64-clang" + mkdir -p "$BUILD_DIR" + info "LegacyEvolved LCE v$VERSION build script" + info "Source: $SOURCE_DIR | Type: $BUILD_TYPE" + echo "" + check_deps + fetch_winsdk + patch_winsdk_symlinks + do_cmake_configure + do_build + do_install +} + +# Do not run main when sourced (required for flake.nix) +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi diff --git a/flake.lock b/flake.lock index f86a22a8..543f86e9 100644 --- a/flake.lock +++ b/flake.lock @@ -18,6 +18,22 @@ "type": "github" } }, + "fourjlibs": { + "flake": false, + "locked": { + "lastModified": 1777358628, + "narHash": "sha256-yV/ugauN5L4FFC88N4CJI01iTOKYBeim8UQ3Usy2vI0=", + "owner": "Patoke", + "repo": "4JLibs", + "rev": "8fb036f6d6ca5aa5aa2e20633638d6232a58d508", + "type": "github" + }, + "original": { + "owner": "Patoke", + "repo": "4JLibs", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1774106199, @@ -37,6 +53,7 @@ "root": { "inputs": { "flake-utils": "flake-utils", + "fourjlibs": "fourjlibs", "nixpkgs": "nixpkgs" } }, diff --git a/flake.nix b/flake.nix index 0dfdb3da..e9589ceb 100644 --- a/flake.nix +++ b/flake.nix @@ -4,18 +4,34 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; + fourjlibs = { + url = "github:Patoke/4JLibs"; + flake = false; + }; }; - outputs = { self, nixpkgs, flake-utils }: - flake-utils.lib.eachDefaultSystem (system: + outputs = + { + self, + nixpkgs, + flake-utils, + fourjlibs, + }: + flake-utils.lib.eachDefaultSystem ( + system: let pkgs = import nixpkgs { inherit system; config.allowUnfree = true; }; - # Version info - version = "1.6.0560.0"; + # Version derived from network protocol version in cmake/GenerateBuildVer.cmake + buildNumber = builtins.head ( + builtins.match ".*set\\(BUILD_NUMBER ([0-9]+)\\).*" ( + builtins.readFile ./cmake/GenerateBuildVer.cmake + ) + ); + version = "0.${buildNumber}.0"; # Windows SDK downloaded via xwin (fixed-output derivation) windowsSdk = pkgs.stdenvNoCC.mkDerivation { @@ -24,9 +40,13 @@ outputHashAlgo = "sha256"; outputHashMode = "recursive"; - outputHash = "sha256-ksSytBUjv/tD3IJzHM9BkAzFjJ+JAGD353Pur0G4rQE="; + outputHash = "sha256-UFQjsFVBwcF/9e9tVFoG0Z1JySxyTnFqoaRwr/tUWzA="; - nativeBuildInputs = [ pkgs.xwin pkgs.cacert pkgs.rsync ]; + nativeBuildInputs = [ + pkgs.xwin + pkgs.cacert + pkgs.rsync + ]; dontUnpack = true; @@ -50,8 +70,45 @@ dontFixup = true; }; - # Helper to create case-insensitive symlinks for SDK headers/libs - sdkWithSymlinks = pkgs.runCommand "windows-sdk-symlinked" {} '' + # Pre fetch NuGet packages for FourKit (dotnet publish --self-contained needs win-x64 runtime) + fourkitNugetDeps = pkgs.stdenvNoCC.mkDerivation { + pname = "fourkit-nuget-deps"; + version = "10.0"; + + outputHashAlgo = "sha256"; + outputHashMode = "recursive"; + outputHash = "sha256-eEkU0MugnFSNvVYvd5V5xLK4oNcLgZcXxMYSuiYMPbA="; + + nativeBuildInputs = [ pkgs.cacert ]; + + dontUnpack = true; + + buildPhase = '' + export HOME=$(mktemp -d) + export DOTNET_CLI_TELEMETRY_OPTOUT=1 + export DOTNET_NOLOGO=1 + export SSL_CERT_FILE="${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + + # Use the unwrapped SDK to allow NuGet network access + DOTNET="${pkgs.dotnetCorePackages.sdk_10_0.passthru.unwrapped}/share/dotnet/dotnet" + + # Copy csproj to writable location (dotnet needs to write obj/) + WORK=$(mktemp -d) + cp ${./.}/Minecraft.Server.FourKit/Minecraft.Server.FourKit.csproj "$WORK/" + cp ${./.}/global.json "$WORK/" + + $DOTNET restore "$WORK/Minecraft.Server.FourKit.csproj" \ + --runtime win-x64 \ + --packages "$out" \ + --source https://api.nuget.org/v3/index.json + ''; + + dontInstall = true; + dontFixup = true; + }; + + # Helper to make case insensitive symlinks for SDK headers/libs + sdkWithSymlinks = pkgs.runCommand "windows-sdk-symlinked" { } '' cp -r ${windowsSdk} $out chmod -R u+w $out @@ -63,27 +120,6 @@ ln -sf $out/sdk/lib/um/x86_64/ws2_32.lib $out/sdk/lib/um/x86_64/Ws2_32.lib 2>/dev/null || true ''; - # CMake toolchain file for clang-cl cross-compilation - clangClToolchain = pkgs.writeText "clang-cl-toolchain.cmake" '' - set(CMAKE_SYSTEM_NAME Windows) - set(CMAKE_SYSTEM_PROCESSOR AMD64) - - set(CMAKE_C_COMPILER clang-cl) - set(CMAKE_CXX_COMPILER clang-cl) - set(CMAKE_RC_COMPILER llvm-rc) - set(CMAKE_ASM_MASM_COMPILER llvm-ml) - set(CMAKE_AR llvm-lib) - set(CMAKE_LINKER lld-link) - - set(CMAKE_CROSSCOMPILING TRUE) - - set(CMAKE_C_LINK_EXECUTABLE " -out: ") - set(CMAKE_CXX_LINK_EXECUTABLE " -out: ") - - add_compile_options(-fms-compatibility -fms-extensions) - add_compile_definitions(_WIN64 _AMD64_ WIN32_LEAN_AND_MEAN) - ''; - # The main build derivation minecraft-lce-unwrapped = pkgs.stdenv.mkDerivation { pname = "minecraft-lce-unwrapped"; @@ -91,84 +127,79 @@ src = pkgs.lib.cleanSourceWith { src = ./.; - filter = path: type: + filter = + path: type: let baseName = baseNameOf path; in # Exclude build directories and other non-source files - !(baseName == "build" || - baseName == "result" || - baseName == ".git" || - baseName == ".direnv" || - pkgs.lib.hasPrefix "result-" baseName); + !( + baseName == "build" + || baseName == "result" + || baseName == ".git" + || baseName == ".direnv" + || pkgs.lib.hasPrefix "result-" baseName + ); }; + # Patch in the 4JLibs submodule (flakes don't fetch submodules) + postUnpack = '' + rm -rf source/Minecraft.Client/Windows64/4JLibs + cp -r ${fourjlibs} source/Minecraft.Client/Windows64/4JLibs + chmod -R u+w source/Minecraft.Client/Windows64/4JLibs + ''; + nativeBuildInputs = with pkgs; [ - llvmPackages.clang-unwrapped # provides clang-cl - llvmPackages.lld # provides lld-link - llvmPackages.llvm # provides llvm-rc, llvm-ml, llvm-lib, llvm-mt + llvmPackages.clang-unwrapped # provides clang-cl + llvmPackages.lld # provides lld-link + llvmPackages.llvm # provides llvm-rc, llvm-ml, llvm-lib, llvm-mt cmake ninja rsync + winePackage # needed to run fxc.exe during build + dotnetCorePackages.sdk_10_0 # needed for FourKit server ]; - # Set up environment for clang-cl - WINSDK = sdkWithSymlinks; - configurePhase = '' runHook preConfigure - export INCLUDE="$WINSDK/crt/include;$WINSDK/sdk/include/um;$WINSDK/sdk/include/ucrt;$WINSDK/sdk/include/shared" - export LIB="$WINSDK/crt/lib/x86_64;$WINSDK/sdk/lib/um/x86_64;$WINSDK/sdk/lib/ucrt/x86_64" + # Point build-linux.sh at the pre-downloaded Windows SDK + export XWIN_CACHE=$(mktemp -d) + ln -s ${sdkWithSymlinks} "$XWIN_CACHE/splat" - cmake -S . -B build \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_TOOLCHAIN_FILE=${clangClToolchain} \ - -DCMAKE_C_COMPILER=clang-cl \ - -DCMAKE_CXX_COMPILER=clang-cl \ - -DCMAKE_LINKER=lld-link \ - -DCMAKE_RC_COMPILER=llvm-rc \ - -DCMAKE_MT=llvm-mt \ - -DPLATFORM_DEFINES="_WINDOWS64" \ - -DPLATFORM_NAME="Windows64" \ - -DIGGY_LIBS="iggy_w64.lib;iggyperfmon_w64.lib;iggyexpruntime_w64.lib" \ - -DCMAKE_SYSTEM_NAME=Windows \ - -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded \ - -DCMAKE_C_FLAGS="/MT -fms-compatibility -fms-extensions --target=x86_64-pc-windows-msvc -imsvc $WINSDK/crt/include -imsvc $WINSDK/sdk/include/ucrt -imsvc $WINSDK/sdk/include/um -imsvc $WINSDK/sdk/include/shared" \ - -DCMAKE_CXX_FLAGS="/MT -fms-compatibility -fms-extensions --target=x86_64-pc-windows-msvc -imsvc $WINSDK/crt/include -imsvc $WINSDK/sdk/include/ucrt -imsvc $WINSDK/sdk/include/um -imsvc $WINSDK/sdk/include/shared" \ - -DCMAKE_ASM_MASM_FLAGS="-m64" \ - -DCMAKE_EXE_LINKER_FLAGS="-libpath:$WINSDK/crt/lib/x86_64 -libpath:$WINSDK/sdk/lib/um/x86_64 -libpath:$WINSDK/sdk/lib/ucrt/x86_64" + # NuGet packages for FourKit dotnet publish + export NUGET_PACKAGES="${fourkitNugetDeps}" + export DOTNET_CLI_TELEMETRY_OPTOUT=1 + export DOTNET_NOLOGO=1 + + # Configure build-linux.sh variables + export SOURCE_DIR=. + export BUILD_TYPE=Release + export INSTALL_PREFIX=$out + + # Source the build script for its functions + source ./build-linux.sh + + BUILD_DIR="$SOURCE_DIR/build/windows64-clang" + mkdir -p "$BUILD_DIR" + + do_cmake_configure + + # Patch shebangs in generated scripts (fxc wine wrapper) + patchShebangs "$BUILD_DIR/tools" 2>/dev/null || true runHook postConfigure ''; buildPhase = '' runHook preBuild - cmake --build build --config Release -j $NIX_BUILD_CORES + do_build runHook postBuild ''; installPhase = '' runHook preInstall - - mkdir -p $out/{client,server} - - # Install client - cp build/Minecraft.Client/Minecraft.Client.exe $out/client/ - cp build/Minecraft.Client/iggy_w64.dll $out/client/ 2>/dev/null || true - cp -r build/Minecraft.Client/Common $out/client/ 2>/dev/null || true - cp -r build/Minecraft.Client/music $out/client/ 2>/dev/null || true - cp -r build/Minecraft.Client/Windows64 $out/client/ 2>/dev/null || true - cp -r build/Minecraft.Client/Windows64Media $out/client/ 2>/dev/null || true - cp -r build/Minecraft.Client/iggy* $out/client/ 2>/dev/null || true - - # Install server - cp build/Minecraft.Server/Minecraft.Server.exe $out/server/ - cp build/Minecraft.Server/iggy_w64.dll $out/server/ 2>/dev/null || true - cp -r build/Minecraft.Server/Common $out/server/ 2>/dev/null || true - cp -r build/Minecraft.Server/Windows64 $out/server/ 2>/dev/null || true - + do_install runHook postInstall ''; @@ -209,40 +240,40 @@ cat > $out/bin/minecraft-lce-client << 'WRAPPER' #!/usr/bin/env bash set -euo pipefail - + GAME_DIR="@gameDir@" PERSIST_DIR="''${MC_DATA_DIR:-$HOME/.local/share/minecraft-lce-client}" - + export WINEARCH=win64 export WINEPREFIX="''${WINEPREFIX:-$HOME/@winePrefixBase@-client}" - + # Wine performance settings export WINEDLLOVERRIDES="winemenubuilder.exe=d" export WINEESYNC=1 export WINEFSYNC=1 export DXVK_LOG_LEVEL=none - + mkdir -p "$PERSIST_DIR" mkdir -p "$WINEPREFIX" - + # Create working directory with symlinks to immutable store WORK_DIR="$(mktemp -d)" trap 'rm -rf "$WORK_DIR"' EXIT - + cp -rs "$GAME_DIR"/* "$WORK_DIR/" chmod -R u+w "$WORK_DIR" - + # Setup persistent data directory mkdir -p "$PERSIST_DIR/GameHDD" rm -rf "$WORK_DIR/Windows64/GameHDD" 2>/dev/null || true ln -sf "$PERSIST_DIR/GameHDD" "$WORK_DIR/Windows64/GameHDD" - + cd "$WORK_DIR" - + echo "[info] Starting Minecraft LCE client" echo "[info] Data directory: $PERSIST_DIR" echo "[info] Wine prefix: $WINEPREFIX" - + exec wine "$WORK_DIR/Minecraft.Client.exe" "$@" WRAPPER @@ -291,33 +322,33 @@ cat > $out/bin/minecraft-lce-server << 'WRAPPER' #!/usr/bin/env bash set -euo pipefail - + GAME_DIR="@gameDir@" SERVER_PORT="''${MC_PORT:-25565}" SERVER_BIND_IP="''${MC_BIND:-0.0.0.0}" PERSIST_DIR="''${MC_DATA_DIR:-$HOME/.local/share/minecraft-lce-server}" - + export WINEARCH=win64 export WINEPREFIX="''${WINEPREFIX:-$HOME/@winePrefixBase@-server}" - + # Wine settings export WINEDLLOVERRIDES="winemenubuilder.exe=d" export WINEESYNC=1 export WINEFSYNC=1 - + mkdir -p "$PERSIST_DIR" mkdir -p "$WINEPREFIX" - + # Create working directory with symlinks to immutable store WORK_DIR="$(mktemp -d)" trap 'rm -rf "$WORK_DIR"' EXIT - + cp -rs "$GAME_DIR"/* "$WORK_DIR/" chmod -R u+w "$WORK_DIR" - + # Setup persistent data mkdir -p "$PERSIST_DIR/GameHDD" - + for file in server.properties banned-players.json banned-ips.json; do if [[ ! -f "$PERSIST_DIR/$file" ]]; then if [[ -f "$WORK_DIR/$file" ]]; then @@ -328,12 +359,12 @@ fi ln -sf "$PERSIST_DIR/$file" "$WORK_DIR/$file" done - + rm -rf "$WORK_DIR/Windows64/GameHDD" 2>/dev/null || true ln -sf "$PERSIST_DIR/GameHDD" "$WORK_DIR/Windows64/GameHDD" - + cd "$WORK_DIR" - + # Start Xvfb if no display (server may require a virtual display) if [[ -z "''${DISPLAY:-}" ]]; then export DISPLAY=":99" @@ -343,11 +374,11 @@ sleep 1 echo "[info] Started Xvfb on $DISPLAY" fi - + echo "[info] Starting Minecraft LCE server on $SERVER_BIND_IP:$SERVER_PORT" echo "[info] Data directory: $PERSIST_DIR" echo "[info] Wine prefix: $WINEPREFIX" - + exec wine "$WORK_DIR/Minecraft.Server.exe" -port "$SERVER_PORT" -bind "$SERVER_BIND_IP" "$@" WRAPPER @@ -386,58 +417,10 @@ rsync coreutils cacert + winePackage ]; text = '' - set -euo pipefail - - SOURCE_DIR="''${1:-.}" - BUILD_TYPE="''${2:-Release}" - XWIN_CACHE="''${XWIN_CACHE:-$HOME/.cache/xwin}" - - export XWIN_CACHE - - echo "[info] Checking Windows SDK cache at $XWIN_CACHE" - - if [[ ! -d "$XWIN_CACHE/splat" ]]; then - echo "[info] Downloading Windows SDK and CRT via xwin..." - mkdir -p "$XWIN_CACHE" - xwin --accept-license splat --output "$XWIN_CACHE/splat" - else - echo "[info] Using cached Windows SDK" - fi - - WINSDK="$XWIN_CACHE/splat" - - export INCLUDE="$WINSDK/crt/include;$WINSDK/sdk/include/um;$WINSDK/sdk/include/ucrt;$WINSDK/sdk/include/shared" - export LIB="$WINSDK/crt/lib/x86_64;$WINSDK/sdk/lib/um/x86_64;$WINSDK/sdk/lib/ucrt/x86_64" - - BUILD_DIR="$SOURCE_DIR/build/windows64-clang" - mkdir -p "$BUILD_DIR" - - echo "[info] Configuring with CMake..." - cmake -S "$SOURCE_DIR" -B "$BUILD_DIR" \ - -G Ninja \ - -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ - -DCMAKE_TOOLCHAIN_FILE="${clangClToolchain}" \ - -DCMAKE_C_COMPILER=clang-cl \ - -DCMAKE_CXX_COMPILER=clang-cl \ - -DCMAKE_LINKER=lld-link \ - -DCMAKE_RC_COMPILER=llvm-rc \ - -DCMAKE_MT=llvm-mt \ - -DPLATFORM_DEFINES="_WINDOWS64" \ - -DPLATFORM_NAME="Windows64" \ - -DIGGY_LIBS="iggy_w64.lib;iggyperfmon_w64.lib;iggyexpruntime_w64.lib" \ - -DCMAKE_SYSTEM_NAME=Windows \ - -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded \ - -DCMAKE_C_FLAGS="/MT -fms-compatibility -fms-extensions --target=x86_64-pc-windows-msvc -imsvc $WINSDK/crt/include -imsvc $WINSDK/sdk/include/ucrt -imsvc $WINSDK/sdk/include/um -imsvc $WINSDK/sdk/include/shared" \ - -DCMAKE_CXX_FLAGS="/MT -fms-compatibility -fms-extensions --target=x86_64-pc-windows-msvc -imsvc $WINSDK/crt/include -imsvc $WINSDK/sdk/include/ucrt -imsvc $WINSDK/sdk/include/um -imsvc $WINSDK/sdk/include/shared" \ - -DCMAKE_ASM_MASM_FLAGS="-m64" \ - -DCMAKE_EXE_LINKER_FLAGS="-libpath:$WINSDK/crt/lib/x86_64 -libpath:$WINSDK/sdk/lib/um/x86_64 -libpath:$WINSDK/sdk/lib/ucrt/x86_64" - - echo "[info] Building..." - cmake --build "$BUILD_DIR" --config "$BUILD_TYPE" -j "$(nproc)" - - echo "[info] Build complete! Output in $BUILD_DIR" + exec bash "${./build-linux.sh}" "$@" ''; }; @@ -451,6 +434,9 @@ # Unwrapped (just the Windows executables) unwrapped = minecraft-lce-unwrapped; + # NuGet deps for FourKit (for debugging) + fourkit-nuget-deps = fourkitNugetDeps; + # Windows SDK (for debugging) windows-sdk = sdkWithSymlinks; @@ -488,6 +474,9 @@ xwin rsync + # .NET SDK for FourKit server + dotnetCorePackages.sdk_10_0 + # Wine for testing winePackage winetricks @@ -500,8 +489,6 @@ cacert ]; - XWIN_CACHE = "$HOME/.cache/xwin"; - shellHook = '' echo "LCE-Revelations development shell" echo "" @@ -510,13 +497,14 @@ echo " nix build .#server # Build server package" echo "" echo "Development build (in-tree):" - echo " minecraft-lce-build [source_dir] [Release|Debug]" + echo " ./build-linux.sh [source_dir] [Release|Debug]" echo "" echo "Run:" echo " nix run .#client" echo " nix run .#server" echo "" echo "Environment variables:" + echo " XWIN_CACHE - Windows SDK cache (default: \$PWD/.xwin)" echo " MC_PORT - Server port (default: 25565)" echo " MC_BIND - Server bind address (default: 0.0.0.0)" echo " MC_DATA_DIR - Persistent data directory" From 2cc554c11b27fa4e4beac6a6b2f198025ad35d86 Mon Sep 17 00:00:00 2001 From: PUFF_MON <90284792+PUFFMON@users.noreply.github.com> Date: Thu, 21 May 2026 19:39:14 +0200 Subject: [PATCH 21/32] fix: Incorrect Rabbit Stew strings (#102) --- .../Windows64Media/Media/languages.loc | Bin 1345659 -> 1356985 bytes .../Windows64Media/loc/stringsGeneric.xml | 6 +++++- Minecraft.World/Item.cpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/Windows64Media/Media/languages.loc b/Minecraft.Client/Windows64Media/Media/languages.loc index 3fb5b1a0671cbbd08835e2fd5b458058d99a0d3f..4b11ca996ff91cf3715ee2b46745fce6437973ee 100644 GIT binary patch delta 8132 zcmcIpeT-byb-y$F{W0tPvH=_0*T%to?2gyQ-x%!KnO*PNyWVAIjf+WJ?#$esdF-2a zhxf7Tu?Wwo&?=Fl*4dCDC={ZqsuGb90(6Aa5*m~$ky6@9L8=8&(^e>Q<0?gtLaR`> zzjNQ4@vhl4iPHW7JNLYE&pG#epW~nZ(adX?W_-q21pl?MCTR_xOta|of5n<|axgW* zqWAq9pw8e3pa;Ihn&#}m(HR!K{{_}$xP#-fEV}nRYkF7=9-C&-b2-*DuLh3;;p10W zQ^_41#?L!iS@YS#U~+7hMXsDCUXdCj)^?e-o_NW>@n@ShB`j`O3x;E4OgX?ap35C& z8abXZRd)X6r+&8WV~wp-V!ryuhR6qzNK^Iojgf`s1ML9uX;WD94vVErwwIN)W9P78 zXA8Ac?3ice&HBfl%I|NB$kvu&VcUkZtN*$!@@{1j938a38m6kLeO%m0jO^UY4F$7e)FexBklb)((czAVC=m`X zstnvnRheqQ)aZ1)uY5OJrr7b%lmbi;@Y^S$EyaHApPPn2=&Y zI_z37g@D)$4;P^ zm6d=@D$6TM%XQ*BbVMx-9yn*KB45VBAr|njg#;{O!x`t0-?*Avj&qF>qUBfSv}hpL zeS{MyzlVh5);yG*jk7}Pn)ffm&X6`f@ zuHt;$<;K$Kx8nXMPSz_ZH>p;Ft7m6*4gEj!Y;C?}I?M(!3y=4g#nt~?SPi9=N9seG zFnOLVQ`eoZ^Pp=K@nD_19Vsai*Ch*vBaxr^iVVd1(?#Uo!gbK*X_Y&xkb{y+*f4L( zuw8aIZ4_a0o2m{0_%L;BPSC?GNW0AQN>vKc$j$x`Y+aIQKuqqIOR(h}>J)Bh3+EJM zvrskiwpt9tkFypyn1wopOoeXHDL~K~q%{GHI`pll{;dvbNg2vsc`s$O1fU&DLznuO z)?Mm51RW9aI2&3mafM$gNg?@{YWz(U7Tl;2xfda;#Xw`W8kO4T)ydRl@pFN|zd)Ao z>*_>WYqn$Ur=-i%C1vF$I}}QjLxZa`S-pS_a5#usq|8GNq(k@#xDEPFluAiyCajWa zVHQy(YlSvEwk2`fnu9qI)E?Sbn|?2S@Xl1f%1RR^1SyvqVhYV;R7`iRr()`8a@Q5p zO;pV{HpH2!fB3wBya2z!Wo{tOmEkB$d0}-CrlZBwxe#3ovOC^^s5el7h%#Dd{j!pB z(Z|EZJBCe9@+r&$MvgTnOw;g+tRrz2y$oE{lP+=<6g2CZMu~M!q3T0>;j!aX0savl z+NTZFkpQn_>WnNS0EFR`+1>b_vOTwKuF_zQXEvgqIPs z)-@NU9=TQK65c{kqWdQ`{X$)@kTX=Z57%lNp<-R6{M}mXWYFt| zeNoWGu+CFR@Ge*kBO$Z95Mh5L8}X$bU)->+HId8lX~Z1Fr0!d@PgUOkUSzY6Vj74+&n5w~ z1#)tSmzB32%}Y__Y09E+Ouf7<1UPWC4y_1IOvrMabBdMy`)O$ zNIhig&S4DB1igdp<*7xbCvilcRmrHJ)e?9|)qC1U}MjY!*!h1rSXo zTi9(U(I*5O1g+?_OM?e}FN%2>h)?4z&XCc^V4+!6GO}Qe%C;{stcn7T+r;-?oK zJgGA1bb`?&kR{H#6MhyGFw#0LT^2E5P#c4ul8S`hHH=$%g?H9P9K~?Eg>_5@l8h-x z(LPLvVGO%Dwr&=z`6&#=R9PUh2BRsNg~cWe$#!UDfw2d7Jjd{@{J{W!fTz8@um_+8 zGw`sHd>-2At(gZRoh%_EU=-f4{IwaF548_k2(;3b!agc%p#JeuD!Bbg;gUt+UwXnN z;1#sU2fv`?;ktl@=%OfW*;X> ze1Z7M<){T#oc3Oz2@Lc*K{#4iXCMkG-~i@%G)n~!>d|xR92LWypma$!5kx?NVvd@a zUd-(@m4)F-qYPU+Ea%W$f%F`0Q%)(J(ILT_QlqL7Dv?e?i?C|M#^|oa{~Pn7;ZR@JP86x z(Ta*bcie6GZMQg%O|_1VG&5mQ+2mGS|ESd$qVTO zUY>!e!AEP8&TPXBq6|$ZUN&rsvwAWyFl5>az8jlRSxhVle$1<76b<|UV$fj5Su5EC z1?w>DKR!av4L#GjZigB|oUPR=Aw}c6v~#w|D+_{*Ai3&|4%=Ke=+ZwvwifxkbqcL8 ztaYGWr zyZmKkmhI4L-#M^v*ZvjdS9f??w=}R(J<%j|P+D@4JPGbD?R=T-3(7II4A}d7_(z*i zc}Xk=vx?RNVPBvVLlU!)27}flw4BwS%X|dO2ew&fDZzseXs54Z_RW4kF zv9-uoSmRSh2gkxymO ze?d<)KMEc~??;59aKKgDC#ZtXV*0gFFKGw{(_k!)Gd`veij^T5y^jnd8BU>x?y%pJ7Jzk2A zw?yu%Jhd1-(h8@rsfJg7xENg%SwFE=HvmNWAfJ$VkuB?bq|8b~Il4RYP-TBP`ru7i zVI$U`9!2v|8+`(0RgRUTK{nAXgu6{O`fa z-#ys(<=}t+t{Oh~XPYlIN2>qzwZ=DZAzt75eq;P4p#J2^rWo=1`PZ99zEQc?YTnqy z*xC`R^8Qbo`>Kyv%}HYM@l`$`18uWSK2o=GPZuavh%Tyo2m;}+J8Vo&wSYayP?o89q#xi zK+YfO`0>tFIXvog9NJ2Hx$soSUwjLw-fwmO%K-kKdb0D0?^iB95F2P5mui#<6NJmHy_V_yJd&nvNudsb!8_2XFo?ZjYPf7b)A0QJ%Dbe)gmZ|4VH zpZabeW6hU;@7c>=d3tgzYqa;N zfmJ1MaC^6F|ypMCDVuRP6~-}~gNm(D#8$xB!J jwzmM|b4~ru`HtX{Ja?Ae!1B=km delta 1195 zcmZ`#drVtZ7(eHBiT6^XYr3u59QCpqW${a)ug zG4K5AfU^}Lgyo|_DqiHCEJLKd45_MlZbtzkjzt-5jy|xM-_)ZXF7ZCfz zNNeoU7%U|vt94IFQZ+BaY#ja#%P%kQ4%lFNJ#J>4Mu)rJ7M|NmeQvS75n0L_{7oKy z4QAdh_&we=kfpduto5y>ty}S-?RLz=Z99G+>zP8I^b+q!N~hGw!_gGHk=$uRuczU0 ztb)TE@CA&u@L~p5Owf)woB*S9__!XVMV$9A#NS8m$Kc*fUfWm9)$DSW$TaOm>iuNo;PuB+ATyAc?ksv^u7{&*9|Q#Q&MX zrA|_ !LO}6R8RT)U##E3~@620C<;^<%x$-p)A>mKq<>MAJ7*&yK)!k_l?fo!fZn7hVbT(Zx z3zHd|AF@F%v0x8-w6#+axB4IiXY?~$ zpweWpriUW~4;U(r!TFyJ2eQDu-q>;0$b{PZLxaZ<6Z-IEXrK%JzGa+d;qrp9rG%E} zn)>0_T$46Cl0Gk*_N0YvYN<&Wf{WYCw=!VDXC4MB*e&t!y4|vcg;SZ9>^yq=1r*DG?jjK`Wp54=Grt<8 diff --git a/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml b/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml index b8150a8a..776c0a4a 100644 --- a/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml +++ b/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml @@ -9100,7 +9100,7 @@ All Ender Chests in a world are linked. Items placed into an Ender Chest are acc - {*ICON_SHANK_01*} + Rabbit Stew @@ -9570,4 +9570,8 @@ All Ender Chests in a world are linked. Items placed into an Ender Chest are acc Hold {*CONTROLLER_VK_Y*} to view Classic Crafting + + + Restores 5{*ICON_SHANK_01*}. + diff --git a/Minecraft.World/Item.cpp b/Minecraft.World/Item.cpp index f46a57e3..db644c77 100644 --- a/Minecraft.World/Item.cpp +++ b/Minecraft.World/Item.cpp @@ -360,7 +360,7 @@ void Item::staticCtor() Item::diamond = ( new Item(8) ) ->setBaseItemTypeAndMaterial(eBaseItemType_treasure, eMaterial_diamond)->setIconName(L"diamond")->setDescriptionId(IDS_ITEM_DIAMOND)->setUseDescriptionId(IDS_DESC_DIAMONDS); Item::stick = ( new Item(24) ) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_stick, Item::eMaterial_wood)->setIconName(L"stick")->handEquipped()->setDescriptionId(IDS_ITEM_STICK)->setUseDescriptionId(IDS_DESC_STICK); Item::mushroomStew = ( new BowlFoodItem(26, 6) ) ->setIconName(L"mushroomStew")->setDescriptionId(IDS_ITEM_MUSHROOM_STEW)->setUseDescriptionId(IDS_DESC_MUSHROOMSTEW); - Item::rabbitStew = ( new BowlFoodItem(157, 10) ) ->setIconName(L"rabbitStew")->setDescriptionId(IDS_ITEM_MUSHROOM_STEW)->setUseDescriptionId(IDS_DESC_MUSHROOMSTEW); + Item::rabbitStew = ( new BowlFoodItem(157, 10) ) ->setIconName(L"rabbitStew")->setDescriptionId(IDS_ITEM_RABBIT_STEW)->setUseDescriptionId(IDS_DESC_RABBIT_STEW); Item::string = ( new TilePlanterItem(31, Tile::tripWire) ) ->setIconName(L"string")->setDescriptionId(IDS_ITEM_STRING)->setUseDescriptionId(IDS_DESC_STRING); Item::feather = ( new Item(32) ) ->setIconName(L"feather")->setDescriptionId(IDS_ITEM_FEATHER)->setUseDescriptionId(IDS_DESC_FEATHER); From d7d38d4f8fd9789ed6c4cbebcf249630c52af30e Mon Sep 17 00:00:00 2001 From: NSDeathman <104826306+NSDeathman@users.noreply.github.com> Date: Fri, 22 May 2026 15:07:06 +0400 Subject: [PATCH 22/32] fix: doubled grass upper block destroying (#104) --- Minecraft.World/TallGrass2.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Minecraft.World/TallGrass2.cpp b/Minecraft.World/TallGrass2.cpp index 93c0538c..e9998c21 100644 --- a/Minecraft.World/TallGrass2.cpp +++ b/Minecraft.World/TallGrass2.cpp @@ -189,11 +189,12 @@ void TallGrass2::neighborChanged(Level* level, int x, int y, int z, int type) if (!isUpper) { - if (!canSurvive(level, x, y, z)) + int upperTileId = level->getTile(x, y + 1, z); + if (!canSurvive(level, x, y, z) || (upperTileId != id)) { spawnResources(level, x, y, z, data, 0); level->setTileAndData(x, y, z, 0, 0, Tile::UPDATE_CLIENTS); - if (level->getTile(x, y + 1, z) == id) + if (upperTileId == id) level->removeTile(x, y + 1, z); } } @@ -211,11 +212,12 @@ void TallGrass2::tick(Level* level, int x, int y, int z, Random* random) if (!isUpper) { - if (!canSurvive(level, x, y, z)) + int upperTileId = level->getTile(x, y + 1, z); + if (!canSurvive(level, x, y, z) || (upperTileId != id)) { spawnResources(level, x, y, z, data, 0); level->setTileAndData(x, y, z, 0, 0, Tile::UPDATE_CLIENTS); - if (level->getTile(x, y + 1, z) == id) + if (upperTileId == id) level->removeTile(x, y + 1, z); } } @@ -224,7 +226,6 @@ void TallGrass2::tick(Level* level, int x, int y, int z, Random* random) int TallGrass2::getResource(int data, Random* random, int playerBonusLevel) { - return -1; } @@ -240,7 +241,6 @@ bool TallGrass2::isSilkTouchable() shared_ptr TallGrass2::getSilkTouchItemInstance(int data) { - if ((data & UPPER_BIT) != 0) return nullptr; int variant = data & ~UPPER_BIT; return std::make_shared(this, 1, variant); From 0b762588d8a89f23007528fc045a6f8578e0898f Mon Sep 17 00:00:00 2001 From: neoapps-dev Date: Fri, 22 May 2026 16:38:10 +0300 Subject: [PATCH 23/32] feat(CMake): add SDK generation --- CMakeLists.txt | 26 +++++ cmake/GenerateSdk.cmake | 204 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 cmake/GenerateSdk.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 52391571..fe59b475 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,6 +266,32 @@ if(PLATFORM_NAME STREQUAL "Windows64") add_dependencies(Minecraft.Server GenerateItemNameMap) endif() +#neo: added - SDK generation +set(SDK_INPUT_DIRS + "${CMAKE_SOURCE_DIR}/Minecraft.World" + "${CMAKE_SOURCE_DIR}/Minecraft.Client" + "${CMAKE_SOURCE_DIR}/include" + "${CMAKE_SOURCE_DIR}/Minecraft.Client/${PLATFORM_NAME}/4JLibs" +) + +set(SDK_OUTPUT "${CMAKE_BINARY_DIR}/Minecraft.Client/$/sdk.h") +add_custom_command( + OUTPUT "${SDK_OUTPUT}" + COMMAND ${CMAKE_COMMAND} + "-DINPUT_DIRS=${SDK_INPUT_DIRS}" + "-DOUTPUT_FILE=${SDK_OUTPUT}" + -P "${CMAKE_SOURCE_DIR}/cmake/GenerateSdk.cmake" + DEPENDS + "${CMAKE_SOURCE_DIR}/cmake/GenerateSdk.cmake" + COMMENT "Generating sdk.h..." + VERBATIM +) + +add_custom_target(GenerateSdk ALL + DEPENDS "${SDK_OUTPUT}" +) + +set_property(TARGET GenerateSdk PROPERTY FOLDER "Build") target_include_directories(Minecraft.Client PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated" ) diff --git a/cmake/GenerateSdk.cmake b/cmake/GenerateSdk.cmake new file mode 100644 index 00000000..62b39a06 --- /dev/null +++ b/cmake/GenerateSdk.cmake @@ -0,0 +1,204 @@ +if(NOT INPUT_DIRS) + message(FATAL_ERROR "INPUT_DIRS must be set to a list of directories.") +endif() +if(NOT OUTPUT_FILE) + message(FATAL_ERROR "OUTPUT_FILE must be set.") +endif() + +set(_all_headers "") +foreach(_dir IN LISTS INPUT_DIRS) + if(EXISTS "${_dir}") + file(GLOB_RECURSE _hfiles "${_dir}/*.h") + list(APPEND _all_headers ${_hfiles}) + endif() +endforeach() + +if(NOT _all_headers) + message(FATAL_ERROR "No .h files found in INPUT_DIRS.") +endif() +list(REMOVE_DUPLICATES _all_headers) +list(SORT _all_headers) + +foreach(_h IN LISTS _all_headers) + get_filename_component(_bn "${_h}" NAME) + string(TOLOWER "${_bn}" _k) + set(_idx_${_k} "${_h}") +endforeach() + +foreach(_h IN LISTS _all_headers) + file(STRINGS "${_h}" _ll REGEX "^[ \t]*#[ \t]*include[ \t]+\"") + get_filename_component(_hd "${_h}" DIRECTORY) + + set(_dd "") + foreach(_l IN LISTS _ll) + if(_l MATCHES "^[ \t]*#[ \t]*include[ \t]+\"([^\"]+)\"") + set(_in "${CMAKE_MATCH_1}") + set(_rv "") + set(_try "${_hd}/${_in}") + get_filename_component(_try "${_try}" ABSOLUTE) + if(EXISTS "${_try}") + set(_rv "${_try}") + else() + string(TOLOWER "${_in}" _k2) + if(DEFINED _idx_${_k2}) + set(_rv "${_idx_${_k2}}") + endif() + endif() + if(_rv AND _rv IN_LIST _all_headers) + list(APPEND _dd "${_rv}") + endif() + endif() + endforeach() + if(_dd) + list(REMOVE_DUPLICATES _dd) + endif() + set(_de_${_h} "${_dd}") +endforeach() + +set(_sorted "") +set(_left ${_all_headers}) +while(_left) + set(_prog 0) + set(_next "") + foreach(_h IN LISTS _left) + set(_rdy 1) + foreach(_d IN LISTS _de_${_h}) + if(_d IN_LIST _left) + set(_rdy 0) + break() + endif() + endforeach() + if(_rdy) + list(APPEND _sorted "${_h}") + set(_prog 1) + else() + list(APPEND _next "${_h}") + endif() + endforeach() + if(NOT _prog) + foreach(_h IN LISTS _next) + list(APPEND _sorted "${_h}") + endforeach() + break() + endif() + set(_left ${_next}) +endwhile() + +set(_sys "") +set(_body "") + +foreach(_h IN LISTS _sorted) + file(STRINGS "${_h}" _ll) + + set(_inbc 0) + set(_out "") + foreach(_l IN LISTS _ll) + if(_l STREQUAL "") + continue() + endif() + + if(_inbc) + string(FIND "${_l}" "*/" _ce) + if(_ce GREATER -1) + math(EXPR _ca "${_ce} + 2") + string(SUBSTRING "${_l}" ${_ca} -1 _l) + set(_inbc 0) + else() + continue() + endif() + endif() + + string(FIND "${_l}" "//" _sl) + if(_sl GREATER -1) + string(SUBSTRING "${_l}" 0 ${_sl} _l) + endif() + + while(TRUE) + string(FIND "${_l}" "/*" _so) + if(_so EQUAL -1) + break() + endif() + string(SUBSTRING "${_l}" 0 ${_so} _bf) + string(SUBSTRING "${_l}" ${_so} -1 _ar) + string(FIND "${_ar}" "*/" _ce) + if(_ce EQUAL -1) + set(_l "${_bf}") + set(_inbc 1) + break() + else() + math(EXPR _ca "${_ce} + 2") + string(SUBSTRING "${_ar}" ${_ca} -1 _af) + set(_l "${_bf}${_af}") + endif() + endwhile() + + string(STRIP "${_l}" _ls) + + if(_ls MATCHES "^#[ \t]*pragma[ \t]+once") + continue() + endif() + if(_ls MATCHES "^#[ \t]*include[ \t]+\"([^\"]+)\"") + continue() + endif() + if(_ls MATCHES "^#[ \t]*include[ \t]+<([^>]+)>") + set(_sn "${CMAKE_MATCH_1}") + if(NOT _sn IN_LIST _sys) + list(APPEND _sys "${_sn}") + endif() + continue() + endif() + + if(_ls STREQUAL "") + continue() + endif() + + string(APPEND _out "${_l}\n") + endforeach() + + if(_out) + if(_body) + string(APPEND _body "\n") + endif() + string(APPEND _body "${_out}") + endif() +endforeach() + +set(_guard "MINECRAFT_LCE_SDK_H") +set(_tmp "${OUTPUT_FILE}.tmp") + +file(WRITE "${_tmp}" + "#ifndef ${_guard}\n" + "#define ${_guard}\n" + "\n" + "// Auto-generated. Do not edit.\n" + "// Minecraft Console Edition SDK Header\n" + "\n" +) + +if(_sys) + list(SORT _sys) + foreach(_s IN LISTS _sys) + file(APPEND "${_tmp}" "#include <${_s}>\n") + endforeach() + file(APPEND "${_tmp}" "\n") +endif() + +file(APPEND "${_tmp}" "${_body}") +file(APPEND "${_tmp}" "\n#endif // ${_guard}\n") + +if(EXISTS "${OUTPUT_FILE}") + execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files "${OUTPUT_FILE}" "${_tmp}" + RESULT_VARIABLE _ch + ) +else() + set(_ch 1) +endif() + +if(_ch) + file(RENAME "${_tmp}" "${OUTPUT_FILE}") + message(STATUS "GenerateSdk: wrote ${OUTPUT_FILE}") +else() + file(REMOVE "${_tmp}") + message(STATUS "GenerateSdk: ${OUTPUT_FILE} is up-to-date") +endif() From 28d9500eca266c32fcc15dbafa5384f58e2cbfad Mon Sep 17 00:00:00 2001 From: neoapps-dev Date: Fri, 22 May 2026 17:53:44 +0300 Subject: [PATCH 24/32] cmake(sdk): includes --- cmake/GenerateSdk.cmake | 52 ++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/cmake/GenerateSdk.cmake b/cmake/GenerateSdk.cmake index 62b39a06..e96a810e 100644 --- a/cmake/GenerateSdk.cmake +++ b/cmake/GenerateSdk.cmake @@ -84,7 +84,6 @@ while(_left) set(_left ${_next}) endwhile() -set(_sys "") set(_body "") foreach(_h IN LISTS _sorted) @@ -140,11 +139,7 @@ foreach(_h IN LISTS _sorted) if(_ls MATCHES "^#[ \t]*include[ \t]+\"([^\"]+)\"") continue() endif() - if(_ls MATCHES "^#[ \t]*include[ \t]+<([^>]+)>") - set(_sn "${CMAKE_MATCH_1}") - if(NOT _sn IN_LIST _sys) - list(APPEND _sys "${_sn}") - endif() + if(_ls MATCHES "^#[ \t]*include[ \t]+<") continue() endif() @@ -173,16 +168,45 @@ file(WRITE "${_tmp}" "// Auto-generated. Do not edit.\n" "// Minecraft Console Edition SDK Header\n" "\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#if defined(_WIN32) || defined(_WIN64)\n" + "#include \n" + "#endif\n" + "\n" ) -if(_sys) - list(SORT _sys) - foreach(_s IN LISTS _sys) - file(APPEND "${_tmp}" "#include <${_s}>\n") - endforeach() - file(APPEND "${_tmp}" "\n") -endif() - file(APPEND "${_tmp}" "${_body}") file(APPEND "${_tmp}" "\n#endif // ${_guard}\n") From 9f37450178e327823c2de9d51bca0bec8a7b8540 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit <116128211+DrPerkyLegit@users.noreply.github.com> Date: Fri, 22 May 2026 11:38:53 -0400 Subject: [PATCH 25/32] chore: switch all entity ids to dedicated ints (#86) * switch all entity ids to dedicated ints * Increment NETWORK_PROTOCOL_VERSION to 79 --- Minecraft.World/AddEntityPacket.cpp | 6 +-- Minecraft.World/AddMobPacket.cpp | 6 +-- Minecraft.World/MoveEntityPacket.cpp | 12 +++--- Minecraft.World/MoveEntityPacketSmall.cpp | 51 +++++++++++------------ Minecraft.World/RespawnPacket.cpp | 6 +-- Minecraft.World/SetEntityMotionPacket.cpp | 15 ++++--- Minecraft.World/SharedConstants.h | 4 +- Minecraft.World/TeleportEntityPacket.cpp | 6 +-- 8 files changed, 51 insertions(+), 55 deletions(-) diff --git a/Minecraft.World/AddEntityPacket.cpp b/Minecraft.World/AddEntityPacket.cpp index f70082b8..99c5d0ab 100644 --- a/Minecraft.World/AddEntityPacket.cpp +++ b/Minecraft.World/AddEntityPacket.cpp @@ -54,7 +54,7 @@ AddEntityPacket::AddEntityPacket(shared_ptr e, int type, int data, int y void AddEntityPacket::read(DataInputStream *dis) // throws IOException TODO 4J JEV add throws statement { - id = dis->readShort(); + id = dis->readInt(); type = dis->readByte(); #ifdef _LARGE_WORLDS x = dis->readInt(); @@ -78,7 +78,7 @@ void AddEntityPacket::read(DataInputStream *dis) // throws IOException TODO 4J void AddEntityPacket::write(DataOutputStream *dos) // throws IOException TODO 4J JEV add throws statement { - dos->writeShort(id); + dos->writeInt(id); dos->writeByte(type); #ifdef _LARGE_WORLDS dos->writeInt(x); @@ -107,5 +107,5 @@ void AddEntityPacket::handle(PacketListener *listener) int AddEntityPacket::getEstimatedSize() { - return 11 + data > -1 ? 6 : 0; + return (11 + data > -1 ? 6 : 0) + 2; } diff --git a/Minecraft.World/AddMobPacket.cpp b/Minecraft.World/AddMobPacket.cpp index d5ccfe08..8aca1055 100644 --- a/Minecraft.World/AddMobPacket.cpp +++ b/Minecraft.World/AddMobPacket.cpp @@ -66,7 +66,7 @@ AddMobPacket::AddMobPacket(shared_ptr mob, int yRotp, int xRotp, i void AddMobPacket::read(DataInputStream *dis) //throws IOException { - id = dis->readShort(); + id = dis->readInt(); type = dis->readByte() & 0xff; #ifdef _LARGE_WORLDS x = dis->readInt(); @@ -90,7 +90,7 @@ void AddMobPacket::read(DataInputStream *dis) //throws IOException void AddMobPacket::write(DataOutputStream *dos) //throws IOException { - dos->writeShort(id); + dos->writeInt(id); dos->writeByte(type & 0xff); #ifdef _LARGE_WORLDS dos->writeInt(x); @@ -127,7 +127,7 @@ int AddMobPacket::getEstimatedSize() // 4J Stu - This is an incoming value which we aren't currently analysing //size += unpack->get } - return size; + return size + 2; } vector > *AddMobPacket::getUnpackedData() diff --git a/Minecraft.World/MoveEntityPacket.cpp b/Minecraft.World/MoveEntityPacket.cpp index a2b79db3..f071a49f 100644 --- a/Minecraft.World/MoveEntityPacket.cpp +++ b/Minecraft.World/MoveEntityPacket.cpp @@ -30,7 +30,7 @@ MoveEntityPacket::MoveEntityPacket(int id) void MoveEntityPacket::read(DataInputStream *dis) //throws IOException { - id = dis->readShort(); + id = dis->readInt(); } void MoveEntityPacket::write(DataOutputStream *dos) //throws IOException @@ -40,7 +40,7 @@ void MoveEntityPacket::write(DataOutputStream *dos) //throws IOException // We shouln't be tracking an entity that doesn't have a short type of id DEBUG_BREAK(); } - dos->writeShort(static_cast(id)); + dos->writeInt(static_cast(id)); } void MoveEntityPacket::handle(PacketListener *listener) @@ -50,7 +50,7 @@ void MoveEntityPacket::handle(PacketListener *listener) int MoveEntityPacket::getEstimatedSize() { - return 2; + return 4; } bool MoveEntityPacket::canBeInvalidated() @@ -101,7 +101,7 @@ void MoveEntityPacket::PosRot::write(DataOutputStream *dos) //throws IOException int MoveEntityPacket::PosRot::getEstimatedSize() { - return 2+5; + return 4+5; } MoveEntityPacket::Pos::Pos() @@ -133,7 +133,7 @@ void MoveEntityPacket::Pos::write(DataOutputStream *dos) //throws IOException int MoveEntityPacket::Pos::getEstimatedSize() { - return 2+3; + return 4+3; } MoveEntityPacket::Rot::Rot() @@ -164,5 +164,5 @@ void MoveEntityPacket::Rot::write(DataOutputStream *dos) //throws IOException int MoveEntityPacket::Rot::getEstimatedSize() { - return 2+2; + return 4+2; } diff --git a/Minecraft.World/MoveEntityPacketSmall.cpp b/Minecraft.World/MoveEntityPacketSmall.cpp index 3e1b922c..2f6d3bc2 100644 --- a/Minecraft.World/MoveEntityPacketSmall.cpp +++ b/Minecraft.World/MoveEntityPacketSmall.cpp @@ -37,7 +37,7 @@ MoveEntityPacketSmall::MoveEntityPacketSmall(int id) void MoveEntityPacketSmall::read(DataInputStream *dis) //throws IOException { - id = dis->readShort(); + id = dis->readInt(); } void MoveEntityPacketSmall::write(DataOutputStream *dos) //throws IOException @@ -47,7 +47,7 @@ void MoveEntityPacketSmall::write(DataOutputStream *dos) //throws IOException // We shouln't be tracking an entity that doesn't have a short type of id DEBUG_BREAK(); } - dos->writeShort(static_cast(id)); + dos->writeInt(id); } void MoveEntityPacketSmall::handle(PacketListener *listener) @@ -57,7 +57,7 @@ void MoveEntityPacketSmall::handle(PacketListener *listener) int MoveEntityPacketSmall::getEstimatedSize() { - return 2; + return 4; } bool MoveEntityPacketSmall::canBeInvalidated() @@ -88,13 +88,12 @@ MoveEntityPacketSmall::PosRot::PosRot(int id, char xa, char ya, char za, char yR void MoveEntityPacketSmall::PosRot::read(DataInputStream *dis) //throws IOException { - int idAndRot = dis->readShort(); - this->id = idAndRot & 0x07ff; - this->yRot = idAndRot >> 11; - int xAndYAndZ = (int)dis->readShort(); - this->xa = xAndYAndZ >> 11; - this->ya = (xAndYAndZ << 21 ) >> 26; - this->za = (xAndYAndZ << 27 ) >> 27; + this->id = dis->readInt(); + this->yRot = dis->readChar(); + int XandYandZ = (int)dis->readShort(); + this->xa = XandYandZ >> 11; + this->ya = (XandYandZ << 21 ) >> 26; + this->za = (XandYandZ << 27 ) >> 27; } void MoveEntityPacketSmall::PosRot::write(DataOutputStream *dos) //throws IOException @@ -104,15 +103,15 @@ void MoveEntityPacketSmall::PosRot::write(DataOutputStream *dos) //throws IOExce // We shouln't be tracking an entity that doesn't have a short type of id DEBUG_BREAK(); } - short idAndRot = id | yRot << 11; - dos->writeShort(idAndRot); - short xAndYAndZ = ( xa << 11 ) | ( ( ya & 0x3f ) << 5 ) | ( za & 0x1f ); - dos->writeShort(xAndYAndZ); + dos->writeInt(id); + dos->writeChar(yRot); + short XandYandZ = ( xa << 11 ) | ( ( ya & 0x3f ) << 5 ) | ( za & 0x1f ); + dos->writeShort(XandYandZ); } int MoveEntityPacketSmall::PosRot::getEstimatedSize() { - return 4; + return 7; } MoveEntityPacketSmall::Pos::Pos() @@ -128,9 +127,8 @@ MoveEntityPacketSmall::Pos::Pos(int id, char xa, char ya, char za) : MoveEntityP void MoveEntityPacketSmall::Pos::read(DataInputStream *dis) //throws IOException { - int idAndY = dis->readShort(); - this->id = idAndY & 0x07ff; - this->ya = idAndY >> 11; + this->id = dis->readInt(); + this->ya = dis->readChar(); int XandZ = (int)static_cast(dis->readByte()); xa = XandZ >> 4; za = ( XandZ << 28 ) >> 28; @@ -143,15 +141,15 @@ void MoveEntityPacketSmall::Pos::write(DataOutputStream *dos) //throws IOExcepti // We shouln't be tracking an entity that doesn't have a short type of id DEBUG_BREAK(); } - short idAndY = id | ya << 11; - dos->writeShort(idAndY); + dos->writeInt(id); + dos->writeChar(ya); char XandZ = ( xa << 4 ) | ( za & 0x0f ); dos->writeByte(XandZ); } int MoveEntityPacketSmall::Pos::getEstimatedSize() { - return 3; + return 7; } MoveEntityPacketSmall::Rot::Rot() @@ -169,9 +167,8 @@ MoveEntityPacketSmall::Rot::Rot(int id, char yRot, char xRot) : MoveEntityPacket void MoveEntityPacketSmall::Rot::read(DataInputStream *dis) //throws IOException { - int idAndRot = (int)dis->readShort(); - this->id = idAndRot & 0x07ff; - this->yRot = idAndRot >> 11; + this->id = dis->readInt(); + this->yRot = dis->readChar(); } void MoveEntityPacketSmall::Rot::write(DataOutputStream *dos) //throws IOException @@ -181,11 +178,11 @@ void MoveEntityPacketSmall::Rot::write(DataOutputStream *dos) //throws IOExcepti // We shouln't be tracking an entity that doesn't have a short type of id DEBUG_BREAK(); } - short idAndRot = id | yRot << 11; - dos->writeShort(idAndRot); + dos->writeInt(id); + dos->writeChar(yRot); } int MoveEntityPacketSmall::Rot::getEstimatedSize() { - return 2; + return 5; } diff --git a/Minecraft.World/RespawnPacket.cpp b/Minecraft.World/RespawnPacket.cpp index c4a19c8c..aee7d729 100644 --- a/Minecraft.World/RespawnPacket.cpp +++ b/Minecraft.World/RespawnPacket.cpp @@ -58,7 +58,7 @@ void RespawnPacket::read(DataInputStream *dis) //throws IOException mapSeed = dis->readLong(); difficulty = dis->readByte(); m_newSeaLevel = dis->readBoolean(); - m_newEntityId = dis->readShort(); + m_newEntityId = dis->readInt(); #ifdef _LARGE_WORLDS m_xzSize = dis->readShort(); m_hellScale = dis->read(); @@ -83,7 +83,7 @@ void RespawnPacket::write(DataOutputStream *dos) //throws IOException dos->writeLong(mapSeed); dos->writeByte(difficulty); dos->writeBoolean(m_newSeaLevel); - dos->writeShort(m_newEntityId); + dos->writeInt(m_newEntityId); #ifdef _LARGE_WORLDS dos->writeShort(m_xzSize); dos->write(m_hellScale); @@ -97,5 +97,5 @@ int RespawnPacket::getEstimatedSize() { length = static_cast(m_pLevelType->getGeneratorName().length()); } - return 13+length; + return 13+length+2; } diff --git a/Minecraft.World/SetEntityMotionPacket.cpp b/Minecraft.World/SetEntityMotionPacket.cpp index 20d841f1..8488eee7 100644 --- a/Minecraft.World/SetEntityMotionPacket.cpp +++ b/Minecraft.World/SetEntityMotionPacket.cpp @@ -49,9 +49,9 @@ SetEntityMotionPacket::SetEntityMotionPacket(int id, double xd, double yd, doubl void SetEntityMotionPacket::read(DataInputStream *dis) //throws IOException { - short idAndFlag = dis->readShort(); - id = idAndFlag & 0x07ff; - if( idAndFlag & 0x0800 ) + useBytes = dis->readBoolean(); + id = dis->readInt(); + if(useBytes) { xa = static_cast(dis->readByte()); ya = static_cast(dis->readByte()); @@ -62,29 +62,28 @@ void SetEntityMotionPacket::read(DataInputStream *dis) //throws IOException xa *= 16; ya *= 16; za *= 16; - useBytes = true; } else { xa = dis->readShort(); ya = dis->readShort(); za = dis->readShort(); - useBytes = false; } } void SetEntityMotionPacket::write(DataOutputStream *dos) //throws IOException { + dos->writeBoolean(useBytes); if( useBytes ) { - dos->writeShort(id | 0x800); + dos->writeInt(id); dos->writeByte(xa/16); dos->writeByte(ya/16); dos->writeByte(za/16); } else { - dos->writeShort(id); + dos->writeInt(id); dos->writeShort(xa); dos->writeShort(ya); dos->writeShort(za); @@ -98,7 +97,7 @@ void SetEntityMotionPacket::handle(PacketListener *listener) int SetEntityMotionPacket::getEstimatedSize() { - return useBytes ? 5 : 8; + return useBytes ? 8 : 11; } bool SetEntityMotionPacket::canBeInvalidated() diff --git a/Minecraft.World/SharedConstants.h b/Minecraft.World/SharedConstants.h index c4db88ae..875fdba1 100644 --- a/Minecraft.World/SharedConstants.h +++ b/Minecraft.World/SharedConstants.h @@ -7,7 +7,7 @@ class SharedConstants public: static void staticCtor(); static const wstring VERSION_STRING; - static const int NETWORK_PROTOCOL_VERSION = 78; + static const int NETWORK_PROTOCOL_VERSION = 79; static const bool INGAME_DEBUG_OUTPUT = false; // NOT texture resolution. How many sub-blocks each block face is made up of. @@ -31,4 +31,4 @@ class SharedConstants static const int TICKS_PER_SECOND = 20; static const int FULLBRIGHT_LIGHTVALUE = 15 << 20 | 15 << 4; -}; \ No newline at end of file +}; diff --git a/Minecraft.World/TeleportEntityPacket.cpp b/Minecraft.World/TeleportEntityPacket.cpp index ccac5e51..7d7a492f 100644 --- a/Minecraft.World/TeleportEntityPacket.cpp +++ b/Minecraft.World/TeleportEntityPacket.cpp @@ -39,7 +39,7 @@ TeleportEntityPacket::TeleportEntityPacket(int id, int x, int y, int z, byte yRo void TeleportEntityPacket::read(DataInputStream *dis) //throws IOException { - id = dis->readShort(); + id = dis->readInt(); #ifdef _LARGE_WORLDS x = dis->readInt(); y = dis->readInt(); @@ -55,7 +55,7 @@ void TeleportEntityPacket::read(DataInputStream *dis) //throws IOException void TeleportEntityPacket::write(DataOutputStream *dos) //throws IOException { - dos->writeShort(id); + dos->writeInt(id); #ifdef _LARGE_WORLDS dos->writeInt(x); dos->writeInt(y); @@ -76,7 +76,7 @@ void TeleportEntityPacket::handle(PacketListener *listener) int TeleportEntityPacket::getEstimatedSize() { - return 2 + 2 + 2 + 2 + 1 + 1; + return 4 + 2 + 2 + 2 + 1 + 1; } bool TeleportEntityPacket::canBeInvalidated() From b67cbb85f393dabb860b86926963c04dd5b949a5 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit <116128211+DrPerkyLegit@users.noreply.github.com> Date: Fri, 22 May 2026 11:39:27 -0400 Subject: [PATCH 26/32] feat: multiplayer feat/less hardcoded lists (#89) * CustomPayloadPacket Changes commented all code for old equip packets cleaned up custom payload key creation added 2 new payload packet definitions for upcoming changes * server sided recipe list * code cleanup for recipes * add dtor for recipe classes to fix memory leak * forgot this part to the dtor commit * dtor changes tested and fixed * server side creative menu list --- Minecraft.Client/ClientConnection.cpp | 23 ++ Minecraft.Client/ClientConnection.h | 2 + .../Common/UI/IUIScene_CreativeMenu.cpp | 126 +++++++---- .../Common/UI/IUIScene_CreativeMenu.h | 7 + Minecraft.Client/PlayerConnection.cpp | 4 +- Minecraft.Client/PlayerList.cpp | 6 + Minecraft.World/CustomPayloadPacket.cpp | 47 ++-- Minecraft.World/CustomPayloadPacket.h | 11 +- Minecraft.World/FireworksRecipe.h | 4 + Minecraft.World/ItemInstance.cpp | 5 + Minecraft.World/ItemInstance.h | 1 + Minecraft.World/Packet.h | 2 - Minecraft.World/Recipes.cpp | 203 ++++++++++-------- Minecraft.World/Recipes.h | 8 + Minecraft.World/Recipy.h | 30 +-- Minecraft.World/ShapedRecipy.cpp | 79 +++++++ Minecraft.World/ShapedRecipy.h | 21 +- Minecraft.World/ShapelessRecipy.cpp | 72 ++++++- Minecraft.World/ShapelessRecipy.h | 3 + 19 files changed, 484 insertions(+), 170 deletions(-) diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index d13356f9..361c2974 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -61,6 +61,8 @@ #include "Windows64/Network/WinsockNetLayer.h" #endif +#include "../Minecraft.World/Recipes.h" + #ifdef _DURANGO #include "../Minecraft.World/DurangoStats.h" @@ -137,6 +139,9 @@ ClientConnection::ClientConnection(Minecraft *minecraft, Socket *socket, int iUs maxPlayers = 20; m_isForkServer = false; + m_recivedRecipeRegistyUpdate = false; + m_recivedCreativeRegistyUpdate = false; + this->minecraft = minecraft; if( iUserIndex < 0 ) @@ -246,6 +251,14 @@ void ClientConnection::handleLogin(shared_ptr packet) { if (done) return; + if (!m_recivedRecipeRegistyUpdate) { + Recipes::getInstance()->loadFromLocal(); + } + + if (!m_recivedCreativeRegistyUpdate) { + IUIScene_CreativeMenu::loadFromLocal(); + } + PlayerUID OnlineXuid; ProfileManager.GetXUID(m_userIndex,&OnlineXuid,true); // online xuid MOJANG_DATA *pMojangData = nullptr; @@ -4025,6 +4038,16 @@ void ClientConnection::handleCustomPayload(shared_ptr custo } } } + else if (CustomPayloadPacket::UPDATE_RECIPE_REGISTRY.compare(customPayloadPacket->identifier) == 0) { + this->m_recivedRecipeRegistyUpdate = true; + + Recipes::getInstance()->loadFromPacket(customPayloadPacket->data); + } + else if (CustomPayloadPacket::UPDATE_CREATIVE_REGISTRY.compare(customPayloadPacket->identifier) == 0) { + this->m_recivedCreativeRegistyUpdate = true; + + IUIScene_CreativeMenu::loadFromPacket(customPayloadPacket->data); + } } Connection *ClientConnection::getConnection() diff --git a/Minecraft.Client/ClientConnection.h b/Minecraft.Client/ClientConnection.h index ad78cac9..4e60b7d4 100644 --- a/Minecraft.Client/ClientConnection.h +++ b/Minecraft.Client/ClientConnection.h @@ -49,6 +49,8 @@ private: std::unordered_set m_trackedEntityIds; std::unordered_set m_visibleChunks; bool m_isForkServer; // true when connected to a fork server (received MC|ForkHello) + bool m_recivedRecipeRegistyUpdate; + bool m_recivedCreativeRegistyUpdate; static int64_t chunkKey(int x, int z) { return ((int64_t)x << 32) | ((int64_t)z & 0xFFFFFFFF); } diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp index 4ddbd620..128df572 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -1,3 +1,4 @@ +#include "IUIScene_CreativeMenu.h" #include "stdafx.h" #include "IUIScene_CreativeMenu.h" @@ -22,6 +23,56 @@ vector< shared_ptr > IUIScene_CreativeMenu::categoryGroups[eCreati #define ITEM_AUX(id, aux) list->push_back( shared_ptr(new ItemInstance(id, 1, aux)) ); #define DEF(index) list = &categoryGroups[index]; +void IUIScene_CreativeMenu::_wipeCreativeItems() { + for (int i = 0; i < eCreativeInventoryGroupsCount; i++) { + IUIScene_CreativeMenu::categoryGroups[i].clear(); + } +} + +void IUIScene_CreativeMenu::loadFromLocal() { + IUIScene_CreativeMenu::_wipeCreativeItems(); + IUIScene_CreativeMenu::staticCtor(); +} + +void IUIScene_CreativeMenu::loadFromPacket(byteArray packetData) { + ByteArrayInputStream bais(packetData); + DataInputStream input(&bais); + + IUIScene_CreativeMenu::_wipeCreativeItems(); + + for (int i = 0; i < eCreativeInventoryGroupsCount; i++) { + int itemCount = input.readShort(); + + for (int j = 0; j < itemCount; j++) { + int itemId = input.readShort(); + int itemAux = input.readShort(); + + shared_ptr item = std::make_shared(itemId, 1, 0); + item->setRawAuxValue(itemAux); + item->tag = Packet::readNbt(&input); + + IUIScene_CreativeMenu::categoryGroups[i].emplace_back(item); + } + } + +} + +std::shared_ptr IUIScene_CreativeMenu::createUpdatePacket() { + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + for (int i = 0; i < eCreativeInventoryGroupsCount; i++) { + dos.writeShort(IUIScene_CreativeMenu::categoryGroups[i].size()); + for (shared_ptr& item : IUIScene_CreativeMenu::categoryGroups[i]) { + dos.writeShort(item->id); + dos.writeShort(item->getAuxValue()); + Packet::writeNbt(item->tag, &dos); + } + } + + return std::make_shared(CustomPayloadPacket::UPDATE_CREATIVE_REGISTRY, baos.toByteArray()); +} + void IUIScene_CreativeMenu::staticCtor() { vector< shared_ptr > *list; @@ -801,55 +852,56 @@ void IUIScene_CreativeMenu::staticCtor() ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2EXTENDED, MASK_JUMPBOOST)) // end of tu31 potions + if (specs == nullptr) { + specs = new TabSpec * [eCreativeInventoryTab_COUNT]; - specs = new TabSpec*[eCreativeInventoryTab_COUNT]; - - // Top Row - ECreative_Inventory_Groups blocksGroup[] = {eCreativeInventory_BuildingBlocks}; - specs[eCreativeInventoryTab_BuildingBlocks] = new TabSpec(L"Structures", IDS_GROUPNAME_BUILDING_BLOCKS, 1, blocksGroup); + // Top Row + ECreative_Inventory_Groups blocksGroup[] = { eCreativeInventory_BuildingBlocks }; + specs[eCreativeInventoryTab_BuildingBlocks] = new TabSpec(L"Structures", IDS_GROUPNAME_BUILDING_BLOCKS, 1, blocksGroup); #ifndef _CONTENT_PACKAGE - ECreative_Inventory_Groups decorationsGroup[] = {eCreativeInventory_Decoration}; - ECreative_Inventory_Groups debugDecorationsGroup[] = {eCreativeInventory_ArtToolsDecorations}; - specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup, 0, nullptr, 1, debugDecorationsGroup); + ECreative_Inventory_Groups decorationsGroup[] = { eCreativeInventory_Decoration }; + ECreative_Inventory_Groups debugDecorationsGroup[] = { eCreativeInventory_ArtToolsDecorations }; + specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup, 0, nullptr, 1, debugDecorationsGroup); #else - ECreative_Inventory_Groups decorationsGroup[] = {eCreativeInventory_Decoration}; - specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup); + ECreative_Inventory_Groups decorationsGroup[] = { eCreativeInventory_Decoration }; + specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup); #endif - ECreative_Inventory_Groups redAndTranGroup[] = {eCreativeInventory_Transport, eCreativeInventory_Redstone}; - specs[eCreativeInventoryTab_RedstoneAndTransport] = new TabSpec(L"RedstoneAndTransport", IDS_GROUPNAME_REDSTONE_AND_TRANSPORT, 2, redAndTranGroup); + ECreative_Inventory_Groups redAndTranGroup[] = { eCreativeInventory_Transport, eCreativeInventory_Redstone }; + specs[eCreativeInventoryTab_RedstoneAndTransport] = new TabSpec(L"RedstoneAndTransport", IDS_GROUPNAME_REDSTONE_AND_TRANSPORT, 2, redAndTranGroup); - ECreative_Inventory_Groups materialsGroup[] = {eCreativeInventory_Materials}; - specs[eCreativeInventoryTab_Materials] = new TabSpec(L"Materials", IDS_GROUPNAME_MATERIALS, 1, materialsGroup); + ECreative_Inventory_Groups materialsGroup[] = { eCreativeInventory_Materials }; + specs[eCreativeInventoryTab_Materials] = new TabSpec(L"Materials", IDS_GROUPNAME_MATERIALS, 1, materialsGroup); - ECreative_Inventory_Groups foodGroup[] = {eCreativeInventory_Food}; - specs[eCreativeInventoryTab_Food] = new TabSpec(L"Food", IDS_GROUPNAME_FOOD, 1, foodGroup); + ECreative_Inventory_Groups foodGroup[] = { eCreativeInventory_Food }; + specs[eCreativeInventoryTab_Food] = new TabSpec(L"Food", IDS_GROUPNAME_FOOD, 1, foodGroup); - ECreative_Inventory_Groups toolsGroup[] = {eCreativeInventory_ToolsArmourWeapons}; - specs[eCreativeInventoryTab_ToolsWeaponsArmor] = new TabSpec(L"Tools", IDS_GROUPNAME_TOOLS_WEAPONS_ARMOR, 1, toolsGroup); + ECreative_Inventory_Groups toolsGroup[] = { eCreativeInventory_ToolsArmourWeapons }; + specs[eCreativeInventoryTab_ToolsWeaponsArmor] = new TabSpec(L"Tools", IDS_GROUPNAME_TOOLS_WEAPONS_ARMOR, 1, toolsGroup); - ECreative_Inventory_Groups brewingGroup[] = {eCreativeInventory_Brewing, eCreativeInventory_Potions_Level2_Extended, eCreativeInventory_Potions_Extended, eCreativeInventory_Potions_Level2, eCreativeInventory_Potions_Basic}; + ECreative_Inventory_Groups brewingGroup[] = { eCreativeInventory_Brewing, eCreativeInventory_Potions_Level2_Extended, eCreativeInventory_Potions_Extended, eCreativeInventory_Potions_Level2, eCreativeInventory_Potions_Basic }; - // Just use the text LT - the graphic doesn't fit in splitscreen either - // In 480p there's not enough room for the LT button, so use text instead - //if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) - { - specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"Brewing", IDS_GROUPNAME_POTIONS_480, 5, brewingGroup); + // Just use the text LT - the graphic doesn't fit in splitscreen either + // In 480p there's not enough room for the LT button, so use text instead + //if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"Brewing", IDS_GROUPNAME_POTIONS_480, 5, brewingGroup); + } + // else + // { + // specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"icon_brewing.png", IDS_GROUPNAME_POTIONS, 1, brewingGroup, 4, potionsGroup); + // } + +#ifndef _CONTENT_PACKAGE + ECreative_Inventory_Groups miscGroup[] = { eCreativeInventory_Misc }; + ECreative_Inventory_Groups debugMiscGroup[] = { eCreativeInventory_ArtToolsMisc }; + specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup, 0, nullptr, 1, debugMiscGroup); +#else + ECreative_Inventory_Groups miscGroup[] = { eCreativeInventory_Misc }; + specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup); +#endif } - // else - // { - // specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"icon_brewing.png", IDS_GROUPNAME_POTIONS, 1, brewingGroup, 4, potionsGroup); - // } - -#ifndef _CONTENT_PACKAGE - ECreative_Inventory_Groups miscGroup[] = {eCreativeInventory_Misc}; - ECreative_Inventory_Groups debugMiscGroup[] = {eCreativeInventory_ArtToolsMisc}; - specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup, 0, nullptr, 1, debugMiscGroup); -#else - ECreative_Inventory_Groups miscGroup[] = {eCreativeInventory_Misc}; - specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup); -#endif } IUIScene_CreativeMenu::IUIScene_CreativeMenu() diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h index 02625233..855e5198 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h @@ -1,6 +1,7 @@ #pragma once #include "IUIScene_AbstractContainerMenu.h" #include "../../../Minecraft.World/AbstractContainerMenu.h" +#include "../../../Minecraft.World/CustomPayloadPacket.h" // 4J Stu - This class is for code that is common between XUI and Iggy class SimpleContainer; @@ -100,10 +101,16 @@ protected: bool m_bCarryingCreativeItem; int m_creativeSlotX, m_creativeSlotY, m_inventorySlotX, m_inventorySlotY; + static void _wipeCreativeItems(); + public: static void staticCtor(); IUIScene_CreativeMenu(); + static void loadFromLocal(); + static void loadFromPacket(byteArray packetData); + + static std::shared_ptr createUpdatePacket(); protected: ECreativeInventoryTabs m_curTab; int m_tabDynamicPos[eCreativeInventoryTab_COUNT]; diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 166e03dc..845d9ee3 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -2417,7 +2417,7 @@ void PlayerConnection::handleCustomPayload(shared_ptr custo player->inventory->setItem(player->inventory->selected, sentItem); } } - else if (CustomPayloadPacket::QUICK_EQUIP_PACKET.compare(customPayloadPacket->identifier) == 0) { + /*else if (CustomPayloadPacket::QUICK_EQUIP_PACKET.compare(customPayloadPacket->identifier) == 0) { //ByteArrayInputStream bais(customPayloadPacket->data); //DataInputStream input(&bais); //shared_ptr sentItem = Packet::readItem(&input); @@ -2448,7 +2448,7 @@ void PlayerConnection::handleCustomPayload(shared_ptr custo //PlayerList* playerList = MinecraftServer::getInstance()->getPlayers(); //playerList->broadcastAll(std::make_shared(player->entityId, slot, sentItem)); - } + }*/ else if (CustomPayloadPacket::TRADER_SELECTION_PACKET.compare(customPayloadPacket->identifier) == 0) { ByteArrayInputStream bais(customPayloadPacket->data); diff --git a/Minecraft.Client/PlayerList.cpp b/Minecraft.Client/PlayerList.cpp index 8814401d..04da1990 100644 --- a/Minecraft.Client/PlayerList.cpp +++ b/Minecraft.Client/PlayerList.cpp @@ -37,6 +37,8 @@ #include "Common/Network/Sony/NetworkPlayerSony.h" #endif +#include "../Minecraft.World/Recipes.h" + #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) #include "../Minecraft.Server/Access/Access.h" #include "../Minecraft.Server/Common/StringUtils.h" @@ -51,6 +53,7 @@ extern bool g_Win64DedicatedServer; static unsigned int s_playerListTickCount = 0; static const int kIdentityResponseGraceTicks = 200; // 10 seconds at 20 TPS #endif +#include "../Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h" // 4J - this class is fairly substantially altered as there didn't seem any point in porting code for banning, whitelisting, ops etc. @@ -300,6 +303,9 @@ bool PlayerList::placeNewPlayer(Connection *connection, shared_ptr app.DebugPrintf("RECONNECT: placeNewPlayer smallId=%d entityId=%d dim=%d\n", newSmallId, player->entityId, level->dimension->id); + playerConnection->send(Recipes::getInstance()->createUpdatePacket()); + playerConnection->send(IUIScene_CreativeMenu::createUpdatePacket()); + playerConnection->send(std::make_shared(L"", player->entityId, level->getLevelData()->getGenerator(), level->getSeed(), player->gameMode->getGameModeForPlayer()->getId(), diff --git a/Minecraft.World/CustomPayloadPacket.cpp b/Minecraft.World/CustomPayloadPacket.cpp index e6dacfeb..6b706d6f 100644 --- a/Minecraft.World/CustomPayloadPacket.cpp +++ b/Minecraft.World/CustomPayloadPacket.cpp @@ -5,30 +5,39 @@ #include "CustomPayloadPacket.h" // Mojang-defined custom packets -const wstring CustomPayloadPacket::CUSTOM_BOOK_PACKET = L"MC|BEdit"; -const wstring CustomPayloadPacket::CUSTOM_BOOK_SIGN_PACKET = L"MC|BSign"; -const wstring CustomPayloadPacket::TEXTURE_PACK_PACKET = L"MC|TPack"; -const wstring CustomPayloadPacket::TRADER_LIST_PACKET = L"MC|TrList"; -const wstring CustomPayloadPacket::TRADER_SELECTION_PACKET = L"MC|TrSel"; -const wstring CustomPayloadPacket::SET_ADVENTURE_COMMAND_PACKET = L"MC|AdvCdm"; -const wstring CustomPayloadPacket::SET_BEACON_PACKET = L"MC|Beacon"; -const wstring CustomPayloadPacket::SET_ITEM_NAME_PACKET = L"MC|ItemName"; +const wstring CustomPayloadPacket::CUSTOM_BOOK_PACKET = CreateVanillaPayloadKey(L"BEdit"); +const wstring CustomPayloadPacket::CUSTOM_BOOK_SIGN_PACKET = CreateVanillaPayloadKey(L"BSign"); +const wstring CustomPayloadPacket::TEXTURE_PACK_PACKET = CreateVanillaPayloadKey(L"TPack"); +const wstring CustomPayloadPacket::TRADER_LIST_PACKET = CreateVanillaPayloadKey(L"TrList"); +const wstring CustomPayloadPacket::TRADER_SELECTION_PACKET = CreateVanillaPayloadKey(L"TrSel"); -const wstring CustomPayloadPacket::CIPHER_KEY_CHANNEL = L"MC|CKey"; -const wstring CustomPayloadPacket::CIPHER_ACK_CHANNEL = L"MC|CAck"; -const wstring CustomPayloadPacket::CIPHER_ON_CHANNEL = L"MC|COn"; +// neoLegacy-defined custom packets +const wstring CustomPayloadPacket::UPDATE_RECIPE_REGISTRY = CreatePayloadKey(L"neo", L"UpdRReg"); +const wstring CustomPayloadPacket::UPDATE_CREATIVE_REGISTRY = CreatePayloadKey(L"neo", L"UpdCReg"); -const wstring CustomPayloadPacket::IDENTITY_TOKEN_ISSUE = L"MC|CTIssue"; -const wstring CustomPayloadPacket::IDENTITY_TOKEN_CHALLENGE = L"MC|CTChallenge"; -const wstring CustomPayloadPacket::IDENTITY_TOKEN_RESPONSE = L"MC|CTResponse"; +//todo: figure out if we should replace the packets in the comment section with a custom payload identifier +//comment section start +const wstring CustomPayloadPacket::SET_ADVENTURE_COMMAND_PACKET = CreateVanillaPayloadKey(L"AdvCdm"); +const wstring CustomPayloadPacket::SET_BEACON_PACKET = CreateVanillaPayloadKey(L"Beacon"); +const wstring CustomPayloadPacket::SET_ITEM_NAME_PACKET = CreateVanillaPayloadKey(L"ItemName"); -const wstring CustomPayloadPacket::FORK_HELLO_CHANNEL = L"MC|ForkHello"; -const wstring CustomPayloadPacket::FORK_PLAYER_LEAVE_CHANNEL = L"MC|ForkPLeave"; +const wstring CustomPayloadPacket::CIPHER_KEY_CHANNEL = CreateVanillaPayloadKey(L"CKey"); +const wstring CustomPayloadPacket::CIPHER_ACK_CHANNEL = CreateVanillaPayloadKey(L"CAck"); +const wstring CustomPayloadPacket::CIPHER_ON_CHANNEL = CreateVanillaPayloadKey(L"COn"); -const wstring CustomPayloadPacket::QUICK_EQUIP_PACKET = L"MC|QEquip"; -const wstring CustomPayloadPacket::QUICK_EQUIP_SERVER_PACKET = L"MC|QEquipServer"; +const wstring CustomPayloadPacket::IDENTITY_TOKEN_ISSUE = CreateVanillaPayloadKey(L"CTIssue"); +const wstring CustomPayloadPacket::IDENTITY_TOKEN_CHALLENGE = CreateVanillaPayloadKey(L"CTChallenge"); +const wstring CustomPayloadPacket::IDENTITY_TOKEN_RESPONSE = CreateVanillaPayloadKey(L"CTResponse"); -const wstring CustomPayloadPacket::ENCHANTMENT_LIST_PACKET = L"MC|EnchList"; +const wstring CustomPayloadPacket::FORK_HELLO_CHANNEL = CreateVanillaPayloadKey(L"ForkHello"); +const wstring CustomPayloadPacket::FORK_PLAYER_LEAVE_CHANNEL = CreateVanillaPayloadKey(L"ForkPLeave"); + +const wstring CustomPayloadPacket::ENCHANTMENT_LIST_PACKET = CreateVanillaPayloadKey(L"EnchList"); +//comment section end + +//removed cause its now handled on the server side +// const wstring CustomPayloadPacket::QUICK_EQUIP_PACKET = CreateVanillaPayloadKey(L"QEquip"); +// const wstring CustomPayloadPacket::QUICK_EQUIP_SERVER_PACKET = CreateVanillaPayloadKey(L"QEquipServer"); CustomPayloadPacket::CustomPayloadPacket() : length(0) diff --git a/Minecraft.World/CustomPayloadPacket.h b/Minecraft.World/CustomPayloadPacket.h index ae7163bf..4fd30f05 100644 --- a/Minecraft.World/CustomPayloadPacket.h +++ b/Minecraft.World/CustomPayloadPacket.h @@ -3,6 +3,9 @@ using namespace std; #include "Packet.h" +#define CreatePayloadKey(identifier, action) identifier L"|" action +#define CreateVanillaPayloadKey(action) CreatePayloadKey(L"MC", action) + class CustomPayloadPacket : public Packet, public enable_shared_from_this { public: @@ -17,6 +20,10 @@ public: static const wstring SET_BEACON_PACKET; static const wstring SET_ITEM_NAME_PACKET; + // neoLegacy-defined custom packets + static const wstring UPDATE_RECIPE_REGISTRY; + static const wstring UPDATE_CREATIVE_REGISTRY; + // Security: stream cipher handshake channels static const wstring CIPHER_KEY_CHANNEL; // server->client: carries 32-byte key (16 AES key + 16 IV) static const wstring CIPHER_ACK_CHANNEL; // client->server: ack (empty payload) @@ -32,8 +39,8 @@ public: static const wstring FORK_PLAYER_LEAVE_CHANNEL; // server->client: player disconnected (payload: UTF gamertag) // Fixes for MP related crashes - static const wstring QUICK_EQUIP_PACKET; - static const wstring QUICK_EQUIP_SERVER_PACKET; + //static const wstring QUICK_EQUIP_PACKET; + //static const wstring QUICK_EQUIP_SERVER_PACKET; static const wstring ENCHANTMENT_LIST_PACKET; diff --git a/Minecraft.World/FireworksRecipe.h b/Minecraft.World/FireworksRecipe.h index b0c7c236..015a2478 100644 --- a/Minecraft.World/FireworksRecipe.h +++ b/Minecraft.World/FireworksRecipe.h @@ -43,4 +43,8 @@ public: // 4J Added static void updatePossibleRecipes(shared_ptr craftSlots, bool *firework, bool *charge, bool *fade); static bool isValidIngredient(shared_ptr item, bool firework, bool charge, bool fade); + + virtual void writeToStream(DataOutputStream* dos) { + dos->writeByte(99); + } }; \ No newline at end of file diff --git a/Minecraft.World/ItemInstance.cpp b/Minecraft.World/ItemInstance.cpp index e4af60c6..dcc2200f 100644 --- a/Minecraft.World/ItemInstance.cpp +++ b/Minecraft.World/ItemInstance.cpp @@ -220,6 +220,11 @@ void ItemInstance::setAuxValue(int value) } } +void ItemInstance::setRawAuxValue(int value) +{ + auxValue = value; +} + int ItemInstance::getMaxDamage() { return Item::items[id]->getMaxDamage(); diff --git a/Minecraft.World/ItemInstance.h b/Minecraft.World/ItemInstance.h index cff33a97..e2b0e059 100644 --- a/Minecraft.World/ItemInstance.h +++ b/Minecraft.World/ItemInstance.h @@ -90,6 +90,7 @@ public: int getDamageValue(); int getAuxValue() const; void setAuxValue(int value); + void setRawAuxValue(int value); int getMaxDamage(); bool hurt(int dmg, Random *random); void hurtAndBreak(int dmg, shared_ptr owner); diff --git a/Minecraft.World/Packet.h b/Minecraft.World/Packet.h index c639c8f1..9938afa6 100644 --- a/Minecraft.World/Packet.h +++ b/Minecraft.World/Packet.h @@ -103,7 +103,5 @@ public: static shared_ptr readItem(DataInputStream *dis); static void writeItem(shared_ptr item, DataOutputStream *dos); static CompoundTag *readNbt(DataInputStream *dis); - -protected: static void writeNbt(CompoundTag *tag, DataOutputStream *dos); }; \ No newline at end of file diff --git a/Minecraft.World/Recipes.cpp b/Minecraft.World/Recipes.cpp index 2eedf59f..f8c1ef41 100644 --- a/Minecraft.World/Recipes.cpp +++ b/Minecraft.World/Recipes.cpp @@ -29,20 +29,14 @@ void Recipes::_init() { // 4J Jev: instance = new Recipes(); recipies = new RecipyList(); -} -Recipes::Recipes() -{ - int iCount=0; - _init(); - - pArmorRecipes = new ArmorRecipes; - pClothDyeRecipes = new ClothDyeRecipes; - pFoodRecipies = new FoodRecipies; - pOreRecipies = new OreRecipies; - pStructureRecipies = new StructureRecipies; - pToolRecipies = new ToolRecipies; - pWeaponRecipies = new WeaponRecipies; + pArmorRecipes = new ArmorRecipes; + pClothDyeRecipes = new ClothDyeRecipes; + pFoodRecipies = new FoodRecipies; + pOreRecipies = new OreRecipies; + pStructureRecipies = new StructureRecipies; + pToolRecipies = new ToolRecipies; + pWeaponRecipies = new WeaponRecipies; // 4J Stu - These just don't work with our crafting menu //recipies->push_back(new ArmorDyeRecipe()); @@ -50,8 +44,10 @@ Recipes::Recipes() //recipies->add(new MapExtendingRecipe()); //recipies->add(new FireworksRecipe()); pFireworksRecipes = new FireworksRecipe(); +} - +void Recipes::_compileRecipes() +{ addShapedRecipy(new ItemInstance(Tile::wood, 4, 0), // L"sczg", L"#", // @@ -186,7 +182,7 @@ Recipes::Recipes() L"W#W", // L"W#W", // - L'#', Item::stick, + L'#', Item::stick, L'W', new ItemInstance(Tile::wood, 1, TreeTile::ACACIA_TRUNK), L'S'); @@ -195,7 +191,7 @@ Recipes::Recipes() L"W#W", // L"W#W", // - L'#', Item::stick, + L'#', Item::stick, L'W', new ItemInstance(Tile::wood, 1, TreeTile::DARK_TRUNK), L'S'); @@ -475,12 +471,10 @@ Recipes::Recipes() L'S'); pArmorRecipes->addRecipes(this); - //iCount=getRecipies()->size(); + pClothDyeRecipes->addRecipes(this); - - addShapedRecipy(new ItemInstance(Tile::snow, 1), // L"sscig", L"##", // @@ -497,7 +491,7 @@ Recipes::Recipes() L'#', Item::prismarine_shard, L'S'); - addShapedRecipy(new ItemInstance(Tile::prismarine, 1,PrismarineTile::TYPE_BRICKS), // + addShapedRecipy(new ItemInstance(Tile::prismarine, 1, PrismarineTile::TYPE_BRICKS), // L"ssscig", L"###", // L"###", // @@ -507,7 +501,7 @@ Recipes::Recipes() L'S'); - addShapedRecipy(new ItemInstance(Tile::prismarine, 1,PrismarineTile::TYPE_DARK), // + addShapedRecipy(new ItemInstance(Tile::prismarine, 1, PrismarineTile::TYPE_DARK), // L"ssscicig", L"###", // L"#X#", // @@ -657,10 +651,6 @@ Recipes::Recipes() - - - //iCount=getRecipies()->size(); - addShapedRecipy(new ItemInstance(Item::cake, 1), // L"ssscicicicig", L"AAA", // @@ -770,7 +760,7 @@ Recipes::Recipes() L'#', Tile::wood, L'V'); - addShapedRecipy(new ItemInstance((Item *)Item::fishingRod, 1), // + addShapedRecipy(new ItemInstance((Item*)Item::fishingRod, 1), // L"ssscicig", L" #", // L" #X", // @@ -803,7 +793,7 @@ Recipes::Recipes() L'F'); // Moved bow and arrow in from weapons to avoid stacking on the group name display - addShapedRecipy(new ItemInstance((Item *)Item::bow, 1), // + addShapedRecipy(new ItemInstance((Item*)Item::bow, 1), // L"ssscicig", L" #X", // L"# X", // @@ -850,7 +840,7 @@ Recipes::Recipes() L'#', Tile::glass, L'T'); - + // torch made of charcoal - moved to be the default due to the tutorial using it addShapedRecipy(new ItemInstance(Tile::torch, 4), // @@ -961,12 +951,12 @@ Recipes::Recipes() addShapelessRecipy(new ItemInstance(Item::fireball, 3), // L"iiig", - Item::gunpowder, Item::blazePowder,Item::coal, + Item::gunpowder, Item::blazePowder, Item::coal, L'T'); addShapelessRecipy(new ItemInstance(Item::fireball, 3), // L"iizg", - Item::gunpowder, Item::blazePowder,new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL), + Item::gunpowder, Item::blazePowder, new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL), L'T'); addShapedRecipy(new ItemInstance(Item::lead, 2), // @@ -1096,24 +1086,24 @@ Recipes::Recipes() L'#', Tile::wood, L'X', Item::diamond, 'D'); - - addShapedRecipy(new ItemInstance(Item::leather, 1), - L"sscig", - L"##", - L"##", - L'#', Item::rabbit_hide, - L'D'); + addShapedRecipy(new ItemInstance(Item::leather, 1), + L"sscig", + L"##", + L"##", + + L'#', Item::rabbit_hide, + L'D'); addShapedRecipy(new ItemInstance(Item::armor_stand, 1), - L"ssscictg", - L"SSS", - L" S ", - L"SXS", - L'S', Item::stick, - L'X', Tile::stoneSlabHalf, + L"ssscictg", + L"SSS", + L" S ", + L"SXS", + L'S', Item::stick, + L'X', Tile::stoneSlabHalf, L'D'); - + addShapedRecipy(new ItemInstance(Item::paper, 3), // @@ -1206,21 +1196,21 @@ Recipes::Recipes() L'D'); // 4J - TODO - put these new 1.7.3 items in required place within recipes - addShapedRecipy(new ItemInstance(static_cast(Tile::pistonBase), 1), // + addShapedRecipy(new ItemInstance(static_cast(Tile::pistonBase), 1), // L"sssctcicictg", - L"TTT", // - L"#X#", // - L"#R#", // + L"TTT", // + L"#X#", // + L"#R#", // - L'#', Tile::cobblestone, L'X', Item::ironIngot, L'R', Item::redStone, L'T', Tile::wood, + L'#', Tile::cobblestone, L'X', Item::ironIngot, L'R', Item::redStone, L'T', Tile::wood, L'M'); - addShapedRecipy(new ItemInstance(static_cast(Tile::pistonStickyBase), 1), // + addShapedRecipy(new ItemInstance(static_cast(Tile::pistonStickyBase), 1), // L"sscictg", - L"S", // - L"P", // + L"S", // + L"P", // - L'S', Item::slimeBall, L'P', Tile::pistonBase, + L'S', Item::slimeBall, L'P', Tile::pistonBase, L'M'); @@ -1233,7 +1223,7 @@ Recipes::Recipes() L'P', Item::paper, L'G', Item::gunpowder, L'D'); - addShapedRecipy(new ItemInstance(Item::fireworksCharge,1), // + addShapedRecipy(new ItemInstance(Item::fireworksCharge, 1), // L"sscicig", L" D ", // L" G ", // @@ -1241,7 +1231,7 @@ Recipes::Recipes() L'D', Item::dye_powder, L'G', Item::gunpowder, L'D'); - addShapedRecipy(new ItemInstance(Item::fireworksCharge,1), // + addShapedRecipy(new ItemInstance(Item::fireworksCharge, 1), // L"sscicig", L" D ", // L" C ", // @@ -1249,37 +1239,38 @@ Recipes::Recipes() L'D', Item::dye_powder, L'C', Item::fireworksCharge, L'D'); - - // Sort so the largest recipes get checked first! - /* 4J-PB - TODO - Collections.sort(recipies, new Comparator() - { - public: int compare(Recipy r0, Recipy r1) - { - - // shapeless recipes are put in the back of the list - if (r0 instanceof ShapelessRecipy && r1 instanceof ShapedRecipy) - { - return 1; - } - if (r1 instanceof ShapelessRecipy && r0 instanceof ShapedRecipy) - { - return -1; - } - - if (r1.size() < r0.size()) return -1; - if (r1.size() > r0.size()) return 1; - return 0; - } - }); - */ - - // 4J-PB removed System.out.println(recipies->size() + L" recipes"); - - // 4J-PB - build the array of ingredients required per recipe buildRecipeIngredientsArray(); } +void Recipes::_wipeRecipes() +{ + int iCount = recipies->size(); + + for (int i = 0; i < iCount; i++) { + Recipy::INGREDIENTS_REQUIRED& req = m_pRecipeIngredientsRequired[i]; + + delete[] req.iIngIDA; + delete[] req.iIngValA; + delete[] req.iIngAuxValA; + delete[] req.uiGridA; + } + + for (int i = 0; i < iCount; i++) { + delete (*recipies)[i]; + } + + recipies->clear(); + + delete[] m_pRecipeIngredientsRequired; + m_pRecipeIngredientsRequired = nullptr; +} + +Recipes::Recipes() +{ + _init(); + _compileRecipes(); +} + // 4J-PB - this function has been substantially changed due to the differences with a va_list of classes in C++ and Java ShapedRecipy *Recipes::addShapedRecipy(ItemInstance *result, ...) { @@ -1563,7 +1554,7 @@ void Recipes::buildRecipeIngredientsArray(void) int iRecipeC=static_cast(recipies->size()); - m_pRecipeIngredientsRequired= new Recipy::INGREDIENTS_REQUIRED [iRecipeC]; + m_pRecipeIngredientsRequired = new Recipy::INGREDIENTS_REQUIRED [iRecipeC]; int iCount=0; for (auto& recipe : *recipies) @@ -1577,4 +1568,46 @@ void Recipes::buildRecipeIngredientsArray(void) Recipy::INGREDIENTS_REQUIRED *Recipes::getRecipeIngredientsArray(void) { return m_pRecipeIngredientsRequired; -} \ No newline at end of file +} + +void Recipes::loadFromLocal() +{ + this->_wipeRecipes(); + this->_compileRecipes(); +} + +void Recipes::loadFromPacket(byteArray packetData) +{ + ByteArrayInputStream bais(packetData); + DataInputStream input(&bais); + + this->_wipeRecipes(); + { + int iCount = input.readInt(); + for (int i = 0; i < iCount; i++) { + int recipeType = input.readByte(); + + if (recipeType == 1) { + recipies->push_back(ShapelessRecipy::readFromStream(&input)); + } else if (recipeType == 2) { + recipies->push_back(ShapedRecipy::readFromStream(&input)); + } + } + } + this->buildRecipeIngredientsArray(); +} + +std::shared_ptr Recipes::createUpdatePacket() +{ + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + int iCount = recipies->size(); + dos.writeInt(iCount); + + for (int i = 0; i < iCount; i++) { + (*recipies)[i]->writeToStream(&dos); + } + + return std::make_shared(CustomPayloadPacket::UPDATE_RECIPE_REGISTRY, baos.toByteArray()); +} diff --git a/Minecraft.World/Recipes.h b/Minecraft.World/Recipes.h index d6e508a8..856e3a64 100644 --- a/Minecraft.World/Recipes.h +++ b/Minecraft.World/Recipes.h @@ -16,6 +16,7 @@ import net.minecraft.world.level.tile.Tile; */ #include "Recipy.h" +#include "../Minecraft.World/CustomPayloadPacket.h" #pragma once using namespace std; @@ -84,6 +85,8 @@ public: private: void _init(); // 4J add + void _compileRecipes(); + void _wipeRecipes(); Recipes(); public: @@ -97,6 +100,11 @@ public: shared_ptr getItemForRecipe(Recipy *r); Recipy::INGREDIENTS_REQUIRED *getRecipeIngredientsArray(); + void loadFromLocal(); + void loadFromPacket(byteArray packetData); + + std::shared_ptr createUpdatePacket(); + private: void buildRecipeIngredientsArray(); Recipy::INGREDIENTS_REQUIRED *m_pRecipeIngredientsRequired; diff --git a/Minecraft.World/Recipy.h b/Minecraft.World/Recipy.h index 9cbf5cac..b2ad0127 100644 --- a/Minecraft.World/Recipy.h +++ b/Minecraft.World/Recipy.h @@ -10,13 +10,13 @@ #define RECIPE_TYPE_2x2 0 #define RECIPE_TYPE_3x3 1 -class Recipy +class Recipy { public: enum _eGroupType { - eGroupType_First=0, - eGroupType_Structure=0, + eGroupType_First = 0, + eGroupType_Structure = 0, eGroupType_Tool, eGroupType_Food, eGroupType_Armour, @@ -28,28 +28,30 @@ public: eGroupType; // to class the item produced by the recipe // 4J-PB - we'll classing an ingredient ID with a different aux value as a different IngID AuxVal pair - typedef struct + typedef struct { int iIngC; int iType; // Can be a 2x2 or a 3x3. Inventory crafting can only make a 2x2. - int *iIngIDA; - int *iIngValA; - int *iIngAuxValA; - Recipy *pRecipy; + int* iIngIDA; + int* iIngValA; + int* iIngAuxValA; + Recipy* pRecipy; bool bCanMake[XUSER_MAX_COUNT]; - unsigned int *uiGridA; // hold the layout of the recipe (id | auxval<<24) + unsigned int* uiGridA; // hold the layout of the recipe (id | auxval<<24) unsigned short usBitmaskMissingGridIngredients[XUSER_MAX_COUNT]; // each bit set means we don't have that grid ingredient } INGREDIENTS_REQUIRED; - ~Recipy() {} - virtual bool matches(shared_ptr craftSlots, Level *level) = 0; + virtual ~Recipy() = default; + virtual bool matches(shared_ptr craftSlots, Level* level) = 0; virtual shared_ptr assemble(shared_ptr craftSlots) = 0; virtual int size() = 0; - virtual const ItemInstance *getResultItem() = 0; - virtual const int getGroup() = 0; + virtual const ItemInstance* getResultItem() = 0; + virtual const int getGroup() = 0; // 4J-PB virtual bool reqs(int iRecipe) = 0; - virtual void reqs(INGREDIENTS_REQUIRED *pIngReq) = 0; + virtual void reqs(INGREDIENTS_REQUIRED* pIngReq) = 0; + + virtual void writeToStream(DataOutputStream* dos) = 0; }; diff --git a/Minecraft.World/ShapedRecipy.cpp b/Minecraft.World/ShapedRecipy.cpp index 7a0e85b0..d73fe6ee 100644 --- a/Minecraft.World/ShapedRecipy.cpp +++ b/Minecraft.World/ShapedRecipy.cpp @@ -23,6 +23,24 @@ ShapedRecipy::ShapedRecipy(int width, int height, ItemInstance **recipeItems, It _keepTag = false; } +ShapedRecipy::~ShapedRecipy() { + // todo: why does this cause a error when clearing out these specifically? + // might be leaking memory here but im not sure cause it crashes when you clear them, so we dont clear them + /*for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + if (x < width && y < height) { + delete recipeItems[x + y * width]; + } + } + }*/ + + delete[] recipeItems; + delete result; + + recipeItems = nullptr; + result = nullptr; +} + const int ShapedRecipy::getGroup() { return group; @@ -227,4 +245,65 @@ ShapedRecipy *ShapedRecipy::keepTag() { _keepTag = true; return this; +} + +void ShapedRecipy::writeToStream(DataOutputStream* dos) { + dos->writeByte(2); + dos->writeByte(this->group); + + //write result item, it should always be valid + { + dos->writeShort(this->result->id); + dos->writeByte(this->result->count); + dos->writeShort(this->result->getAuxValue()); + + Packet::writeNbt(this->result->tag, dos); + } + + dos->writeByte((this->width << 2) | this->height); + + for (int i = 0; i < (this->width * this->height); i++) { + ItemInstance* ingredients_item = this->recipeItems[i]; + dos->writeBoolean(ingredients_item == nullptr); + if (ingredients_item == nullptr) continue; + + dos->writeShort(ingredients_item->id); + dos->writeShort(ingredients_item->getAuxValue()); + Packet::writeNbt(ingredients_item->tag, dos); + } +} + +ShapedRecipy* ShapedRecipy::readFromStream(DataInputStream* dis) { + int groupType = dis->readByte(); + + int resultItemID = dis->readShort(); + int resultItemCount = dis->readByte(); + int resultItemAux = dis->readShort(); + + ItemInstance* resultItem = new ItemInstance(resultItemID, resultItemCount, 0); + resultItem->setRawAuxValue(resultItemAux); + resultItem->tag = Packet::readNbt(dis); + + unsigned char packedSize = dis->readByte(); + + int width = (packedSize >> 2) & 0x3; + int height = packedSize & 0x3; + ItemInstance** ids = new ItemInstance*[width * height]; + + for (int i = 0; i < width * height; i++) { + ItemInstance* ingredients_item = nullptr; + bool isNull = dis->readBoolean(); + if (!isNull) { + int itemId = dis->readShort(); + int itemAux = dis->readShort(); + + ingredients_item = new ItemInstance(itemId, 1, 0); + ingredients_item->setRawAuxValue(itemAux); + ingredients_item->tag = Packet::readNbt(dis); + } + + ids[i] = ingredients_item; + } + + return new ShapedRecipy(width, height, ids, resultItem, groupType); } \ No newline at end of file diff --git a/Minecraft.World/ShapedRecipy.h b/Minecraft.World/ShapedRecipy.h index c111b7c7..25b6cc29 100644 --- a/Minecraft.World/ShapedRecipy.h +++ b/Minecraft.World/ShapedRecipy.h @@ -1,21 +1,22 @@ #pragma once -class ShapedRecipy : public Recipy +class ShapedRecipy : public Recipy { private: int width, height, group; - ItemInstance **recipeItems; - ItemInstance *result; + ItemInstance** recipeItems; + ItemInstance* result; bool _keepTag; public: const int resultId; public: - ShapedRecipy(int width, int height, ItemInstance **recipeItems, ItemInstance *result, int iGroup=Recipy::eGroupType_Decoration); + ShapedRecipy(int width, int height, ItemInstance** recipeItems, ItemInstance* result, int iGroup = Recipy::eGroupType_Decoration); + virtual ~ShapedRecipy() override; - virtual const ItemInstance *getResultItem(); + virtual const ItemInstance* getResultItem(); virtual const int getGroup(); - virtual bool matches(shared_ptr craftSlots, Level *level); + virtual bool matches(shared_ptr craftSlots, Level* level); private: bool matches(shared_ptr craftSlots, int xOffs, int yOffs, bool xFlip); @@ -23,10 +24,14 @@ private: public: virtual shared_ptr assemble(shared_ptr craftSlots); virtual int size(); - ShapedRecipy *keepTag(); + ShapedRecipy* keepTag(); // 4J-PB - to return the items required to make a recipe virtual bool reqs(int iRecipe); - virtual void reqs(INGREDIENTS_REQUIRED *pIngReq); + virtual void reqs(INGREDIENTS_REQUIRED* pIngReq); + + virtual void writeToStream(DataOutputStream* dos); + static ShapedRecipy* readFromStream(DataInputStream* dis); + }; diff --git a/Minecraft.World/ShapelessRecipy.cpp b/Minecraft.World/ShapelessRecipy.cpp index 8fab1b7e..3bf24ad7 100644 --- a/Minecraft.World/ShapelessRecipy.cpp +++ b/Minecraft.World/ShapelessRecipy.cpp @@ -19,6 +19,19 @@ ShapelessRecipy::ShapelessRecipy(ItemInstance *result, vector *i { } +ShapelessRecipy::~ShapelessRecipy() { + for (int i = 0; i < ingredients->size(); i++) { + delete (*ingredients)[i]; + } + + delete ingredients; + delete result; + + ingredients = nullptr; + result = nullptr; +} + + const int ShapelessRecipy::getGroup() { return group; @@ -173,4 +186,61 @@ void ShapelessRecipy::reqs(INGREDIENTS_REQUIRED *pIngReq) delete [] TempIngReq.iIngValA; delete [] TempIngReq.iIngAuxValA; delete [] TempIngReq.uiGridA; -} \ No newline at end of file +} + +void ShapelessRecipy::writeToStream(DataOutputStream* dos) { + dos->writeByte(1); + dos->writeByte(this->group); + + //write result item, it should always be valid + { + dos->writeShort(this->result->id); + dos->writeByte(this->result->count); + dos->writeShort(this->result->getAuxValue()); + + Packet::writeNbt(this->result->tag, dos); + } + + byte iCount = ingredients->size(); + dos->writeByte(iCount); + + for (int i = 0; i < iCount; i++) { + ItemInstance* item = (*ingredients)[i]; + dos->writeBoolean(item == nullptr); + if (item == nullptr) continue; + + dos->writeShort(item->id); + dos->writeShort(item->getAuxValue()); + Packet::writeNbt(item->tag, dos); + } + +} + +ShapelessRecipy* ShapelessRecipy::readFromStream(DataInputStream* dis) { + unsigned char groupType = dis->readByte(); + + int resultItemID = dis->readShort(); + int resultItemCount = dis->readByte(); + int resultItemAux = dis->readShort(); + + ItemInstance* resultItem = new ItemInstance(resultItemID, resultItemCount, 0); + resultItem->setRawAuxValue(resultItemAux); + resultItem->tag = Packet::readNbt(dis); + + vector* ingredients = new vector(); + int iCount = dis->readByte(); + for (int i = 0; i < iCount; i++) { + if (dis->readBoolean() == true) continue; //item is null or something weird + + int itemID = dis->readShort(); + int itemAux = dis->readShort(); + + ItemInstance* ingredients_item = new ItemInstance(itemID, 1, 0); + ingredients_item->setRawAuxValue(itemAux); + ingredients_item->tag = Packet::readNbt(dis); + + ingredients->push_back(ingredients_item); + } + + return new ShapelessRecipy(resultItem, ingredients, (Recipy::_eGroupType)groupType); +} diff --git a/Minecraft.World/ShapelessRecipy.h b/Minecraft.World/ShapelessRecipy.h index bb1dfcf7..8e2c9e3e 100644 --- a/Minecraft.World/ShapelessRecipy.h +++ b/Minecraft.World/ShapelessRecipy.h @@ -9,6 +9,7 @@ private: public: ShapelessRecipy(ItemInstance *result, vector *ingredients, _eGroupType egroup=Recipy::eGroupType_Decoration); + virtual ~ShapelessRecipy() override; virtual const ItemInstance *getResultItem(); virtual const int getGroup(); @@ -20,4 +21,6 @@ public: virtual bool reqs(int iRecipe); virtual void reqs(INGREDIENTS_REQUIRED *pIngReq); + virtual void writeToStream(DataOutputStream* dos); + static ShapelessRecipy* readFromStream(DataInputStream* dos); }; From 8446d10fa9945b06f47a898032260671f655a498 Mon Sep 17 00:00:00 2001 From: Lord_Cambion Date: Fri, 22 May 2026 21:21:32 +0200 Subject: [PATCH 27/32] update: english comments i forgot to change! --- Minecraft.World/LeafTile2.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Minecraft.World/LeafTile2.cpp b/Minecraft.World/LeafTile2.cpp index 319ff1b8..bff00322 100644 --- a/Minecraft.World/LeafTile2.cpp +++ b/Minecraft.World/LeafTile2.cpp @@ -15,13 +15,13 @@ const unsigned int LeafTile2::LEAF2_NAMES[LEAF2_NAMES_SIZE] = { }; const wstring LeafTile2::TEXTURES[2][2] = { - { L"leaves_acacia", L"leaves_dark_oak" }, // Indice 0: Fancy - { L"leaves_acacia_opaque", L"leaves_dark_oak_opaque" } // Indice 1: Veloce/Opaca + { L"leaves_acacia", L"leaves_dark_oak" }, // index 0: Fancy + { L"leaves_acacia_opaque", L"leaves_dark_oak_opaque" } // index 1: Fast }; LeafTile2::LeafTile2(int id) : LeafTile(id) { - // Non serve fare checkBuffer qui, ci pensa già la classe padre LeafTile! + // do nothing here } Icon *LeafTile2::getTexture(int face, int data) @@ -29,8 +29,8 @@ Icon *LeafTile2::getTexture(int face, int data) int type = data & 3; if (type >= LEAF2_NAMES_SIZE) type = 0; - // isSolidRender() in LeafTile restituisce 'true' se la grafica è su Veloce/Opaca. - // Quindi se è true usiamo l'indice 1, se è false (Trasparente) usiamo l'indice 0. + // isSolidRender() in LeafTile returns 'true' if graphics is Fast + // if true -> index is 1, else 0. int textureSet = isSolidRender(false) ? 1 : 0; return icons[textureSet][type]; @@ -56,13 +56,14 @@ void LeafTile2::registerIcons(IconRegister *iconRegister) int LeafTile2::getColor(int data) { - // In inventario o in mano, l'Acacia e la Dark Oak usano il verde base + // in the inventory use the default colour for leaves return FoliageColor::getDefaultColor(); } int LeafTile2::getColor(LevelSource *level, int x, int y, int z, int data) { - // Codice di blending per il colore del bioma (copiato dal tuo LeafTile.cpp) + // Codice di blending per il colore del bioma (copiato dal tuo LeafTile.cpp)) + // blending biome colors copied from LeafTile.cpp int totalRed = 0; int totalGreen = 0; int totalBlue = 0; @@ -71,7 +72,7 @@ int LeafTile2::getColor(LevelSource *level, int x, int y, int z, int data) { for (int ox = -1; ox <= 1; ox++) { - int foliageColor = level->getBiome(x + ox, z + oz)->getFolageColor(); // Attento, nel tuo engine si chiama getFolageColor() senza la 'i' + int foliageColor = level->getBiome(x + ox, z + oz)->getFolageColor(); // they mispelled the word. getFolageColor without "i" totalRed += (foliageColor & 0xff0000) >> 16; totalGreen += (foliageColor & 0xff00) >> 8; totalBlue += (foliageColor & 0xff); @@ -83,7 +84,7 @@ int LeafTile2::getColor(LevelSource *level, int x, int y, int z, int data) void LeafTile2::playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data) { - // Se il giocatore usa le cesoie, vogliamo droppare "leaves2" (ID 161) e non "leaves" (ID 18) + // if player is using shears, drop "leaves2" (ID 161) , instead of "leaves" (ID 18) if (!level->isClientSide && player->getSelectedItem() != nullptr && player->getSelectedItem()->id == Item::shears->id) { player->awardStat( @@ -95,7 +96,7 @@ void LeafTile2::playerDestroy(Level *level, shared_ptr player, int x, in } else { - // Altrimenti usa la distruzione standard di TransparentTile + // or default destroy TransparentTile::playerDestroy(level, player, x, y, z, data); } } From 5d196d97cf163fa16cc0f94a0886dbf29411d04b Mon Sep 17 00:00:00 2001 From: Lord_Cambion Date: Fri, 22 May 2026 22:15:46 +0200 Subject: [PATCH 28/32] update italian comments to english --- Minecraft.World/LeafTile2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.World/LeafTile2.h b/Minecraft.World/LeafTile2.h index 59166c46..d3bc3c81 100644 --- a/Minecraft.World/LeafTile2.h +++ b/Minecraft.World/LeafTile2.h @@ -13,7 +13,7 @@ public: static const unsigned int LEAF2_NAMES[LEAF2_NAMES_SIZE]; private: - //[0] = Fancy (Trasparenti), [1] = Fast (Opache) + //index 0, fancy; index 1, fast static const wstring TEXTURES[2][2]; Icon *icons[2][2]; From 98c33aa6775b736937087fe140bad073e879679b Mon Sep 17 00:00:00 2001 From: DrPerkyLegit <116128211+DrPerkyLegit@users.noreply.github.com> Date: Sun, 24 May 2026 14:50:12 -0400 Subject: [PATCH 29/32] feat: Feat/expanded worlds (#107) * it works kinda but its laggy * attempt at making it faster * Revert "attempt at making it faster" This reverts commit 32a68ed3aec5827362f6a232adfc6d06a93ea355. * Revert "it works kinda but its laggy" This reverts commit 2830b973d4fecafd86969192917a5ab5fc7b9758. * round robin client chunk cache, expanded world type --- Minecraft.Client/Common/App_Defines.h | 3 ++- Minecraft.Client/MultiPlayerChunkCache.cpp | 29 ++++++++++++---------- Minecraft.Client/MultiPlayerChunkCache.h | 5 ++++ Minecraft.Server/ServerProperties.cpp | 11 ++++++++ Minecraft.World/ChunkSource.h | 6 +++-- Minecraft.World/Level.cpp | 7 +++--- Minecraft.World/LevelData.cpp | 1 + 7 files changed, 43 insertions(+), 19 deletions(-) diff --git a/Minecraft.Client/Common/App_Defines.h b/Minecraft.Client/Common/App_Defines.h index eeb9942d..693bad2c 100644 --- a/Minecraft.Client/Common/App_Defines.h +++ b/Minecraft.Client/Common/App_Defines.h @@ -56,7 +56,8 @@ enum EGameHostOptionWorldSize e_worldSize_Classic, e_worldSize_Small, e_worldSize_Medium, - e_worldSize_Large + e_worldSize_Large, + e_worldSize_Expanded }; diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp index 302ff119..e4cd1e0d 100644 --- a/Minecraft.Client/MultiPlayerChunkCache.cpp +++ b/Minecraft.Client/MultiPlayerChunkCache.cpp @@ -15,8 +15,8 @@ MultiPlayerChunkCache::MultiPlayerChunkCache(Level *level) XZSIZE = level->dimension->getXZSize(); // 4J Added XZOFFSET = XZSIZE/2; // 4J Added m_XZSize = XZSIZE; - hasData = new bool[XZSIZE * XZSIZE]; - memset(hasData, 0, sizeof(bool) * XZSIZE * XZSIZE); + hasData = new bool[LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH]; + memset(hasData, 0, sizeof(bool) * LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH); emptyChunk = new EmptyLevelChunk(level, byteArray(16 * 16 * Level::maxBuildHeight), 0, 0); @@ -93,8 +93,8 @@ MultiPlayerChunkCache::MultiPlayerChunkCache(Level *level) this->level = level; - this->cache = new LevelChunk *[XZSIZE * XZSIZE]; - memset(this->cache, 0, XZSIZE * XZSIZE * sizeof(LevelChunk *)); + this->cache = new LevelChunk *[LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH]; + memset(this->cache, 0, sizeof(LevelChunk*) * LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH); InitializeCriticalSectionAndSpinCount(&m_csLoadCreate,4000); } @@ -129,10 +129,11 @@ bool MultiPlayerChunkCache::reallyHasChunk(int x, int z) // Check we're in range of the stored level - if we aren't, then consider that we do have that chunk as we'll be able to use the water chunk there if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return true; if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return true; - int idx = ix * XZSIZE + iz; + + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); LevelChunk *chunk = cache[idx]; - if( chunk == nullptr ) + if (chunk == nullptr || chunk->x != x || chunk->z != z) { return false; } @@ -145,10 +146,11 @@ void MultiPlayerChunkCache::drop(const int x, const int z) const int iz = z + XZOFFSET; if ((ix < 0) || (ix >= XZSIZE)) return; if ((iz < 0) || (iz >= XZSIZE)) return; - const int idx = ix * XZSIZE + iz; + + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); LevelChunk* chunk = cache[idx]; - if (chunk != nullptr && !chunk->isEmpty()) + if (chunk != nullptr && !chunk->isEmpty() && chunk->x == x && chunk->z == z) { // Drop entities in the chunks, especially for the case when a player is dead // as they will not get the RemoveEntity packet if an entity is removed. @@ -168,11 +170,12 @@ LevelChunk *MultiPlayerChunkCache::create(int x, int z) // Check we're in range of the stored level if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); - int idx = ix * XZSIZE + iz; + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + LevelChunk *chunk = cache[idx]; LevelChunk *lastChunk = chunk; - if( chunk == nullptr ) + if( chunk == nullptr || chunk->x != x || chunk->z != z ) { EnterCriticalSection(&m_csLoadCreate); @@ -251,10 +254,10 @@ LevelChunk *MultiPlayerChunkCache::getChunk(int x, int z) // Check we're in range of the stored level if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); - int idx = ix * XZSIZE + iz; + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); LevelChunk *chunk = cache[idx]; - if( chunk == nullptr ) + if( chunk == nullptr || chunk->x != x || chunk->z != z ) { return emptyChunk; } @@ -313,6 +316,6 @@ void MultiPlayerChunkCache::dataReceived(int x, int z) // Check we're in range of the stored level if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return; if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return; - int idx = ix * XZSIZE + iz; + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); hasData[idx] = true; } \ No newline at end of file diff --git a/Minecraft.Client/MultiPlayerChunkCache.h b/Minecraft.Client/MultiPlayerChunkCache.h index 203f3124..e018790b 100644 --- a/Minecraft.Client/MultiPlayerChunkCache.h +++ b/Minecraft.Client/MultiPlayerChunkCache.h @@ -45,4 +45,9 @@ public: virtual void dataReceived(int x, int z); // 4J added virtual LevelChunk **getCache() { return cache; } // 4J added + + static inline int wrapCoord(int v, int size) { + int r = v % size; + return (r < 0) ? r + size : r; + } }; \ No newline at end of file diff --git a/Minecraft.Server/ServerProperties.cpp b/Minecraft.Server/ServerProperties.cpp index 1b6c5c0a..525be788 100644 --- a/Minecraft.Server/ServerProperties.cpp +++ b/Minecraft.Server/ServerProperties.cpp @@ -628,6 +628,8 @@ static std::string WorldSizeToPropertyValue(int worldSize) return "medium"; case e_worldSize_Large: return "large"; + case e_worldSize_Expanded: + return "expanded"; case e_worldSize_Classic: default: return "classic"; @@ -644,6 +646,8 @@ static int WorldSizeToXzChunks(int worldSize) return LEVEL_WIDTH_MEDIUM; case e_worldSize_Large: return LEVEL_WIDTH_LARGE; + case e_worldSize_Expanded: + return LEVEL_WIDTH_EXPANDED; case e_worldSize_Classic: default: return LEVEL_WIDTH_CLASSIC; @@ -659,6 +663,7 @@ static int WorldSizeToHellScale(int worldSize) case e_worldSize_Medium: return HELL_LEVEL_SCALE_MEDIUM; case e_worldSize_Large: + case e_worldSize_Expanded: return HELL_LEVEL_SCALE_LARGE; case e_worldSize_Classic: default: @@ -694,6 +699,12 @@ static bool TryParseWorldSize(const std::string &lowered, int *outWorldSize) return true; } + if (lowered == "expanded" || lowered == "344" || lowered == "8") + { + *outWorldSize = e_worldSize_Expanded; + return true; + } + return false; } diff --git a/Minecraft.World/ChunkSource.h b/Minecraft.World/ChunkSource.h index 37b0ecc3..5b510ddb 100644 --- a/Minecraft.World/ChunkSource.h +++ b/Minecraft.World/ChunkSource.h @@ -7,12 +7,14 @@ class TilePos; // The maximum number of chunks that we can store #ifdef _LARGE_WORLDS // 4J Stu - Our default map (at zoom level 3) is 1024x1024 blocks (or 64 chunks) -#define LEVEL_MAX_WIDTH (5*64) //(6*54) - #define LEVEL_WIDTH_CLASSIC 54 #define LEVEL_WIDTH_SMALL 64 #define LEVEL_WIDTH_MEDIUM (3*64) #define LEVEL_WIDTH_LARGE (5*64) +#define LEVEL_WIDTH_EXPANDED (5*64) + 24 + + +#define LEVEL_MAX_WIDTH LEVEL_WIDTH_EXPANDED #else #define LEVEL_MAX_WIDTH 54 diff --git a/Minecraft.World/Level.cpp b/Minecraft.World/Level.cpp index 8478535b..49819aa6 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -46,6 +46,7 @@ DWORD Level::tlsIdxLightCache = TlsAlloc(); // 4J : WESTY : Added for time played stats. #include "net.minecraft.stats.h" +#include "../Minecraft.Client/MultiPlayerChunkCache.h" // 4J - Caching of lighting data added. This is implemented as a 16x16x16 cache of ints (ie 16K storage in total). The index of the element to be used in the array is determined by the lower // four bits of each x/y/z position, and the upper 7/4/7 bits of the x/y/z positions are stored within the element itself along with the cached values etc. The cache can be enabled per thread by @@ -1332,10 +1333,10 @@ int Level::getBrightness(LightLayer::variety layer, int x, int y, int z) if( ( ix < 0 ) || ( ix >= chunkSourceXZSize ) ) return 0; if( ( iz < 0 ) || ( iz >= chunkSourceXZSize ) ) return 0; - int idx = ix * chunkSourceXZSize + iz; + int idx = MultiPlayerChunkCache::wrapCoord(ix, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + MultiPlayerChunkCache::wrapCoord(iz, LEVEL_MIN_WIDTH); LevelChunk *c = chunkSourceCache[idx]; - if( c == nullptr ) return (int)layer; + if( c == nullptr) return (int)layer; if (y < 0) y = 0; if (y >= maxBuildHeight) y = maxBuildHeight - 1; @@ -1381,7 +1382,7 @@ void Level::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety laye return; } - int idx = ix * chunkSourceXZSize + iz; + int idx = MultiPlayerChunkCache::wrapCoord(ix, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + MultiPlayerChunkCache::wrapCoord(iz, LEVEL_MIN_WIDTH); LevelChunk *c = chunkSourceCache[idx]; // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we diff --git a/Minecraft.World/LevelData.cpp b/Minecraft.World/LevelData.cpp index 0edd2d7e..e9f22fdb 100644 --- a/Minecraft.World/LevelData.cpp +++ b/Minecraft.World/LevelData.cpp @@ -185,6 +185,7 @@ LevelData::LevelData(CompoundTag *tag) case LEVEL_WIDTH_SMALL: hostOptionworldSize = e_worldSize_Small; break; case LEVEL_WIDTH_MEDIUM: hostOptionworldSize = e_worldSize_Medium; break; case LEVEL_WIDTH_LARGE: hostOptionworldSize = e_worldSize_Large; break; + case LEVEL_WIDTH_EXPANDED: hostOptionworldSize = e_worldSize_Expanded; break; default: assert(0); break; } app.SetGameHostOption(eGameHostOption_WorldSize, hostOptionworldSize ); From 9c55f803688fefa4ff6a31c9622c8f72556bed7d Mon Sep 17 00:00:00 2001 From: Lord Cambion Date: Sun, 24 May 2026 23:16:05 +0200 Subject: [PATCH 30/32] feat: Comparator +Item Frame (#106) * feat: rotation in 8 directions i implemented rotation in 8 directions for the item frames using decompilation * fix: map render + normal items map renders only explorated zones. also fixed the size of the item and place inside item frame * feat: itemframe drops items when hurt * feat: comparator detects itemframe --- Minecraft.Client/ItemFrameRenderer.cpp | 126 +++++++++--------- Minecraft.World/ComparatorTile.cpp | 81 +++++++---- Minecraft.World/ComparatorTile.h | 2 + Minecraft.World/ItemFrame.cpp | 103 ++++++++++++-- Minecraft.World/ItemFrame.h | 5 + .../net.minecraft.world.level.tile.entity.h | 1 + 6 files changed, 214 insertions(+), 104 deletions(-) diff --git a/Minecraft.Client/ItemFrameRenderer.cpp b/Minecraft.Client/ItemFrameRenderer.cpp index be442176..a9fb5899 100644 --- a/Minecraft.Client/ItemFrameRenderer.cpp +++ b/Minecraft.Client/ItemFrameRenderer.cpp @@ -110,81 +110,75 @@ void ItemFrameRenderer::drawFrame(shared_ptr itemFrame) void ItemFrameRenderer::drawItem(shared_ptr entity) { - Minecraft *pMinecraft=Minecraft::GetInstance(); - shared_ptr instance = entity->getItem(); - if (instance == nullptr) return; + shared_ptr instance = entity->getItem(); + if (instance == nullptr) return; - shared_ptr itemEntity = std::make_shared(entity->level, 0, 0, 0, instance); - itemEntity->getItem()->count = 1; - itemEntity->bobOffs = 0; + shared_ptr itemEntity = std::make_shared(entity->level, 0, 0, 0, instance); + itemEntity->getItem()->count = 1; + itemEntity->bobOffs = 0; - glPushMatrix(); + glPushMatrix(); - glTranslatef((-7.25f / 16.0f) * Direction::STEP_X[entity->dir], -0.18f, (-7.25f / 16.0f) * Direction::STEP_Z[entity->dir]); - glRotatef(180 + entity->yRot, 0, 1, 0); - glRotatef(-90 * entity->getRotation(), 0, 0, 1); + glRotatef(180.0f + entity->yRot, 0, 1, 0); + glTranslatef(0.0f, 0.0f, -0.4375f); - switch (entity->getRotation()) - { - case 1: - glTranslatef(-0.16f, -0.16f, 0); - break; - case 2: - glTranslatef(0, -0.32f, 0); - break; - case 3: - glTranslatef(0.16f, -0.16f, 0); - break; - } + int rotation = entity->getRotation(); + bool isMap = (itemEntity->getItem()->getItem() == Item::map); + int effectiveRotation = isMap ? 2 * (rotation % 4) : rotation; - if (itemEntity->getItem()->getItem() == Item::map) - { - entityRenderDispatcher->textures->bindTexture(&MAP_BACKGROUND_LOCATION); - Tesselator *t = Tesselator::getInstance(); + glRotatef(-45.0f * effectiveRotation, 0, 0, 1); + glTranslatef(0.0f, -0.41f/2, 0.0f); - glRotatef(180, 0, 1, 0); - glRotatef(180, 0, 0, 1); - glScalef(1.0f / 128.0f, 1.0f / 128.0f, 1.0f / 128.0f); - glTranslatef(-64.0f, -87.0f, -3.0f); - glNormal3f(0, 0, -1); - t->begin(); - int vo = 7; - t->vertexUV(0.0f, 128.0f, 0.0f, 0.0f, 1.0f); - t->vertexUV(128.0f, 128.0f, 0.0f, 1.0f, 1.0f); - t->vertexUV(128.0f, 0.0f, 0.0f, 1.0f, 0.0f); - t->vertexUV(0.0f, 0.0f, 0.0f, 0.0f, 0.0f); - t->end(); + if (isMap) + { + //entityRenderDispatcher->textures->bindTexture(&MAP_BACKGROUND_LOCATION); + //Tesselator *t = Tesselator::getInstance(); - shared_ptr data = Item::map->getSavedData(itemEntity->getItem(), entity->level); - if (data != nullptr) - { - entityRenderDispatcher->itemInHandRenderer->minimap->render(nullptr, entityRenderDispatcher->textures, data, entity->entityId); - } - } - else - { - if (itemEntity->getItem()->getItem() == Item::compass) - { - CompassTexture *ct = CompassTexture::instance; - double compassRot = ct->rot; - double compassRotA = ct->rota; - ct->rot = 0; - ct->rota = 0; - ct->updateFromPosition(entity->level, entity->x, entity->z, Mth::wrapDegrees( static_cast(180 + entity->dir * 90) ), false, true); - ct->rot = compassRot; - ct->rota = compassRotA; - } + glRotatef(180, 0, 1, 0); + glRotatef(180, 0, 0, 1); + glScalef(1.0f / 128.0f, 1.0f / 128.0f, 1.0f / 128.0f); + glTranslatef(-64.0f, -87.0f, -3.0f); - EntityRenderDispatcher::instance->render(itemEntity, 0, 0, 0, 0, 0, true); + //glNormal3f(0, 0, -1); + //t->begin(); + //t->vertexUV(0.0f, 128.0f, 0.0f, 0.0f, 1.0f); + //t->vertexUV(128.0f, 128.0f, 0.0f, 1.0f, 1.0f); + //t->vertexUV(128.0f, 0.0f, 0.0f, 1.0f, 0.0f); + //t->vertexUV(0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + //t->end(); - if (itemEntity->getItem()->getItem() == Item::compass) - { - CompassTexture *ct = CompassTexture::instance; - ct->cycleFrames(); - } - } - - glPopMatrix(); + shared_ptr data = Item::map->getSavedData(itemEntity->getItem(), entity->level); + if (data != nullptr) + { + entityRenderDispatcher->itemInHandRenderer->minimap->render( + nullptr, entityRenderDispatcher->textures, data, entity->entityId); + } + } + else + { + if (itemEntity->getItem()->getItem() == Item::compass) + { + CompassTexture *ct = CompassTexture::instance; + double compassRot = ct->rot; + double compassRotA = ct->rota; + ct->rot = 0; + ct->rota = 0; + ct->updateFromPosition(entity->level, entity->x, entity->z, + Mth::wrapDegrees(static_cast(180 + entity->dir * 90)), false, true); + ct->rot = compassRot; + ct->rota = compassRotA; + } + + EntityRenderDispatcher::instance->render(itemEntity, 0, 0, 0, 0, 0, true); + + if (itemEntity->getItem()->getItem() == Item::compass) + { + CompassTexture *ct = CompassTexture::instance; + ct->cycleFrames(); + } + } + + glPopMatrix(); } diff --git a/Minecraft.World/ComparatorTile.cpp b/Minecraft.World/ComparatorTile.cpp index e7d8f178..3fd46fde 100644 --- a/Minecraft.World/ComparatorTile.cpp +++ b/Minecraft.World/ComparatorTile.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "net.minecraft.world.item.h" #include "net.minecraft.world.level.h" #include "net.minecraft.world.level.redstone.h" @@ -107,33 +107,42 @@ bool ComparatorTile::shouldTurnOn(Level *level, int x, int y, int z, int data) int ComparatorTile::getInputSignal(Level *level, int x, int y, int z, int data) { - int result = DiodeTile::getInputSignal(level, x, y, z, data); + int result = DiodeTile::getInputSignal(level, x, y, z, data); - int dir = getDirection(data); - int xx = x + Direction::STEP_X[dir]; - int zz = z + Direction::STEP_Z[dir]; - int tile = level->getTile(xx, y, zz); + int dir = getDirection(data); + int xx = x + Direction::STEP_X[dir]; + int zz = z + Direction::STEP_Z[dir]; + int tile = level->getTile(xx, y, zz); - if (tile > 0) - { - if (Tile::tiles[tile]->hasAnalogOutputSignal()) - { - result = Tile::tiles[tile]->getAnalogOutputSignal(level, xx, y, zz, Direction::DIRECTION_OPPOSITE[dir]); - } - else if (result < Redstone::SIGNAL_MAX && Tile::isSolidBlockingTile(tile)) - { - xx += Direction::STEP_X[dir]; - zz += Direction::STEP_Z[dir]; - tile = level->getTile(xx, y, zz); + if (tile > 0) + { + if (Tile::tiles[tile]->hasAnalogOutputSignal()) + { + result = Tile::tiles[tile]->getAnalogOutputSignal(level, xx, y, zz, Direction::DIRECTION_OPPOSITE[dir]); + } + else if (result < Redstone::SIGNAL_MAX && Tile::isSolidBlockingTile(tile)) + { + xx += Direction::STEP_X[dir]; + zz += Direction::STEP_Z[dir]; + tile = level->getTile(xx, y, zz); - if (tile > 0 && Tile::tiles[tile]->hasAnalogOutputSignal()) - { - result = Tile::tiles[tile]->getAnalogOutputSignal(level, xx, y, zz, Direction::DIRECTION_OPPOSITE[dir]); - } - } - } + if (tile > 0 && Tile::tiles[tile]->hasAnalogOutputSignal()) + { + result = Tile::tiles[tile]->getAnalogOutputSignal(level, xx, y, zz, Direction::DIRECTION_OPPOSITE[dir]); + } + + else if (tile == 0) + { + shared_ptr frame = getItemFrame(level, xx, y, zz); + if (frame != nullptr) + { + result = frame->getAnalogOutput(); + } + } + } + } - return result; + return result; } shared_ptr ComparatorTile::getComparator(LevelSource *level, int x, int y, int z) @@ -250,4 +259,28 @@ shared_ptr ComparatorTile::newTileEntity(Level *level) bool ComparatorTile::TestUse() { return true; +} + +shared_ptr ComparatorTile::getItemFrame( + Level* level, + int x, + int y, + int z) +{ + AABB* box = AABB::newTemp( + x, + y, + z, + x + 1, + y + 1, + z + 1 + ); + + vector>* entities = + level->getEntitiesOfClass(typeid(ItemFrame), box); + + if (entities == nullptr || entities->size() != 1) + return nullptr; + + return dynamic_pointer_cast((*entities)[0]); } \ No newline at end of file diff --git a/Minecraft.World/ComparatorTile.h b/Minecraft.World/ComparatorTile.h index 3358d3ab..3663c651 100644 --- a/Minecraft.World/ComparatorTile.h +++ b/Minecraft.World/ComparatorTile.h @@ -2,6 +2,7 @@ #include "DiodeTile.h" #include "EntityTile.h" +#include "AABB.h" class ComparatorTileEntity; @@ -57,4 +58,5 @@ public: virtual bool triggerEvent(Level *level, int x, int y, int z, int b0, int b1); virtual shared_ptr newTileEntity(Level *level); virtual bool TestUse(); + shared_ptr ComparatorTile::getItemFrame(Level* level,int x,int y,int z); }; \ No newline at end of file diff --git a/Minecraft.World/ItemFrame.cpp b/Minecraft.World/ItemFrame.cpp index f5988b9f..528c0711 100644 --- a/Minecraft.World/ItemFrame.cpp +++ b/Minecraft.World/ItemFrame.cpp @@ -10,7 +10,8 @@ #include "net.minecraft.world.level.saveddata.h" #include "com.mojang.nbt.h" #include "ItemFrame.h" - +#include "DamageSource.h" +#include "Level.h" @@ -87,29 +88,52 @@ shared_ptr ItemFrame::getItem() return getEntityData()->getItemInstance(DATA_ITEM); } -void ItemFrame::setItem(shared_ptr item) +void ItemFrame::setItem(shared_ptr item, bool notifyNeighbors) { - if(item != nullptr) - { - item = item->copy(); - item->count = 1; + if (item != nullptr) + { + item = item->copy(); + item->count = 1; + item->setFramed(dynamic_pointer_cast(shared_from_this())); + } + getEntityData()->set(DATA_ITEM, item); + getEntityData()->markDirty(DATA_ITEM); - item->setFramed(dynamic_pointer_cast( shared_from_this() )); - } - getEntityData()->set(DATA_ITEM, item); - getEntityData()->markDirty(DATA_ITEM); + if (notifyNeighbors) + { + level->updateNeighbourForOutputSignal(xTile, yTile, zTile, Tile::comparator_off->id); + } } -int ItemFrame::getRotation() +void ItemFrame::setItem(shared_ptr item) { - return getEntityData()->getByte(DATA_ROTATION); + setItem(item, true); } -void ItemFrame::setRotation(int rotation) +int ItemFrame::getRotation() { - getEntityData()->set(DATA_ROTATION, static_cast(rotation % 4)); + return getEntityData()->getByte(DATA_ROTATION); } +void ItemFrame::setRotation(int rotation, bool notifyNeighbors) +{ + getEntityData()->set(DATA_ROTATION, static_cast(rotation % 8)); + + if (notifyNeighbors) + { + level->updateNeighbourForOutputSignal(xTile, yTile, zTile, Tile::comparator_off->id); + } +} + +void ItemFrame::setRotation(int rotation) +{ + + getEntityData()->set(DATA_ROTATION, static_cast(rotation % 8)); + level->updateNeighbourForOutputSignal(xTile, yTile, zTile, Tile::comparator_off->id); +} + + + void ItemFrame::addAdditonalSaveData(CompoundTag *tag) { if (getItem() != nullptr) @@ -171,3 +195,54 @@ bool ItemFrame::interact(shared_ptr player) return true; } + +bool ItemFrame::hurt(DamageSource *source, float damage) +{ + if (level->isClientSide) return false; + + shared_ptr item = getItem(); + + if (!source->isExplosion() && item != nullptr) + { + shared_ptr sourceEntity = source->getEntity(); + + if (sourceEntity != nullptr && sourceEntity->instanceof(eTYPE_PLAYER)) + { + shared_ptr player = dynamic_pointer_cast(sourceEntity); + if (!player->abilities.instabuild) + { + shared_ptr copy = item->copy(); + removeFramedMap(copy); + spawnAtLocation(copy, 0); + } + else + { + removeFramedMap(item); + } + } + else + { + shared_ptr copy = item->copy(); + removeFramedMap(copy); + spawnAtLocation(copy, 0); + } + + setItem(nullptr); + return true; + } + + return HangingEntity::hurt(source, damage); +} + +int ItemFrame::getAnalogOutput() +{ + shared_ptr item = getItem(); + if (item == nullptr) return 0; + return getRotation() % 8 + 1; +} + + +float ItemFrame::getPickRadius() +{ + return 0.0f; +} \ No newline at end of file diff --git a/Minecraft.World/ItemFrame.h b/Minecraft.World/ItemFrame.h index 06c1f111..dc83505f 100644 --- a/Minecraft.World/ItemFrame.h +++ b/Minecraft.World/ItemFrame.h @@ -38,9 +38,14 @@ private: public: shared_ptr getItem(); + void setItem(shared_ptr item, bool notifyNeighbors); void setItem(shared_ptr item); int getRotation(); + void setRotation(int rotation, bool notifyNeighbors); void setRotation(int rotation); + virtual bool hurt(DamageSource *source, float damage) override; + virtual int getAnalogOutput(); + virtual float getPickRadius()override; virtual void addAdditonalSaveData(CompoundTag *tag); virtual void readAdditionalSaveData(CompoundTag *tag); diff --git a/Minecraft.World/net.minecraft.world.level.tile.entity.h b/Minecraft.World/net.minecraft.world.level.tile.entity.h index c3209df9..1d6e7f76 100644 --- a/Minecraft.World/net.minecraft.world.level.tile.entity.h +++ b/Minecraft.World/net.minecraft.world.level.tile.entity.h @@ -20,3 +20,4 @@ #include "TheEndPortalTileEntity.h" #include "SkullTileEntity.h" #include "EnderChestTileEntity.h" +#include "ItemFrame.h" From ed63aaf8c4932bfd43b5b190395750bcc378999b Mon Sep 17 00:00:00 2001 From: Lord_Cambion Date: Mon, 25 May 2026 02:04:06 +0200 Subject: [PATCH 31/32] fix: player ticks if game paused before: player will tick if the game is paused allowing him to take damage from poison or lava. now: player will not tick anymore if game is paused, so will not take damage anymore --- Minecraft.Client/Common/Consoles_App.cpp | 4 ++-- Minecraft.Client/Minecraft.cpp | 2 ++ Minecraft.Client/MinecraftServer.cpp | 2 +- Minecraft.Client/MinecraftServer.h | 3 ++- Minecraft.Client/PlayerConnection.cpp | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 7b4cdfaa..25826b63 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "../../Minecraft.World/net.minecraft.world.entity.item.h" #include "../../Minecraft.World/net.minecraft.world.entity.player.h" #include "../../Minecraft.World/net.minecraft.world.level.tile.entity.h" @@ -453,7 +453,7 @@ void CMinecraftApp::SetAction(int iPad, eXuiAction action, LPVOID param) bool CMinecraftApp::IsAppPaused() { -#if defined(_XBOX_ONE) || defined(__ORBIS__) +#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64) bool paused = m_bIsAppPaused; EnterCriticalSection(&m_saveNotificationCriticalSection); if( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 ) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 0095e3cc..0889da34 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -1268,6 +1268,8 @@ void Minecraft::applyFrameMouseLook() void Minecraft::run_middle() { + pause = app.IsAppPaused(); + static int64_t lastTime = 0; static bool bFirstTimeIntoGame = true; static bool bAutosaveTimerSet=false; diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index 621af1c5..420bba64 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -1848,7 +1848,7 @@ void MinecraftServer::run(int64_t seed, void *lpParameter) lastTime = now; // 4J Added ability to pause the server - if( !m_isServerPaused ) + if( !m_isServerPaused && !app.IsAppPaused() ) { bool didTick = false; if (levels[0]->allPlayersAreSleeping()) diff --git a/Minecraft.Client/MinecraftServer.h b/Minecraft.Client/MinecraftServer.h index 6b76c194..7be94a71 100644 --- a/Minecraft.Client/MinecraftServer.h +++ b/Minecraft.Client/MinecraftServer.h @@ -266,7 +266,7 @@ private: #endif #endif - bool IsServerPaused() { return m_isServerPaused; } + private: // 4J Added @@ -291,6 +291,7 @@ public: const wstring& getSaveFolderName() const { return m_saveFolderName; } void Suspend(); bool IsSuspending(); + bool IsServerPaused() { return m_isServerPaused; } // 4J Stu - A load of functions were all added in 1.0.1 in the ServerInterface, but I don't think we need any of them }; diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 845d9ee3..80cef559 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -183,7 +183,7 @@ void PlayerConnection::tick() // Ensure server-side player tick runs even when no move packet was received this tick. // Without this, environmental damage (drowning, fire, lava) is never applied to clients // that don't send frequent move packets. - if (!didTick && player != nullptr) + if (!didTick && player != nullptr && !server->IsServerPaused() && !app.IsAppPaused()) { player->doTick(false); } From e1383cc6a3f8aeca6aa69aa6815e5bf00ae7b802 Mon Sep 17 00:00:00 2001 From: Lord_Cambion Date: Mon, 25 May 2026 02:16:33 +0200 Subject: [PATCH 32/32] fix: poison kill player i fixed before this behaviour but somehow it is broken. now i am clamping if 1 or 2 hp player is left with 1 hp. --- Minecraft.World/MobEffect.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Minecraft.World/MobEffect.cpp b/Minecraft.World/MobEffect.cpp index 71620b0c..6ca532a7 100644 --- a/Minecraft.World/MobEffect.cpp +++ b/Minecraft.World/MobEffect.cpp @@ -137,9 +137,13 @@ void MobEffect::applyEffectTick(shared_ptr mob, int amplification) } else if (id == poison->id) { - if (mob->getHealth() > 1.0f) + // poison must never reduce health below 1 hp + // if the current health is between 1 and 2 hp the player is left at exactly 1 HP rather than dying. + float currentHealth = mob->getHealth(); + if (currentHealth > 1.0f) { - mob->hurt(DamageSource::magic, 1.0f); + float poisonDmg = min(1.0f, currentHealth - 1.0f); + mob->hurt(DamageSource::magic, poisonDmg); } } else if (id == wither->id)