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 01/35] 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 02/35] 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 03/35] 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 04/35] 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 05/35] 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 06/35] 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 07/35] 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 08/35] 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 09/35] 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 10/35] 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 11/35] 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 12/35] 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 13/35] 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 14/35] 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 15/35] 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) From eef6b1c12942b97dd690a569dfdecc17ee8c82f5 Mon Sep 17 00:00:00 2001 From: Nytreon Date: Mon, 25 May 2026 07:46:19 -0500 Subject: [PATCH 16/35] fix(TU31): pistons not updating when they should (#113) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed pistons not updating when they should I removed the code that 4J added to make pistons ignore updates because it seems they removed it in later versions for parity with java edition! This allows for 0 tick piston interactions amount other things. * Update PistonBaseTile.h I forgot to update the header 🥹 --- Minecraft.World/PistonBaseTile.cpp | 31 ++++-------------------------- Minecraft.World/PistonBaseTile.h | 5 ++--- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/Minecraft.World/PistonBaseTile.cpp b/Minecraft.World/PistonBaseTile.cpp index b5196471..24749a89 100644 --- a/Minecraft.World/PistonBaseTile.cpp +++ b/Minecraft.World/PistonBaseTile.cpp @@ -29,21 +29,12 @@ DWORD PistonBaseTile::tlsIdx = TlsAlloc(); // For us, that means that if we create a piston next to another one, then one of them gets two events to createPush, the second of which fails, leaving the // piston in a bad (simultaneously extended & not extended) state. // 4J - ignoreUpdate is a static in java, implementing as TLS here to make thread safe -bool PistonBaseTile::ignoreUpdate() -{ - return (TlsGetValue(tlsIdx) != nullptr); -} -void PistonBaseTile::ignoreUpdate(bool set) -{ - TlsSetValue(tlsIdx,(LPVOID)(set?1:0)); -} +//I removed the code for ignoreUpdate so the above comment no longer applies ^.^ PistonBaseTile::PistonBaseTile(int id, bool isSticky) : Tile(id, Material::piston, isSolidRender() ) { // 4J - added initialiser - ignoreUpdate(false); - this->isSticky = isSticky; setSoundType(SOUND_STONE); setDestroyTime(0.5f); @@ -131,7 +122,7 @@ void PistonBaseTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr(by) ); level->setData(x, y, z, targetData, Tile::UPDATE_CLIENTS); - if (!level->isClientSide && !ignoreUpdate()) + if (!level->isClientSide) { checkIfExtend(level, x, y, z); } @@ -139,7 +130,7 @@ void PistonBaseTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptrisClientSide && !ignoreUpdate()) + if (!level->isClientSide) { checkIfExtend(level, x, y, z); } @@ -147,7 +138,7 @@ void PistonBaseTile::neighborChanged(Level *level, int x, int y, int z, int type void PistonBaseTile::onPlace(Level *level, int x, int y, int z) { - if (!level->isClientSide && level->getTileEntity(x, y, z) == nullptr && !ignoreUpdate()) + if (!level->isClientSide && level->getTileEntity(x, y, z) == nullptr) { checkIfExtend(level, x, y, z); } @@ -212,7 +203,6 @@ bool PistonBaseTile::getNeighborSignal(Level *level, int x, int y, int z, int fa bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, int facing) { - ignoreUpdate(true); if (!level->isClientSide) { @@ -221,12 +211,10 @@ bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, if (extend && param1 == TRIGGER_CONTRACT) { level->setData(x, y, z, facing | EXTENDED_BIT, UPDATE_CLIENTS); - ignoreUpdate(false); return false; } else if (!extend && param1 == TRIGGER_EXTEND) { - ignoreUpdate(false); return false; } } @@ -253,7 +241,6 @@ bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, } if (FourKitBridge::FirePistonExtend(level->dimension->id, x, y, z, facing, pushLength)) { - ignoreUpdate(false); return false; } } @@ -277,7 +264,6 @@ bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, } else { - ignoreUpdate(false); return false; } PIXEndNamedEvent(); @@ -288,7 +274,6 @@ bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, if (FourKitBridge::FirePistonRetract(level->dimension->id, x, y, z, facing)) { level->setData(x, y, z, facing | EXTENDED_BIT, UPDATE_CLIENTS); - ignoreUpdate(false); return false; } #endif @@ -353,32 +338,24 @@ bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, level->setTileAndData(x, y, z, Tile::pistonMovingPiece_Id, blockData, Tile::UPDATE_ALL); level->setTileEntity(x, y, z, PistonMovingPiece::newMovingPieceEntity(block, blockData, facing, false, false)); - ignoreUpdate(false); level->removeTile(twoX, twoY, twoZ); - ignoreUpdate(true); } else if (!pistonPiece) { stopSharingIfServer(level, x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); // 4J added - ignoreUpdate(false); level->removeTile(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); - ignoreUpdate(true); } PIXEndNamedEvent(); } else { stopSharingIfServer(level, x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); // 4J added - ignoreUpdate(false); level->removeTile(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); - ignoreUpdate(true); } level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_TILE_PISTON_IN, 0.5f, level->random->nextFloat() * 0.15f + 0.6f); } - ignoreUpdate(false); - return true; } diff --git a/Minecraft.World/PistonBaseTile.h b/Minecraft.World/PistonBaseTile.h index 59c2e833..b307c7b4 100644 --- a/Minecraft.World/PistonBaseTile.h +++ b/Minecraft.World/PistonBaseTile.h @@ -27,8 +27,7 @@ private: static DWORD tlsIdx; // 4J - was just a static but implemented with TLS for our version - static bool ignoreUpdate(); - static void ignoreUpdate(bool set); + //code removed so the above comment no longer applies public: PistonBaseTile(int id, bool isSticky); @@ -68,4 +67,4 @@ private: static void stopSharingIfServer(Level *level, int x, int y, int z); // 4J added bool createPush(Level *level, int sx, int sy, int sz, int facing); -}; \ No newline at end of file +}; From c9920f66794157ec27c1b54bdad652dea53c6b1f Mon Sep 17 00:00:00 2001 From: DrPerkyLegit <116128211+DrPerkyLegit@users.noreply.github.com> Date: Mon, 25 May 2026 08:46:44 -0400 Subject: [PATCH 17/35] fix: blue flashing on motd page (#111) --- .../Common/UI/UIScene_JoinMenu.cpp | 271 +++++++++--------- Minecraft.Client/Common/UI/UIScene_JoinMenu.h | 2 + 2 files changed, 141 insertions(+), 132 deletions(-) diff --git a/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp index 88cdbb5f..48ee615e 100644 --- a/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp @@ -82,6 +82,8 @@ UIScene_JoinMenu::UIScene_JoinMenu(int iPad, void *_initData, UILayer *parentLay IggyValueSetBooleanRS(&path, visibleName, nullptr, false); } } + + updateServerDescription(); } } } @@ -129,46 +131,46 @@ void UIScene_JoinMenu::updateTooltips() void UIScene_JoinMenu::tick() { - if( !m_friendInfoRequestIssued ) + if (!m_friendInfoRequestIssued) { ui.NavigateToScene(m_iPad, eUIScene_Timer); g_NetworkManager.GetFullFriendSessionInfo(m_selectedSession, &friendSessionUpdated, this); m_friendInfoRequestIssued = true; } - if( m_friendInfoUpdatedOK ) + if (m_friendInfoUpdatedOK) { m_friendInfoUpdatedOK = false; - m_buttonJoinGame.init(app.GetString(IDS_JOIN_GAME),eControl_JoinGame); + m_buttonJoinGame.init(app.GetString(IDS_JOIN_GAME), eControl_JoinGame); m_buttonListPlayers.init(eControl_GamePlayers); - m_buttonListPlayers.setYPos( m_buttonListPlayers.getYPos() + 300 ); + m_buttonListPlayers.setYPos(m_buttonListPlayers.getYPos() + 300); #if defined(__PS3__) || defined(__ORBIS__) || defined __PSVITA__ - for( int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++ ) + for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++) { - if( m_selectedSession->data.players[i] != nullptr ) + if (m_selectedSession->data.players[i] != nullptr) { - #ifndef _CONTENT_PACKAGE - if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<data.players[i].getOnlineID()); - #ifndef __PSVITA__ +#ifndef __PSVITA__ // Append guest number (any players in an online game not signed into PSN are guests) - if( m_selectedSession->data.players[i].isSignedIntoPSN() == false ) + if (m_selectedSession->data.players[i].isSignedIntoPSN() == false) { char suffix[5]; sprintf(suffix, " (%d)", m_selectedSession->data.players[i].getQuadrant() + 1); playerName.append(suffix); } - #endif +#endif m_buttonListPlayers.addItem(playerName); } } @@ -179,9 +181,9 @@ void UIScene_JoinMenu::tick() } } #elif defined(_DURANGO) - for( int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++ ) + for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++) { - if ( m_selectedSession->searchResult.m_playerNames[i].size() ) + if (m_selectedSession->searchResult.m_playerNames[i].size()) { m_buttonListPlayers.addItem(m_selectedSession->searchResult.m_playerNames[i]); } @@ -214,74 +216,74 @@ void UIScene_JoinMenu::tick() m_labelLabels[eLabel_FireOn].init(app.GetString(IDS_LABEL_FIRE_SPREADS)); unsigned int uiGameHostSettings = m_selectedSession->data.m_uiGameHostSettings; - switch(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_Difficulty)) + switch (app.GetGameHostOption(uiGameHostSettings, eGameHostOption_Difficulty)) { case Difficulty::EASY: - m_labelValues[eLabel_Difficulty].init( app.GetString(IDS_DIFFICULTY_TITLE_EASY) ); + m_labelValues[eLabel_Difficulty].init(app.GetString(IDS_DIFFICULTY_TITLE_EASY)); break; case Difficulty::NORMAL: - m_labelValues[eLabel_Difficulty].init( app.GetString(IDS_DIFFICULTY_TITLE_NORMAL) ); + m_labelValues[eLabel_Difficulty].init(app.GetString(IDS_DIFFICULTY_TITLE_NORMAL)); break; case Difficulty::HARD: - m_labelValues[eLabel_Difficulty].init( app.GetString(IDS_DIFFICULTY_TITLE_HARD) ); + m_labelValues[eLabel_Difficulty].init(app.GetString(IDS_DIFFICULTY_TITLE_HARD)); break; case Difficulty::PEACEFUL: default: - m_labelValues[eLabel_Difficulty].init( app.GetString(IDS_DIFFICULTY_TITLE_PEACEFUL) ); + m_labelValues[eLabel_Difficulty].init(app.GetString(IDS_DIFFICULTY_TITLE_PEACEFUL)); break; } - int option = app.GetGameHostOption(uiGameHostSettings,eGameHostOption_GameType); - if(option == GameType::CREATIVE->getId()) + int option = app.GetGameHostOption(uiGameHostSettings, eGameHostOption_GameType); + if (option == GameType::CREATIVE->getId()) { - m_labelValues[eLabel_GameType].init( app.GetString(IDS_CREATIVE) ); + m_labelValues[eLabel_GameType].init(app.GetString(IDS_CREATIVE)); } - else if(option == GameType::ADVENTURE->getId()) + else if (option == GameType::ADVENTURE->getId()) { - m_labelValues[eLabel_GameType].init( app.GetString(IDS_ADVENTURE) ); + m_labelValues[eLabel_GameType].init(app.GetString(IDS_ADVENTURE)); } else { - m_labelValues[eLabel_GameType].init( app.GetString(IDS_SURVIVAL) ); + m_labelValues[eLabel_GameType].init(app.GetString(IDS_SURVIVAL)); } - if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_Gamertags)) m_labelValues[eLabel_GamertagsOn].init( app.GetString(IDS_ON) ); - else m_labelValues[eLabel_GamertagsOn].init( app.GetString(IDS_OFF) ); + if (app.GetGameHostOption(uiGameHostSettings, eGameHostOption_Gamertags)) m_labelValues[eLabel_GamertagsOn].init(app.GetString(IDS_ON)); + else m_labelValues[eLabel_GamertagsOn].init(app.GetString(IDS_OFF)); - if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_Structures)) m_labelValues[eLabel_Structures].init( app.GetString(IDS_ON) ); - else m_labelValues[eLabel_Structures].init( app.GetString(IDS_OFF) ); + if (app.GetGameHostOption(uiGameHostSettings, eGameHostOption_Structures)) m_labelValues[eLabel_Structures].init(app.GetString(IDS_ON)); + else m_labelValues[eLabel_Structures].init(app.GetString(IDS_OFF)); - if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_LevelType)) m_labelValues[eLabel_LevelType].init( app.GetString(IDS_LEVELTYPE_SUPERFLAT) ); - else m_labelValues[eLabel_LevelType].init( app.GetString(IDS_LEVELTYPE_NORMAL) ); + if (app.GetGameHostOption(uiGameHostSettings, eGameHostOption_LevelType)) m_labelValues[eLabel_LevelType].init(app.GetString(IDS_LEVELTYPE_SUPERFLAT)); + else m_labelValues[eLabel_LevelType].init(app.GetString(IDS_LEVELTYPE_NORMAL)); - if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_PvP))m_labelValues[eLabel_PVP].init( app.GetString(IDS_ON) ); - else m_labelValues[eLabel_PVP].init( app.GetString(IDS_OFF) ); + if (app.GetGameHostOption(uiGameHostSettings, eGameHostOption_PvP))m_labelValues[eLabel_PVP].init(app.GetString(IDS_ON)); + else m_labelValues[eLabel_PVP].init(app.GetString(IDS_OFF)); - if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_TrustPlayers)) m_labelValues[eLabel_Trust].init( app.GetString(IDS_ON) ); - else m_labelValues[eLabel_Trust].init( app.GetString(IDS_OFF) ); + if (app.GetGameHostOption(uiGameHostSettings, eGameHostOption_TrustPlayers)) m_labelValues[eLabel_Trust].init(app.GetString(IDS_ON)); + else m_labelValues[eLabel_Trust].init(app.GetString(IDS_OFF)); - if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_TNT)) m_labelValues[eLabel_TNTOn].init( app.GetString(IDS_ON) ); - else m_labelValues[eLabel_TNTOn].init( app.GetString(IDS_OFF) ); + if (app.GetGameHostOption(uiGameHostSettings, eGameHostOption_TNT)) m_labelValues[eLabel_TNTOn].init(app.GetString(IDS_ON)); + else m_labelValues[eLabel_TNTOn].init(app.GetString(IDS_OFF)); - if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_FireSpreads)) m_labelValues[eLabel_FireOn].init( app.GetString(IDS_ON) ); - else m_labelValues[eLabel_FireOn].init( app.GetString(IDS_OFF) ); + if (app.GetGameHostOption(uiGameHostSettings, eGameHostOption_FireSpreads)) m_labelValues[eLabel_FireOn].init(app.GetString(IDS_ON)); + else m_labelValues[eLabel_FireOn].init(app.GetString(IDS_OFF)); m_bIgnoreInput = false; // Alert the app the we want to be informed of ethernet connections - app.SetLiveLinkRequired( true ); + app.SetLiveLinkRequired(true); TelemetryManager->RecordMenuShown(m_iPad, eUIScene_JoinMenu, 0); - addTimer(UPDATE_PLAYERS_TIMER_ID,UPDATE_PLAYERS_TIMER_TIME); + addTimer(UPDATE_PLAYERS_TIMER_ID, UPDATE_PLAYERS_TIMER_TIME); } - if( m_friendInfoUpdatedERROR ) + if (m_friendInfoUpdatedERROR) { - m_buttonJoinGame.init(app.GetString(IDS_JOIN_GAME),eControl_JoinGame); + m_buttonJoinGame.init(app.GetString(IDS_JOIN_GAME), eControl_JoinGame); m_buttonListPlayers.init(eControl_GamePlayers); - m_buttonListPlayers.setYPos( m_buttonListPlayers.getYPos() + 300 ); + m_buttonListPlayers.setYPos(m_buttonListPlayers.getYPos() + 300); m_labelLabels[eLabel_Difficulty].init(app.GetString(IDS_LABEL_DIFFICULTY)); m_labelLabels[eLabel_GameType].init(app.GetString(IDS_LABEL_GAME_TYPE)); @@ -294,14 +296,14 @@ void UIScene_JoinMenu::tick() m_labelLabels[eLabel_FireOn].init(app.GetString(IDS_LABEL_FIRE_SPREADS)); m_labelValues[eLabel_Difficulty].init(app.GetString(IDS_DIFFICULTY_TITLE_PEACEFUL)); - m_labelValues[eLabel_GameType].init( app.GetString(IDS_CREATIVE) ); - m_labelValues[eLabel_GamertagsOn].init( app.GetString(IDS_OFF) ); - m_labelValues[eLabel_Structures].init( app.GetString(IDS_OFF) ); - m_labelValues[eLabel_LevelType].init( app.GetString(IDS_LEVELTYPE_NORMAL) ); - m_labelValues[eLabel_PVP].init( app.GetString(IDS_OFF) ); - m_labelValues[eLabel_Trust].init( app.GetString(IDS_OFF) ); - m_labelValues[eLabel_TNTOn].init( app.GetString(IDS_OFF) ); - m_labelValues[eLabel_FireOn].init( app.GetString(IDS_OFF) ); + m_labelValues[eLabel_GameType].init(app.GetString(IDS_CREATIVE)); + m_labelValues[eLabel_GamertagsOn].init(app.GetString(IDS_OFF)); + m_labelValues[eLabel_Structures].init(app.GetString(IDS_OFF)); + m_labelValues[eLabel_LevelType].init(app.GetString(IDS_LEVELTYPE_NORMAL)); + m_labelValues[eLabel_PVP].init(app.GetString(IDS_OFF)); + m_labelValues[eLabel_Trust].init(app.GetString(IDS_OFF)); + m_labelValues[eLabel_TNTOn].init(app.GetString(IDS_OFF)); + m_labelValues[eLabel_FireOn].init(app.GetString(IDS_OFF)); m_friendInfoUpdatedERROR = false; @@ -310,9 +312,9 @@ void UIScene_JoinMenu::tick() UINT uiIDA[1]; uiIDA[0] = IDS_CONFIRM_OK; #ifdef _XBOX_ONE - ui.RequestErrorMessage( IDS_CONNECTION_FAILED, IDS_DISCONNECTED_SERVER_QUIT, uiIDA,1,m_iPad,ErrorDialogReturned,this); + ui.RequestErrorMessage(IDS_CONNECTION_FAILED, IDS_DISCONNECTED_SERVER_QUIT, uiIDA, 1, m_iPad, ErrorDialogReturned, this); #else - ui.RequestErrorMessage( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA,1,m_iPad,ErrorDialogReturned,this); + ui.RequestErrorMessage(IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, m_iPad, ErrorDialogReturned, this); #endif } @@ -323,84 +325,7 @@ void UIScene_JoinMenu::tick() { IggyPlayerTickRS(s_movieServerDesc); - IggyValuePath *root = IggyPlayerRootPath(s_movieServerDesc); - if (root) - { - // scale the size before Iggy reads it - IggyValuePath textPath; - if (IggyValuePathMakeNameRef(&textPath, root, "HowToPlayText_0")) - { - IggyName nameX = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16 *)L"x", -1); - IggyName nameY = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16 *)L"y", -1); - IggyName nameW = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16 *)L"width", -1); - IggyName nameH = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16 *)L"height", -1); - - if (m_loadedResolution == eSceneResolution_1080) - { - IggyValueSetF64RS(&textPath, nameX, nullptr, 333.0);// horizontal - IggyValueSetF64RS(&textPath, nameY, nullptr, 340.0);// vertical - IggyValueSetF64RS(&textPath, nameW, nullptr, 580.0); - IggyValueSetF64RS(&textPath, nameH, nullptr, 270.0); - } - else //720 - { - IggyValueSetF64RS(&textPath, nameX, nullptr, 252.0); - IggyValueSetF64RS(&textPath, nameY, nullptr, 285.0); - IggyValueSetF64RS(&textPath, nameW, nullptr, 440.0); - IggyValueSetF64RS(&textPath, nameH, nullptr, 220.0); - } - } - - // harcoded text for test it, later im gonna delete this and - // and convert it so that people can add their description when adding the server - if (!s_textInjected && s_funcLoadPage != 0) - { - IggyDataValue result; - IggyDataValue args[2]; - args[0].type = IGGY_DATATYPE_number; - args[0].number = 0; // 0 is the what's new page on howtoplay don't change it - - wstring testText = L"\nNothing yet..."; - IggyStringUTF16 iggyStr; - wstring formattedText = app.FormatChatMessage(testText); - iggyStr.string = (IggyUTF16*)formattedText.c_str(); - iggyStr.length = (unsigned int)formattedText.length(); - - args[1].type = IGGY_DATATYPE_string_UTF16; - args[1].string16 = iggyStr; - - IggyResult res = IggyPlayerCallMethodRS(s_movieServerDesc, &result, root, s_funcLoadPage, 2, args); - if (res == IGGY_RESULT_SUCCESS) - { - s_textInjected = true; - } - } - - // keeps the text fixed so it doesn't move from its place - IggyValuePath panelPath; - if (s_textInjected && IggyValuePathMakeNameRef(&panelPath, root, "DynamicHtmlText")) - { - IggyName nameX = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16 *)L"x", -1); - IggyName nameY = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16 *)L"y", -1); - IggyName nameW = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16 *)L"width", -1); - IggyName nameH = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16 *)L"height", -1); - - if (m_loadedResolution == eSceneResolution_1080) - { - IggyValueSetF64RS(&panelPath, nameX, nullptr, 332.0);// horizontal - IggyValueSetF64RS(&panelPath, nameY, nullptr, 340.0);// vertical - IggyValueSetF64RS(&panelPath, nameW, nullptr, 580.0); - IggyValueSetF64RS(&panelPath, nameH, nullptr, 270.0); - } - else //720p - { - IggyValueSetF64RS(&panelPath, nameX, nullptr, 250.0); - IggyValueSetF64RS(&panelPath, nameY, nullptr, 290.0); - IggyValueSetF64RS(&panelPath, nameW, nullptr, 400.0); - IggyValueSetF64RS(&panelPath, nameH, nullptr, 230.0); - } - } - } + updateServerDescription(); } } #endif @@ -408,6 +333,88 @@ void UIScene_JoinMenu::tick() UIScene::tick(); } +void UIScene_JoinMenu::updateServerDescription() { + IggyValuePath* root = IggyPlayerRootPath(s_movieServerDesc); + if (root) + { + // scale the size before Iggy reads it + IggyValuePath textPath; + if (IggyValuePathMakeNameRef(&textPath, root, "HowToPlayText_0")) + { + IggyName nameX = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16*)L"x", -1); + IggyName nameY = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16*)L"y", -1); + IggyName nameW = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16*)L"width", -1); + IggyName nameH = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16*)L"height", -1); + + if (m_loadedResolution == eSceneResolution_1080) + { + IggyValueSetF64RS(&textPath, nameX, nullptr, 333.0);// horizontal + IggyValueSetF64RS(&textPath, nameY, nullptr, 340.0);// vertical + IggyValueSetF64RS(&textPath, nameW, nullptr, 580.0); + IggyValueSetF64RS(&textPath, nameH, nullptr, 270.0); + } + else //720 + { + IggyValueSetF64RS(&textPath, nameX, nullptr, 252.0); + IggyValueSetF64RS(&textPath, nameY, nullptr, 285.0); + IggyValueSetF64RS(&textPath, nameW, nullptr, 440.0); + IggyValueSetF64RS(&textPath, nameH, nullptr, 220.0); + } + } + + // harcoded text for test it, later im gonna delete this and + // and convert it so that people can add their description when adding the server + if (s_funcLoadPage != 0) + { + IggyDataValue result; + IggyDataValue args[2]; + args[0].type = IGGY_DATATYPE_number; + args[0].number = 0; // 0 is the what's new page on howtoplay don't change it + + wstring testText = L"\nNothing yet..."; + IggyStringUTF16 iggyStr; + wstring formattedText = app.EscapeHTMLString(testText); + formattedText = app.FormatChatMessage(formattedText); + iggyStr.string = (IggyUTF16*)formattedText.c_str(); + iggyStr.length = (unsigned int)formattedText.length(); + + args[1].type = IGGY_DATATYPE_string_UTF16; + args[1].string16 = iggyStr; + + IggyResult res = IggyPlayerCallMethodRS(s_movieServerDesc, &result, root, s_funcLoadPage, 2, args); + if (res == IGGY_RESULT_SUCCESS) + { + s_textInjected = true; + } + } + + // keeps the text fixed so it doesn't move from its place + IggyValuePath panelPath; + if (s_textInjected && IggyValuePathMakeNameRef(&panelPath, root, "DynamicHtmlText")) + { + IggyName nameX = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16*)L"x", -1); + IggyName nameY = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16*)L"y", -1); + IggyName nameW = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16*)L"width", -1); + IggyName nameH = IggyPlayerCreateFastName(s_movieServerDesc, (IggyUTF16*)L"height", -1); + + if (m_loadedResolution == eSceneResolution_1080) + { + IggyValueSetF64RS(&panelPath, nameX, nullptr, 332.0);// horizontal + IggyValueSetF64RS(&panelPath, nameY, nullptr, 340.0);// vertical + IggyValueSetF64RS(&panelPath, nameW, nullptr, 580.0); + IggyValueSetF64RS(&panelPath, nameH, nullptr, 270.0); + } + else //720p + { + IggyValueSetF64RS(&panelPath, nameX, nullptr, 250.0); + IggyValueSetF64RS(&panelPath, nameY, nullptr, 290.0); + IggyValueSetF64RS(&panelPath, nameW, nullptr, 400.0); + IggyValueSetF64RS(&panelPath, nameH, nullptr, 230.0); + } + } + } +} + void UIScene_JoinMenu::friendSessionUpdated(bool success, void *pParam) { UIScene_JoinMenu *scene = static_cast(pParam); diff --git a/Minecraft.Client/Common/UI/UIScene_JoinMenu.h b/Minecraft.Client/Common/UI/UIScene_JoinMenu.h index f8ae2159..5042a91a 100644 --- a/Minecraft.Client/Common/UI/UIScene_JoinMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_JoinMenu.h @@ -93,6 +93,8 @@ protected: // TODO: This should be pure virtual in this class virtual wstring getMoviePath(); + void updateServerDescription(); + public: public: // INPUT From af674463b4b2033de74132e83ef6553bc48926a4 Mon Sep 17 00:00:00 2001 From: /home/neo <158327205+neoapps-dev@users.noreply.github.com> Date: Mon, 25 May 2026 15:49:47 +0300 Subject: [PATCH 18/35] ci: trigger on commit on main --- .github/workflows/nightly.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a887724c..7a251578 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,9 +1,8 @@ name: Nightly Release - on: push: branches: - - experimental + - main workflow_dispatch: permissions: From 25e1358311d26be9cc89a15062b7e2d8cfe4561c Mon Sep 17 00:00:00 2001 From: pieeebot <274605694+pieeebot@users.noreply.github.com> Date: Mon, 25 May 2026 16:06:12 +0300 Subject: [PATCH 19/35] chore: release v1.0.5b --- BUMP | 2 +- NOTES.md | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/BUMP b/BUMP index d99a1985..038016fc 100644 --- a/BUMP +++ b/BUMP @@ -1 +1 @@ -1.0.4b +1.0.5b diff --git a/NOTES.md b/NOTES.md index 933ebe47..aed5cad5 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,14 +1,18 @@ -# neoLegacy v1.0.4b +# neoLegacy v1.0.5b ### Bug Fixes -- Fixed Podzol bottom face displaying incorrect texture (was using side texture instead of dirt) +- Fixed blue flashing when clicking on a server. +- Added crafting recipes for andesite, diorite and granite**.** +- Fixed incorrect Rabbit Stew strings**.** +- Fixed breaking the top half of tall blocks failing to destroy the entire block. +- Fixed damage still occurring when the gameis paused. +- Poison can no longer kill players, only damaging them to 1 HP. ### 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 - - Villagers now transform into Witches when struck by lightning + - Item Frames can now rotate in 8 directions. + - Comparators can now detect item frame rotations. + - Pistons no longer ignore block updates. roadmap From bb62a9f559ceb83a1297a859afccbc4d31502789 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit <116128211+DrPerkyLegit@users.noreply.github.com> Date: Mon, 25 May 2026 11:54:30 -0400 Subject: [PATCH 20/35] fix: remove round robin chunk cache (#114) --- Minecraft.Client/MultiPlayerChunkCache.cpp | 27 +++++++++++----------- Minecraft.Client/MultiPlayerChunkCache.h | 5 ---- Minecraft.World/Level.cpp | 4 ++-- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp index e4cd1e0d..1f8dc357 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[LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH]; - memset(hasData, 0, sizeof(bool) * LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH); + hasData = new bool[XZSIZE * XZSIZE]; + memset(hasData, 0, sizeof(bool) * XZSIZE * XZSIZE); 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 *[LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH]; - memset(this->cache, 0, sizeof(LevelChunk*) * LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH); + this->cache = new LevelChunk *[XZSIZE * XZSIZE]; + memset(this->cache, 0, sizeof(LevelChunk*) * XZSIZE * XZSIZE); InitializeCriticalSectionAndSpinCount(&m_csLoadCreate,4000); } @@ -129,11 +129,10 @@ 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 = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + int idx = ix * XZSIZE + iz; LevelChunk *chunk = cache[idx]; - if (chunk == nullptr || chunk->x != x || chunk->z != z) + if (chunk == nullptr) { return false; } @@ -147,10 +146,10 @@ void MultiPlayerChunkCache::drop(const int x, const int z) if ((ix < 0) || (ix >= XZSIZE)) return; if ((iz < 0) || (iz >= XZSIZE)) return; - int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + int idx = ix * XZSIZE + iz; LevelChunk* chunk = cache[idx]; - if (chunk != nullptr && !chunk->isEmpty() && chunk->x == x && chunk->z == z) + if (chunk != nullptr && !chunk->isEmpty()) { // 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. @@ -170,12 +169,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 = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + int idx = ix * XZSIZE + iz; LevelChunk *chunk = cache[idx]; LevelChunk *lastChunk = chunk; - if( chunk == nullptr || chunk->x != x || chunk->z != z ) + if( chunk == nullptr ) { EnterCriticalSection(&m_csLoadCreate); @@ -254,10 +253,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 = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + int idx = ix * XZSIZE + iz; LevelChunk *chunk = cache[idx]; - if( chunk == nullptr || chunk->x != x || chunk->z != z ) + if( chunk == nullptr ) { return emptyChunk; } @@ -316,6 +315,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 = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + int idx = ix * XZSIZE + iz; hasData[idx] = true; } \ No newline at end of file diff --git a/Minecraft.Client/MultiPlayerChunkCache.h b/Minecraft.Client/MultiPlayerChunkCache.h index e018790b..203f3124 100644 --- a/Minecraft.Client/MultiPlayerChunkCache.h +++ b/Minecraft.Client/MultiPlayerChunkCache.h @@ -45,9 +45,4 @@ 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.World/Level.cpp b/Minecraft.World/Level.cpp index 49819aa6..d6284d95 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -1333,7 +1333,7 @@ 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 = MultiPlayerChunkCache::wrapCoord(ix, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + MultiPlayerChunkCache::wrapCoord(iz, LEVEL_MIN_WIDTH); + int idx = ix * chunkSourceXZSize + iz; LevelChunk *c = chunkSourceCache[idx]; if( c == nullptr) return (int)layer; @@ -1382,7 +1382,7 @@ void Level::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety laye return; } - int idx = MultiPlayerChunkCache::wrapCoord(ix, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + MultiPlayerChunkCache::wrapCoord(iz, LEVEL_MIN_WIDTH); + int idx = ix * chunkSourceXZSize + iz; LevelChunk *c = chunkSourceCache[idx]; // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we From f5d9db3397b3878cbe79366ec1dcb49212c0e050 Mon Sep 17 00:00:00 2001 From: pieeebot <274605694+pieeebot@users.noreply.github.com> Date: Mon, 25 May 2026 18:56:59 +0300 Subject: [PATCH 21/35] chore: release v1.0.6b --- BUMP | 2 +- NOTES.md | 13 +------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/BUMP b/BUMP index 038016fc..24a364be 100644 --- a/BUMP +++ b/BUMP @@ -1 +1 @@ -1.0.5b +1.0.6b diff --git a/NOTES.md b/NOTES.md index aed5cad5..01c3f9c2 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,18 +1,7 @@ # neoLegacy v1.0.5b ### Bug Fixes -- Fixed blue flashing when clicking on a server. -- Added crafting recipes for andesite, diorite and granite**.** -- Fixed incorrect Rabbit Stew strings**.** -- Fixed breaking the top half of tall blocks failing to destroy the entire block. -- Fixed damage still occurring when the gameis paused. -- Poison can no longer kill players, only damaging them to 1 HP. - -### Changes -- Added TU31 parity changes which include: - - Item Frames can now rotate in 8 directions. - - Comparators can now detect item frame rotations. - - Pistons no longer ignore block updates. +- Fixed crashing, lagging, and lighting issues caused by expanded world generation. roadmap From 0bd0cd2452b68bc35a5219875de1f58f928de7c2 Mon Sep 17 00:00:00 2001 From: Lord_Cambion Date: Mon, 25 May 2026 18:18:10 +0200 Subject: [PATCH 22/35] feat: double the entity limit in your worlds --- Minecraft.Client/ServerLevel.h | 21 +++++++++++++++------ Minecraft.World/MobCategory.cpp | 22 +++++++++++----------- Minecraft.World/MobCategory.h | 8 ++++---- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/Minecraft.Client/ServerLevel.h b/Minecraft.Client/ServerLevel.h index 792132a4..4ef2d4a5 100644 --- a/Minecraft.Client/ServerLevel.h +++ b/Minecraft.Client/ServerLevel.h @@ -138,12 +138,21 @@ private: // 4J - added for implementation of finite limit to number of item entities, tnt and falling block entities public: - static const int MAX_HANGING_ENTITIES = 400; - static const int MAX_ITEM_ENTITIES = 200; - static const int MAX_ARROW_ENTITIES = 200; - static const int MAX_EXPERIENCEORB_ENTITIES = 50; - static const int MAX_PRIMED_TNT = 20; - static const int MAX_FALLING_TILE = 20; + static const int MAX_HANGING_ENTITIES = 800; + static const int MAX_ITEM_ENTITIES = 400; + static const int MAX_ARROW_ENTITIES = 400; + static const int MAX_EXPERIENCEORB_ENTITIES = 100; + static const int MAX_PRIMED_TNT = 40; + static const int MAX_FALLING_TILE = 40; + + //static const int MAX_HANGING_ENTITIES = 400; + //static const int MAX_ITEM_ENTITIES = 200; + //static const int MAX_ARROW_ENTITIES = 200; + //static const int MAX_EXPERIENCEORB_ENTITIES = 50; + //static const int MAX_PRIMED_TNT = 20; + //static const int MAX_FALLING_TILE = 20; + + int m_primedTntCount; int m_fallingTileCount; diff --git a/Minecraft.World/MobCategory.cpp b/Minecraft.World/MobCategory.cpp index cde98dc4..0dbfcba2 100644 --- a/Minecraft.World/MobCategory.cpp +++ b/Minecraft.World/MobCategory.cpp @@ -63,18 +63,18 @@ void MobCategory::setMaxInstancesPerLevel(int max) m_maxPerLevel = max; } -int MobCategory::maxAnimalsWithBreeding() { return creature->getMaxInstancesPerLevel() + 20; } -int MobCategory::maxChickensWithBreeding() { return creature_chicken->getMaxInstancesPerLevel() + 8; } -int MobCategory::maxMushroomCowsWithBreeding() { return creature_mushroomcow->getMaxInstancesPerLevel() + 20; } -int MobCategory::maxWolvesWithBreeding() { return creature_wolf->getMaxInstancesPerLevel() + 8; } +int MobCategory::maxAnimalsWithBreeding() { return (creature->getMaxInstancesPerLevel() + 20)*2; } +int MobCategory::maxChickensWithBreeding() { return (creature_chicken->getMaxInstancesPerLevel() + 8)*2; } +int MobCategory::maxMushroomCowsWithBreeding() { return (creature_mushroomcow->getMaxInstancesPerLevel() + 20)*2; } +int MobCategory::maxWolvesWithBreeding() { return (creature_wolf->getMaxInstancesPerLevel() + 8)*2; } -int MobCategory::maxAnimalsWithSpawnEgg() { return maxAnimalsWithBreeding() + 20; } -int MobCategory::maxChickensWithSpawnEgg() { return maxChickensWithBreeding() + 10; } -int MobCategory::maxWolvesWithSpawnEgg() { return maxWolvesWithBreeding() + 10; } -int MobCategory::maxMonstersWithSpawnEgg() { return monster->getMaxInstancesPerLevel() + 20; } -int MobCategory::maxMushroomCowsWithSpawnEgg() { return maxMushroomCowsWithBreeding() + 8; } -int MobCategory::maxSquidsWithSpawnEgg() { return waterCreature->getMaxInstancesPerLevel() + 8; } -int MobCategory::maxAmbientWithSpawnEgg() { return ambient->getMaxInstancesPerLevel() + 8; } +int MobCategory::maxAnimalsWithSpawnEgg() { return (maxAnimalsWithBreeding() + 20)*2; } +int MobCategory::maxChickensWithSpawnEgg() { return (maxChickensWithBreeding() + 10)*2; } +int MobCategory::maxWolvesWithSpawnEgg() { return (maxWolvesWithBreeding() + 10)*2; } +int MobCategory::maxMonstersWithSpawnEgg() { return (monster->getMaxInstancesPerLevel() + 20)*2; } +int MobCategory::maxMushroomCowsWithSpawnEgg() { return (maxMushroomCowsWithBreeding() + 8)*2; } +int MobCategory::maxSquidsWithSpawnEgg() { return (waterCreature->getMaxInstancesPerLevel() + 8)*2; } +int MobCategory::maxAmbientWithSpawnEgg() { return (ambient->getMaxInstancesPerLevel() + 8)*2; } Material *MobCategory::getSpawnPositionMaterial() { diff --git a/Minecraft.World/MobCategory.h b/Minecraft.World/MobCategory.h index d5eab0c3..eafbc962 100644 --- a/Minecraft.World/MobCategory.h +++ b/Minecraft.World/MobCategory.h @@ -7,9 +7,9 @@ class MobCategory { public: // 4J - putting constants for xbox spawning in one place to tidy things up a bit - all numbers are per level - static const int CONSOLE_MONSTERS_HARD_LIMIT = 50; // Max number of enemies (skeleton, zombie, creeper etc) that the mob spawner will produce - static const int CONSOLE_ANIMALS_HARD_LIMIT = 50; // Max number of animals (cows, sheep, pigs) that the mob spawner will produce - static const int CONSOLE_AMBIENT_HARD_LIMIT = 20; // Ambient mobs + static const int CONSOLE_MONSTERS_HARD_LIMIT = 100; // Max number of enemies (skeleton, zombie, creeper etc) that the mob spawner will produce + static const int CONSOLE_ANIMALS_HARD_LIMIT = 100; // Max number of animals (cows, sheep, pigs) that the mob spawner will produce + static const int CONSOLE_AMBIENT_HARD_LIMIT = 40; // Ambient mobs static const int MAX_XBOX_CHICKENS = 8; // Max number of chickens that the mob spawner will produce static const int MAX_XBOX_WOLVES = 8; // Max number of wolves that the mob spawner will produce @@ -20,7 +20,7 @@ public: static const int MAX_CONSOLE_BOSS = 1; // Max number of bosses (enderdragon/wither) // 4J Villager breeding/egg limits - villagers are not a MobCategory so these stay hardcoded - static const int MAX_VILLAGERS_WITH_BREEDING = 35; + static const int MAX_VILLAGERS_WITH_BREEDING = 70; static const int MAX_XBOX_VILLAGERS_WITH_SPAWN_EGG = MAX_VILLAGERS_WITH_BREEDING + 15; // Breeding headroom above the natural spawn cap. Read at call time so these From f904fb19727041deb6cf8602445c26dfb42908f7 Mon Sep 17 00:00:00 2001 From: Lord_Cambion Date: Mon, 25 May 2026 18:34:32 +0200 Subject: [PATCH 23/35] fix: fences turn grass into dirt problem: the game used is solid and it was wrong because blocks like fences or walls are also flagged as solit blocks because their material. --- Minecraft.World/GrassTile.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Minecraft.World/GrassTile.cpp b/Minecraft.World/GrassTile.cpp index c42ea940..61460727 100644 --- a/Minecraft.World/GrassTile.cpp +++ b/Minecraft.World/GrassTile.cpp @@ -121,8 +121,14 @@ void GrassTile::tick(Level *level, int x, int y, int z, Random *random) } } + // using isSolid() here is wrong because non full blocks like iron bars, + // fences, walls are also flagged as solid by their material + int aboveTileId = level->getTile(x, y + 1, z); Material* above = level->getMaterial(x, y + 1, z); - if (above->isSolid() || above->isLiquid()) level->setTileAndUpdate(x, y, z, Tile::dirt_Id); + if (above->isLiquid() || Tile::lightBlock[aboveTileId] > 2) + { + level->setTileAndUpdate(x, y, z, Tile::dirt_Id); + } } int GrassTile::getResource(int data, Random *random, int playerBonusLevel) From 4d745ab65cd26584af3670cfd4bae1494dc599b7 Mon Sep 17 00:00:00 2001 From: pieeebot <274605694+pieeebot@users.noreply.github.com> Date: Mon, 25 May 2026 21:05:50 +0300 Subject: [PATCH 24/35] Revert "fix: remove round robin chunk cache (#114)" This reverts commit bb62a9f559ceb83a1297a859afccbc4d31502789. --- Minecraft.Client/MultiPlayerChunkCache.cpp | 27 +++++++++++----------- Minecraft.Client/MultiPlayerChunkCache.h | 5 ++++ Minecraft.World/Level.cpp | 4 ++-- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp index 1f8dc357..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, sizeof(LevelChunk*) * XZSIZE * XZSIZE); + 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; } @@ -146,10 +147,10 @@ void MultiPlayerChunkCache::drop(const int x, const int z) 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); 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. @@ -169,12 +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); @@ -253,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; } @@ -315,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.World/Level.cpp b/Minecraft.World/Level.cpp index d6284d95..49819aa6 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -1333,7 +1333,7 @@ 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; @@ -1382,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 From cd50236b8ad0c1fb79396253913deba966acea6c Mon Sep 17 00:00:00 2001 From: pieeebot <274605694+pieeebot@users.noreply.github.com> Date: Mon, 25 May 2026 21:06:22 +0300 Subject: [PATCH 25/35] Revert "feat: Feat/expanded worlds (#107)" This reverts commit 98c33aa6775b736937087fe140bad073e879679b. --- 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, 19 insertions(+), 43 deletions(-) diff --git a/Minecraft.Client/Common/App_Defines.h b/Minecraft.Client/Common/App_Defines.h index 693bad2c..eeb9942d 100644 --- a/Minecraft.Client/Common/App_Defines.h +++ b/Minecraft.Client/Common/App_Defines.h @@ -56,8 +56,7 @@ enum EGameHostOptionWorldSize e_worldSize_Classic, e_worldSize_Small, e_worldSize_Medium, - e_worldSize_Large, - e_worldSize_Expanded + e_worldSize_Large }; diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp index e4cd1e0d..302ff119 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[LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH]; - memset(hasData, 0, sizeof(bool) * LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH); + hasData = new bool[XZSIZE * XZSIZE]; + memset(hasData, 0, sizeof(bool) * XZSIZE * XZSIZE); 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 *[LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH]; - memset(this->cache, 0, sizeof(LevelChunk*) * LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH); + this->cache = new LevelChunk *[XZSIZE * XZSIZE]; + memset(this->cache, 0, XZSIZE * XZSIZE * sizeof(LevelChunk *)); InitializeCriticalSectionAndSpinCount(&m_csLoadCreate,4000); } @@ -129,11 +129,10 @@ 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 = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + int idx = ix * XZSIZE + iz; LevelChunk *chunk = cache[idx]; - if (chunk == nullptr || chunk->x != x || chunk->z != z) + if( chunk == nullptr ) { return false; } @@ -146,11 +145,10 @@ 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; - - int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + const int idx = ix * XZSIZE + iz; LevelChunk* chunk = cache[idx]; - if (chunk != nullptr && !chunk->isEmpty() && chunk->x == x && chunk->z == z) + if (chunk != nullptr && !chunk->isEmpty()) { // 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. @@ -170,12 +168,11 @@ 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 = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); - + int idx = ix * XZSIZE + iz; LevelChunk *chunk = cache[idx]; LevelChunk *lastChunk = chunk; - if( chunk == nullptr || chunk->x != x || chunk->z != z ) + if( chunk == nullptr ) { EnterCriticalSection(&m_csLoadCreate); @@ -254,10 +251,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 = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + int idx = ix * XZSIZE + iz; LevelChunk *chunk = cache[idx]; - if( chunk == nullptr || chunk->x != x || chunk->z != z ) + if( chunk == nullptr ) { return emptyChunk; } @@ -316,6 +313,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 = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + int idx = ix * XZSIZE + iz; hasData[idx] = true; } \ No newline at end of file diff --git a/Minecraft.Client/MultiPlayerChunkCache.h b/Minecraft.Client/MultiPlayerChunkCache.h index e018790b..203f3124 100644 --- a/Minecraft.Client/MultiPlayerChunkCache.h +++ b/Minecraft.Client/MultiPlayerChunkCache.h @@ -45,9 +45,4 @@ 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 525be788..1b6c5c0a 100644 --- a/Minecraft.Server/ServerProperties.cpp +++ b/Minecraft.Server/ServerProperties.cpp @@ -628,8 +628,6 @@ 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"; @@ -646,8 +644,6 @@ 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; @@ -663,7 +659,6 @@ 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: @@ -699,12 +694,6 @@ 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 5b510ddb..37b0ecc3 100644 --- a/Minecraft.World/ChunkSource.h +++ b/Minecraft.World/ChunkSource.h @@ -7,14 +7,12 @@ 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 49819aa6..8478535b 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -46,7 +46,6 @@ 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 @@ -1333,10 +1332,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 = MultiPlayerChunkCache::wrapCoord(ix, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + MultiPlayerChunkCache::wrapCoord(iz, LEVEL_MIN_WIDTH); + int idx = ix * chunkSourceXZSize + iz; 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; @@ -1382,7 +1381,7 @@ void Level::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety laye return; } - int idx = MultiPlayerChunkCache::wrapCoord(ix, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + MultiPlayerChunkCache::wrapCoord(iz, LEVEL_MIN_WIDTH); + int idx = ix * chunkSourceXZSize + iz; 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 e9f22fdb..0edd2d7e 100644 --- a/Minecraft.World/LevelData.cpp +++ b/Minecraft.World/LevelData.cpp @@ -185,7 +185,6 @@ 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 6ed34078fd97c5056137461ea7e44a4c030471c2 Mon Sep 17 00:00:00 2001 From: pieeebot <274605694+pieeebot@users.noreply.github.com> Date: Mon, 25 May 2026 21:10:22 +0300 Subject: [PATCH 26/35] chore: release v1.0.7b --- BUMP | 2 +- NOTES.md | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/BUMP b/BUMP index 24a364be..ce96854b 100644 --- a/BUMP +++ b/BUMP @@ -1 +1 @@ -1.0.6b +1.0.7b diff --git a/NOTES.md b/NOTES.md index 01c3f9c2..5a3445e9 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,7 +1,6 @@ -# neoLegacy v1.0.5b +# neoLegacy v1.0.7b -### Bug Fixes -- Fixed crashing, lagging, and lighting issues caused by expanded world generation. +- Reverted "Expanded" world size due to it causing crashing and lighting issues. roadmap From ef0dad4ffc4094cc83fa05212613bed2899a418a Mon Sep 17 00:00:00 2001 From: Joud Kandeel <97121061+CDevJoud@users.noreply.github.com> Date: Mon, 25 May 2026 21:13:28 +0200 Subject: [PATCH 27/35] fix: memory leaks and heavily optimize the game (#117) * fixed performance issue on getting texture height for rendering the player model as it loades the player texture on every frame * updated the lifetime of the texture * fixed 20 MB memory leak * delay window display until engine init is done --- Minecraft.Client/MemTexture.h | 9 +++++++- Minecraft.Client/Textures.cpp | 8 ++++++- .../Windows64/Windows64_Minecraft.cpp | 22 +++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Minecraft.Client/MemTexture.h b/Minecraft.Client/MemTexture.h index d11d68b1..4afa799c 100644 --- a/Minecraft.Client/MemTexture.h +++ b/Minecraft.Client/MemTexture.h @@ -10,7 +10,14 @@ public: int id; bool isLoaded; int ticksSinceLastUse; - static const int UNUSED_TICKS_TO_FREE = 20; + // @CDevJoud + // changing the lifetime of the texture from 20 ticks(1 sec) to 200 ticks (10 sec) + // as it helps the texture to have longer lifetime and reducing the usage of loadTexture for every second/frame + // note that we dont remove the code that removes the textures from `tick()` as it is required in memory limited environment such as older Consoles(PS3/XBOX360) + static const int UNUSED_TICKS_TO_FREE = 200; + + //default ctor for int Texture::getHeight(const wstring& url, int backup) + MemTexture() = default; MemTexture(const wstring& _name, PBYTE pbData, DWORD dwBytes, MemTextureProcessor *processor); ~MemTexture(); diff --git a/Minecraft.Client/Textures.cpp b/Minecraft.Client/Textures.cpp index 627255b6..0db18171 100644 --- a/Minecraft.Client/Textures.cpp +++ b/Minecraft.Client/Textures.cpp @@ -1256,8 +1256,14 @@ int Textures::getHeight(const wstring& url, int backup) if (img) { + MemTexture* _texture = new MemTexture(); + _texture->loadedImage = img; + _texture->isLoaded = true; + _texture->id = getTexture(_texture->loadedImage, C4JRender::TEXTURE_FORMAT_RxGyBzAw, MIPMAP); + int h = img->getHeight(); - delete img; + //delete img; // commenting this out and inserting the loaded texture to memTextures unordered_map + this->memTextures[url] = _texture; return h; } diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 03467061..91fd812f 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -824,8 +824,7 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) return FALSE; } - ShowWindow(g_hWnd, (nCmdShow != SW_HIDE) ? SW_SHOWMAXIMIZED : nCmdShow); - UpdateWindow(g_hWnd); + return TRUE; } @@ -1456,8 +1455,9 @@ void CleanupDevice() static Minecraft* InitialiseMinecraftRuntime() { app.loadMediaArchive(); - - RenderManager.Initialise(g_pd3dDevice, g_pSwapChain); + // @CDevJoud: No need to call this method as it gets called once in `InitDevice()` + // Calling it again and it results of 20MB of memory leak! + //RenderManager.Initialise(g_pd3dDevice, g_pSwapChain); app.loadStringTable(); ui.init(g_pd3dDevice, g_pImmediateContext, g_pRenderTargetView, g_pDepthStencilView, g_rScreenWidth, g_rScreenHeight); @@ -1790,6 +1790,20 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, hr = XuiTimersRun(); } #endif + + // @CDevJoud The window should only be shown after the engine/game + // initialization has fully completed. + // + // Showing the window too early especially on low end devices, + // may cause windows to display a "Not Responding" state while + // initialization is still in progress. + // + // This creates an unprofessional first impression for the player. + // Instead, initialize all engine systems first, then display the + // window once everything is ready. + ShowWindow(g_hWnd, (nCmdShow != SW_HIDE) ? SW_SHOWMAXIMIZED : nCmdShow); + UpdateWindow(g_hWnd); + MSG msg = {0}; while( WM_QUIT != msg.message && !app.m_bShutdown) { From ac3ece01c74ed0059833201758fc51a70ba85c03 Mon Sep 17 00:00:00 2001 From: Necmi Date: Mon, 25 May 2026 22:17:36 +0300 Subject: [PATCH 28/35] fix: stop elytra sound when going to mainmenu (#118) --- Minecraft.Client/Minecraft.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 0889da34..578a9479 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -4438,6 +4438,8 @@ void Minecraft::setLevel(MultiPlayerLevel *level, int message /*=-1*/, shared_pt // 4J If we are setting the level to nullptr then we are exiting, so delete the levels if( level == nullptr ) { + if (soundEngine) soundEngine->stopElytraSound(); + if(levels[0]!=nullptr) { delete levels[0]; From 285fab70b2d110bcc67e461a0d32138be3686cd7 Mon Sep 17 00:00:00 2001 From: Fireblade <72758695+Firebladedoge229@users.noreply.github.com> Date: Mon, 25 May 2026 15:30:55 -0400 Subject: [PATCH 29/35] chore: update revelations url --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 12d5fe38..c4514c62 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -62,7 +62,7 @@ body: - type: input id: itsrevela attributes: - label: Is this reproducable in itsRevela/LCE-Revelations? (https://github.com/itsRevela/LCE-Revelations) + label: Is this reproducable in itsRevela/LCE-Revelations? (https://git.revela.dev/itsRevela/LCE-Revelations) description: If this was a "no idea" or similar, it will be rejected. validations: required: true From 544befe162d3d7a3647c6f40ae61c7cdf994baf4 Mon Sep 17 00:00:00 2001 From: Zero <128979226+lux-zero-161@users.noreply.github.com> Date: Tue, 26 May 2026 14:47:43 +0200 Subject: [PATCH 30/35] Fix multiple memory leaks and stale pooled allocations (#123) --- Minecraft.Client/GuiParticles.cpp | 10 ++++++++++ Minecraft.Client/GuiParticles.h | 1 + Minecraft.Client/Minecraft.cpp | 5 +++-- Minecraft.Client/Screen.cpp | 12 ++++++++++++ Minecraft.Client/Screen.h | 1 + Minecraft.World/AABB.cpp | 6 ++++++ Minecraft.World/Vec3.cpp | 6 ++++++ 7 files changed, 39 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/GuiParticles.cpp b/Minecraft.Client/GuiParticles.cpp index 79240288..864ea29e 100644 --- a/Minecraft.Client/GuiParticles.cpp +++ b/Minecraft.Client/GuiParticles.cpp @@ -8,6 +8,15 @@ GuiParticles::GuiParticles(Minecraft *mc) this->mc = mc; } +GuiParticles::~GuiParticles() +{ + for (GuiParticle *gp : particles) + { + delete gp; + } + particles.clear(); +} + void GuiParticles::tick() { for (unsigned int i = 0; i < particles.size(); i++) @@ -19,6 +28,7 @@ void GuiParticles::tick() if (gp->removed) { + delete gp; particles.erase(particles.begin()+i); i--; } diff --git a/Minecraft.Client/GuiParticles.h b/Minecraft.Client/GuiParticles.h index 0fe31811..cbfc089d 100644 --- a/Minecraft.Client/GuiParticles.h +++ b/Minecraft.Client/GuiParticles.h @@ -13,6 +13,7 @@ private: public: GuiParticles(Minecraft *mc); + ~GuiParticles(); void tick(); void add(GuiParticle *guiParticle); void render(float a); diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 578a9479..886b96a9 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -526,9 +526,10 @@ LevelStorageSource *Minecraft::getLevelSource() void Minecraft::setScreen(Screen *screen) { - if (this->screen != nullptr) + Screen *oldScreen = this->screen; + if (oldScreen != nullptr) { - this->screen->removed(); + oldScreen->removed(); } #ifdef _WINDOWS64 diff --git a/Minecraft.Client/Screen.cpp b/Minecraft.Client/Screen.cpp index 071b42cc..38a645bb 100644 --- a/Minecraft.Client/Screen.cpp +++ b/Minecraft.Client/Screen.cpp @@ -23,6 +23,18 @@ Screen::Screen() // 4J added clickedButton = nullptr; } +Screen::~Screen() +{ + delete particles; + particles = nullptr; + + for (Button *button : buttons) + { + delete button; + } + buttons.clear(); +} + void Screen::render(int xm, int ym, float a) { for (Button* button : buttons) diff --git a/Minecraft.Client/Screen.h b/Minecraft.Client/Screen.h index 6b2cb945..04f9296d 100644 --- a/Minecraft.Client/Screen.h +++ b/Minecraft.Client/Screen.h @@ -22,6 +22,7 @@ public: GuiParticles *particles; Screen(); // 4J added + virtual ~Screen(); virtual void render(int xm, int ym, float a); protected: virtual void keyPressed(wchar_t eventCharacter, int eventKey); diff --git a/Minecraft.World/AABB.cpp b/Minecraft.World/AABB.cpp index af0501b4..b6fc1375 100644 --- a/Minecraft.World/AABB.cpp +++ b/Minecraft.World/AABB.cpp @@ -54,10 +54,16 @@ AABB *AABB::newPermanent(double x0, double y0, double z0, double x1, double y1, void AABB::clearPool() { + ThreadStorage *tls = static_cast(TlsGetValue(tlsIdx)); + if (tls != nullptr) + { + tls->poolPointer = 0; + } } void AABB::resetPool() { + clearPool(); } AABB *AABB::newTemp(double x0, double y0, double z0, double x1, double y1, double z1) diff --git a/Minecraft.World/Vec3.cpp b/Minecraft.World/Vec3.cpp index 75619bb0..cc9901a6 100644 --- a/Minecraft.World/Vec3.cpp +++ b/Minecraft.World/Vec3.cpp @@ -49,10 +49,16 @@ Vec3 *Vec3::newPermanent(double x, double y, double z) void Vec3::clearPool() { + ThreadStorage *tls = static_cast(TlsGetValue(tlsIdx)); + if (tls != nullptr) + { + tls->poolPointer = 0; + } } void Vec3::resetPool() { + clearPool(); } Vec3 *Vec3::newTemp(double x, double y, double z) From bb37600c87dee0c0e4ae96444f32f385ff547c49 Mon Sep 17 00:00:00 2001 From: Necmi Date: Tue, 26 May 2026 19:00:08 +0300 Subject: [PATCH 31/35] fix: game startup not checking fullscren values correct (#122) * fix outdated fullscreenload & added a bool to check at startupfullscreen * fix outdated methods & add back accidental deletes + debug things * used old vars & added deleted showwindow & updatewindow * added deleted few things --- .../Windows64/Windows64_Minecraft.cpp | 81 +++++++++++-------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 91fd812f..8e28cb3d 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -149,47 +149,57 @@ static void CopyWideArgToAnsi(LPCWSTR source, char* dest, size_t destSize) dest[destSize - 1] = 0; } -// ---------- Persistent options (options.txt next to exe) ---------- -static void GetOptionsFilePath(char *out, size_t outSize) +// ---------- Persistent options (options.dat next to exe) ---------- +static void GetOptionsFilePath(char *out, DWORD outSize) { - GetModuleFileNameA(nullptr, out, static_cast(outSize)); - char *p = strrchr(out, '\\'); - if (p) *(p + 1) = '\0'; - strncat_s(out, outSize, "options.txt", _TRUNCATE); + GetModuleFileNameA(nullptr, out, outSize); + char *p = strrchr(out, '\\'); + if (p) *(p + 1) = '\0'; + strncat_s(out, outSize, "settings.dat", _TRUNCATE); } static void SaveFullscreenOption(bool fullscreen) { - char path[MAX_PATH]; - GetOptionsFilePath(path, sizeof(path)); - FILE *f = nullptr; - if (fopen_s(&f, path, "w") == 0 && f) - { - fprintf(f, "fullscreen=%d\n", fullscreen ? 1 : 0); - fclose(f); - } + GAME_SETTINGS settings = {}; + + char path[MAX_PATH] = {}; + GetOptionsFilePath(path, MAX_PATH); + FILE *f = nullptr; + if (fopen_s(&f, path, "rb") == 0 && f) + { + fread(&settings, sizeof(GAME_SETTINGS), 1, f); + fclose(f); + } + + if (fullscreen) + settings.uiBitmaskValues |= (1UL << 25); + else + settings.uiBitmaskValues &= ~(1UL << 25); + + if (fopen_s(&f, path, "wb") == 0 && f) + { + fwrite(&settings, sizeof(GAME_SETTINGS), 1, f); + fclose(f); + } } static bool LoadFullscreenOption() { - char path[MAX_PATH]; + char path[MAX_PATH] = {}; GetOptionsFilePath(path, sizeof(path)); - FILE *f = nullptr; - if (fopen_s(&f, path, "r") == 0 && f) - { - char line[256]; - while (fgets(line, sizeof(line), f)) - { - int val = 0; - if (sscanf_s(line, "fullscreen=%d", &val) == 1) - { - fclose(f); - return val != 0; - } - } - fclose(f); - } - return false; + + FILE *f = nullptr; + if (fopen_s(&f, path, "rb") != 0 || !f) + return false; + + GAME_SETTINGS current = {}; + bool ok = (fread(¤t, sizeof(GAME_SETTINGS), 1, f) == 1); + fclose(f); + + if (!ok) + return false; + + return (current.uiBitmaskValues & (1UL << 25)) != 0; } // ------------------------------------------------------------------ @@ -824,7 +834,7 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) return FALSE; } - + return TRUE; } @@ -1695,11 +1705,12 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, CleanupDevice(); return 0; } - // Restore fullscreen state from previous session. Route through the // deferred exclusive fullscreen path so the main loop applies it on the // first tick (safer than transitioning during init). - if ((LoadFullscreenOption() && !g_isFullscreen) || launchOptions.fullscreen) + + bool FullScreenEnabled = LoadFullscreenOption(); + if (FullScreenEnabled && !g_isFullscreen) { g_bPendingExclusiveFullscreen = true; g_bPendingExclusiveFullscreenValue = true; @@ -1764,7 +1775,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, return 1; } g_bResizeReady = true; - + //app.TemporaryCreateGameStart(); //Sleep(10000); From 311503ec466379c01689e26e0b16de6533987e64 Mon Sep 17 00:00:00 2001 From: Lord_Cambion Date: Tue, 26 May 2026 19:02:54 +0200 Subject: [PATCH 32/35] fix: /kill now kills the player if in creative --- Minecraft.Client/ServerPlayer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/ServerPlayer.cpp b/Minecraft.Client/ServerPlayer.cpp index 04f5691c..3cffcd5f 100644 --- a/Minecraft.Client/ServerPlayer.cpp +++ b/Minecraft.Client/ServerPlayer.cpp @@ -850,8 +850,10 @@ void ServerPlayer::die(DamageSource *source) bool ServerPlayer::hurt(DamageSource *dmgSource, float dmg) { - if (isInvulnerable()) return false; - if (gameMode == nullptr||gameMode->isCreative()) return false; + if (isInvulnerable() && dmgSource != DamageSource::outOfWorld) return false; + if (gameMode == nullptr || gameMode->isCreative()) { + if (dmgSource != DamageSource::outOfWorld) return false; + } // 4J: Not relevant to console servers // Allow falldamage on dedicated pvpservers -- so people cannot cheat their way out of 'fall traps' From 2d07c7abeedc807771ef48f924040d0d6e2de933 Mon Sep 17 00:00:00 2001 From: Lord_Cambion Date: Wed, 27 May 2026 19:20:09 +0200 Subject: [PATCH 33/35] fix: boat not following correctly the player when speeding --- Minecraft.Client/TrackedEntity.cpp | 59 +++++++++++++++++++++-------- Minecraft.World/Boat.cpp | 61 ++++++++++++++++++------------ 2 files changed, 80 insertions(+), 40 deletions(-) diff --git a/Minecraft.Client/TrackedEntity.cpp b/Minecraft.Client/TrackedEntity.cpp index 161c5b0c..4cff1725 100644 --- a/Minecraft.Client/TrackedEntity.cpp +++ b/Minecraft.Client/TrackedEntity.cpp @@ -290,24 +290,53 @@ void TrackedEntity::tick(EntityTracker *tracker, vector > *pl wasRiding = false; } else - { - bool rot = abs(yRotn - yRotp) >= TOLERANCE_LEVEL || abs(xRotn - xRotp) >= TOLERANCE_LEVEL; - if (rot) - { - // 4J: Changed this to use deltas - broadcast(std::make_shared(e->entityId, static_cast(yRota), static_cast(xRota))); - yRotp = yRotn; - xRotp = xRotn; - } +{ + // the entity have a rider, the code didnt send position updates, + // causing desync between client and server when boat was spritning. - xp = Mth::floor(e->x * 32.0); - yp = Mth::floor(e->y * 32.0); - zp = Mth::floor(e->z * 32.0); + bool rot = abs(yRotn - yRotp) >= TOLERANCE_LEVEL || abs(xRotn - xRotp) >= TOLERANCE_LEVEL; + if (rot) + { + + broadcast(std::make_shared(e->entityId, static_cast(yRota), static_cast(xRota))); + yRotp = yRotn; + xRotp = xRotn; + } - sendDirtyEntityData(); + int xn = Mth::floor(e->x * 32.0); + int yn = Mth::floor(e->y * 32.0); + int zn = Mth::floor(e->z * 32.0); + int xa = xn - xp; + int ya = yn - yp; + int za = zn - zp; - wasRiding = true; - } + // send only if the boat moved enough + // or 3 seconds periodically + bool pos = abs(xa) >= TOLERANCE_LEVEL || abs(ya) >= TOLERANCE_LEVEL || abs(za) >= TOLERANCE_LEVEL + || (tickCount % (SharedConstants::TICKS_PER_SECOND * 3) == 0); + + if (pos) + { + // if deltapos is too much big use teleport. + if (xa < -128 || xa >= 128 || ya < -128 || ya >= 128 || za < -128 || za >= 128) + { + broadcast(std::make_shared(e->entityId, xn, yn, zn, + static_cast(yRotn), static_cast(xRotn))); + } + else + { + // small movement, delta + broadcast(std::make_shared(e->entityId, + static_cast(xa), static_cast(ya), static_cast(za))); + } + xp = xn; + yp = yn; + zp = zn; + } + + sendDirtyEntityData(); + wasRiding = true; +} int yHeadRot = Mth::floor(e->getYHeadRot() * 256 / 360); if (abs(yHeadRot - yHeadRotp) >= TOLERANCE_LEVEL) diff --git a/Minecraft.World/Boat.cpp b/Minecraft.World/Boat.cpp index 8cb14e1e..7c62e4ee 100644 --- a/Minecraft.World/Boat.cpp +++ b/Minecraft.World/Boat.cpp @@ -148,7 +148,7 @@ void Boat::lerpTo(double x, double y, double z, float yRot, float xRot, int step { if (doLerp) { - lSteps = steps + 5; + lSteps = steps +5; } else { @@ -188,6 +188,10 @@ void Boat::lerpMotion(double xd, double yd, double zd) void Boat::tick() { Entity::tick(); + + + + if (getHurtTime() > 0) setHurtTime(getHurtTime() - 1); if (getDamage() > 0) setDamage(getDamage() - 1); xo = x; @@ -199,8 +203,8 @@ void Boat::tick() double waterPercentage = 0; for (int i = 0; i < steps; i++) { - double y0 = bb->y0 + (bb->y1 - bb->y0) * (i + 0) / steps - 2 / 16.0f; - double y1 = bb->y0 + (bb->y1 - bb->y0) * (i + 1) / steps - 2 / 16.0f; + double y0 = bb->y0 + (bb->y1 - bb->y0) * (i + 0) / steps + 1.5f / 16.0f; + double y1 = bb->y0 + (bb->y1 - bb->y0) * (i + 1) / steps + 1.5f / 16.0f; AABB *bb2 = AABB::newTemp(bb->x0, y0, bb->z0, bb->x1, y1, bb->z1); if (level->containsLiquid(bb2, Material::water)) { @@ -257,18 +261,19 @@ void Boat::tick() return; } - // Bob in water - if (waterPercentage > 0) + // Bob in water & gravity + if (waterPercentage < 1.0) { - double bob = waterPercentage * 2 - 1; + double bob = waterPercentage * 2.0 - 1.0; yd += 0.04f * bob; } - - // Reimplement gravity again (??) - int tileUnder = level->getTile(Mth::floor(x), Mth::floor(y-0.15), Mth::floor(z)); - if (tileUnder == 0 && !onGround) + else { - yd -= 0.04f; + if (yd < 0.0) + { + yd /= 2.0; + } + yd += 0.007f; } // Rider controls @@ -281,24 +286,16 @@ void Boat::tick() { double riderXd = -sin(livingRider->yRot * PI / 180); double riderZd = cos(livingRider->yRot * PI / 180); - float mult = livingRider->isSprinting() ? 2.0f : 1.0f; + double currentSpeed = sqrt(xd * xd + zd * zd); float moveFactor = (float)forward; if (forward < 0) moveFactor *= 0.5f; // Move slower backwards - xd += riderXd * acceleration * 0.05f * mult * moveFactor; - zd += riderZd * acceleration * 0.05f * mult * moveFactor; + xd += riderXd * acceleration * 0.05f * moveFactor; + zd += riderZd * acceleration * 0.05f * moveFactor; } } double curSpeed = sqrt(xd * xd + zd * zd); double maxSpeed = MAX_SPEED; - if (rider.lock() != nullptr && rider.lock()->instanceof(eTYPE_LIVINGENTITY)) - { - shared_ptr livingRider = dynamic_pointer_cast(rider.lock()); - if (livingRider->isSprinting()) - { - maxSpeed *= 1.5; - } - } if (curSpeed > maxSpeed) { @@ -330,10 +327,12 @@ void Boat::tick() move(xd, yd, zd); // Break boat on high speed collision + float breakThreshold = (rider.lock() != nullptr) ? 0.35f : 0.20f; if ((horizontalCollision && lastSpeed > 0.20)) { if (!level->isClientSide && !removed) { + remove(); for (int i = 0; i < 3; i++) { @@ -343,6 +342,9 @@ void Boat::tick() { spawnAtLocation(Item::stick->id, 1, 0); } + + + } } else @@ -472,10 +474,19 @@ bool Boat::interact(shared_ptr player) if ( (rider.lock() != nullptr) && rider.lock()->instanceof(eTYPE_PLAYER) && (rider.lock() != player) ) return true; if (!level->isClientSide) { + bool isRiding = (rider.lock() == player); + + if (isRiding) + { + + player->xd = 0; + player->yd = 0; + player->zd = 0; + } // 4J HEG - Fixed issue with player not being able to dismount boat (issue #4446) - player->ride( rider.lock() == player ? nullptr : shared_from_this() ); - } - return true; + player->ride(isRiding ? nullptr : shared_from_this()); + } + return true; } void Boat::setDamage(float damage) From c19d5bb58ec8de6fbc7e86b80889d10f85a44cef Mon Sep 17 00:00:00 2001 From: Lord Cambion Date: Wed, 27 May 2026 19:22:12 +0200 Subject: [PATCH 34/35] fix: boat not following correctly the player when speeding (#127) --- Minecraft.Client/TrackedEntity.cpp | 59 +++++++++++++++++++++-------- Minecraft.World/Boat.cpp | 61 ++++++++++++++++++------------ 2 files changed, 80 insertions(+), 40 deletions(-) diff --git a/Minecraft.Client/TrackedEntity.cpp b/Minecraft.Client/TrackedEntity.cpp index 161c5b0c..4cff1725 100644 --- a/Minecraft.Client/TrackedEntity.cpp +++ b/Minecraft.Client/TrackedEntity.cpp @@ -290,24 +290,53 @@ void TrackedEntity::tick(EntityTracker *tracker, vector > *pl wasRiding = false; } else - { - bool rot = abs(yRotn - yRotp) >= TOLERANCE_LEVEL || abs(xRotn - xRotp) >= TOLERANCE_LEVEL; - if (rot) - { - // 4J: Changed this to use deltas - broadcast(std::make_shared(e->entityId, static_cast(yRota), static_cast(xRota))); - yRotp = yRotn; - xRotp = xRotn; - } +{ + // the entity have a rider, the code didnt send position updates, + // causing desync between client and server when boat was spritning. - xp = Mth::floor(e->x * 32.0); - yp = Mth::floor(e->y * 32.0); - zp = Mth::floor(e->z * 32.0); + bool rot = abs(yRotn - yRotp) >= TOLERANCE_LEVEL || abs(xRotn - xRotp) >= TOLERANCE_LEVEL; + if (rot) + { + + broadcast(std::make_shared(e->entityId, static_cast(yRota), static_cast(xRota))); + yRotp = yRotn; + xRotp = xRotn; + } - sendDirtyEntityData(); + int xn = Mth::floor(e->x * 32.0); + int yn = Mth::floor(e->y * 32.0); + int zn = Mth::floor(e->z * 32.0); + int xa = xn - xp; + int ya = yn - yp; + int za = zn - zp; - wasRiding = true; - } + // send only if the boat moved enough + // or 3 seconds periodically + bool pos = abs(xa) >= TOLERANCE_LEVEL || abs(ya) >= TOLERANCE_LEVEL || abs(za) >= TOLERANCE_LEVEL + || (tickCount % (SharedConstants::TICKS_PER_SECOND * 3) == 0); + + if (pos) + { + // if deltapos is too much big use teleport. + if (xa < -128 || xa >= 128 || ya < -128 || ya >= 128 || za < -128 || za >= 128) + { + broadcast(std::make_shared(e->entityId, xn, yn, zn, + static_cast(yRotn), static_cast(xRotn))); + } + else + { + // small movement, delta + broadcast(std::make_shared(e->entityId, + static_cast(xa), static_cast(ya), static_cast(za))); + } + xp = xn; + yp = yn; + zp = zn; + } + + sendDirtyEntityData(); + wasRiding = true; +} int yHeadRot = Mth::floor(e->getYHeadRot() * 256 / 360); if (abs(yHeadRot - yHeadRotp) >= TOLERANCE_LEVEL) diff --git a/Minecraft.World/Boat.cpp b/Minecraft.World/Boat.cpp index 8cb14e1e..7c62e4ee 100644 --- a/Minecraft.World/Boat.cpp +++ b/Minecraft.World/Boat.cpp @@ -148,7 +148,7 @@ void Boat::lerpTo(double x, double y, double z, float yRot, float xRot, int step { if (doLerp) { - lSteps = steps + 5; + lSteps = steps +5; } else { @@ -188,6 +188,10 @@ void Boat::lerpMotion(double xd, double yd, double zd) void Boat::tick() { Entity::tick(); + + + + if (getHurtTime() > 0) setHurtTime(getHurtTime() - 1); if (getDamage() > 0) setDamage(getDamage() - 1); xo = x; @@ -199,8 +203,8 @@ void Boat::tick() double waterPercentage = 0; for (int i = 0; i < steps; i++) { - double y0 = bb->y0 + (bb->y1 - bb->y0) * (i + 0) / steps - 2 / 16.0f; - double y1 = bb->y0 + (bb->y1 - bb->y0) * (i + 1) / steps - 2 / 16.0f; + double y0 = bb->y0 + (bb->y1 - bb->y0) * (i + 0) / steps + 1.5f / 16.0f; + double y1 = bb->y0 + (bb->y1 - bb->y0) * (i + 1) / steps + 1.5f / 16.0f; AABB *bb2 = AABB::newTemp(bb->x0, y0, bb->z0, bb->x1, y1, bb->z1); if (level->containsLiquid(bb2, Material::water)) { @@ -257,18 +261,19 @@ void Boat::tick() return; } - // Bob in water - if (waterPercentage > 0) + // Bob in water & gravity + if (waterPercentage < 1.0) { - double bob = waterPercentage * 2 - 1; + double bob = waterPercentage * 2.0 - 1.0; yd += 0.04f * bob; } - - // Reimplement gravity again (??) - int tileUnder = level->getTile(Mth::floor(x), Mth::floor(y-0.15), Mth::floor(z)); - if (tileUnder == 0 && !onGround) + else { - yd -= 0.04f; + if (yd < 0.0) + { + yd /= 2.0; + } + yd += 0.007f; } // Rider controls @@ -281,24 +286,16 @@ void Boat::tick() { double riderXd = -sin(livingRider->yRot * PI / 180); double riderZd = cos(livingRider->yRot * PI / 180); - float mult = livingRider->isSprinting() ? 2.0f : 1.0f; + double currentSpeed = sqrt(xd * xd + zd * zd); float moveFactor = (float)forward; if (forward < 0) moveFactor *= 0.5f; // Move slower backwards - xd += riderXd * acceleration * 0.05f * mult * moveFactor; - zd += riderZd * acceleration * 0.05f * mult * moveFactor; + xd += riderXd * acceleration * 0.05f * moveFactor; + zd += riderZd * acceleration * 0.05f * moveFactor; } } double curSpeed = sqrt(xd * xd + zd * zd); double maxSpeed = MAX_SPEED; - if (rider.lock() != nullptr && rider.lock()->instanceof(eTYPE_LIVINGENTITY)) - { - shared_ptr livingRider = dynamic_pointer_cast(rider.lock()); - if (livingRider->isSprinting()) - { - maxSpeed *= 1.5; - } - } if (curSpeed > maxSpeed) { @@ -330,10 +327,12 @@ void Boat::tick() move(xd, yd, zd); // Break boat on high speed collision + float breakThreshold = (rider.lock() != nullptr) ? 0.35f : 0.20f; if ((horizontalCollision && lastSpeed > 0.20)) { if (!level->isClientSide && !removed) { + remove(); for (int i = 0; i < 3; i++) { @@ -343,6 +342,9 @@ void Boat::tick() { spawnAtLocation(Item::stick->id, 1, 0); } + + + } } else @@ -472,10 +474,19 @@ bool Boat::interact(shared_ptr player) if ( (rider.lock() != nullptr) && rider.lock()->instanceof(eTYPE_PLAYER) && (rider.lock() != player) ) return true; if (!level->isClientSide) { + bool isRiding = (rider.lock() == player); + + if (isRiding) + { + + player->xd = 0; + player->yd = 0; + player->zd = 0; + } // 4J HEG - Fixed issue with player not being able to dismount boat (issue #4446) - player->ride( rider.lock() == player ? nullptr : shared_from_this() ); - } - return true; + player->ride(isRiding ? nullptr : shared_from_this()); + } + return true; } void Boat::setDamage(float damage) From 6e29b9f778285d68aa9051c6e2bc78eb2c27978c Mon Sep 17 00:00:00 2001 From: Lord_Cambion Date: Wed, 27 May 2026 19:58:09 +0200 Subject: [PATCH 35/35] Update: BlockPos.cpp comments changed from italian to english --- Minecraft.World/BlockPos.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Minecraft.World/BlockPos.cpp b/Minecraft.World/BlockPos.cpp index 13703c50..c48496cc 100644 --- a/Minecraft.World/BlockPos.cpp +++ b/Minecraft.World/BlockPos.cpp @@ -8,7 +8,7 @@ const BlockPos BlockPos::ZERO = BlockPos(0, 0, 0); -// Costruttori + BlockPos::BlockPos() : Vec3i(0, 0, 0) {} BlockPos::BlockPos(int x, int y, int z) : Vec3i(x, y, z) {} @@ -49,7 +49,7 @@ BlockPos::BlockPos(int compressed) : Vec3i(0, 0, 0) { BlockPos::BlockPos(BlockSource& source) : Vec3i(source.getBlockX(), source.getBlockY(), source.getBlockZ()) {} -// Metodi di confronto + bool BlockPos::equals(const BlockPos& other) const { return x == other.x && y == other.y && z == other.z; } @@ -88,7 +88,7 @@ BlockPos BlockPos::relative(int direction, int distance) const { return BlockPos(x + dx, y, z + dz); } -// Metodi direzionali +// directional methods BlockPos BlockPos::above(int distance) const { return BlockPos(x, y + distance, z); } @@ -119,7 +119,7 @@ BlockPos BlockPos::multiply(int factor) const { return BlockPos(x * factor, y * factor, z * factor); } -// Compressione +// compression int BlockPos::compress() const { static const int MASK_X = (1 << BITS_X) - 1; static const int MASK_Y = (1 << BITS_Y) - 1;