From a2d1f04c58c6f8892ead659e3671f6621b270528 Mon Sep 17 00:00:00 2001 From: "Echo J." Date: Mon, 9 Mar 2026 16:52:33 +0200 Subject: [PATCH 01/33] CI: Split debug build into a independent job This also means the bundle isn't being packaged for that build (as I don't think it's important) --- .github/workflows/build-linux.yml | 128 ++++++++++++++++-------------- 1 file changed, 67 insertions(+), 61 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 42cb25d20..d4799065f 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -25,9 +25,6 @@ on: jobs: build-linux: runs-on: ubuntu-latest - concurrency: - group: build-linux-${{ github.ref }} - cancel-in-progress: true steps: - name: Checkout repository uses: actions/checkout@v4 @@ -77,16 +74,6 @@ jobs: export CCACHE_DIR="$CCACHE_DIR" meson setup build_meson --wipe --buildtype=release - - name: Configure Meson (debug) - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - mkdir -p "$CCACHE_DIR" - export CCACHE_DIR="$CCACHE_DIR" - meson setup build_debug --wipe --buildtype=debug - - name: Build with Ninja env: CC: "ccache clang" @@ -97,15 +84,6 @@ jobs: # Use all available cores for faster parallel builds ninja -C build_meson -j$(nproc) -v - - name: Build Debug with Ninja - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - export CCACHE_DIR="${{ runner.temp }}/ccache" - ninja -C build_debug -j$(nproc) -v - - name: Install patchelf run: sudo apt-get install -y patchelf @@ -185,47 +163,75 @@ jobs: echo "Bundle ready: $BUNDLE" ls -lh "$BUNDLE" "$BUNDLE/lib" - - name: Bundle debug executable + libraries - env: - GITHUB_SHA: ${{ github.sha }} - run: | - set -euo pipefail - EXE_PATH=build_debug/Minecraft.Client/Minecraft.Client - if [ ! -f "$EXE_PATH" ]; then - echo "ERROR: expected debug executable at $EXE_PATH" >&2 - ls -la build_debug || true - exit 1 - fi - - SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-8) - BUNDLE=out/minecraft-client-linux-${SHORT_SHA}-debug - mkdir -p "$BUNDLE/lib" - - # Copy the binary - cp "$EXE_PATH" "$BUNDLE/Minecraft.Client.debug" - - # Collect non-system shared library dependencies and copy them in. - ldd "$EXE_PATH" \ - | awk '/=>/ { print $3 }' \ - | grep -v '^(' \ - | grep -Ev '/(libc|libm|libdl|libpthread|librt|libgcc_s|libstdc\+\+|ld-linux)[^/]*\.so' \ - | sort -u \ - | while read -r lib; do - [ -f "$lib" ] && cp "$lib" "$BUNDLE/lib/" || true - done - - # Patch the binary RPATH so it finds libs in ./lib at runtime - patchelf --set-rpath '$ORIGIN/lib' "$BUNDLE/Minecraft.Client.debug" - - # Keep a copy of the unstripped debug binary (symbols are already present in debug build) - echo "Debug bundle ready: $BUNDLE" - ls -lh "$BUNDLE" "$BUNDLE/lib" - - name: Upload artifact uses: actions/upload-artifact@v4 with: name: minecraft-client-linux-${{ github.sha }} - path: | - out/minecraft-client-linux-*/ - out/minecraft-client-linux-*-debug/ + path: out/minecraft-client-linux-*/ + retention-days: 7 + build-linux-debug: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential python3 python3-pip python3-setuptools libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev libpng-dev pkg-config clang lld ccache libssl-dev + # Set a reasonable ccache size + ccache -M 5G || true + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Cache pip packages + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/build-linux.yml') }} + + - name: Install Meson and Ninja (pip) + run: | + python -m pip install --upgrade pip + pip install meson ninja + + - name: Restore ccache + uses: actions/cache@v4 + with: + path: ~/.ccache + key: ${{ runner.os }}-ccache-debug-${{ hashFiles('**/meson.build') }} + + - name: Restore meson cache + uses: actions/cache@v4 + with: + path: ~/.cache/meson + key: ${{ runner.os }}-meson-debug-${{ hashFiles('**/meson.build') }} + + - name: Configure Meson (debug) + env: + CC: "ccache clang" + CXX: "ccache clang++" + CCACHE_DIR: ${{ runner.temp }}/ccache + run: | + mkdir -p "$CCACHE_DIR" + export CCACHE_DIR="$CCACHE_DIR" + meson setup build_debug --wipe --buildtype=debug + + - name: Build Debug with Ninja + env: + CC: "ccache clang" + CXX: "ccache clang++" + CCACHE_DIR: ${{ runner.temp }}/ccache + run: | + export CCACHE_DIR="${{ runner.temp }}/ccache" + ninja -C build_debug -j$(nproc) -v + + - name: Upload debug executable + uses: actions/upload-artifact@v4 + with: + name: minecraft-client-linux-debug_exe-${{ github.sha }} + path: build_debug/Minecraft.Client/Minecraft.Client retention-days: 7 From 4eaf02bfd086efa5a1f79413dcfe18fa46e9c91d Mon Sep 17 00:00:00 2001 From: "Echo J." Date: Mon, 9 Mar 2026 17:16:35 +0200 Subject: [PATCH 02/33] CI: Compile the client directly with Meson I'm not sure why Ninja had to be called here --- .github/workflows/build-linux.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index d4799065f..1a3896803 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -74,7 +74,7 @@ jobs: export CCACHE_DIR="$CCACHE_DIR" meson setup build_meson --wipe --buildtype=release - - name: Build with Ninja + - name: Build with Meson env: CC: "ccache clang" CXX: "ccache clang++" @@ -82,7 +82,7 @@ jobs: run: | export CCACHE_DIR="${{ runner.temp }}/ccache" # Use all available cores for faster parallel builds - ninja -C build_meson -j$(nproc) -v + meson compile -C build_meson -j $(nproc) -v Minecraft.Client - name: Install patchelf run: sudo apt-get install -y patchelf @@ -220,14 +220,15 @@ jobs: export CCACHE_DIR="$CCACHE_DIR" meson setup build_debug --wipe --buildtype=debug - - name: Build Debug with Ninja + - name: Build Debug with Meson env: CC: "ccache clang" CXX: "ccache clang++" CCACHE_DIR: ${{ runner.temp }}/ccache run: | export CCACHE_DIR="${{ runner.temp }}/ccache" - ninja -C build_debug -j$(nproc) -v + # Use all available cores for faster parallel builds + meson compile -C build_debug -j $(nproc) -v Minecraft.Client - name: Upload debug executable uses: actions/upload-artifact@v4 From fb39f1afde6c7f1ec531218fa498366a2d50425a Mon Sep 17 00:00:00 2001 From: "Echo J." Date: Mon, 9 Mar 2026 17:29:57 +0200 Subject: [PATCH 03/33] Remove an unused CI file This was effectively replaced by the build-linux.yml script --- .github/workflows/ci.yml | 49 ---------------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index bcfa32d7d..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: CI Build - -on: - push: - branches: [ main, master ] - pull_request: - branches: [ main, master ] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential pkg-config ca-certificates curl git \ - libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev libpng-dev libx11-dev \ - libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libudev-dev - - - name: Install Meson and Ninja (pip) - run: | - python -m pip install --upgrade pip - pip install meson ninja - - - name: Configure Meson - run: | - meson setup build_meson --wipe --buildtype=release - - - name: Build with Ninja - run: | - ninja -C build_meson -v - - - name: Package build output - run: | - tar -czf minecraft-binaries.tar.gz -C build_meson . - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: minecraft-binaries - path: minecraft-binaries.tar.gz From 81e9256b7a49193eec4c95c722f5abb32076acb3 Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Mon, 9 Mar 2026 17:22:15 +0100 Subject: [PATCH 04/33] added asan for debug builds --- meson.build | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 3cc9b0c41..357396709 100644 --- a/meson.build +++ b/meson.build @@ -12,6 +12,9 @@ pymod = import('python') python = pymod.find_installation('python3', required: true) cc = meson.get_compiler('cpp') +buildtype = get_option('buildtype') +#langs cant sadly be set before calling project +langs = ['cpp', 'c'] # system deps gl_dep = dependency('gl') @@ -21,6 +24,19 @@ png_dep = dependency('libpng') thread_dep = dependency('threads') dl_dep = cc.find_library('dl') +if buildtype.startswith('debug') + strAsan = '-fsanitize=address' + strLsan = '-fsanitize=leak' + if cc.has_argument(strAsan) + add_project_arguments(strAsan, language: langs) + add_project_link_arguments(strAsan, language: langs) + endif + if cc.has_argument(strLsan) + add_project_arguments(strLsan, language: langs) + add_project_link_arguments(strLsan, language: langs) + endif +endif + # compile flags (chagne ts juicey) global_cpp_args = [ '-fpermissive', @@ -52,4 +68,4 @@ subdir('4J.Profile') subdir('4J.Storage') subdir('Minecraft.Assets') subdir('Minecraft.World') -subdir('Minecraft.Client') \ No newline at end of file +subdir('Minecraft.Client') From 5265eef7595f24b433c7d8aabedc3e4a6929d16f Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Mon, 9 Mar 2026 21:54:43 +0100 Subject: [PATCH 05/33] first patch of UB --- .../Platform/Common/Consoles_App.cpp | 2 +- .../Textures/Stitching/StitchedTexture.cpp | 18 +++++++++++++----- .../Textures/Stitching/StitchedTexture.h | 2 +- Minecraft.Client/Textures/TextureManager.cpp | 2 +- Minecraft.Client/Utils/MemoryTracker.h | 1 - Minecraft.World/Util/Icon.h | 2 ++ debug.sh | 6 ++++++ run.sh | 6 ++++++ 8 files changed, 30 insertions(+), 9 deletions(-) create mode 100755 debug.sh create mode 100755 run.sh diff --git a/Minecraft.Client/Platform/Common/Consoles_App.cpp b/Minecraft.Client/Platform/Common/Consoles_App.cpp index 5268792f2..4207fe362 100644 --- a/Minecraft.Client/Platform/Common/Consoles_App.cpp +++ b/Minecraft.Client/Platform/Common/Consoles_App.cpp @@ -4111,7 +4111,7 @@ void CMinecraftApp::loadStringTable() { byteArray locFile = m_mediaArchive->getFile(localisationFile); m_stringTable = new StringTable(locFile.data, locFile.length); - delete locFile.data; + delete[] locFile.data; } else { diff --git a/Minecraft.Client/Textures/Stitching/StitchedTexture.cpp b/Minecraft.Client/Textures/Stitching/StitchedTexture.cpp index 86eb37577..77e0a6ba9 100644 --- a/Minecraft.Client/Textures/Stitching/StitchedTexture.cpp +++ b/Minecraft.Client/Textures/Stitching/StitchedTexture.cpp @@ -60,11 +60,19 @@ void StitchedTexture::freeFrameTextures() StitchedTexture::~StitchedTexture() { - for(AUTO_VAR(it, frames->begin()); it != frames->end(); ++it) - { - delete *it; + // 4jcraft added null check, in PreStitchedTextures::stitch(), than ::loadUVs() + // all new SimpleIcons deriving from StitchedIcons are calling the + // constructor which does frames = NULL + // so the program breaks on delete oldClock in ::loadUVs() + // but scince "frames" is never allocated (StitchedTexture::init()) + // not called, frames was never called (only the constructor) + if(frames) { + for(AUTO_VAR(it, frames->begin()); it != frames->end(); ++it) + { + delete *it; + } + delete frames; } - delete frames; } void StitchedTexture::initUVs(float U0, float V0, float U1, float V1) @@ -351,4 +359,4 @@ int StitchedTexture::getFlags() const bool StitchedTexture::hasOwnData() { return true; -} \ No newline at end of file +} diff --git a/Minecraft.Client/Textures/Stitching/StitchedTexture.h b/Minecraft.Client/Textures/Stitching/StitchedTexture.h index edb3c3c05..306ffd1b7 100644 --- a/Minecraft.Client/Textures/Stitching/StitchedTexture.h +++ b/Minecraft.Client/Textures/Stitching/StitchedTexture.h @@ -86,4 +86,4 @@ public: void setFlags(int flags); // 4J added virtual void freeFrameTextures(); // 4J added virtual bool hasOwnData(); // 4J Added -}; \ No newline at end of file +}; diff --git a/Minecraft.Client/Textures/TextureManager.cpp b/Minecraft.Client/Textures/TextureManager.cpp index 6fd3813fa..546bdc17f 100644 --- a/Minecraft.Client/Textures/TextureManager.cpp +++ b/Minecraft.Client/Textures/TextureManager.cpp @@ -193,4 +193,4 @@ Texture *TextureManager::createTexture(const std::wstring &name, int mode, int w // 4J Stu - Don't clamp as it causes issues with how we signal non-mipmmapped textures to the pixel shader //return createTexture(name, mode, width, height, Texture::WM_CLAMP, format, Texture::TFLT_NEAREST, Texture::TFLT_NEAREST, mipmap, NULL); return createTexture(name, mode, width, height, Texture::WM_WRAP, format, Texture::TFLT_NEAREST, Texture::TFLT_NEAREST, mipmap, NULL); -} \ No newline at end of file +} diff --git a/Minecraft.Client/Utils/MemoryTracker.h b/Minecraft.Client/Utils/MemoryTracker.h index 8bf103be5..8492aae28 100644 --- a/Minecraft.Client/Utils/MemoryTracker.h +++ b/Minecraft.Client/Utils/MemoryTracker.h @@ -1,5 +1,4 @@ #pragma once -#include "MemoryTracker.h" class ByteBuffer; class IntBuffer; class FloatBuffer; diff --git a/Minecraft.World/Util/Icon.h b/Minecraft.World/Util/Icon.h index fa606ff48..c77fba2c9 100644 --- a/Minecraft.World/Util/Icon.h +++ b/Minecraft.World/Util/Icon.h @@ -16,6 +16,8 @@ public: static const int IS_ALPHA_CUT_OUT = 4; #endif + virtual ~Icon() {} // added by 4jcraft, needed for abstract class + virtual int getX() const = 0; virtual int getY() const = 0; virtual int getWidth() const = 0; diff --git a/debug.sh b/debug.sh new file mode 100755 index 000000000..63f1c0541 --- /dev/null +++ b/debug.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +meson compile -C build && \ +cd build/Minecraft.Client/ && \ +gdb -tui ./Minecraft.Client && \ +cd ../.. diff --git a/run.sh b/run.sh new file mode 100755 index 000000000..f4ac7e7f3 --- /dev/null +++ b/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +meson compile -C build && \ +cd build/Minecraft.Client/ && \ +./Minecraft.Client && \ +cd ../.. From 66b31669c3cb3fcb9cae3409cb2c869b25ed0eb7 Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Mon, 9 Mar 2026 22:48:36 +0100 Subject: [PATCH 06/33] new batch of delete operator missmatch --- .../Platform/Common/GameRules/ConsoleSchematicFile.cpp | 4 ++-- .../Platform/Common/Tutorial/LookAtTileHint.h | 2 +- Minecraft.Client/Platform/Common/Tutorial/TakeItemHint.h | 2 +- Minecraft.Client/Platform/Common/Tutorial/TutorialHint.h | 2 +- .../Rendering/EntityRenderers/TileRenderer.cpp | 2 +- Minecraft.World/IO/Streams/BufferedOutputStream.cpp | 6 +++++- Minecraft.World/Items/MapItem.cpp | 2 +- Minecraft.World/Level/Storage/MapItemSavedData.cpp | 2 +- Minecraft.World/Network/Packets/AwardStatPacket.cpp | 9 +++++++-- Minecraft.World/Network/Packets/RemoveEntitiesPacket.cpp | 2 +- Minecraft.World/Util/AABB.cpp | 4 ++-- Minecraft.World/WorldGen/Biomes/BiomeSource.cpp | 4 ++-- .../WorldGen/Sources/TheEndLevelRandomLevelSource.cpp | 3 ++- 13 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Minecraft.Client/Platform/Common/GameRules/ConsoleSchematicFile.cpp b/Minecraft.Client/Platform/Common/GameRules/ConsoleSchematicFile.cpp index 586e29997..f8af5039a 100644 --- a/Minecraft.Client/Platform/Common/GameRules/ConsoleSchematicFile.cpp +++ b/Minecraft.Client/Platform/Common/GameRules/ConsoleSchematicFile.cpp @@ -285,12 +285,12 @@ __int64 ConsoleSchematicFile::applyBlocksAndData(LevelChunk *chunk, AABB *chunkB PIXBeginNamedEvent(0,"Setting Block data"); chunk->setBlockData(blockData); PIXEndNamedEvent(); - delete blockData.data; + delete[] blockData.data; //4jcraft changed to array delete chunk->recalcHeightmapOnly(); PIXBeginNamedEvent(0,"Setting Data data"); chunk->setDataData(dataData); PIXEndNamedEvent(); - delete dataData.data; + delete[] dataData.data; //4jcraft, same here // A basic pass through to roughly do the lighting. At this point of post-processing, we don't have all the neighbouring chunks loaded in, // so any lighting here should be things that won't propagate out of this chunk. diff --git a/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.h b/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.h index 856dbb63f..1c03e08d6 100644 --- a/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.h +++ b/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.h @@ -19,4 +19,4 @@ public: ~LookAtTileHint(); virtual bool onLookAt(int id, int iData=0); -}; \ No newline at end of file +}; diff --git a/Minecraft.Client/Platform/Common/Tutorial/TakeItemHint.h b/Minecraft.Client/Platform/Common/Tutorial/TakeItemHint.h index a7641f64b..fe2744f36 100644 --- a/Minecraft.Client/Platform/Common/Tutorial/TakeItemHint.h +++ b/Minecraft.Client/Platform/Common/Tutorial/TakeItemHint.h @@ -16,4 +16,4 @@ public: ~TakeItemHint(); virtual bool onTake( std::shared_ptr item ); -}; \ No newline at end of file +}; diff --git a/Minecraft.Client/Platform/Common/Tutorial/TutorialHint.h b/Minecraft.Client/Platform/Common/Tutorial/TutorialHint.h index 191b021f3..f114392c7 100644 --- a/Minecraft.Client/Platform/Common/Tutorial/TutorialHint.h +++ b/Minecraft.Client/Platform/Common/Tutorial/TutorialHint.h @@ -50,4 +50,4 @@ public: virtual bool onLookAtEntity(eINSTANCEOF type); virtual int tick(); virtual bool allowFade() { return m_allowFade; } -}; \ No newline at end of file +}; diff --git a/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp index a84a13eeb..30d05c2b3 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp @@ -160,7 +160,7 @@ TileRenderer::TileRenderer( LevelSource* level, int xMin, int yMin, int zMin, un TileRenderer::~TileRenderer() { - delete cache; + delete[] cache; //4jcraft, changed to [] } TileRenderer::TileRenderer( LevelSource* level ) diff --git a/Minecraft.World/IO/Streams/BufferedOutputStream.cpp b/Minecraft.World/IO/Streams/BufferedOutputStream.cpp index 4e1051a7c..379686138 100644 --- a/Minecraft.World/IO/Streams/BufferedOutputStream.cpp +++ b/Minecraft.World/IO/Streams/BufferedOutputStream.cpp @@ -15,7 +15,11 @@ BufferedOutputStream::BufferedOutputStream(OutputStream *out, int size) BufferedOutputStream::~BufferedOutputStream() { - delete buf.data; + //4jcraft, changed to [], deallocates internal buffer + //TODO: ArrayWithLength.h doesnt have a destructor. + //this wouldnt need to be done manually. + //but for some reason the destructor is commented out in the source code? + delete[] buf.data; } //Flushes this buffered output stream. This forces any buffered output bytes to be written out to the underlying output stream. diff --git a/Minecraft.World/Items/MapItem.cpp b/Minecraft.World/Items/MapItem.cpp index 292320b27..908d0f776 100644 --- a/Minecraft.World/Items/MapItem.cpp +++ b/Minecraft.World/Items/MapItem.cpp @@ -342,6 +342,6 @@ std::shared_ptr MapItem::getUpdatePacket(std::shared_ptr i if (data.data == NULL || data.length == 0) return nullptr; std::shared_ptr retval = std::shared_ptr(new ComplexItemDataPacket((short) Item::map->id, (short) itemInstance->getAuxValue(), data)); - delete data.data; + delete[] data.data; //4jcraft, changed to [] return retval; } diff --git a/Minecraft.World/Level/Storage/MapItemSavedData.cpp b/Minecraft.World/Level/Storage/MapItemSavedData.cpp index c0ee9b024..ae98d8698 100644 --- a/Minecraft.World/Level/Storage/MapItemSavedData.cpp +++ b/Minecraft.World/Level/Storage/MapItemSavedData.cpp @@ -123,7 +123,7 @@ charArray MapItemSavedData::HoldingPlayer::nextUpdatePacket(std::shared_ptr servPlayer = std::dynamic_pointer_cast(player); for (int d = 0; d < 10; d++) diff --git a/Minecraft.World/Network/Packets/AwardStatPacket.cpp b/Minecraft.World/Network/Packets/AwardStatPacket.cpp index efa503b9b..576cead1b 100644 --- a/Minecraft.World/Network/Packets/AwardStatPacket.cpp +++ b/Minecraft.World/Network/Packets/AwardStatPacket.cpp @@ -16,7 +16,12 @@ AwardStatPacket::AwardStatPacket(int statId, int count) { this->statId = statId; - this->m_paramData.data = (uint8_t *) new int(count); + // 4jcraft, changed from new int(count); to new int[1]; + // and initializing the array with count + // reason: .data needs to be delete with delete[] but its + // allocated with new, not new T[] + this->m_paramData.data = (uint8_t *) new int[1]; + ((int*)this->m_paramData.data)[0] = count; this->m_paramData.length = sizeof(int); } @@ -30,7 +35,7 @@ AwardStatPacket::~AwardStatPacket() { if (m_paramData.data != NULL) { - delete [] m_paramData.data; + delete[] m_paramData.data; m_paramData.data = NULL; } } diff --git a/Minecraft.World/Network/Packets/RemoveEntitiesPacket.cpp b/Minecraft.World/Network/Packets/RemoveEntitiesPacket.cpp index a007849a2..9207ca8bf 100644 --- a/Minecraft.World/Network/Packets/RemoveEntitiesPacket.cpp +++ b/Minecraft.World/Network/Packets/RemoveEntitiesPacket.cpp @@ -16,7 +16,7 @@ RemoveEntitiesPacket::RemoveEntitiesPacket(intArray ids) RemoveEntitiesPacket::~RemoveEntitiesPacket() { - delete ids.data; + delete[] ids.data; //4jcraft, changed to [] } void RemoveEntitiesPacket::read(DataInputStream *dis) //throws IOException diff --git a/Minecraft.World/Util/AABB.cpp b/Minecraft.World/Util/AABB.cpp index b768f76f6..3dc459497 100644 --- a/Minecraft.World/Util/AABB.cpp +++ b/Minecraft.World/Util/AABB.cpp @@ -12,14 +12,14 @@ AABB::ThreadStorage *AABB::tlsDefault = NULL; AABB::ThreadStorage::ThreadStorage() { - pool = new AABB[POOL_SIZE]; + pool = new AABB[POOL_SIZE]; //4jcraft, needs to be deleted with delete[] poolPointer = 0; } AABB::ThreadStorage::~ThreadStorage() { - delete pool; + delete[] pool; //4jcraft, changed to [] } void AABB::CreateNewThreadStorage() diff --git a/Minecraft.World/WorldGen/Biomes/BiomeSource.cpp b/Minecraft.World/WorldGen/Biomes/BiomeSource.cpp index f5c142e12..3a77b436d 100644 --- a/Minecraft.World/WorldGen/Biomes/BiomeSource.cpp +++ b/Minecraft.World/WorldGen/Biomes/BiomeSource.cpp @@ -218,7 +218,7 @@ void BiomeSource::getBiomeBlock(BiomeArray& biomes, int x, int z, int w, int h, { BiomeArray tmp = cache->getBiomeBlockAt(x, z); System::arraycopy(tmp, 0, &biomes, 0, w * h); - delete tmp.data; // MGH - added, the caching creates this array from the indices now. + delete[] tmp.data; // MGH - added, the caching creates this array from the indices now. //4jcraft made it array delete //return biomes; } @@ -635,4 +635,4 @@ bool BiomeSource::getIsMatch(float *frac) // Consider as suitable if we've got all the critical ones, and in total 9 or more - currently there's 8 critical so this just forces at least 1 more others return ( typeCount >= 9 ); -} \ No newline at end of file +} diff --git a/Minecraft.World/WorldGen/Sources/TheEndLevelRandomLevelSource.cpp b/Minecraft.World/WorldGen/Sources/TheEndLevelRandomLevelSource.cpp index 63bfa9837..7280ea617 100644 --- a/Minecraft.World/WorldGen/Sources/TheEndLevelRandomLevelSource.cpp +++ b/Minecraft.World/WorldGen/Sources/TheEndLevelRandomLevelSource.cpp @@ -188,7 +188,8 @@ LevelChunk *TheEndLevelRandomLevelSource::getChunk(int xOffs, int zOffs) levelChunk->recalcHeightmap(); //delete blocks.data; // Don't delete the blocks as the array data is actually owned by the chunk now - delete biomes.data; + //4jcraft changed to [] + delete[] biomes.data; return levelChunk; } From 0047a442a3240820efe32df6395f29354afe6946 Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Mon, 9 Mar 2026 23:06:45 +0100 Subject: [PATCH 07/33] hide the files --- debug.sh | 6 ------ run.sh | 6 ------ 2 files changed, 12 deletions(-) delete mode 100755 debug.sh delete mode 100755 run.sh diff --git a/debug.sh b/debug.sh deleted file mode 100755 index 63f1c0541..000000000 --- a/debug.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -meson compile -C build && \ -cd build/Minecraft.Client/ && \ -gdb -tui ./Minecraft.Client && \ -cd ../.. diff --git a/run.sh b/run.sh deleted file mode 100755 index f4ac7e7f3..000000000 --- a/run.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -meson compile -C build && \ -cd build/Minecraft.Client/ && \ -./Minecraft.Client && \ -cd ../.. From 2ae8ac3b80b523078381d175571edbaec9003b33 Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Mon, 9 Mar 2026 23:15:34 +0100 Subject: [PATCH 08/33] fix typos in comment --- .../Textures/Stitching/StitchedTexture.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Minecraft.Client/Textures/Stitching/StitchedTexture.cpp b/Minecraft.Client/Textures/Stitching/StitchedTexture.cpp index 77e0a6ba9..124c7207f 100644 --- a/Minecraft.Client/Textures/Stitching/StitchedTexture.cpp +++ b/Minecraft.Client/Textures/Stitching/StitchedTexture.cpp @@ -60,12 +60,10 @@ void StitchedTexture::freeFrameTextures() StitchedTexture::~StitchedTexture() { - // 4jcraft added null check, in PreStitchedTextures::stitch(), than ::loadUVs() - // all new SimpleIcons deriving from StitchedIcons are calling the - // constructor which does frames = NULL - // so the program breaks on delete oldClock in ::loadUVs() - // but scince "frames" is never allocated (StitchedTexture::init()) - // not called, frames was never called (only the constructor) + // 4jcraft, added null check + // the constructor does not allocate the frames vector. + // in some scenarios the destructor/delete is called + // without ever calling ::init() if(frames) { for(AUTO_VAR(it, frames->begin()); it != frames->end(); ++it) { From c6ab0e1177c72d2ad356af7488221dcf662377fb Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Tue, 10 Mar 2026 00:12:26 +0100 Subject: [PATCH 09/33] unhide files --- .gitignore | 2 +- debug.sh | 6 ++++++ run.sh | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100755 debug.sh create mode 100755 run.sh diff --git a/.gitignore b/.gitignore index ffdd21a84..0c9c8e1f4 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ meson-logs/ *.out *.d compile_commands.json - +.clangd # ----- Scratch / legacy ----- diff --git a/debug.sh b/debug.sh new file mode 100755 index 000000000..b5a3c173d --- /dev/null +++ b/debug.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +CC=clang CXX=clang++ meson compile -C build && \ +cd build/Minecraft.Client/ && \ +gdb -tui ./Minecraft.Client && \ +cd ../.. diff --git a/run.sh b/run.sh new file mode 100755 index 000000000..d261f892b --- /dev/null +++ b/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +CC=clang CXX=clang++ meson compile -C build && \ +cd build/Minecraft.Client/ && \ +./Minecraft.Client && \ +cd ../.. From 021c2809f452ac8b52f0cc13038dd4c74c5ae46f Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Tue, 10 Mar 2026 02:13:56 +0100 Subject: [PATCH 10/33] purged -Wdelete-non-abstract-non-virtual-dtor --- Minecraft.Client/Input/ConsoleInputSource.h | 1 + Minecraft.Client/Input/Input.h | 3 ++- .../Platform/Common/GameRules/GameRuleDefinition.h | 1 + .../Platform/Common/GameRules/LevelGenerationOptions.h | 1 + .../Platform/Common/Tutorial/LookAtEntityHint.h | 5 +++-- .../Platform/Common/Tutorial/LookAtTileHint.cpp | 3 ++- Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.h | 3 ++- Minecraft.Client/Platform/Common/Tutorial/TakeItemHint.h | 3 ++- Minecraft.Client/Platform/Common/Tutorial/Tutorial.h | 2 +- Minecraft.Client/Platform/Common/Tutorial/TutorialHint.h | 1 + Minecraft.Client/Player/MultiPlayerGameMode.h | 3 ++- Minecraft.Client/Rendering/Models/Model.h | 1 + Minecraft.Client/Textures/MemTextureProcessor.h | 3 ++- Minecraft.Client/Textures/Packs/DLCTexturePack.h | 2 +- Minecraft.Client/Textures/Packs/TexturePack.h | 1 + Minecraft.World/AI/Control/JumpControl.h | 4 +++- Minecraft.World/AI/Control/LookControl.h | 3 ++- Minecraft.World/AI/Control/MoveControl.h | 3 ++- Minecraft.World/Containers/Container.h | 3 ++- Minecraft.World/Containers/CraftingContainer.h | 4 ++-- Minecraft.World/Containers/Merchant.h | 3 ++- Minecraft.World/Containers/ResultContainer.h | 3 ++- Minecraft.World/Items/ItemInstance.h | 4 ++-- Minecraft.World/Level/Dimensions/Dimension.h | 2 +- Minecraft.World/Level/LevelChunk.h | 2 +- Minecraft.World/Level/LevelData.h | 1 + Minecraft.World/Level/Storage/LevelStorage.h | 1 + Minecraft.World/Level/Storage/PlayerIO.h | 2 +- Minecraft.World/Level/Storage/SavedData.h | 3 ++- Minecraft.World/Network/Packets/Packet.h | 5 +++-- Minecraft.World/Network/Packets/PacketListener.h | 1 + Minecraft.World/WorldGen/Biomes/BiomeDecorator.h | 4 +++- Minecraft.World/WorldGen/Biomes/BiomeSource.h | 2 +- Minecraft.World/WorldGen/Features/HouseFeature.cpp | 6 +++--- Minecraft.World/WorldGen/Features/LakeFeature.cpp | 5 +++-- Minecraft.World/WorldGen/Features/LargeFeature.h | 4 ++-- Minecraft.World/WorldGen/Features/TreeFeature.h | 2 +- Minecraft.World/WorldGen/Layers/BiomeOverrideLayer.cpp | 4 ++-- Minecraft.World/WorldGen/Layers/Layer.h | 3 ++- Minecraft.World/WorldGen/Noise/Synth.h | 1 + Minecraft.World/WorldGen/Structures/StrongholdPieces.h | 1 + 41 files changed, 70 insertions(+), 39 deletions(-) diff --git a/Minecraft.Client/Input/ConsoleInputSource.h b/Minecraft.Client/Input/ConsoleInputSource.h index 7d79c0941..391d12916 100644 --- a/Minecraft.Client/Input/ConsoleInputSource.h +++ b/Minecraft.Client/Input/ConsoleInputSource.h @@ -3,6 +3,7 @@ class ConsoleInputSource { public: + virtual ~ConsoleInputSource(){} virtual void info(const std::wstring& string) = 0; virtual void warn(const std::wstring& string) = 0; virtual std::wstring getConsoleName() = 0; diff --git a/Minecraft.Client/Input/Input.h b/Minecraft.Client/Input/Input.h index 0a44d7656..daac8bf6d 100644 --- a/Minecraft.Client/Input/Input.h +++ b/Minecraft.Client/Input/Input.h @@ -12,6 +12,7 @@ public: bool sneaking; Input(); // 4J - added + virtual ~Input(){} virtual void tick(LocalPlayer *player); @@ -19,4 +20,4 @@ private: bool lReset; bool rReset; -}; \ No newline at end of file +}; diff --git a/Minecraft.Client/Platform/Common/GameRules/GameRuleDefinition.h b/Minecraft.Client/Platform/Common/GameRules/GameRuleDefinition.h index d41e1aea7..289734428 100644 --- a/Minecraft.Client/Platform/Common/GameRules/GameRuleDefinition.h +++ b/Minecraft.Client/Platform/Common/GameRules/GameRuleDefinition.h @@ -27,6 +27,7 @@ protected: public: GameRuleDefinition(); + virtual ~GameRuleDefinition(){} virtual ConsoleGameRules::EGameRuleType getActionType() = 0; diff --git a/Minecraft.Client/Platform/Common/GameRules/LevelGenerationOptions.h b/Minecraft.Client/Platform/Common/GameRules/LevelGenerationOptions.h index d81436bee..11970a555 100644 --- a/Minecraft.Client/Platform/Common/GameRules/LevelGenerationOptions.h +++ b/Minecraft.Client/Platform/Common/GameRules/LevelGenerationOptions.h @@ -22,6 +22,7 @@ public: // was being mixed in with all the game information as they have // completely different lifespans. + virtual ~GrSource(){} virtual bool requiresTexturePack()=0; virtual UINT getRequiredTexturePackId()=0; virtual std::wstring getDefaultSaveName()=0; diff --git a/Minecraft.Client/Platform/Common/Tutorial/LookAtEntityHint.h b/Minecraft.Client/Platform/Common/Tutorial/LookAtEntityHint.h index a30701a37..a8c389c37 100644 --- a/Minecraft.Client/Platform/Common/Tutorial/LookAtEntityHint.h +++ b/Minecraft.Client/Platform/Common/Tutorial/LookAtEntityHint.h @@ -14,7 +14,8 @@ private: public: LookAtEntityHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, int titleId, eINSTANCEOF type); - ~LookAtEntityHint(); + //TODO: LEAK LEKAFDSAKFDJ;SALKDJF;ASLKFJ ITS NEVER IMPLEMENTED + ~LookAtEntityHint(){}; virtual bool onLookAtEntity(eINSTANCEOF type); -}; \ No newline at end of file +}; diff --git a/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.cpp b/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.cpp index 5d03960c9..c9ef4ce0e 100644 --- a/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.cpp +++ b/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.cpp @@ -10,6 +10,7 @@ LookAtTileHint::LookAtTileHint(eTutorial_Hint id, Tutorial *tutorial, int tiles[ { m_iTilesCount = tilesLength; + //TODO LEAK LEAK LEAK LEAKS EVERY WHERE AAAAAAAAAAAAAAAAH m_iTiles= new int [m_iTilesCount]; for(unsigned int i=0;i item ); }; diff --git a/Minecraft.Client/Platform/Common/Tutorial/Tutorial.h b/Minecraft.Client/Platform/Common/Tutorial/Tutorial.h index 3f06df12f..5e9aaaad1 100644 --- a/Minecraft.Client/Platform/Common/Tutorial/Tutorial.h +++ b/Minecraft.Client/Platform/Common/Tutorial/Tutorial.h @@ -123,7 +123,7 @@ public: bool m_isFullTutorial; public: Tutorial(int iPad, bool isFullTutorial = false); - ~Tutorial(); + virtual ~Tutorial(); void tick(); int getPad() { return m_iPad; } diff --git a/Minecraft.Client/Platform/Common/Tutorial/TutorialHint.h b/Minecraft.Client/Platform/Common/Tutorial/TutorialHint.h index f114392c7..e40759841 100644 --- a/Minecraft.Client/Platform/Common/Tutorial/TutorialHint.h +++ b/Minecraft.Client/Platform/Common/Tutorial/TutorialHint.h @@ -37,6 +37,7 @@ protected: public: TutorialHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, eHintType type, bool allowFade = true); + virtual ~TutorialHint(){} eTutorial_Hint getId() { return m_id; } diff --git a/Minecraft.Client/Player/MultiPlayerGameMode.h b/Minecraft.Client/Player/MultiPlayerGameMode.h index 19ef46af6..34396aebd 100644 --- a/Minecraft.Client/Player/MultiPlayerGameMode.h +++ b/Minecraft.Client/Player/MultiPlayerGameMode.h @@ -23,6 +23,7 @@ protected: public: MultiPlayerGameMode(Minecraft *minecraft, ClientConnection *connection); + virtual ~MultiPlayerGameMode(){} static void creativeDestroyBlock(Minecraft *minecraft, MultiPlayerGameMode *gameMode, int x, int y, int z, int face); void adjustPlayer(std::shared_ptr player); bool isCutScene(); @@ -64,4 +65,4 @@ public: virtual bool isInputAllowed(int mapping) { return true; } virtual bool isTutorial() { return false; } virtual Tutorial *getTutorial() { return NULL; } -}; \ No newline at end of file +}; diff --git a/Minecraft.Client/Rendering/Models/Model.h b/Minecraft.Client/Rendering/Models/Model.h index 104e6ce27..c80f9e8f6 100644 --- a/Minecraft.Client/Rendering/Models/Model.h +++ b/Minecraft.Client/Rendering/Models/Model.h @@ -19,6 +19,7 @@ public: int texHeight; Model(); // 4J added + virtual ~Model(){} virtual void render(std::shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) {} virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim=0) {} virtual void prepareMobModel(std::shared_ptr mob, float time, float r, float a) {} diff --git a/Minecraft.Client/Textures/MemTextureProcessor.h b/Minecraft.Client/Textures/MemTextureProcessor.h index 8e945e1d5..56590d428 100644 --- a/Minecraft.Client/Textures/MemTextureProcessor.h +++ b/Minecraft.Client/Textures/MemTextureProcessor.h @@ -4,5 +4,6 @@ class BufferedImage; class MemTextureProcessor { public: + virtual ~MemTextureProcessor(){} virtual BufferedImage *process(BufferedImage *read) = 0; -}; \ No newline at end of file +}; diff --git a/Minecraft.Client/Textures/Packs/DLCTexturePack.h b/Minecraft.Client/Textures/Packs/DLCTexturePack.h index 375c8cafe..f69eb0258 100644 --- a/Minecraft.Client/Textures/Packs/DLCTexturePack.h +++ b/Minecraft.Client/Textures/Packs/DLCTexturePack.h @@ -23,7 +23,7 @@ public: using AbstractTexturePack::getResource; DLCTexturePack(DWORD id, DLCPack *pack, TexturePack *fallback); - ~DLCTexturePack(); + ~DLCTexturePack(){}; virtual std::wstring getResource(const std::wstring& name); virtual DLCPack * getDLCPack(); diff --git a/Minecraft.Client/Textures/Packs/TexturePack.h b/Minecraft.Client/Textures/Packs/TexturePack.h index edbabc796..781fbe474 100644 --- a/Minecraft.Client/Textures/Packs/TexturePack.h +++ b/Minecraft.Client/Textures/Packs/TexturePack.h @@ -12,6 +12,7 @@ class TexturePack public: TexturePack() { m_bHasAudio=false;} + virtual ~TexturePack(){} virtual bool hasData() = 0; virtual bool hasAudio() { return m_bHasAudio;} virtual void setHasAudio(bool bVal) {m_bHasAudio=bVal;} diff --git a/Minecraft.World/AI/Control/JumpControl.h b/Minecraft.World/AI/Control/JumpControl.h index b33648c7e..4ff9cfce7 100644 --- a/Minecraft.World/AI/Control/JumpControl.h +++ b/Minecraft.World/AI/Control/JumpControl.h @@ -12,7 +12,9 @@ private: public: JumpControl(Mob *mob); + virtual ~JumpControl(){} void jump(); + //genuinly, why tf is this VIRTUAL virtual void tick(); -}; \ No newline at end of file +}; diff --git a/Minecraft.World/AI/Control/LookControl.h b/Minecraft.World/AI/Control/LookControl.h index fe182e7c7..3f9dabb2e 100644 --- a/Minecraft.World/AI/Control/LookControl.h +++ b/Minecraft.World/AI/Control/LookControl.h @@ -15,6 +15,7 @@ private: public: LookControl(Mob *mob); + virtual ~LookControl(){} void setLookAt(std::shared_ptr target, float yMax, float xMax); void setLookAt(double x, double y, double z, float yMax, float xMax); @@ -30,4 +31,4 @@ public: double getWantedX(); double getWantedY(); double getWantedZ(); -}; \ No newline at end of file +}; diff --git a/Minecraft.World/AI/Control/MoveControl.h b/Minecraft.World/AI/Control/MoveControl.h index 4138280e0..89ac8162b 100644 --- a/Minecraft.World/AI/Control/MoveControl.h +++ b/Minecraft.World/AI/Control/MoveControl.h @@ -22,6 +22,7 @@ private: public: MoveControl(Mob *mob); + virtual ~MoveControl(){} bool hasWanted(); float getSpeed(); @@ -31,4 +32,4 @@ public: private: float rotlerp(float a, float b, float max); -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Containers/Container.h b/Minecraft.World/Containers/Container.h index 46444696f..06ee42a28 100644 --- a/Minecraft.World/Containers/Container.h +++ b/Minecraft.World/Containers/Container.h @@ -7,6 +7,7 @@ class Player; class Container { public: + virtual ~Container(){} static const int LARGE_MAX_STACK_SIZE = 64; virtual unsigned int getContainerSize() = 0; @@ -20,4 +21,4 @@ public: virtual bool stillValid(std::shared_ptr player) = 0; virtual void startOpen() = 0; virtual void stopOpen() = 0; -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Containers/CraftingContainer.h b/Minecraft.World/Containers/CraftingContainer.h index 5ef82ea20..e912c0957 100644 --- a/Minecraft.World/Containers/CraftingContainer.h +++ b/Minecraft.World/Containers/CraftingContainer.h @@ -13,7 +13,7 @@ private: public: CraftingContainer(AbstractContainerMenu *menu, unsigned int w, unsigned int h); - ~CraftingContainer(); + virtual ~CraftingContainer(); virtual unsigned int getContainerSize(); virtual std::shared_ptr getItem(unsigned int slot); @@ -29,4 +29,4 @@ public: void startOpen() { } // TODO Auto-generated method stub void stopOpen() { } // TODO Auto-generated method stub -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Containers/Merchant.h b/Minecraft.World/Containers/Merchant.h index ad7ab1fcb..6c700c7ff 100644 --- a/Minecraft.World/Containers/Merchant.h +++ b/Minecraft.World/Containers/Merchant.h @@ -7,6 +7,7 @@ class Player; class Merchant { public: + virtual ~Merchant(){} virtual void setTradingPlayer(std::shared_ptr player) = 0; virtual std::shared_ptr getTradingPlayer() = 0; virtual MerchantRecipeList *getOffers(std::shared_ptr forPlayer) = 0; @@ -14,4 +15,4 @@ public: virtual void notifyTrade(MerchantRecipe *activeRecipe) = 0; virtual void notifyTradeUpdated(std::shared_ptr item) = 0; virtual int getDisplayName() = 0; -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Containers/ResultContainer.h b/Minecraft.World/Containers/ResultContainer.h index 1b030d810..8f83543d6 100644 --- a/Minecraft.World/Containers/ResultContainer.h +++ b/Minecraft.World/Containers/ResultContainer.h @@ -10,6 +10,7 @@ private: public: // 4J Stu Added a ctor to init items ResultContainer(); + virtual ~ResultContainer(){} virtual unsigned int getContainerSize(); virtual std::shared_ptr getItem(unsigned int slot); @@ -23,4 +24,4 @@ public: void startOpen() { } // TODO Auto-generated method stub void stopOpen() { } // TODO Auto-generated method stub -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Items/ItemInstance.h b/Minecraft.World/Items/ItemInstance.h index 465ee1ec0..6a706d0ad 100644 --- a/Minecraft.World/Items/ItemInstance.h +++ b/Minecraft.World/Items/ItemInstance.h @@ -63,7 +63,7 @@ private: ItemInstance() { _init(-1,0,0); } public: - ~ItemInstance(); + virtual ~ItemInstance(); std::shared_ptr remove(int count); Item *getItem() const; @@ -151,4 +151,4 @@ public: int getBaseRepairCost(); void setRepairCost(int cost); -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Level/Dimensions/Dimension.h b/Minecraft.World/Level/Dimensions/Dimension.h index 752ed0df4..9d79c990f 100644 --- a/Minecraft.World/Level/Dimensions/Dimension.h +++ b/Minecraft.World/Level/Dimensions/Dimension.h @@ -28,7 +28,7 @@ protected: public: Dimension(); - ~Dimension(); + virtual ~Dimension(); virtual ChunkSource *createRandomLevelSource() const; virtual ChunkSource *createFlatLevelSource() const; virtual ChunkStorage *createStorage(File dir); diff --git a/Minecraft.World/Level/LevelChunk.h b/Minecraft.World/Level/LevelChunk.h index f7d208273..72aeb04f2 100644 --- a/Minecraft.World/Level/LevelChunk.h +++ b/Minecraft.World/Level/LevelChunk.h @@ -152,7 +152,7 @@ public: LevelChunk(Level *level, int x, int z); LevelChunk(Level *level, byteArray blocks, int x, int z); LevelChunk(Level *level, int x, int z, LevelChunk *lc); - ~LevelChunk(); + virtual ~LevelChunk(); virtual bool isAt(int x, int z); diff --git a/Minecraft.World/Level/LevelData.h b/Minecraft.World/Level/LevelData.h index ac5148164..8e8cb77a8 100644 --- a/Minecraft.World/Level/LevelData.h +++ b/Minecraft.World/Level/LevelData.h @@ -71,6 +71,7 @@ protected: virtual void setTagData(CompoundTag *tag); // 4J - removed CompoundTag *playerTag public: + virtual ~LevelData(){} virtual __int64 getSeed(); virtual int getXSpawn(); virtual int getYSpawn(); diff --git a/Minecraft.World/Level/Storage/LevelStorage.h b/Minecraft.World/Level/Storage/LevelStorage.h index 4212eaf32..1ed3f5b34 100644 --- a/Minecraft.World/Level/Storage/LevelStorage.h +++ b/Minecraft.World/Level/Storage/LevelStorage.h @@ -18,6 +18,7 @@ public: static const std::wstring NETHER_FOLDER; static const std::wstring ENDER_FOLDER; + virtual ~LevelStorage(){} virtual LevelData *prepareLevel() = 0; virtual void checkSession() = 0; virtual ChunkStorage *createChunkStorage(Dimension *dimension) = 0; diff --git a/Minecraft.World/Level/Storage/PlayerIO.h b/Minecraft.World/Level/Storage/PlayerIO.h index 2de00aeeb..141fb6b0a 100644 --- a/Minecraft.World/Level/Storage/PlayerIO.h +++ b/Minecraft.World/Level/Storage/PlayerIO.h @@ -19,4 +19,4 @@ public: virtual void saveMapIdLookup() = 0; virtual void deleteMapFilesForPlayer(std::shared_ptr player) = 0; virtual void saveAllCachedData() = 0; -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Level/Storage/SavedData.h b/Minecraft.World/Level/Storage/SavedData.h index b81956cd4..37e9f2579 100644 --- a/Minecraft.World/Level/Storage/SavedData.h +++ b/Minecraft.World/Level/Storage/SavedData.h @@ -15,6 +15,7 @@ private: public: SavedData(const std::wstring& id); + virtual ~SavedData(){} virtual void load(CompoundTag *tag) = 0; virtual void save(CompoundTag *tag) = 0; @@ -22,4 +23,4 @@ public: void setDirty(); void setDirty(bool dirty); bool isDirty(); -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Network/Packets/Packet.h b/Minecraft.World/Network/Packets/Packet.h index 57e420d3d..20a3bfb04 100644 --- a/Minecraft.World/Network/Packets/Packet.h +++ b/Minecraft.World/Network/Packets/Packet.h @@ -32,7 +32,7 @@ public: const int id; public: - PacketStatistics(int id) : id( id ), count( 0 ), totalSize( 0 ), samplesPos( 0 ), firstSampleTime( 0 ) { countSamples[0] = 0; sizeSamples[0] = 0; } + PacketStatistics(int id) : count( 0 ), totalSize( 0 ), samplesPos( 0 ), firstSampleTime( 0 ), id( id ) { countSamples[0] = 0; sizeSamples[0] = 0; } void addPacket(int bytes); int getCount(); double getAverageSize(); @@ -61,6 +61,7 @@ public: const __int64 createTime; Packet(); + virtual ~Packet(){} static std::shared_ptr getPacket(int id); @@ -110,4 +111,4 @@ public: protected: static void writeNbt(CompoundTag *tag, DataOutputStream *dos); -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Network/Packets/PacketListener.h b/Minecraft.World/Network/Packets/PacketListener.h index 1fb81e19d..766e96f58 100644 --- a/Minecraft.World/Network/Packets/PacketListener.h +++ b/Minecraft.World/Network/Packets/PacketListener.h @@ -106,6 +106,7 @@ class GameCommandPacket; class PacketListener { public: + virtual ~PacketListener(){} virtual bool isServerPacketListener() = 0; virtual void handleBlockRegionUpdate(std::shared_ptr packet); virtual void onUnhandledPacket(std::shared_ptr packet); diff --git a/Minecraft.World/WorldGen/Biomes/BiomeDecorator.h b/Minecraft.World/WorldGen/Biomes/BiomeDecorator.h index 01560045a..480def9dd 100644 --- a/Minecraft.World/WorldGen/Biomes/BiomeDecorator.h +++ b/Minecraft.World/WorldGen/Biomes/BiomeDecorator.h @@ -24,6 +24,8 @@ protected: public: BiomeDecorator(Biome *biome); + virtual ~BiomeDecorator(){} + void decorate(Level *level, Random *random, int xo, int zo); @@ -72,4 +74,4 @@ protected: void decorateDepthSpan(int count, Feature *feature, int y0, int y1); void decorateDepthAverage(int count, Feature *feature, int yMid, int ySpan); void decorateOres(); -}; \ No newline at end of file +}; diff --git a/Minecraft.World/WorldGen/Biomes/BiomeSource.h b/Minecraft.World/WorldGen/Biomes/BiomeSource.h index 550531fa9..a85908934 100644 --- a/Minecraft.World/WorldGen/Biomes/BiomeSource.h +++ b/Minecraft.World/WorldGen/Biomes/BiomeSource.h @@ -40,7 +40,7 @@ public: #else static __int64 findSeed(LevelType *generator); // 4J added #endif - ~BiomeSource(); + virtual ~BiomeSource(); public: std::vector getPlayerSpawnBiomes() { return playerSpawnBiomes; } diff --git a/Minecraft.World/WorldGen/Features/HouseFeature.cpp b/Minecraft.World/WorldGen/Features/HouseFeature.cpp index 8fb59910a..dc72d33d8 100644 --- a/Minecraft.World/WorldGen/Features/HouseFeature.cpp +++ b/Minecraft.World/WorldGen/Features/HouseFeature.cpp @@ -7,9 +7,9 @@ bool HouseFeature::place(Level *level, Random *random, int x, int y, int z) { - while (y > 0 && !level->getMaterial(x, y - 1, z)->blocksMotion()) + while (y > 0 && !level->getMaterial(x, y - 1, z)->blocksMotion()) { y--; - + } int w = random->nextInt(7) + 7; int h = 4 + random->nextInt(3) / 2; int d = random->nextInt(7) + 7; @@ -190,4 +190,4 @@ bool HouseFeature::place(Level *level, Random *random, int x, int y, int z) return true; -} \ No newline at end of file +} diff --git a/Minecraft.World/WorldGen/Features/LakeFeature.cpp b/Minecraft.World/WorldGen/Features/LakeFeature.cpp index c70939596..ad07da9d4 100644 --- a/Minecraft.World/WorldGen/Features/LakeFeature.cpp +++ b/Minecraft.World/WorldGen/Features/LakeFeature.cpp @@ -13,8 +13,9 @@ bool LakeFeature::place(Level *level, Random *random, int x, int y, int z) { x -= 8; z -= 8; - while (y > 5 && level->isEmptyTile(x, y, z)) + while (y > 5 && level->isEmptyTile(x, y, z)) { y--; + } if (y <= 4) { return false; @@ -172,4 +173,4 @@ bool LakeFeature::place(Level *level, Random *random, int x, int y, int z) } return true; -} \ No newline at end of file +} diff --git a/Minecraft.World/WorldGen/Features/LargeFeature.h b/Minecraft.World/WorldGen/Features/LargeFeature.h index 0bbce2c92..b35fc566b 100644 --- a/Minecraft.World/WorldGen/Features/LargeFeature.h +++ b/Minecraft.World/WorldGen/Features/LargeFeature.h @@ -14,10 +14,10 @@ protected: public: LargeFeature(); - ~LargeFeature(); + virtual ~LargeFeature(); virtual void apply(ChunkSource *ChunkSource, Level *level, int xOffs, int zOffs, byteArray blocks); protected: virtual void addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks) {} -}; \ No newline at end of file +}; diff --git a/Minecraft.World/WorldGen/Features/TreeFeature.h b/Minecraft.World/WorldGen/Features/TreeFeature.h index e29c0b0c3..c58f72d9e 100644 --- a/Minecraft.World/WorldGen/Features/TreeFeature.h +++ b/Minecraft.World/WorldGen/Features/TreeFeature.h @@ -5,9 +5,9 @@ class TreeFeature : public Feature { private: const int baseHeight; - const bool addJungleFeatures; const int trunkType; const int leafType; + const bool addJungleFeatures; public: TreeFeature(bool doUpdate); diff --git a/Minecraft.World/WorldGen/Layers/BiomeOverrideLayer.cpp b/Minecraft.World/WorldGen/Layers/BiomeOverrideLayer.cpp index 436cdc5ae..eefe26b4d 100644 --- a/Minecraft.World/WorldGen/Layers/BiomeOverrideLayer.cpp +++ b/Minecraft.World/WorldGen/Layers/BiomeOverrideLayer.cpp @@ -22,7 +22,7 @@ BiomeOverrideLayer::BiomeOverrideLayer(int seedMixup) : Layer(seedMixup) #endif if( file == INVALID_HANDLE_VALUE ) { - DWORD error = GetLastError(); + // DWORD error = GetLastError(); //assert(false); app.DebugPrintf("Biome override not found, using plains as default\n"); @@ -78,4 +78,4 @@ intArray BiomeOverrideLayer::getArea(int xo, int yo, int w, int h) } } return result; -} \ No newline at end of file +} diff --git a/Minecraft.World/WorldGen/Layers/Layer.h b/Minecraft.World/WorldGen/Layers/Layer.h index 3afe13acc..2104d219d 100644 --- a/Minecraft.World/WorldGen/Layers/Layer.h +++ b/Minecraft.World/WorldGen/Layers/Layer.h @@ -24,6 +24,7 @@ public: static LayerArray getDefaultLayers(__int64 seed, LevelType *levelType); Layer(__int64 seedMixup); + virtual ~Layer(){} virtual void init(__int64 seed); virtual void initRandom(__int64 x, __int64 y); @@ -33,4 +34,4 @@ protected: public: virtual intArray getArea(int xo, int yo, int w, int h) = 0; -}; \ No newline at end of file +}; diff --git a/Minecraft.World/WorldGen/Noise/Synth.h b/Minecraft.World/WorldGen/Noise/Synth.h index 74e686342..033632cf4 100644 --- a/Minecraft.World/WorldGen/Noise/Synth.h +++ b/Minecraft.World/WorldGen/Noise/Synth.h @@ -3,6 +3,7 @@ class Synth { public: virtual double getValue(double x, double y) = 0; + virtual ~Synth(){} doubleArray create(int width, int height); }; diff --git a/Minecraft.World/WorldGen/Structures/StrongholdPieces.h b/Minecraft.World/WorldGen/Structures/StrongholdPieces.h index b73a89e12..d9779d6a8 100644 --- a/Minecraft.World/WorldGen/Structures/StrongholdPieces.h +++ b/Minecraft.World/WorldGen/Structures/StrongholdPieces.h @@ -39,6 +39,7 @@ private: int maxPlaceCount; PieceWeight(EPieceClass pieceClass, int weight, int maxPlaceCount); + virtual ~PieceWeight(){} virtual bool doPlace(int depth); bool isValid(); }; From 6be6aadf0e5f92f01ba541b25471116243d9b45c Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Tue, 10 Mar 2026 02:27:16 +0100 Subject: [PATCH 11/33] another delete operator missmatch --- Minecraft.Client/Network/PlayerChunkMap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/Network/PlayerChunkMap.cpp b/Minecraft.Client/Network/PlayerChunkMap.cpp index 8bedd4d6e..0f86fc0bf 100644 --- a/Minecraft.Client/Network/PlayerChunkMap.cpp +++ b/Minecraft.Client/Network/PlayerChunkMap.cpp @@ -33,7 +33,7 @@ PlayerChunkMap::PlayerChunk::PlayerChunk(int x, int z, PlayerChunkMap *pcm) : po PlayerChunkMap::PlayerChunk::~PlayerChunk() { - delete changedTiles.data; + delete[] changedTiles.data; //4jcraft, changed to [] } // 4J added - construct an an array of flags that indicate which entities are still waiting to have network packets sent out to say that they have been removed @@ -797,4 +797,4 @@ void PlayerChunkMap::setRadius(int newRadius) assert(radius >= MIN_VIEW_DISTANCE); this->radius = newRadius; } -} \ No newline at end of file +} From 1c67f134b24639336afbe3e1525117a31512a571 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Tue, 10 Mar 2026 09:18:20 +0100 Subject: [PATCH 12/33] added files from pr --- 4J.Input/4J_Input.cpp | 695 ++++++------------ 4J.Input/meson.build | 4 + 4J.Render/4J_Render.cpp | 276 ++++--- 4J.Render/meson.build | 2 +- .../Platform/Linux/Iggy/gdraw/gdraw_glfw.c | 266 +++---- Minecraft.Client/meson.build | 6 +- README.md | 11 +- flake.nix | 5 +- meson.build | 14 +- 9 files changed, 539 insertions(+), 740 deletions(-) diff --git a/4J.Input/4J_Input.cpp b/4J.Input/4J_Input.cpp index b0946a810..5f1c319a4 100644 --- a/4J.Input/4J_Input.cpp +++ b/4J.Input/4J_Input.cpp @@ -1,536 +1,265 @@ -// 4J_Input.cpp - GLFW keyboard + mouse input for the Linux port -// Replaces the SDL2 oldimpl with GLFW equivalents. -// Uses glfwGetCurrentContext() to get the window the render manager created, -// avoiding a coupling dependency on 4J_Render.h. - #include "4J_Input.h" #include "../Minecraft.Client/Platform/Common/App_enums.h" -#include +#include "../4J.Render/4J_Render.h" +#include #include #include #include C_4JInput InputManager; -// --------------------------------------------------------------------------- -// State - all static to avoid adding new fields to C_4JInput -// --------------------------------------------------------------------------- - -static const int KEY_COUNT = GLFW_KEY_LAST + 1; // 349 on GLFW 3 - +static const int KEY_COUNT = SDL_NUM_SCANCODES; +static const float MOUSE_SCALE = 0.015f; +// Vars +static bool s_sdlInitialized = false; static bool s_keysCurrent[KEY_COUNT] = {}; -static bool s_keysPrev[KEY_COUNT] = {}; - +static bool s_keysPrev [KEY_COUNT] = {}; static bool s_mouseLeftCurrent = false, s_mouseLeftPrev = false; static bool s_mouseRightCurrent = false, s_mouseRightPrev = false; +static bool s_menuDisplayed[4] = {}; +static bool s_prevMenuDisplayed = false; +static bool s_snapTaken = false, s_scrollSnapTaken = false; +static float s_accumRelX = 0, s_accumRelY = 0; +static float s_snapRelX = 0, s_snapRelY = 0; +static float s_scrollAccum = 0, s_scrollSnap = 0; -// Accumulated cursor delta from the GLFW cursor-pos callback. -// Snapshotted into s_frameRelX/Y at Tick() start, then reset to 0. -static double s_lastCursorX = 0.0, s_lastCursorY = 0.0; -static bool s_cursorInitialized = false; -static float s_mouseAccumX = 0.0f, s_mouseAccumY = 0.0f; // callback accumulator -static float s_frameRelX = 0.0f, s_frameRelY = 0.0f; // per-frame snapshot - -// Scroll wheel -static float s_scrollAccum = 0.0f; // callback accumulator -static float s_scrollFrame = 0.0f; // current frame snapshot -static float s_scrollPrevFrame = 0.0f; - -// Mouse lock / menu state -static bool s_mouseLocked = false; -static bool s_menuDisplayed[4] = {}; -static bool s_prevMenuDisplayed = true; // start as "in menu" so auto-lock triggers after first frame - -// Sensitivity: scales raw pixel delta before sqrt-compression -// Smaller value = less mouse movement per pixel -static const float MOUSE_SCALE = 0.012f; - -// --------------------------------------------------------------------------- -// GLFW window (obtained lazily via glfwGetCurrentContext on the render thread) -// --------------------------------------------------------------------------- -static GLFWwindow *s_inputWindow = nullptr; - -static GLFWwindow *getWindow() { - if (!s_inputWindow) { - s_inputWindow = glfwGetCurrentContext(); - } - return s_inputWindow; -} - -// --------------------------------------------------------------------------- -// GLFW callbacks -// --------------------------------------------------------------------------- - -static void onCursorPos(GLFWwindow * /*w*/, double x, double y) { - if (s_cursorInitialized) { - s_mouseAccumX += (float)(x - s_lastCursorX); - s_mouseAccumY += (float)(y - s_lastCursorY); - } else { - s_cursorInitialized = true; - } - s_lastCursorX = x; - s_lastCursorY = y; -} - -static void onScroll(GLFWwindow * /*w*/, double /*xoffset*/, double yoffset) { - s_scrollAccum += (float)yoffset; -} - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -static inline bool KDown(int key) { - return (key >= 0 && key < KEY_COUNT) ? s_keysCurrent[key] : false; -} -static inline bool KPressed(int key) { - return (key >= 0 && key < KEY_COUNT) ? (s_keysCurrent[key] && !s_keysPrev[key]) : false; -} -static inline bool KReleased(int key) { - return (key >= 0 && key < KEY_COUNT) ? (!s_keysCurrent[key] && s_keysPrev[key]) : false; -} - -static inline bool MouseLDown() { return s_mouseLeftCurrent; } -static inline bool MouseLPressed() { return s_mouseLeftCurrent && !s_mouseLeftPrev; } -static inline bool MouseLReleased() { return !s_mouseLeftCurrent && s_mouseLeftPrev; } -static inline bool MouseRDown() { return s_mouseRightCurrent; } -static inline bool MouseRPressed() { return s_mouseRightCurrent && !s_mouseRightPrev; } -static inline bool MouseRReleased() { return !s_mouseRightCurrent && s_mouseRightPrev; } - -static inline bool WheelUp() { return s_scrollFrame > 0.1f; } -static inline bool WheelDown() { return s_scrollFrame < -0.1f; } -static inline bool WheelUpEdge() { return s_scrollFrame > 0.1f && s_scrollPrevFrame <= 0.1f; } -static inline bool WheelDownEdge() { return s_scrollFrame < -0.1f && s_scrollPrevFrame >= -0.1f; } - -// Keys to snapshot each Tick (avoid iterating all 349 entries) +// We set all the watched keys +// I don't know if I'll need to change this if we add chat support soon. static const int s_watchedKeys[] = { - GLFW_KEY_W, GLFW_KEY_A, GLFW_KEY_S, GLFW_KEY_D, - GLFW_KEY_SPACE, GLFW_KEY_LEFT_SHIFT, GLFW_KEY_RIGHT_SHIFT, - GLFW_KEY_E, GLFW_KEY_Q, GLFW_KEY_F, - GLFW_KEY_ESCAPE, GLFW_KEY_ENTER, - GLFW_KEY_F3, GLFW_KEY_F5, - GLFW_KEY_UP, GLFW_KEY_DOWN, GLFW_KEY_LEFT, GLFW_KEY_RIGHT, - GLFW_KEY_PAGE_UP, GLFW_KEY_PAGE_DOWN, - GLFW_KEY_TAB, - GLFW_KEY_LEFT_CONTROL, GLFW_KEY_RIGHT_CONTROL, - GLFW_KEY_1, GLFW_KEY_2, GLFW_KEY_3, GLFW_KEY_4, GLFW_KEY_5, - GLFW_KEY_6, GLFW_KEY_7, GLFW_KEY_8, GLFW_KEY_9, + SDL_SCANCODE_W, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D, + SDL_SCANCODE_SPACE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT, + SDL_SCANCODE_E, SDL_SCANCODE_Q, SDL_SCANCODE_F, + SDL_SCANCODE_C, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_RETURN, + SDL_SCANCODE_F3, SDL_SCANCODE_F5, + SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, + SDL_SCANCODE_PAGEUP, SDL_SCANCODE_PAGEDOWN, + SDL_SCANCODE_TAB, SDL_SCANCODE_LCTRL, SDL_SCANCODE_RCTRL, + SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, + SDL_SCANCODE_5, SDL_SCANCODE_6, SDL_SCANCODE_7, SDL_SCANCODE_8, + SDL_SCANCODE_9, }; static const int s_watchedKeyCount = (int)(sizeof(s_watchedKeys) / sizeof(s_watchedKeys[0])); -// --------------------------------------------------------------------------- -// C_4JInput::Initialise -// --------------------------------------------------------------------------- -void C_4JInput::Initialise(int /*iInputStateC*/, unsigned char /*ucMapC*/, - unsigned char /*ucActionC*/, unsigned char /*ucMenuActionC*/) { - memset(s_keysCurrent, 0, sizeof(s_keysCurrent)); - memset(s_keysPrev, 0, sizeof(s_keysPrev)); - memset(s_menuDisplayed, 0, sizeof(s_menuDisplayed)); +static inline bool KDown (int sc) { return (sc > 0 && sc < KEY_COUNT) ? s_keysCurrent[sc] : false; } +static inline bool KPressed (int sc) { return (sc > 0 && sc < KEY_COUNT) ? !s_keysPrev[sc] && s_keysCurrent[sc] : false; } +static inline bool KReleased(int sc) { return (sc > 0 && sc < KEY_COUNT) ? s_keysPrev[sc] && !s_keysCurrent[sc] : false; } - s_mouseLeftCurrent = s_mouseLeftPrev = false; - s_mouseRightCurrent = s_mouseRightPrev = false; - s_mouseAccumX = s_mouseAccumY = 0.0f; - s_frameRelX = s_frameRelY = 0.0f; - s_scrollAccum = s_scrollFrame = s_scrollPrevFrame = 0.0f; - s_mouseLocked = false; - s_prevMenuDisplayed = true; // triggers auto-lock once game leaves first menu - s_cursorInitialized = false; +static inline bool MouseLDown () { return s_mouseLeftCurrent; } +static inline bool MouseLPressed () { return s_mouseLeftCurrent && !s_mouseLeftPrev; } +static inline bool MouseLReleased() { return !s_mouseLeftCurrent && s_mouseLeftPrev; } +static inline bool MouseRDown () { return s_mouseRightCurrent; } +static inline bool MouseRPressed () { return s_mouseRightCurrent && !s_mouseRightPrev; } +static inline bool MouseRReleased() { return !s_mouseRightCurrent && s_mouseRightPrev; } - GLFWwindow *w = getWindow(); - if (w) { - glfwSetCursorPosCallback(w, onCursorPos); - glfwSetScrollCallback(w, onScroll); - // NOTE: GLFW_RAW_MOUSE_MOTION must only be set when cursor mode is - // GLFW_CURSOR_DISABLED (Wayland zwp_relative_pointer_v1 requirement). - // It is activated at the cursor-lock call sites below in Tick(). - } - - printf("[4J_Input] GLFW input initialised\n"); - printf(" WASD=move Mouse=look LMB=attack RMB=use\n"); - printf(" Space=jump LShift=sneak E=inventory Q=drop Esc=pause\n"); - printf(" F5=3rd-person F3=debug Scroll=hotbar\n"); - fflush(stdout); +// Snap the scroll value so that it doesn't go crazy! +static float ScrollSnap() { + if (!s_scrollSnapTaken) { s_scrollSnap = s_scrollAccum; s_scrollAccum = 0; s_scrollSnapTaken = true; } + return s_scrollSnap; } -// --------------------------------------------------------------------------- -// C_4JInput::Tick (called once per frame, BEFORE Present / glfwPollEvents) -// --------------------------------------------------------------------------- -void C_4JInput::Tick(void) { - GLFWwindow *w = getWindow(); - if (!w) return; +static void TakeSnapIfNeeded() { + if (!s_snapTaken) { + s_snapRelX = s_accumRelX; s_accumRelX = 0; + s_snapRelY = s_accumRelY; s_accumRelY = 0; + s_snapTaken = true; + } +} + +// We initialize the SDL input +void C_4JInput::Initialise(int, unsigned char, unsigned char, unsigned char) { + if (!s_sdlInitialized) { + s_sdlInitialized = SDL_Init(SDL_INIT_VIDEO) == 0; + } + + memset(s_keysCurrent, 0, sizeof(s_keysCurrent)); + memset(s_keysPrev, 0, sizeof(s_keysPrev)); + memset(s_menuDisplayed, 0, sizeof(s_menuDisplayed)); + + s_mouseLeftCurrent = s_mouseLeftPrev = s_mouseRightCurrent = s_mouseRightPrev = false; + s_accumRelX = s_accumRelY = s_snapRelX = s_snapRelY = 0; + s_scrollAccum = s_scrollSnap = 0; + s_snapTaken = s_scrollSnapTaken = s_prevMenuDisplayed = false; + + if (s_sdlInitialized) { + SDL_SetRelativeMouseMode(SDL_TRUE); + } +} + +// Each tick we update the input state by polling SDL, this is where we get the kbd and mouse state. +void C_4JInput::Tick() { + if (!s_sdlInitialized) return; - // 1. Save previous frame memcpy(s_keysPrev, s_keysCurrent, sizeof(s_keysCurrent)); s_mouseLeftPrev = s_mouseLeftCurrent; s_mouseRightPrev = s_mouseRightCurrent; - s_scrollPrevFrame = s_scrollFrame; + s_snapTaken = s_scrollSnapTaken = false; + s_snapRelX = s_snapRelY = s_scrollSnap = 0; - // 2. Snapshot current keyboard state for watched keys - for (int i = 0; i < s_watchedKeyCount; i++) { - int k = s_watchedKeys[i]; - s_keysCurrent[k] = (glfwGetKey(w, k) == GLFW_PRESS); + SDL_PumpEvents(); + // Keyboard State. + const Uint8 *state = SDL_GetKeyboardState(NULL); + for (int i = 0; i < s_watchedKeyCount; ++i) { + int sc = s_watchedKeys[i]; + if (sc > 0 && sc < KEY_COUNT) s_keysCurrent[sc] = state[sc] != 0; } + // Scroll Wheel events. It's a bit weird, but it works. + SDL_Event ev; + while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEWHEEL, SDL_MOUSEWHEEL) > 0) + s_scrollAccum += (float)ev.wheel.y; - // 3. Snapshot and reset scroll accumulator - s_scrollFrame = s_scrollAccum; - s_scrollAccum = 0.0f; - - // 4. Mouse-lock management based on menu display state - bool menuNow = s_menuDisplayed[0]; - - if (menuNow && s_mouseLocked) { - // Re-entered a menu → release mouse cursor - s_mouseLocked = false; - if (glfwRawMouseMotionSupported()) - glfwSetInputMode(w, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE); - glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - // Discard stale delta so the view doesn't jerk on re-lock - s_mouseAccumX = s_mouseAccumY = 0.0f; - s_cursorInitialized = false; + int dx = 0, dy = 0; + while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) { + dx += ev.motion.xrel; dy += ev.motion.yrel; } - if (!menuNow && s_prevMenuDisplayed && !s_mouseLocked) { - // Left the menu → lock mouse for look control - s_mouseLocked = true; - glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - // Enable raw (un-accelerated) relative motion now that cursor is disabled. - // On Wayland this activates zwp_relative_pointer_v1 for sub-pixel precise - // mouse deltas; on X11 it bypasses the compositor acceleration curve. - if (glfwRawMouseMotionSupported()) - glfwSetInputMode(w, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); - s_mouseAccumX = s_mouseAccumY = 0.0f; - s_cursorInitialized = false; - } - s_prevMenuDisplayed = menuNow; + // Mouse Position & Button Events. + if (dx == 0 && dy == 0 && SDL_GetRelativeMouseMode()) + SDL_GetRelativeMouseState(&dx, &dy); + s_accumRelX += (float)dx; + s_accumRelY += (float)dy; - // 5. Snapshot and reset mouse delta from callback - s_frameRelX = s_mouseAccumX; - s_frameRelY = s_mouseAccumY; - s_mouseAccumX = s_mouseAccumY = 0.0f; + Uint32 btns = SDL_GetMouseState(NULL, NULL); + s_mouseLeftCurrent = (btns & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; + s_mouseRightCurrent = (btns & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; - // 6. Mouse buttons (only meaningful when locked in-game) - if (s_mouseLocked) { - s_mouseLeftCurrent = (glfwGetMouseButton(w, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); - s_mouseRightCurrent = (glfwGetMouseButton(w, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS); - } else { - // Not locked. Allow a left-click to re-lock (if not in a menu) - bool lclick = (glfwGetMouseButton(w, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); - if (!menuNow && lclick) { - s_mouseLocked = true; - glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - if (glfwRawMouseMotionSupported()) - glfwSetInputMode(w, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); - s_mouseAccumX = s_mouseAccumY = 0.0f; - s_cursorInitialized = false; - } - s_mouseLeftCurrent = false; - s_mouseRightCurrent = false; - s_frameRelX = s_frameRelY = 0.0f; + if (!SDL_GetKeyboardFocus()) { + SDL_Window *mf = SDL_GetMouseFocus(); + if (mf) { SDL_RaiseWindow(mf); SDL_SetWindowGrab(mf, SDL_TRUE); } } } +// been a while i haven't used cases +#define ACTION_CASES(FN) \ +case ACTION_MENU_UP: return FN(SDL_SCANCODE_UP); \ +case ACTION_MENU_DOWN: return FN(SDL_SCANCODE_DOWN); \ +case ACTION_MENU_LEFT: return FN(SDL_SCANCODE_LEFT); \ +case ACTION_MENU_RIGHT: return FN(SDL_SCANCODE_RIGHT); \ +case ACTION_MENU_PAGEUP: return FN(SDL_SCANCODE_PAGEUP); \ +case ACTION_MENU_PAGEDOWN: return FN(SDL_SCANCODE_PAGEDOWN); \ +case ACTION_MENU_OK: return FN(SDL_SCANCODE_RETURN); \ +case ACTION_MENU_CANCEL: return FN(SDL_SCANCODE_ESCAPE); \ +case MINECRAFT_ACTION_JUMP: return FN(SDL_SCANCODE_SPACE); \ +case MINECRAFT_ACTION_FORWARD: return FN(SDL_SCANCODE_W); \ +case MINECRAFT_ACTION_BACKWARD: return FN(SDL_SCANCODE_S); \ +case MINECRAFT_ACTION_LEFT: return FN(SDL_SCANCODE_A); \ +case MINECRAFT_ACTION_RIGHT: return FN(SDL_SCANCODE_D); \ +case MINECRAFT_ACTION_INVENTORY: return FN(SDL_SCANCODE_E); \ +case MINECRAFT_ACTION_PAUSEMENU: return FN(SDL_SCANCODE_ESCAPE); \ +case MINECRAFT_ACTION_DROP: return FN(SDL_SCANCODE_Q); \ +case MINECRAFT_ACTION_CRAFTING: return FN(SDL_SCANCODE_C); \ +case MINECRAFT_ACTION_RENDER_THIRD_PERSON:return FN(SDL_SCANCODE_F5); \ +case MINECRAFT_ACTION_GAME_INFO: return FN(SDL_SCANCODE_F3); \ +case MINECRAFT_ACTION_DPAD_LEFT: return FN(SDL_SCANCODE_LEFT); \ +case MINECRAFT_ACTION_DPAD_RIGHT: return FN(SDL_SCANCODE_RIGHT); \ +case MINECRAFT_ACTION_DPAD_UP: return FN(SDL_SCANCODE_UP); \ +case MINECRAFT_ACTION_DPAD_DOWN: return FN(SDL_SCANCODE_DOWN); \ +default: return false; -// --------------------------------------------------------------------------- -// ButtonDown – is action held this frame? -// --------------------------------------------------------------------------- -bool C_4JInput::ButtonDown(int /*iPad*/, unsigned char ucAction) { +// The part that handles Pressing a button. +bool C_4JInput::ButtonDown(int iPad, unsigned char ucAction) { + if (iPad != 0) return false; + if (ucAction == 255) { + for (int i = 0; i < s_watchedKeyCount; ++i) + if (s_keysCurrent[s_watchedKeys[i]]) return true; + return s_mouseLeftCurrent || s_mouseRightCurrent; + } switch (ucAction) { - // ---- Menu navigation ---- - case ACTION_MENU_UP: return KDown(GLFW_KEY_UP) || KDown(GLFW_KEY_W); - case ACTION_MENU_DOWN: return KDown(GLFW_KEY_DOWN) || KDown(GLFW_KEY_S); - case ACTION_MENU_LEFT: return KDown(GLFW_KEY_LEFT) || KDown(GLFW_KEY_A); - case ACTION_MENU_RIGHT: return KDown(GLFW_KEY_RIGHT) || KDown(GLFW_KEY_D); - - case ACTION_MENU_A: - case ACTION_MENU_OK: - return KDown(GLFW_KEY_ENTER) || KDown(GLFW_KEY_SPACE); - - case ACTION_MENU_B: - case ACTION_MENU_CANCEL: - return KDown(GLFW_KEY_ESCAPE); - - case ACTION_MENU_X: return KDown(GLFW_KEY_F); - case ACTION_MENU_Y: return KDown(GLFW_KEY_E); - - case ACTION_MENU_PAGEUP: return KDown(GLFW_KEY_PAGE_UP) || KDown(GLFW_KEY_LEFT_SHIFT); - case ACTION_MENU_PAGEDOWN: return KDown(GLFW_KEY_PAGE_DOWN) || KDown(GLFW_KEY_RIGHT_SHIFT); - case ACTION_MENU_RIGHT_SCROLL: return KDown(GLFW_KEY_RIGHT_SHIFT) || WheelUp(); - case ACTION_MENU_LEFT_SCROLL: return KDown(GLFW_KEY_LEFT_SHIFT) || WheelDown(); - - case ACTION_MENU_STICK_PRESS: return false; - case ACTION_MENU_OTHER_STICK_PRESS: return false; - case ACTION_MENU_OTHER_STICK_UP: return KDown(GLFW_KEY_UP); - case ACTION_MENU_OTHER_STICK_DOWN: return KDown(GLFW_KEY_DOWN); - case ACTION_MENU_OTHER_STICK_LEFT: return KDown(GLFW_KEY_LEFT); - case ACTION_MENU_OTHER_STICK_RIGHT: return KDown(GLFW_KEY_RIGHT); - - case ACTION_MENU_PAUSEMENU: return KDown(GLFW_KEY_ESCAPE); - - // ---- Minecraft in-game ---- - case MINECRAFT_ACTION_JUMP: return KDown(GLFW_KEY_SPACE); - case MINECRAFT_ACTION_FORWARD: return KDown(GLFW_KEY_W); - case MINECRAFT_ACTION_BACKWARD: return KDown(GLFW_KEY_S); - case MINECRAFT_ACTION_LEFT: return KDown(GLFW_KEY_A); - case MINECRAFT_ACTION_RIGHT: return KDown(GLFW_KEY_D); - - // Look axes are handled by analog stick RX/RY (mouse) - case MINECRAFT_ACTION_LOOK_LEFT: return false; - case MINECRAFT_ACTION_LOOK_RIGHT: return false; - case MINECRAFT_ACTION_LOOK_UP: return false; - case MINECRAFT_ACTION_LOOK_DOWN: return false; - - case MINECRAFT_ACTION_USE: return MouseRDown(); - case MINECRAFT_ACTION_ACTION: return MouseLDown(); - case MINECRAFT_ACTION_LEFT_SCROLL: return WheelDown(); - case MINECRAFT_ACTION_RIGHT_SCROLL: return WheelUp(); - - case MINECRAFT_ACTION_INVENTORY: return KDown(GLFW_KEY_E); - case MINECRAFT_ACTION_PAUSEMENU: return KDown(GLFW_KEY_ESCAPE); - case MINECRAFT_ACTION_DROP: return KDown(GLFW_KEY_Q); - case MINECRAFT_ACTION_SNEAK_TOGGLE: return KDown(GLFW_KEY_LEFT_SHIFT); - case MINECRAFT_ACTION_CRAFTING: return KDown(GLFW_KEY_F); - case MINECRAFT_ACTION_RENDER_THIRD_PERSON: return KDown(GLFW_KEY_F5); - case MINECRAFT_ACTION_GAME_INFO: return KDown(GLFW_KEY_F3); - - case MINECRAFT_ACTION_DPAD_LEFT: return KDown(GLFW_KEY_LEFT); - case MINECRAFT_ACTION_DPAD_RIGHT: return KDown(GLFW_KEY_RIGHT); - case MINECRAFT_ACTION_DPAD_UP: return KDown(GLFW_KEY_UP); - case MINECRAFT_ACTION_DPAD_DOWN: return KDown(GLFW_KEY_DOWN); - - default: return false; + case MINECRAFT_ACTION_ACTION: return MouseLDown() || KDown(SDL_SCANCODE_RETURN); + case MINECRAFT_ACTION_USE: return MouseRDown() || KDown(SDL_SCANCODE_F); + case MINECRAFT_ACTION_SNEAK_TOGGLE: return KDown(SDL_SCANCODE_LSHIFT) || KDown(SDL_SCANCODE_RSHIFT) || KDown(SDL_SCANCODE_LCTRL) || KDown(SDL_SCANCODE_RCTRL); + case MINECRAFT_ACTION_LEFT_SCROLL: + case ACTION_MENU_LEFT_SCROLL: return ScrollSnap() > 0.1f; + case MINECRAFT_ACTION_RIGHT_SCROLL: + case ACTION_MENU_RIGHT_SCROLL: return ScrollSnap() < -0.1f; + ACTION_CASES(KDown) } } - -// --------------------------------------------------------------------------- -// ButtonPressed – rising edge (press event this frame) -// --------------------------------------------------------------------------- -bool C_4JInput::ButtonPressed(int /*iPad*/, unsigned char ucAction) { +// The part that handles completing the action of pressing a button. +bool C_4JInput::ButtonPressed(int iPad, unsigned char ucAction) { + if (iPad != 0 || ucAction == 255) return false; switch (ucAction) { - case ACTION_MENU_UP: return KPressed(GLFW_KEY_UP) || KPressed(GLFW_KEY_W); - case ACTION_MENU_DOWN: return KPressed(GLFW_KEY_DOWN) || KPressed(GLFW_KEY_S); - case ACTION_MENU_LEFT: return KPressed(GLFW_KEY_LEFT) || KPressed(GLFW_KEY_A); - case ACTION_MENU_RIGHT: return KPressed(GLFW_KEY_RIGHT) || KPressed(GLFW_KEY_D); - - case ACTION_MENU_A: - case ACTION_MENU_OK: - return KPressed(GLFW_KEY_ENTER) || KPressed(GLFW_KEY_SPACE); - - case ACTION_MENU_B: - case ACTION_MENU_CANCEL: - return KPressed(GLFW_KEY_ESCAPE); - - case ACTION_MENU_X: return KPressed(GLFW_KEY_F); - case ACTION_MENU_Y: return KPressed(GLFW_KEY_E); - - case ACTION_MENU_PAGEUP: return KPressed(GLFW_KEY_PAGE_UP) || KPressed(GLFW_KEY_LEFT_SHIFT); - case ACTION_MENU_PAGEDOWN: return KPressed(GLFW_KEY_PAGE_DOWN) || KPressed(GLFW_KEY_RIGHT_SHIFT); - case ACTION_MENU_RIGHT_SCROLL: return KPressed(GLFW_KEY_RIGHT_SHIFT) || WheelUpEdge(); - case ACTION_MENU_LEFT_SCROLL: return KPressed(GLFW_KEY_LEFT_SHIFT) || WheelDownEdge(); - - case ACTION_MENU_STICK_PRESS: return false; - case ACTION_MENU_OTHER_STICK_PRESS: return false; - case ACTION_MENU_OTHER_STICK_UP: return KPressed(GLFW_KEY_UP); - case ACTION_MENU_OTHER_STICK_DOWN: return KPressed(GLFW_KEY_DOWN); - case ACTION_MENU_OTHER_STICK_LEFT: return KPressed(GLFW_KEY_LEFT); - case ACTION_MENU_OTHER_STICK_RIGHT: return KPressed(GLFW_KEY_RIGHT); - case ACTION_MENU_PAUSEMENU: return KPressed(GLFW_KEY_ESCAPE); - - case MINECRAFT_ACTION_JUMP: return KPressed(GLFW_KEY_SPACE); - case MINECRAFT_ACTION_FORWARD: return KPressed(GLFW_KEY_W); - case MINECRAFT_ACTION_BACKWARD: return KPressed(GLFW_KEY_S); - case MINECRAFT_ACTION_LEFT: return KPressed(GLFW_KEY_A); - case MINECRAFT_ACTION_RIGHT: return KPressed(GLFW_KEY_D); - - case MINECRAFT_ACTION_LOOK_LEFT: - case MINECRAFT_ACTION_LOOK_RIGHT: - case MINECRAFT_ACTION_LOOK_UP: - case MINECRAFT_ACTION_LOOK_DOWN: - return false; - - case MINECRAFT_ACTION_USE: return MouseRPressed(); - case MINECRAFT_ACTION_ACTION: return MouseLPressed(); - case MINECRAFT_ACTION_LEFT_SCROLL: return WheelDownEdge(); - case MINECRAFT_ACTION_RIGHT_SCROLL: return WheelUpEdge(); - - case MINECRAFT_ACTION_INVENTORY: return KPressed(GLFW_KEY_E); - case MINECRAFT_ACTION_PAUSEMENU: return KPressed(GLFW_KEY_ESCAPE); - case MINECRAFT_ACTION_DROP: return KPressed(GLFW_KEY_Q); - case MINECRAFT_ACTION_SNEAK_TOGGLE: return KPressed(GLFW_KEY_LEFT_SHIFT); - case MINECRAFT_ACTION_CRAFTING: return KPressed(GLFW_KEY_F); - case MINECRAFT_ACTION_RENDER_THIRD_PERSON: return KPressed(GLFW_KEY_F5); - case MINECRAFT_ACTION_GAME_INFO: return KPressed(GLFW_KEY_F3); - - case MINECRAFT_ACTION_DPAD_LEFT: return KPressed(GLFW_KEY_LEFT); - case MINECRAFT_ACTION_DPAD_RIGHT: return KPressed(GLFW_KEY_RIGHT); - case MINECRAFT_ACTION_DPAD_UP: return KPressed(GLFW_KEY_UP); - case MINECRAFT_ACTION_DPAD_DOWN: return KPressed(GLFW_KEY_DOWN); - - default: return false; + case MINECRAFT_ACTION_ACTION: return MouseLPressed() || KPressed(SDL_SCANCODE_RETURN); + case MINECRAFT_ACTION_USE: return MouseRPressed() || KPressed(SDL_SCANCODE_F); + case MINECRAFT_ACTION_SNEAK_TOGGLE: return KPressed(SDL_SCANCODE_LSHIFT) || KPressed(SDL_SCANCODE_RSHIFT) || KPressed(SDL_SCANCODE_LCTRL) || KPressed(SDL_SCANCODE_RCTRL); + case MINECRAFT_ACTION_LEFT_SCROLL: + case ACTION_MENU_LEFT_SCROLL: return ScrollSnap() > 0.1f; + case MINECRAFT_ACTION_RIGHT_SCROLL: + case ACTION_MENU_RIGHT_SCROLL: return ScrollSnap() < -0.1f; + ACTION_CASES(KPressed) } } - -// --------------------------------------------------------------------------- -// ButtonReleased – falling edge (released this frame) -// --------------------------------------------------------------------------- -bool C_4JInput::ButtonReleased(int /*iPad*/, unsigned char ucAction) { +// The part that handles Releasing a button. +bool C_4JInput::ButtonReleased(int iPad, unsigned char ucAction) { + if (iPad != 0 || ucAction == 255) return false; switch (ucAction) { - case ACTION_MENU_UP: return KReleased(GLFW_KEY_UP) || KReleased(GLFW_KEY_W); - case ACTION_MENU_DOWN: return KReleased(GLFW_KEY_DOWN) || KReleased(GLFW_KEY_S); - case ACTION_MENU_LEFT: return KReleased(GLFW_KEY_LEFT) || KReleased(GLFW_KEY_A); - case ACTION_MENU_RIGHT: return KReleased(GLFW_KEY_RIGHT) || KReleased(GLFW_KEY_D); - - case ACTION_MENU_A: - case ACTION_MENU_OK: - return KReleased(GLFW_KEY_ENTER) || KReleased(GLFW_KEY_SPACE); - - case ACTION_MENU_B: - case ACTION_MENU_CANCEL: - return KReleased(GLFW_KEY_ESCAPE); - - case ACTION_MENU_X: return KReleased(GLFW_KEY_F); - case ACTION_MENU_Y: return KReleased(GLFW_KEY_E); - - case ACTION_MENU_PAGEUP: return KReleased(GLFW_KEY_PAGE_UP) || KReleased(GLFW_KEY_LEFT_SHIFT); - case ACTION_MENU_PAGEDOWN: return KReleased(GLFW_KEY_PAGE_DOWN) || KReleased(GLFW_KEY_RIGHT_SHIFT); - case ACTION_MENU_RIGHT_SCROLL: return KReleased(GLFW_KEY_RIGHT_SHIFT); - case ACTION_MENU_LEFT_SCROLL: return KReleased(GLFW_KEY_LEFT_SHIFT); - - case ACTION_MENU_STICK_PRESS: return false; - case ACTION_MENU_OTHER_STICK_PRESS: return false; - case ACTION_MENU_OTHER_STICK_UP: return KReleased(GLFW_KEY_UP); - case ACTION_MENU_OTHER_STICK_DOWN: return KReleased(GLFW_KEY_DOWN); - case ACTION_MENU_OTHER_STICK_LEFT: return KReleased(GLFW_KEY_LEFT); - case ACTION_MENU_OTHER_STICK_RIGHT: return KReleased(GLFW_KEY_RIGHT); - case ACTION_MENU_PAUSEMENU: return KReleased(GLFW_KEY_ESCAPE); - - case MINECRAFT_ACTION_JUMP: return KReleased(GLFW_KEY_SPACE); - case MINECRAFT_ACTION_FORWARD: return KReleased(GLFW_KEY_W); - case MINECRAFT_ACTION_BACKWARD: return KReleased(GLFW_KEY_S); - case MINECRAFT_ACTION_LEFT: return KReleased(GLFW_KEY_A); - case MINECRAFT_ACTION_RIGHT: return KReleased(GLFW_KEY_D); - - case MINECRAFT_ACTION_LOOK_LEFT: - case MINECRAFT_ACTION_LOOK_RIGHT: - case MINECRAFT_ACTION_LOOK_UP: - case MINECRAFT_ACTION_LOOK_DOWN: - return false; - - case MINECRAFT_ACTION_USE: return MouseRReleased(); - case MINECRAFT_ACTION_ACTION: return MouseLReleased(); - case MINECRAFT_ACTION_LEFT_SCROLL: return false; // scroll wheel has no "release" - case MINECRAFT_ACTION_RIGHT_SCROLL: return false; - - case MINECRAFT_ACTION_INVENTORY: return KReleased(GLFW_KEY_E); - case MINECRAFT_ACTION_PAUSEMENU: return KReleased(GLFW_KEY_ESCAPE); - case MINECRAFT_ACTION_DROP: return KReleased(GLFW_KEY_Q); - case MINECRAFT_ACTION_SNEAK_TOGGLE: return KReleased(GLFW_KEY_LEFT_SHIFT); - case MINECRAFT_ACTION_CRAFTING: return KReleased(GLFW_KEY_F); - case MINECRAFT_ACTION_RENDER_THIRD_PERSON: return KReleased(GLFW_KEY_F5); - case MINECRAFT_ACTION_GAME_INFO: return KReleased(GLFW_KEY_F3); - - case MINECRAFT_ACTION_DPAD_LEFT: return KReleased(GLFW_KEY_LEFT); - case MINECRAFT_ACTION_DPAD_RIGHT: return KReleased(GLFW_KEY_RIGHT); - case MINECRAFT_ACTION_DPAD_UP: return KReleased(GLFW_KEY_UP); - case MINECRAFT_ACTION_DPAD_DOWN: return KReleased(GLFW_KEY_DOWN); - - default: return false; + case MINECRAFT_ACTION_ACTION: return MouseLReleased() || KReleased(SDL_SCANCODE_RETURN); + case MINECRAFT_ACTION_USE: return MouseRReleased() || KReleased(SDL_SCANCODE_F); + case MINECRAFT_ACTION_SNEAK_TOGGLE: return KReleased(SDL_SCANCODE_LSHIFT) || KReleased(SDL_SCANCODE_RSHIFT) || KReleased(SDL_SCANCODE_LCTRL) || KReleased(SDL_SCANCODE_RCTRL); + case MINECRAFT_ACTION_LEFT_SCROLL: + case ACTION_MENU_LEFT_SCROLL: + case MINECRAFT_ACTION_RIGHT_SCROLL: + case ACTION_MENU_RIGHT_SCROLL: return false; + ACTION_CASES(KReleased) } } -// --------------------------------------------------------------------------- -// GetValue – returns 1 if action held, 0 otherwise -// --------------------------------------------------------------------------- -unsigned int C_4JInput::GetValue(int iPad, unsigned char ucAction, bool /*bRepeat*/) { +unsigned int C_4JInput::GetValue(int iPad, unsigned char ucAction, bool) { return ButtonDown(iPad, ucAction) ? 1u : 0u; } - -// --------------------------------------------------------------------------- -// Analog sticks -// -// Left stick = WASD keyboard (±1.0) -// Right stick = mouse delta with sqrt-compression to linearise the quadratic -// look-speed formula used by the console game code: -// turnSpeed = rx * |rx| * 50 * sensitivity -// Passing sqrt(|raw|)*sign(raw) makes turn speed proportional -// to raw pixel delta, giving a natural mouse feel. -// --------------------------------------------------------------------------- - -float C_4JInput::GetJoypadStick_LX(int /*iPad*/, bool /*bCheckMenuDisplay*/) { - // Return 0 while cursor is still in menu mode so Input::tick()'s - // lReset guard sees a zero-stick frame during the menu->game transition. - // Once s_mouseLocked becomes true (cursor captured for gameplay), return - // the real WASD values. - if (!s_mouseLocked) return 0.0f; - float v = 0.0f; - if (KDown(GLFW_KEY_A)) v -= 1.0f; - if (KDown(GLFW_KEY_D)) v += 1.0f; - return v; +// Left stick movement, the one that moves the player around or selects menu options. (Soon be tested.) +float C_4JInput::GetJoypadStick_LX(int, bool) { + return (KDown(SDL_SCANCODE_D) ? 1.f : 0.f) - (KDown(SDL_SCANCODE_A) ? 1.f : 0.f); +} +float C_4JInput::GetJoypadStick_LY(int, bool) { + return (KDown(SDL_SCANCODE_W) ? 1.f : 0.f) - (KDown(SDL_SCANCODE_S) ? 1.f : 0.f); } -float C_4JInput::GetJoypadStick_LY(int /*iPad*/, bool /*bCheckMenuDisplay*/) { - if (!s_mouseLocked) return 0.0f; - float v = 0.0f; - if (KDown(GLFW_KEY_W)) v += 1.0f; // W = forward = negative Y on consoles - if (KDown(GLFW_KEY_S)) v -= 1.0f; - return v; +// We use mouse movement and convert it into a Right Stick output using logarithmic scaling +// This is the most important mouse part. Yet it's so small. +static float MouseAxis(float raw) { + if (fabsf(raw) < 0.0001f) return 0.f; // from glfw previous code + return (raw >= 0.f ? 1.f : -1.f) * sqrtf(fabsf(raw)); +} +// We apply the Stick movement on the R(Right) X(2D Position) +float C_4JInput::GetJoypadStick_RX(int, bool) { + if (!SDL_GetRelativeMouseMode()) return 0.f; + TakeSnapIfNeeded(); + return MouseAxis(s_snapRelX * MOUSE_SCALE); +} +// Bis. but with Y(2D Position) +float C_4JInput::GetJoypadStick_RY(int, bool) { + if (!SDL_GetRelativeMouseMode()) return 0.f; + TakeSnapIfNeeded(); + return MouseAxis(-s_snapRelY * MOUSE_SCALE); } -float C_4JInput::GetJoypadStick_RX(int /*iPad*/, bool /*bCheckMenuDisplay*/) { - if (!s_mouseLocked) return 0.0f; - float raw = s_frameRelX * MOUSE_SCALE; - float absRaw = fabsf(raw); - if (absRaw > 1.0f) absRaw = 1.0f; - if (absRaw < 0.0001f) return 0.0f; - return (raw >= 0.0f ? 1.0f : -1.0f) * sqrtf(absRaw); -} - -float C_4JInput::GetJoypadStick_RY(int /*iPad*/, bool /*bCheckMenuDisplay*/) { - if (!s_mouseLocked) return 0.0f; - float raw = -s_frameRelY * MOUSE_SCALE; - float absRaw = fabsf(raw); - if (absRaw > 1.0f) absRaw = 1.0f; - if (absRaw < 0.0001f) return 0.0f; - return (raw >= 0.0f ? 1.0f : -1.0f) * sqrtf(absRaw); -} - -// Left trigger = right mouse button (use/place) -// Right trigger = left mouse button (attack/destroy) -unsigned char C_4JInput::GetJoypadLTrigger(int /*iPad*/, bool /*bCheckMenuDisplay*/) { - return s_mouseRightCurrent ? 255 : 0; -} -unsigned char C_4JInput::GetJoypadRTrigger(int /*iPad*/, bool /*bCheckMenuDisplay*/) { - return s_mouseLeftCurrent ? 255 : 0; -} - -// --------------------------------------------------------------------------- -// Joypad map / sensitivity stubs (not meaningful for keyboard+mouse) -// --------------------------------------------------------------------------- -void C_4JInput::SetDeadzoneAndMovementRange(unsigned int /*uiDeadzone*/, unsigned int /*uiMovementRangeMax*/) {} -void C_4JInput::SetGameJoypadMaps(unsigned char /*ucMap*/, unsigned char /*ucAction*/, unsigned int /*uiActionVal*/) {} -unsigned int C_4JInput::GetGameJoypadMaps(unsigned char /*ucMap*/, unsigned char /*ucAction*/) { return 0; } -void C_4JInput::SetJoypadMapVal(int /*iPad*/, unsigned char /*ucMap*/) {} -unsigned char C_4JInput::GetJoypadMapVal(int /*iPad*/) { return 0; } -void C_4JInput::SetJoypadSensitivity(int /*iPad*/, float /*fSensitivity*/) {} -void C_4JInput::SetJoypadStickAxisMap(int /*iPad*/, unsigned int /*uiFrom*/, unsigned int /*uiTo*/) {} -void C_4JInput::SetJoypadStickTriggerMap(int /*iPad*/, unsigned int /*uiFrom*/, unsigned int /*uiTo*/) {} -void C_4JInput::SetKeyRepeatRate(float /*fRepeatDelaySecs*/, float /*fRepeatRateSecs*/) {} -void C_4JInput::SetDebugSequence(const char * /*chSequenceA*/, int(*/*Func*/)(LPVOID), LPVOID /*lpParam*/) {} -FLOAT C_4JInput::GetIdleSeconds(int /*iPad*/) { return 0.0f; } -bool C_4JInput::IsPadConnected(int iPad) { return iPad == 0; } // slot 0 = keyboard+mouse - +unsigned char C_4JInput::GetJoypadLTrigger(int, bool) { return s_mouseRightCurrent ? 255 : 0; } +unsigned char C_4JInput::GetJoypadRTrigger(int, bool) { return s_mouseLeftCurrent ? 255 : 0; } +// We detect if a Menu is visible on the player's screen to the mouse being stuck. void C_4JInput::SetMenuDisplayed(int iPad, bool bVal) { if (iPad >= 0 && iPad < 4) s_menuDisplayed[iPad] = bVal; + if (!s_sdlInitialized || bVal == s_prevMenuDisplayed) return; + SDL_SetRelativeMouseMode(bVal ? SDL_FALSE : SDL_TRUE); + s_prevMenuDisplayed = bVal; } -// --------------------------------------------------------------------------- -// Keyboard (text entry) / string verification stubs -// --------------------------------------------------------------------------- -EKeyboardResult C_4JInput::RequestKeyboard(LPCWSTR /*Title*/, LPCWSTR /*Text*/, DWORD /*dwPad*/, - UINT /*uiMaxChars*/, - int(*/*Func*/)(LPVOID, const bool), LPVOID /*lpParam*/, - C_4JInput::EKeyboardMode /*eMode*/) { - return EKeyboard_Cancelled; -} -void C_4JInput::GetText(uint16_t *UTF16String) { if (UTF16String) UTF16String[0] = 0; } -bool C_4JInput::VerifyStrings(WCHAR ** /*pwStringA*/, int /*iStringC*/, - int(*/*Func*/)(LPVOID, STRING_VERIFY_RESPONSE *), LPVOID /*lpParam*/) { return true; } -void C_4JInput::CancelQueuedVerifyStrings(int(*/*Func*/)(LPVOID, STRING_VERIFY_RESPONSE *), LPVOID /*lpParam*/) {} -void C_4JInput::CancelAllVerifyInProgress(void) {} +void C_4JInput::SetDeadzoneAndMovementRange(unsigned int, unsigned int){} +void C_4JInput::SetGameJoypadMaps(unsigned char, unsigned char, unsigned int){} +unsigned int C_4JInput::GetGameJoypadMaps(unsigned char, unsigned char){ return 0; } +void C_4JInput::SetJoypadMapVal(int, unsigned char){} +unsigned char C_4JInput::GetJoypadMapVal(int){ return 0; } +void C_4JInput::SetJoypadSensitivity(int, float){} +void C_4JInput::SetJoypadStickAxisMap(int, unsigned int, unsigned int){} +void C_4JInput::SetJoypadStickTriggerMap(int, unsigned int, unsigned int){} +void C_4JInput::SetKeyRepeatRate(float, float){} +void C_4JInput::SetDebugSequence(const char*, int(*)(LPVOID), LPVOID){} +FLOAT C_4JInput::GetIdleSeconds(int){ return 0.f; } +bool C_4JInput::IsPadConnected(int iPad){ return iPad == 0; } + +// Silly check, we check if we have a keyboard. +EKeyboardResult C_4JInput::RequestKeyboard(LPCWSTR, LPCWSTR, DWORD, UINT, + int(*)(LPVOID, const bool), LPVOID, C_4JInput::EKeyboardMode) +{ return EKeyboard_Cancelled; } + +void C_4JInput::GetText(uint16_t *s){ if (s) s[0] = 0; } +bool C_4JInput::VerifyStrings(WCHAR**, int, int(*)(LPVOID, STRING_VERIFY_RESPONSE*), LPVOID){ return true; } +void C_4JInput::CancelQueuedVerifyStrings(int(*)(LPVOID, STRING_VERIFY_RESPONSE*), LPVOID){} +void C_4JInput::CancelAllVerifyInProgress(){} diff --git a/4J.Input/meson.build b/4J.Input/meson.build index abad05d4e..3a2cb9ffa 100644 --- a/4J.Input/meson.build +++ b/4J.Input/meson.build @@ -15,7 +15,11 @@ lib_input = static_library('4J_Input', ], ) +# We import SDL2 but not SDL3.. which is a bit sad, +sdl_dep = dependency('sdl2', required : false) + input_dep = declare_dependency( link_with : lib_input, + dependencies : [sdl_dep], include_directories : include_directories('.'), ) diff --git a/4J.Render/4J_Render.cpp b/4J.Render/4J_Render.cpp index 351532d4f..c16920e82 100644 --- a/4J.Render/4J_Render.cpp +++ b/4J.Render/4J_Render.cpp @@ -1,14 +1,11 @@ - -// TODO: ADD BETTER COMMENTS. #include "4J_Render.h" #include #include // getenv -#define GL_GLEXT_PROTOTYPES #include #include #include -#include +#include #include #include #include @@ -18,38 +15,53 @@ C4JRender RenderManager; -static GLFWwindow *s_window = nullptr; +// Hello SDL! +static SDL_Window *s_window = nullptr; +static SDL_GLContext s_glContext = nullptr; +static bool s_shouldClose = false; static int s_textureLevels = 1; -static int s_windowWidth = 1280; // updated to actual framebuffer size each frame -static int s_windowHeight = 720; -static int s_reqWidth = 0; // 0 = auto-detect from primary monitor +static int s_windowWidth = 0; +static int s_windowHeight = 0; + +// We set Window size with the monitor's res, so that I can get rid of ugly values. +static void SetInitialWindowSize() +{ + int w = 0, h = 0; + if (SDL_Init(SDL_INIT_VIDEO) == 0) { + SDL_DisplayMode mode; + if (SDL_GetCurrentDisplayMode(0, &mode) == 0) { + w = (int)(mode.w * 0.4f); + h = (int)(mode.h * 0.4f); + } + } + if (w > 0 && h > 0) { s_windowWidth = w; s_windowHeight = h; } + else { s_windowWidth = 1280; s_windowHeight = 720; } +} +// (can't believe i had to rewrite this, i literally did it TODAY.) +static int s_reqWidth = 0; static int s_reqHeight = 0; +// When we'll have a settings system in order, we'll set bool to that value, right now it's hardcoded. static bool s_fullscreen = false; -// Thread-local storage for per-thread shared GL contexts. -// The main thread uses s_window directly; worker threads get invisible -// windows that share objects (textures, display lists) with s_window. static pthread_key_t s_glCtxKey; static pthread_once_t s_glCtxKeyOnce = PTHREAD_ONCE_INIT; static void makeGLCtxKey() { pthread_key_create(&s_glCtxKey, nullptr); } - -// Pre-created pool of shared contexts for worker threads - -// AMD drivers (especially on Linux/Mesa) can be very sensitive to the number of shared contexts -// and concurrent display list compilation. 8 was original, 4 was an attempt to fix it. -// 6 covers the 5 concurrent worker threads (update + 3x rebuild + main thread). -static const int MAX_SHARED_CONTEXTS = 6; -static GLFWwindow *s_sharedContexts[MAX_SHARED_CONTEXTS] = {}; +// Do not touch exactly this number | +static const int MAX_SHARED_CONTEXTS = 6; // <- this one, do not touch +static SDL_Window *s_sharedContextWindows[MAX_SHARED_CONTEXTS] = {}; +static SDL_GLContext s_sharedContexts[MAX_SHARED_CONTEXTS] = {}; static int s_sharedContextCount = 0; static int s_nextSharedContext = 0; static pthread_mutex_t s_sharedCtxMutex = PTHREAD_MUTEX_INITIALIZER; +// Tells thread to do Direct GL calls, just don't touch. +static pthread_mutex_t s_glCallMutex = PTHREAD_MUTEX_INITIALIZER; // Track which thread is the main (rendering) thread static pthread_t s_mainThread; static bool s_mainThreadSet = false; // viewport go brr -static void onFramebufferResize(GLFWwindow * /*win*/, int w, int h) +static void onFramebufferResize(int w, int h) { if (w < 1) w = 1; if (h < 1) h = 1; @@ -58,48 +70,62 @@ static void onFramebufferResize(GLFWwindow * /*win*/, int w, int h) ::glViewport(0, 0, w, h); } +// Initialize OpenGL & The SDL window. void C4JRender::Initialise() { - if (!glfwInit()) { - fprintf(stderr, "[4J_Render] Failed to initialise GLFW\n"); + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "[4J_Render] Failed to initialise SDL: %s\n", SDL_GetError()); return; } - GLFWmonitor *primaryMonitor = glfwGetPrimaryMonitor(); - const GLFWvidmode *mode = primaryMonitor ? glfwGetVideoMode(primaryMonitor) : nullptr; + SDL_DisplayMode mode; + int haveMode = (SDL_GetCurrentDisplayMode(0, &mode) == 0); if (s_reqWidth > 0 && s_reqHeight > 0) { s_windowWidth = s_reqWidth; s_windowHeight = s_reqHeight; - } else if (mode) { - s_windowWidth = mode->width; - s_windowHeight = mode->height; + } else if (haveMode) { + s_windowWidth = mode.w; + s_windowHeight = mode.h; } fprintf(stderr, "[4J_Render] Window %dx%d fullscreen=%s\n", s_windowWidth, s_windowHeight, s_fullscreen ? "yes" : "no"); fflush(stderr); - // opengl 2.1!!! - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); - glfwWindowHint(GLFW_DEPTH_BITS, 24); - glfwWindowHint(GLFW_STENCIL_BITS, 8); + // Setting the sdl_gl ver. Change in future incase we want to use shaders + // Yes i'm still using fixed functions, get mad at me + // I don't care. + // Im not gonna be rewriting the whole renderer.. AGAIN. ;w; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - GLFWmonitor *fsMonitor = s_fullscreen ? primaryMonitor : nullptr; - s_window = glfwCreateWindow(s_windowWidth, s_windowHeight, - "Minecraft Console Edition", fsMonitor, nullptr); + Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + if (s_fullscreen) winFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + s_window = SDL_CreateWindow("Minecraft Console Edition", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + s_windowWidth, s_windowHeight, + winFlags); if (!s_window) { - fprintf(stderr, "[4J_Render] Failed to create GLFW window\n"); - glfwTerminate(); + fprintf(stderr, "[4J_Render] Failed to create SDL window: %s\n", SDL_GetError()); + SDL_Quit(); return; } - glfwMakeContextCurrent(s_window); - glfwSwapInterval(1); // vsync - - // Keep viewport in sync with OS-driven window resizes. - glfwSetFramebufferSizeCallback(s_window, onFramebufferResize); + s_glContext = SDL_GL_CreateContext(s_window); + if (!s_glContext) { + fprintf(stderr, "[4J_Render] Failed to create GL context: %s\n", SDL_GetError()); + SDL_DestroyWindow(s_window); + s_window = nullptr; + SDL_Quit(); + return; + } + SDL_GL_SetSwapInterval(0); // V-Sync Off Please. - // init opengl + int fw, fh; SDL_GetWindowSize(s_window, &fw, &fh); onFramebufferResize(fw, fh); + + // We initialize the OpenGL states. Touching those values makes some funny artifacts appear. ::glEnable(GL_TEXTURE_2D); ::glEnable(GL_DEPTH_TEST); ::glDepthFunc(GL_LEQUAL); @@ -116,7 +142,7 @@ void C4JRender::Initialise() ::glViewport(0, 0, s_windowWidth, s_windowHeight); ::glEnable(GL_COLOR_MATERIAL); ::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - + // Print the renderer's version incase we change it in the future. printf("[4J_Render] OpenGL %s | %s\n", (const char*)::glGetString(GL_VERSION), (const char*)::glGetString(GL_RENDERER)); @@ -126,25 +152,34 @@ void C4JRender::Initialise() pthread_once(&s_glCtxKeyOnce, makeGLCtxKey); s_mainThread = pthread_self(); s_mainThreadSet = true; - pthread_setspecific(s_glCtxKey, s_window); + pthread_setspecific(s_glCtxKey, (void*)s_window); // Pre-create shared GL contexts for worker threads (chunk builders etc.) - // Must be done on the main thread because GLFW requires it. // Ensure they are invisible so they don't interfere with the window manager. - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + + // Pre-create shared GL contexts for worker threads (chunk builders & other shit etc.) + // SDL_GL_SHARE_WITH_CURRENT_CONTEXT my saviour. for (int i = 0; i < MAX_SHARED_CONTEXTS; i++) { - s_sharedContexts[i] = glfwCreateWindow(1, 1, "", nullptr, s_window); - if (s_sharedContexts[i]) { - s_sharedContextCount++; - } else { - fprintf(stderr, "[4J_Render] WARN: only created %d/%d shared contexts\n", i, MAX_SHARED_CONTEXTS); + SDL_Window *w = SDL_CreateWindow("", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + 1, 1, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); + if (!w) break; + // Ensure sharing + // I've been stuck on this for a while. Im stupid.. + SDL_GL_MakeCurrent(s_window, s_glContext); + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + SDL_GLContext ctx = SDL_GL_CreateContext(w); + if (!ctx) { + SDL_DestroyWindow(w); break; } + s_sharedContextWindows[s_sharedContextCount] = w; + s_sharedContexts[s_sharedContextCount] = ctx; + s_sharedContextCount++; } - glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Ensure main thread still has the context - glfwMakeContextCurrent(s_window); + SDL_GL_MakeCurrent(s_window, s_glContext); fprintf(stderr, "[4J_Render] Created %d shared GL contexts for worker threads\n", s_sharedContextCount); fflush(stderr); } @@ -156,21 +191,25 @@ void C4JRender::InitialiseContext() // Main thread reclaiming context (e.g. after startup thread finishes) if (s_mainThreadSet && pthread_equal(pthread_self(), s_mainThread)) { - glfwMakeContextCurrent(s_window); - pthread_setspecific(s_glCtxKey, s_window); + SDL_GL_MakeCurrent(s_window, s_glContext); + pthread_setspecific(s_glCtxKey, (void*)s_window); return; } - // Worker thread: check if it already has a shared context - GLFWwindow *ctx = (GLFWwindow*)pthread_getspecific(s_glCtxKey); - if (ctx) { - glfwMakeContextCurrent(ctx); + // Worker thread checks if there's a context, we don't want to have multiple contexts. + void *ctxPtr = pthread_getspecific(s_glCtxKey); + if (ctxPtr) { + // ctxPtr -> SDL_GLContext pointer + SDL_GLContext ctx = (SDL_GLContext)ctxPtr; + int idx = -1; + for (int i = 0; i < s_sharedContextCount; ++i) if (s_sharedContexts[i] == ctx) { idx = i; break; } + if (idx >= 0 && s_sharedContextWindows[idx]) SDL_GL_MakeCurrent(s_sharedContextWindows[idx], ctx); return; } // Grab a pre-created shared context from the pool pthread_mutex_lock(&s_sharedCtxMutex); - GLFWwindow *shared = nullptr; + SDL_GLContext shared = nullptr; if (s_nextSharedContext < s_sharedContextCount) { shared = s_sharedContexts[s_nextSharedContext++]; } @@ -181,7 +220,10 @@ void C4JRender::InitialiseContext() fflush(stderr); return; } - glfwMakeContextCurrent(shared); + // ewww..... look at line 201-203, we gotta make a function for that.... + int idx = -1; + for (int i = 0; i < s_sharedContextCount; ++i) if (s_sharedContexts[i] == shared) { idx = i; break; } + if (idx >= 0 && s_sharedContextWindows[idx]) SDL_GL_MakeCurrent(s_sharedContextWindows[idx], shared); // Initialize some basic state for this context to ensure consistent display list recording ::glEnable(GL_TEXTURE_2D); @@ -194,7 +236,7 @@ void C4JRender::InitialiseContext() ::glEnable(GL_COLOR_MATERIAL); ::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - pthread_setspecific(s_glCtxKey, shared); + pthread_setspecific(s_glCtxKey, (void*)shared); fprintf(stderr, "[4J_Render] Assigned shared GL context %p to worker thread %lu\n", (void*)shared, (unsigned long)pthread_self()); fflush(stderr); } @@ -202,18 +244,28 @@ void C4JRender::InitialiseContext() void C4JRender::StartFrame() { if (!s_window) return; - glfwGetFramebufferSize(s_window, &s_windowWidth, &s_windowHeight); - if (s_windowWidth < 1) s_windowWidth = 1; - if (s_windowHeight < 1) s_windowHeight = 1; + int w,h; SDL_GetWindowSize(s_window, &w, &h); + s_windowWidth = w > 0 ? w : 1; + s_windowHeight = h > 0 ? h : 1; ::glViewport(0, 0, s_windowWidth, s_windowHeight); } void C4JRender::Present() { if (!s_window) return; + SDL_Event ev; + while (SDL_PollEvent(&ev)) { + if (ev.type == SDL_QUIT) s_shouldClose = true; + else if (ev.type == SDL_WINDOWEVENT) { + if (ev.window.event == SDL_WINDOWEVENT_CLOSE) s_shouldClose = true; + else if (ev.window.event == SDL_WINDOWEVENT_RESIZED) onFramebufferResize(ev.window.data1, ev.window.data2); + } + } + // Present the rendered frame after processing input/events to avoid input timing issues ::glFlush(); - glfwSwapBuffers(s_window); - glfwPollEvents(); + // debug log to help diagnose mouse issues + // printf("[4J_Render] Presenting frame (mouse lock=%d)\n", s_mouseLocked); fflush(stdout); + SDL_GL_SwapWindow(s_window); } void C4JRender::SetWindowSize(int w, int h) @@ -229,21 +281,38 @@ void C4JRender::SetFullscreen(bool fs) bool C4JRender::ShouldClose() { - return !s_window || glfwWindowShouldClose(s_window); + return !s_window || s_shouldClose; } void C4JRender::Shutdown() { - // Destroy the main window and terminate GLFW cleanly so that + // Destroy the main window and clean up SDL resources so that // destructors running after the game loop don't touch a dead context. if (s_window) { - glfwDestroyWindow(s_window); + if (s_glContext) { + SDL_GL_DeleteContext(s_glContext); + s_glContext = nullptr; + } + SDL_DestroyWindow(s_window); s_window = nullptr; } - glfwTerminate(); + + for (int i = 0; i < s_sharedContextCount; ++i) { + if (s_sharedContexts[i]) { + SDL_GL_DeleteContext(s_sharedContexts[i]); + s_sharedContexts[i] = 0; + } + if (s_sharedContextWindows[i]) { + SDL_DestroyWindow(s_sharedContextWindows[i]); + s_sharedContextWindows[i] = nullptr; + } + } + s_sharedContextCount = 0; + SDL_Quit(); } +// rip glfw. you won't be missed. (i hope) void C4JRender::DoScreenGrabOnNextPresent() {} void C4JRender::Clear(int flags) @@ -272,8 +341,8 @@ void C4JRender::MatrixTranslate(float x, float y, float z) { ::glTranslatef(x, y void C4JRender::MatrixRotate(float angle, float x, float y, float z) { - // can't fucking afford pi - ::glRotatef(angle * (180.0f / 3.14159265358979f), x, y, z); + // We use math from the math lib instead of hardcoding it. How Ugly. + ::glRotatef(angle * (180.0f / static_cast(M_PI)), x, y, z); } void C4JRender::MatrixScale(float x, float y, float z) { ::glScalef(x, y, z); } @@ -284,7 +353,7 @@ void C4JRender::MatrixPerspective(float fovy, float aspect, float zNear, float z } void C4JRender::MatrixOrthogonal(float left, float right, float bottom, float top, - float zNear, float zFar) + float zNear, float zFar) { ::glOrtho(left, right, bottom, top, zNear, zFar); } @@ -314,27 +383,29 @@ static GLenum mapPrimType(int pt) // Map from ePrimitiveType enum switch (pt) { - case C4JRender::PRIMITIVE_TYPE_TRIANGLE_LIST: return GL_TRIANGLES; - case C4JRender::PRIMITIVE_TYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; - case C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN: return GL_TRIANGLE_FAN; - case C4JRender::PRIMITIVE_TYPE_QUAD_LIST: return GL_QUADS; - case C4JRender::PRIMITIVE_TYPE_LINE_LIST: return GL_LINES; - case C4JRender::PRIMITIVE_TYPE_LINE_STRIP: return GL_LINE_STRIP; - default: return GL_TRIANGLES; + case C4JRender::PRIMITIVE_TYPE_TRIANGLE_LIST: return GL_TRIANGLES; + case C4JRender::PRIMITIVE_TYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; + case C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN: return GL_TRIANGLE_FAN; + case C4JRender::PRIMITIVE_TYPE_QUAD_LIST: return GL_QUADS; + case C4JRender::PRIMITIVE_TYPE_LINE_LIST: return GL_LINES; + case C4JRender::PRIMITIVE_TYPE_LINE_STRIP: return GL_LINE_STRIP; + default: return GL_TRIANGLES; } } -// clientside awawawa +// This is the clientside vertex processing. void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count, void *dataIn, eVertexType vType, C4JRender::ePixelShaderType psType) { if (count <= 0 || !dataIn) return; + // trash trash trash trash + pthread_mutex_lock(&s_glCallMutex); + GLenum mode = mapPrimType((int)PrimitiveType); if (vType == VERTEX_TYPE_COMPRESSED) { - // NO NEED TO REWRITE IT ALL YAY int16_t *sdata = (int16_t *)dataIn; ::glBegin(mode); for (int i = 0; i < count; i++) { @@ -352,7 +423,7 @@ void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count, float fu = vert[4] / 8192.0f; float fv = vert[5] / 8192.0f; - // Strip mipmap-disable flag: Tesselator adds +1.0 (= +8192) to u when mipmaps off + // Tesselator does that. Thanks 4J. if (fu >= 1.0f) fu -= 1.0f; // Unit 1 (lightmap) UVs @@ -388,8 +459,7 @@ void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count, ::glNormal3f(nx / 127.0f, ny / 127.0f, nz / 127.0f); } - // Only override current GL color when the vertex actually carries one. - // colorInt == 0 is the Tesselator sentinel for "no colour set" + // This breaks particle colors.. i think. fixme! if (colorInt != 0) { ::glColor4ub(cr, cg, cb, ca); } @@ -397,6 +467,7 @@ void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count, ::glTexCoord2f(fdata[3], fdata[4]); // Unit 1 (lightmap) UVs - 0xfe00fe00 is sentinel for "no Unit 1 UVs" + // Ugly hack, replace soon. if (tex2Int != 0xfe00fe00) { float u2 = (float)(short)(tex2Int & 0xFFFF) / 256.0f; float v2 = (float)(short)((tex2Int >> 16) & 0xFFFF) / 256.0f; @@ -408,6 +479,8 @@ void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count, ::glEnd(); } ::glFlush(); + + pthread_mutex_unlock(&s_glCallMutex); } @@ -438,9 +511,9 @@ void C4JRender::CBuffStart(int index, bool /*full*/) void C4JRender::CBuffClear(int index) { - if (index > 0) { - ::glNewList(index, GL_COMPILE); - ::glEndList(); + if (index > 0) { + ::glNewList(index, GL_COMPILE); + ::glEndList(); ::glFlush(); } } @@ -508,9 +581,7 @@ void C4JRender::TextureBindVertex(int idx) void C4JRender::TextureSetTextureLevels(int levels) { - // Set GL_TEXTURE_MAX_LEVEL so OpenGL knows how many mip levels this texture has. - // Without this, the default is 1000, and any texture that doesn't upload all 1000 - // levels is considered "incomplete" and renders as white. + // base level is always 0, no mipmaps sadly. I'll add them later. ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels > 0 ? levels - 1 : 0); s_textureLevels = levels; } @@ -519,8 +590,7 @@ int C4JRender::TextureGetTextureLevels() { return s_textureLevels; } void C4JRender::TextureData(int width, int height, void *data, int level, eTextureFormat /*format*/) { - // Game produces [r,g,b,a] bytes via the non-Xbox transferFromImage/loadTexture paths. - // Use GL_RGBA so OpenGL interprets them correctly. GL_BGRA would swap R and B channels. + // TODO: Check if correct format. ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); @@ -554,7 +624,7 @@ void C4JRender::TextureDynamicUpdateEnd() {} void C4JRender::Tick() {} void C4JRender::UpdateGamma(unsigned short) {} -// This sucks, but at least better than libpng +// Converts RGBA data to the format expected by the texture loader. static HRESULT LoadFromSTB(unsigned char* data, int width, int height, D3DXIMAGE_INFO* pSrcInfo, int** ppDataOut) { int pixelCount = width * height; @@ -614,7 +684,6 @@ HRESULT C4JRender::SaveTextureDataToMemory(void *pOutput, int outputCapacity, in void C4JRender::TextureGetStats() {} void* C4JRender::TextureGetTexture(int idx) { return nullptr; } -// we handle opengl calls cuz multiplatform is painful!! void C4JRender::StateSetColour(float r, float g, float b, float a) { ::glColor4f(r, g, b, a); @@ -755,13 +824,12 @@ void C4JRender::StateSetLightAmbientColour(float red, float green, float blue) float ambient[4] = {red, green, blue, 1.0f}; float model[4] = {red, green, blue, 1.0f}; ::glLightModelfv(GL_LIGHT_MODEL_AMBIENT, model); - // Also set on light 0 as a fallback incase ::glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); } void C4JRender::StateSetLightDirection(int light, float x, float y, float z) { - float dir[4] = {x, y, z, 0.0f}; // w=0 → directional light + float dir[4] = {x, y, z, 0.0f}; // TODO: Java seems to do the reverse, gotta check. ::glLightfv(GL_LIGHT0 + light, GL_POSITION, dir); } @@ -787,11 +855,11 @@ void C4JRender::StateSetTexGenCol(int col, float x, float y, float z, float w, b { GLenum coord; switch (col) { - case 0: coord = GL_S; break; - case 1: coord = GL_T; break; - case 2: coord = GL_R; break; - case 3: coord = GL_Q; break; - default: coord = GL_S; break; + case 0: coord = GL_S; break; + case 1: coord = GL_T; break; + case 2: coord = GL_R; break; + case 3: coord = GL_Q; break; + default: coord = GL_S; break; } float plane[4] = {x, y, z, w}; GLenum planeMode = eyeSpace ? GL_EYE_PLANE : GL_OBJECT_PLANE; diff --git a/4J.Render/meson.build b/4J.Render/meson.build index 3dee4717c..aaf38dc4f 100644 --- a/4J.Render/meson.build +++ b/4J.Render/meson.build @@ -6,7 +6,7 @@ render_sources = files( lib_render = static_library('4J_Render', render_sources, include_directories : include_directories('.'), - dependencies : [png_dep, glfw_dep, gl_dep, thread_dep], + dependencies : [png_dep, sdl2_dep, gl_dep, thread_dep], cpp_args : global_cpp_args + global_cpp_defs + [ '-include', meson.current_source_dir() / 'stdafx.h', ], diff --git a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_glfw.c b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_glfw.c index 7962ef494..96fe64b4e 100644 --- a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_glfw.c +++ b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_glfw.c @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -23,26 +23,26 @@ // Iggy GDraw support functions - normally in the Iggy library, stubbed here void * IggyGDrawMallocAnnotated(SINTa size, const char *file, int line) { - (void)file; (void)line; - return malloc((size_t)size); + (void)file; (void)line; + return malloc((size_t)size); } void IggyGDrawFree(void *ptr) { - free(ptr); + free(ptr); } void IggyGDrawSendWarning(Iggy *f, char const *message, ...) { - (void)f; - va_list args; - va_start(args, message); - fprintf(stderr, "[Iggy GDraw Warning] "); - vfprintf(stderr, message, args); - fprintf(stderr, "\n"); - va_end(args); + (void)f; + va_list args; + va_start(args, message); + fprintf(stderr, "[Iggy GDraw Warning] "); + vfprintf(stderr, message, args); + fprintf(stderr, "\n"); + va_end(args); } void IggyDiscardVertexBufferCallback(void *owner, void *buf) { - (void)owner; (void)buf; + (void)owner; (void)buf; } /////////////////////////////////////////////////////////////////////////////// @@ -56,56 +56,56 @@ void IggyDiscardVertexBufferCallback(void *owner, void *buf) { // #define GDRAW_GL_EXTENSION_LIST \ - /* identifier import procname */ \ - /* GL_ARB_vertex_buffer_object */ \ - GLE(GenBuffers, "GenBuffersARB", GENBUFFERSARB) \ - GLE(DeleteBuffers, "DeleteBuffersARB", DELETEBUFFERSARB) \ - GLE(BindBuffer, "BindBufferARB", BINDBUFFERARB) \ - GLE(BufferData, "BufferDataARB", BUFFERDATAARB) \ - GLE(MapBuffer, "MapBufferARB", MAPBUFFERARB) \ - GLE(UnmapBuffer, "UnmapBufferARB", UNMAPBUFFERARB) \ - GLE(VertexAttribPointer, "VertexAttribPointerARB", VERTEXATTRIBPOINTERARB) \ - GLE(EnableVertexAttribArray, "EnableVertexAttribArrayARB", ENABLEVERTEXATTRIBARRAYARB) \ - GLE(DisableVertexAttribArray, "DisableVertexAttribArrayARB", DISABLEVERTEXATTRIBARRAYARB) \ - /* GL_ARB_shader_objects */ \ - GLE(CreateShader, "CreateShaderObjectARB", CREATESHADEROBJECTARB) \ - GLE(DeleteShader, "DeleteObjectARB", DELETEOBJECTARB) \ - GLE(ShaderSource, "ShaderSourceARB", SHADERSOURCEARB) \ - GLE(CompileShader, "CompileShaderARB", COMPILESHADERARB) \ - GLE(GetShaderiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ - GLE(GetShaderInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ - GLE(CreateProgram, "CreateProgramObjectARB", CREATEPROGRAMOBJECTARB) \ - GLE(DeleteProgram, "DeleteObjectARB", DELETEOBJECTARB) \ - GLE(AttachShader, "AttachObjectARB", ATTACHOBJECTARB) \ - GLE(LinkProgram, "LinkProgramARB", LINKPROGRAMARB) \ - GLE(GetUniformLocation, "GetUniformLocationARB", GETUNIFORMLOCATIONARB) \ - GLE(UseProgram, "UseProgramObjectARB", USEPROGRAMOBJECTARB) \ - GLE(GetProgramiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ - GLE(GetProgramInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ - GLE(Uniform1i, "Uniform1iARB", UNIFORM1IARB) \ - GLE(Uniform4f, "Uniform4fARB", UNIFORM4FARB) \ - GLE(Uniform4fv, "Uniform4fvARB", UNIFORM4FVARB) \ - /* GL_ARB_vertex_shader */ \ - GLE(BindAttribLocation, "BindAttribLocationARB", BINDATTRIBLOCATIONARB) \ - /* Missing from WGL but needed by shared code */ \ - GLE(Uniform1f, "Uniform1fARB", UNIFORM1FARB) \ - /* GL_EXT_framebuffer_object */ \ - GLE(GenRenderbuffers, "GenRenderbuffersEXT", GENRENDERBUFFERSEXT) \ - GLE(DeleteRenderbuffers, "DeleteRenderbuffersEXT", DELETERENDERBUFFERSEXT) \ - GLE(BindRenderbuffer, "BindRenderbufferEXT", BINDRENDERBUFFEREXT) \ - GLE(RenderbufferStorage, "RenderbufferStorageEXT", RENDERBUFFERSTORAGEEXT) \ - GLE(GenFramebuffers, "GenFramebuffersEXT", GENFRAMEBUFFERSEXT) \ - GLE(DeleteFramebuffers, "DeleteFramebuffersEXT", DELETEFRAMEBUFFERSEXT) \ - GLE(BindFramebuffer, "BindFramebufferEXT", BINDFRAMEBUFFEREXT) \ - GLE(CheckFramebufferStatus, "CheckFramebufferStatusEXT", CHECKFRAMEBUFFERSTATUSEXT) \ - GLE(FramebufferRenderbuffer, "FramebufferRenderbufferEXT", FRAMEBUFFERRENDERBUFFEREXT) \ - GLE(FramebufferTexture2D, "FramebufferTexture2DEXT", FRAMEBUFFERTEXTURE2DEXT) \ - GLE(GenerateMipmap, "GenerateMipmapEXT", GENERATEMIPMAPEXT) \ - /* GL_EXT_framebuffer_blit */ \ - GLE(BlitFramebuffer, "BlitFramebufferEXT", BLITFRAMEBUFFEREXT) \ - /* GL_EXT_framebuffer_multisample */ \ - GLE(RenderbufferStorageMultisample, "RenderbufferStorageMultisampleEXT",RENDERBUFFERSTORAGEMULTISAMPLEEXT) \ - /* */ +/* identifier import procname */ \ +/* GL_ARB_vertex_buffer_object */ \ +GLE(GenBuffers, "GenBuffersARB", GENBUFFERSARB) \ +GLE(DeleteBuffers, "DeleteBuffersARB", DELETEBUFFERSARB) \ +GLE(BindBuffer, "BindBufferARB", BINDBUFFERARB) \ +GLE(BufferData, "BufferDataARB", BUFFERDATAARB) \ +GLE(MapBuffer, "MapBufferARB", MAPBUFFERARB) \ +GLE(UnmapBuffer, "UnmapBufferARB", UNMAPBUFFERARB) \ +GLE(VertexAttribPointer, "VertexAttribPointerARB", VERTEXATTRIBPOINTERARB) \ +GLE(EnableVertexAttribArray, "EnableVertexAttribArrayARB", ENABLEVERTEXATTRIBARRAYARB) \ +GLE(DisableVertexAttribArray, "DisableVertexAttribArrayARB", DISABLEVERTEXATTRIBARRAYARB) \ +/* GL_ARB_shader_objects */ \ +GLE(CreateShader, "CreateShaderObjectARB", CREATESHADEROBJECTARB) \ +GLE(DeleteShader, "DeleteObjectARB", DELETEOBJECTARB) \ +GLE(ShaderSource, "ShaderSourceARB", SHADERSOURCEARB) \ +GLE(CompileShader, "CompileShaderARB", COMPILESHADERARB) \ +GLE(GetShaderiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ +GLE(GetShaderInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ +GLE(CreateProgram, "CreateProgramObjectARB", CREATEPROGRAMOBJECTARB) \ +GLE(DeleteProgram, "DeleteObjectARB", DELETEOBJECTARB) \ +GLE(AttachShader, "AttachObjectARB", ATTACHOBJECTARB) \ +GLE(LinkProgram, "LinkProgramARB", LINKPROGRAMARB) \ +GLE(GetUniformLocation, "GetUniformLocationARB", GETUNIFORMLOCATIONARB) \ +GLE(UseProgram, "UseProgramObjectARB", USEPROGRAMOBJECTARB) \ +GLE(GetProgramiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ +GLE(GetProgramInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ +GLE(Uniform1i, "Uniform1iARB", UNIFORM1IARB) \ +GLE(Uniform4f, "Uniform4fARB", UNIFORM4FARB) \ +GLE(Uniform4fv, "Uniform4fvARB", UNIFORM4FVARB) \ +/* GL_ARB_vertex_shader */ \ +GLE(BindAttribLocation, "BindAttribLocationARB", BINDATTRIBLOCATIONARB) \ +/* Missing from WGL but needed by shared code */ \ +GLE(Uniform1f, "Uniform1fARB", UNIFORM1FARB) \ +/* GL_EXT_framebuffer_object */ \ +GLE(GenRenderbuffers, "GenRenderbuffersEXT", GENRENDERBUFFERSEXT) \ +GLE(DeleteRenderbuffers, "DeleteRenderbuffersEXT", DELETERENDERBUFFERSEXT) \ +GLE(BindRenderbuffer, "BindRenderbufferEXT", BINDRENDERBUFFEREXT) \ +GLE(RenderbufferStorage, "RenderbufferStorageEXT", RENDERBUFFERSTORAGEEXT) \ +GLE(GenFramebuffers, "GenFramebuffersEXT", GENFRAMEBUFFERSEXT) \ +GLE(DeleteFramebuffers, "DeleteFramebuffersEXT", DELETEFRAMEBUFFERSEXT) \ +GLE(BindFramebuffer, "BindFramebufferEXT", BINDFRAMEBUFFEREXT) \ +GLE(CheckFramebufferStatus, "CheckFramebufferStatusEXT", CHECKFRAMEBUFFERSTATUSEXT) \ +GLE(FramebufferRenderbuffer, "FramebufferRenderbufferEXT", FRAMEBUFFERRENDERBUFFEREXT) \ +GLE(FramebufferTexture2D, "FramebufferTexture2DEXT", FRAMEBUFFERTEXTURE2DEXT) \ +GLE(GenerateMipmap, "GenerateMipmapEXT", GENERATEMIPMAPEXT) \ +/* GL_EXT_framebuffer_blit */ \ +GLE(BlitFramebuffer, "BlitFramebufferEXT", BLITFRAMEBUFFEREXT) \ +/* GL_EXT_framebuffer_multisample */ \ +GLE(RenderbufferStorageMultisample, "RenderbufferStorageMultisampleEXT",RENDERBUFFERSTORAGEMULTISAMPLEEXT) \ +/* */ #define gdraw_GLx_(id) gdraw_GL_##id #define GDRAW_GLx_(id) GDRAW_GL_##id @@ -125,19 +125,19 @@ GDRAW_GL_EXTENSION_LIST static void load_extensions(void) { -#define GLE(id, import, procname) gl##id = (PFNGL##procname##PROC) glfwGetProcAddress("gl" import); - GDRAW_GL_EXTENSION_LIST -#undef GLE + #define GLE(id, import, procname) gl##id = (PFNGL##procname##PROC) SDL_GL_GetProcAddress("gl" import); + GDRAW_GL_EXTENSION_LIST + #undef GLE } static void clear_renderstate_platform_specific(void) { - glDisable(GL_ALPHA_TEST); + glDisable(GL_ALPHA_TEST); } static void error_msg_platform_specific(const char *msg) { - fprintf(stderr, "[GDraw GL] %s\n", msg); + fprintf(stderr, "[GDraw GL] %s\n", msg); } /////////////////////////////////////////////////////////////////////////////// @@ -162,80 +162,80 @@ static void error_msg_platform_specific(const char *msg) GDrawFunctions *gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples) { - static const TextureFormatDesc tex_formats[] = { - { IFT_FORMAT_rgba_8888, 1, 1, 4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_rgba_4444_LE, 1, 1, 2, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 }, - { IFT_FORMAT_rgba_5551_LE, 1, 1, 2, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 }, - { IFT_FORMAT_la_88, 1, 1, 2, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_la_44, 1, 1, 1, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_i_8, 1, 1, 1, GL_INTENSITY8, GL_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_i_4, 1, 1, 1, GL_INTENSITY4, GL_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_l_8, 1, 1, 1, GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_l_4, 1, 1, 1, GL_LUMINANCE4, GL_LUMINANCE, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_DXT1, 4, 4, 8, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_DXT3, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_DXT5, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_UNSIGNED_BYTE }, - { 0, 0, 0, 0, 0, 0, 0 }, - }; + static const TextureFormatDesc tex_formats[] = { + { IFT_FORMAT_rgba_8888, 1, 1, 4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, + { IFT_FORMAT_rgba_4444_LE, 1, 1, 2, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 }, + { IFT_FORMAT_rgba_5551_LE, 1, 1, 2, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 }, + { IFT_FORMAT_la_88, 1, 1, 2, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE }, + { IFT_FORMAT_la_44, 1, 1, 1, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE }, + { IFT_FORMAT_i_8, 1, 1, 1, GL_INTENSITY8, GL_ALPHA, GL_UNSIGNED_BYTE }, + { IFT_FORMAT_i_4, 1, 1, 1, GL_INTENSITY4, GL_ALPHA, GL_UNSIGNED_BYTE }, + { IFT_FORMAT_l_8, 1, 1, 1, GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE }, + { IFT_FORMAT_l_4, 1, 1, 1, GL_LUMINANCE4, GL_LUMINANCE, GL_UNSIGNED_BYTE }, + { IFT_FORMAT_DXT1, 4, 4, 8, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_UNSIGNED_BYTE }, + { IFT_FORMAT_DXT3, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_UNSIGNED_BYTE }, + { IFT_FORMAT_DXT5, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_UNSIGNED_BYTE }, + { 0, 0, 0, 0, 0, 0, 0 }, + }; - GDrawFunctions *funcs; - const char *s; - GLint n; + GDrawFunctions *funcs; + const char *s; + GLint n; - // check for the extensions we need - s = (const char *) glGetString(GL_EXTENSIONS); - if (s == NULL) { - fprintf(stderr, "[GDraw GL] glGetString(GL_EXTENSIONS) returned NULL - GL context not current?\n"); - assert(s != NULL); - return NULL; - } + // check for the extensions we need + s = (const char *) glGetString(GL_EXTENSIONS); + if (s == NULL) { + fprintf(stderr, "[GDraw GL] glGetString(GL_EXTENSIONS) returned NULL - GL context not current?\n"); + assert(s != NULL); + return NULL; + } - // check for the extensions we won't work without - if (!hasext(s, "GL_ARB_multitexture") || - !hasext(s, "GL_ARB_texture_compression") || - !hasext(s, "GL_ARB_texture_mirrored_repeat") || - !hasext(s, "GL_ARB_texture_non_power_of_two") || - !hasext(s, "GL_ARB_vertex_buffer_object") || - !hasext(s, "GL_EXT_framebuffer_object") || - !hasext(s, "GL_ARB_shader_objects") || - !hasext(s, "GL_ARB_vertex_shader") || - !hasext(s, "GL_ARB_fragment_shader")) - { - fprintf(stderr, "[GDraw GL] Required GL extensions not available\n"); - return NULL; - } + // check for the extensions we won't work without + if (!hasext(s, "GL_ARB_multitexture") || + !hasext(s, "GL_ARB_texture_compression") || + !hasext(s, "GL_ARB_texture_mirrored_repeat") || + !hasext(s, "GL_ARB_texture_non_power_of_two") || + !hasext(s, "GL_ARB_vertex_buffer_object") || + !hasext(s, "GL_EXT_framebuffer_object") || + !hasext(s, "GL_ARB_shader_objects") || + !hasext(s, "GL_ARB_vertex_shader") || + !hasext(s, "GL_ARB_fragment_shader")) + { + fprintf(stderr, "[GDraw GL] Required GL extensions not available\n"); + return NULL; + } - // if user requests multisampling and HW doesn't support it, bail - if (!hasext(s, "GL_EXT_framebuffer_multisample") && msaa_samples > 1) - return NULL; + // if user requests multisampling and HW doesn't support it, bail + if (!hasext(s, "GL_EXT_framebuffer_multisample") && msaa_samples > 1) + return NULL; - load_extensions(); - funcs = create_context(w, h); - if (!funcs) - return NULL; + load_extensions(); + funcs = create_context(w, h); + if (!funcs) + return NULL; - gdraw->tex_formats = tex_formats; + gdraw->tex_formats = tex_formats; - // check for optional extensions - gdraw->has_mapbuffer = true; // part of core VBO extension on regular GL - gdraw->has_depth24 = true; // we just assume. - gdraw->has_texture_max_level = true; // core on regular GL + // check for optional extensions + gdraw->has_mapbuffer = true; // part of core VBO extension on regular GL + gdraw->has_depth24 = true; // we just assume. + gdraw->has_texture_max_level = true; // core on regular GL - if (hasext(s, "GL_EXT_packed_depth_stencil")) gdraw->has_packed_depth_stencil = true; + if (hasext(s, "GL_EXT_packed_depth_stencil")) gdraw->has_packed_depth_stencil = true; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &n); - gdraw->has_conditional_non_power_of_two = n < 8192; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &n); + gdraw->has_conditional_non_power_of_two = n < 8192; - // clamp number of multisampling levels to max supported - if (msaa_samples > 1) { - glGetIntegerv(GL_MAX_SAMPLES, &n); - gdraw->multisampling = RR_MIN(msaa_samples, n); - } + // clamp number of multisampling levels to max supported + if (msaa_samples > 1) { + glGetIntegerv(GL_MAX_SAMPLES, &n); + gdraw->multisampling = RR_MIN(msaa_samples, n); + } - opengl_check(); + opengl_check(); - fprintf(stderr, "[GDraw GL] Context created successfully (%dx%d, msaa=%d)\n", w, h, msaa_samples); - return funcs; + fprintf(stderr, "[GDraw GL] Context created successfully (%dx%d, msaa=%d)\n", w, h, msaa_samples); + return funcs; } /////////////////////////////////////////////////////////////////////////////// @@ -245,12 +245,12 @@ GDrawFunctions *gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples) void gdraw_GL_BeginCustomDraw_4J(IggyCustomDrawCallbackRegion *region, F32 *matrix) { - // Same as BeginCustomDraw but uses different depth param - clear_renderstate(); - gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, depth_from_id(0), 1); + // Same as BeginCustomDraw but uses different depth param + clear_renderstate(); + gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, depth_from_id(0), 1); } void gdraw_GL_CalculateCustomDraw_4J(IggyCustomDrawCallbackRegion *region, F32 *matrix) { - gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, 0.0f, 0); + gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, 0.0f, 0); } diff --git a/Minecraft.Client/meson.build b/Minecraft.Client/meson.build index b362462c8..f786de862 100644 --- a/Minecraft.Client/meson.build +++ b/Minecraft.Client/meson.build @@ -57,7 +57,7 @@ client = executable('Minecraft.Client', world_dep, gl_dep, glu_dep, - glfw_dep, + thread_dep, thread_dep, dl_dep, dependency('zlib'), @@ -80,10 +80,10 @@ custom_target('copy_assets_to_client', command : [ python, meson.project_source_root() / 'scripts/copy_assets_to_client.py', meson.project_source_root(), - meson.project_build_root(), + meson.build_root(), meson.current_build_dir(), '@INPUT1@', '@OUTPUT@', ], build_by_default: true, -) \ No newline at end of file +) diff --git a/README.md b/README.md index d77b02b2c..3c6c41b56 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,6 @@ At the moment, we're aiming to support the following platforms: - iOS (not started) - Android (not started) -> [!WARNING] -> There NO Windows support, for that, go to [smartcmd/MinecraftConsoles](https://github.com/smartcmd/MinecraftConsoles/). - > All efforts are focused towards a native Linux port, OpenGL rendering pipeline, and modernizing the existing LCE codebase/tooling to make future platform ports easier. > > `Windows64` and other platforms originally supported by LCE are currently unsupported, since the original Visual Studio tooling has been stripped from this repository and replaced with our own. @@ -40,7 +37,7 @@ Install the following packages before building (Debian/Ubuntu names shown): ```bash sudo apt install \ build-essential cmake \ - libglfw3-dev libgl-dev libglu1-mesa-dev \ + libsdl2-dev libgl-dev libglu1-mesa-dev \ libopenal-dev libvorbis-dev \ libpng-dev libpthread-stubs0-dev ``` @@ -48,16 +45,16 @@ sudo apt install \ #### Arch/Manjaro ```bash -sudo pacman -S base-devel gcc pkgconf cmake glfw-x11 mesa openal libvorbis glu +sudo pacman -S base-devel gcc pkgconf cmake sdl2 mesa openal libvorbis glu ``` > [!TIP] -> If you are on wayland, you may swap `glfw-x11` to `glfw-wayland` for native wayland windowing instead of xwayland. +> SDL2 supports both X11 and Wayland backends; no package swap is necessary for Wayland!!!!!!! #### Fedora/Red Hat/Nobara ```bash -sudo dnf in gcc gcc-c++ make cmake glfw-devel mesa-libGL-devel mesa-libGLU-devel openal-soft-devel libvorbis-devel libpng-devel openssl-devel +sudo dnf in gcc gcc-c++ make cmake SDL2-devel mesa-libGL-devel mesa-libGLU-devel openal-soft-devel libvorbis-devel libpng-devel openssl-devel ``` #### Docker diff --git a/flake.nix b/flake.nix index 9897d7fc6..ba17a0f9d 100644 --- a/flake.nix +++ b/flake.nix @@ -21,18 +21,17 @@ { devShells = forAllSystems ({ pkgs }: let - libs = with pkgs; [ + libs = with pkgs; [ openssl.dev libGL libGLU - glfw + sdl2 libpng zlib openal libvorbis ]; packages = with pkgs; [ - python3 gcc15 lld cmake diff --git a/meson.build b/meson.build index 3cc9b0c41..fa5c4c35f 100644 --- a/meson.build +++ b/meson.build @@ -1,10 +1,11 @@ project('4jcraft-chucklegrounds', ['cpp', 'c'], version : '0.1.0', default_options : [ - 'buildtype=debug', # for now - 'unity=on', # merge source files per target - 'unity_size=8', # TODO: mess around with this - 'b_pch=true', # precompiled headers + 'warning_level=0', + 'buildtype=debug', + 'unity=on', # merge source files per target → fewer compile units + 'unity_size=8', # files per unity batch (tune up for faster full builds) + 'b_pch=true', # precompiled headers ], ) @@ -16,7 +17,8 @@ cc = meson.get_compiler('cpp') # system deps gl_dep = dependency('gl') glu_dep = dependency('glu') -glfw_dep = dependency('glfw3') +sdl2_dep = dependency('sdl2') # Yes.. i know sdl3 is out, but there's not point upgrading right now except when + # someone is gonna ask me "Hey juicey can you make it SDL3?" and i'd be like fuck you and still do it. png_dep = dependency('libpng') thread_dep = dependency('threads') dl_dep = cc.find_library('dl') @@ -52,4 +54,4 @@ subdir('4J.Profile') subdir('4J.Storage') subdir('Minecraft.Assets') subdir('Minecraft.World') -subdir('Minecraft.Client') \ No newline at end of file +subdir('Minecraft.Client') From 71f542a860e20ae6878dc3ead79fa59dd7e7b868 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Tue, 10 Mar 2026 09:43:30 +0100 Subject: [PATCH 13/33] added back build-linux workflow --- .github/workflows/build-linux.yml | 131 ++++++++++++++++++------------ 1 file changed, 78 insertions(+), 53 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 42cb25d20..8b56d1895 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -1,5 +1,6 @@ name: Build Linux Release - +# document this later, while that, put both release & debug in parallel +# debug still always comes second and that's sad. on: push: paths: @@ -23,10 +24,10 @@ on: - '.github/workflows/build-linux.yml' jobs: - build-linux: + build-release: runs-on: ubuntu-latest concurrency: - group: build-linux-${{ github.ref }} + group: build-linux-release-${{ github.ref }} cancel-in-progress: true steps: - name: Checkout repository @@ -35,8 +36,7 @@ jobs: - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install -y build-essential python3 python3-pip python3-setuptools libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev libpng-dev pkg-config clang lld ccache libssl-dev - # Set a reasonable ccache size + sudo apt-get install -y build-essential python3 python3-pip python3-setuptools libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev libpng-dev pkg-config clang lld ccache libssl-dev patchelf ccache -M 5G || true - name: Setup Python @@ -50,7 +50,7 @@ jobs: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/build-linux.yml') }} - - name: Install Meson and Ninja (pip) + - name: Install Meson and Ninja run: | python -m pip install --upgrade pip pip install meson ninja @@ -59,7 +59,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.ccache - key: ${{ runner.os }}-ccache-${{ hashFiles('**/meson.build') }} + key: ${{ runner.os }}-ccache-release-${{ hashFiles('**/meson.build') }} - name: Restore meson cache uses: actions/cache@v4 @@ -74,19 +74,8 @@ jobs: CCACHE_DIR: ${{ runner.temp }}/ccache run: | mkdir -p "$CCACHE_DIR" - export CCACHE_DIR="$CCACHE_DIR" meson setup build_meson --wipe --buildtype=release - - name: Configure Meson (debug) - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - mkdir -p "$CCACHE_DIR" - export CCACHE_DIR="$CCACHE_DIR" - meson setup build_debug --wipe --buildtype=debug - - name: Build with Ninja env: CC: "ccache clang" @@ -94,21 +83,8 @@ jobs: CCACHE_DIR: ${{ runner.temp }}/ccache run: | export CCACHE_DIR="${{ runner.temp }}/ccache" - # Use all available cores for faster parallel builds ninja -C build_meson -j$(nproc) -v - - name: Build Debug with Ninja - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - export CCACHE_DIR="${{ runner.temp }}/ccache" - ninja -C build_debug -j$(nproc) -v - - - name: Install patchelf - run: sudo apt-get install -y patchelf - - name: Bundle executable + libraries env: GITHUB_SHA: ${{ github.sha }} @@ -125,12 +101,8 @@ jobs: BUNDLE=out/minecraft-client-linux-${SHORT_SHA} mkdir -p "$BUNDLE/lib" - # Copy the binary cp "$EXE_PATH" "$BUNDLE/Minecraft.Client" - # Collect non-system shared library dependencies and copy them in. - # Exclude glibc/libstdc++/libgcc — these are ABI-specific and must - # come from the user's system, not from the build runner. ldd "$EXE_PATH" \ | awk '/=>/ { print $3 }' \ | grep -v '^(' \ @@ -140,18 +112,10 @@ jobs: [ -f "$lib" ] && cp "$lib" "$BUNDLE/lib/" || true done - # Patch the binary RPATH so it finds libs in ./lib at runtime patchelf --set-rpath '$ORIGIN/lib' "$BUNDLE/Minecraft.Client" - # Write a launcher script cat > "$BUNDLE/run.sh" << 'RUNEOF' #!/usr/bin/env bash - # 4JCraft Linux launcher - # ------------------------------------------------------------------- - # IMPORTANT: Before running, copy the "Common" assets folder from - # https://github.com/smartcmd/MinecraftConsoles/releases/tag/nightly - # (LCEWindows64.zip → extract → copy "Common" next to this script) - # ------------------------------------------------------------------- DIR="$(cd "$(dirname "$0")" && pwd)" if [ ! -d "$DIR/Common" ]; then echo "ERROR: Missing 'Common' assets folder." @@ -165,7 +129,6 @@ jobs: RUNEOF chmod +x "$BUNDLE/run.sh" - # Write a README cat > "$BUNDLE/README.txt" << 'EOF' 4JCraft Linux Build =================== @@ -185,6 +148,74 @@ jobs: echo "Bundle ready: $BUNDLE" ls -lh "$BUNDLE" "$BUNDLE/lib" + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: minecraft-client-linux-${{ github.sha }} + path: out/minecraft-client-linux-*/ + retention-days: 7 + + build-debug: + runs-on: ubuntu-latest + concurrency: + group: build-linux-debug-${{ github.ref }} + cancel-in-progress: true + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential python3 python3-pip python3-setuptools libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev libpng-dev pkg-config clang lld ccache libssl-dev patchelf + ccache -M 5G || true + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Cache pip packages + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/build-linux.yml') }} + + - name: Install Meson and Ninja + run: | + python -m pip install --upgrade pip + pip install meson ninja + + - name: Restore ccache + uses: actions/cache@v4 + with: + path: ~/.ccache + key: ${{ runner.os }}-ccache-debug-${{ hashFiles('**/meson.build') }} + + - name: Restore meson cache + uses: actions/cache@v4 + with: + path: ~/.cache/meson + key: ${{ runner.os }}-meson-${{ hashFiles('**/meson.build') }} + + - name: Configure Meson + env: + CC: "ccache clang" + CXX: "ccache clang++" + CCACHE_DIR: ${{ runner.temp }}/ccache + run: | + mkdir -p "$CCACHE_DIR" + meson setup build_debug --wipe --buildtype=debug + + - name: Build with Ninja + env: + CC: "ccache clang" + CXX: "ccache clang++" + CCACHE_DIR: ${{ runner.temp }}/ccache + run: | + export CCACHE_DIR="${{ runner.temp }}/ccache" + ninja -C build_debug -j$(nproc) -v + - name: Bundle debug executable + libraries env: GITHUB_SHA: ${{ github.sha }} @@ -201,10 +232,8 @@ jobs: BUNDLE=out/minecraft-client-linux-${SHORT_SHA}-debug mkdir -p "$BUNDLE/lib" - # Copy the binary cp "$EXE_PATH" "$BUNDLE/Minecraft.Client.debug" - # Collect non-system shared library dependencies and copy them in. ldd "$EXE_PATH" \ | awk '/=>/ { print $3 }' \ | grep -v '^(' \ @@ -214,18 +243,14 @@ jobs: [ -f "$lib" ] && cp "$lib" "$BUNDLE/lib/" || true done - # Patch the binary RPATH so it finds libs in ./lib at runtime patchelf --set-rpath '$ORIGIN/lib' "$BUNDLE/Minecraft.Client.debug" - # Keep a copy of the unstripped debug binary (symbols are already present in debug build) echo "Debug bundle ready: $BUNDLE" ls -lh "$BUNDLE" "$BUNDLE/lib" - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: minecraft-client-linux-${{ github.sha }} - path: | - out/minecraft-client-linux-*/ - out/minecraft-client-linux-*-debug/ - retention-days: 7 + name: minecraft-client-linux-${{ github.sha }}-debug + path: out/minecraft-client-linux-*-debug/ + retention-days: 7 \ No newline at end of file From 45c90ef35882f8a65d77f29c1b62936efb02c076 Mon Sep 17 00:00:00 2001 From: vekidev <93841364+vekidev@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:27:24 +0100 Subject: [PATCH 14/33] the biggest commit of them all its the biggest one what can i tell you --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d77b02b2c..9320bef8c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ At the moment, we're aiming to support the following platforms: - Android (not started) > [!WARNING] -> There NO Windows support, for that, go to [smartcmd/MinecraftConsoles](https://github.com/smartcmd/MinecraftConsoles/). +> There is NO Windows support, for that, go to [smartcmd/MinecraftConsoles](https://github.com/smartcmd/MinecraftConsoles/). > All efforts are focused towards a native Linux port, OpenGL rendering pipeline, and modernizing the existing LCE codebase/tooling to make future platform ports easier. > From 712c3f1f47878a1e42e060a291619f09c86cbd0f Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Tue, 10 Mar 2026 10:41:56 +0100 Subject: [PATCH 15/33] better ci --- .github/workflows/build-linux.yml | 256 ------------------------------ .github/workflows/ci.yml | 201 ++++++++++++++++++++--- 2 files changed, 180 insertions(+), 277 deletions(-) delete mode 100644 .github/workflows/build-linux.yml diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml deleted file mode 100644 index 8b56d1895..000000000 --- a/.github/workflows/build-linux.yml +++ /dev/null @@ -1,256 +0,0 @@ -name: Build Linux Release -# document this later, while that, put both release & debug in parallel -# debug still always comes second and that's sad. -on: - push: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**/meson.build' - - 'meson.build' - - '**/CMakeLists.txt' - - 'CMakeLists.txt' - - '.github/workflows/build-linux.yml' - pull_request: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**/meson.build' - - 'meson.build' - - '**/CMakeLists.txt' - - 'CMakeLists.txt' - - '.github/workflows/build-linux.yml' - -jobs: - build-release: - runs-on: ubuntu-latest - concurrency: - group: build-linux-release-${{ github.ref }} - cancel-in-progress: true - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential python3 python3-pip python3-setuptools libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev libpng-dev pkg-config clang lld ccache libssl-dev patchelf - ccache -M 5G || true - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Cache pip packages - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/build-linux.yml') }} - - - name: Install Meson and Ninja - run: | - python -m pip install --upgrade pip - pip install meson ninja - - - name: Restore ccache - uses: actions/cache@v4 - with: - path: ~/.ccache - key: ${{ runner.os }}-ccache-release-${{ hashFiles('**/meson.build') }} - - - name: Restore meson cache - uses: actions/cache@v4 - with: - path: ~/.cache/meson - key: ${{ runner.os }}-meson-${{ hashFiles('**/meson.build') }} - - - name: Configure Meson - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - mkdir -p "$CCACHE_DIR" - meson setup build_meson --wipe --buildtype=release - - - name: Build with Ninja - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - export CCACHE_DIR="${{ runner.temp }}/ccache" - ninja -C build_meson -j$(nproc) -v - - - name: Bundle executable + libraries - env: - GITHUB_SHA: ${{ github.sha }} - run: | - set -euo pipefail - EXE_PATH=build_meson/Minecraft.Client/Minecraft.Client - if [ ! -f "$EXE_PATH" ]; then - echo "ERROR: expected executable at $EXE_PATH" >&2 - ls -la build_meson || true - exit 1 - fi - - SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-8) - BUNDLE=out/minecraft-client-linux-${SHORT_SHA} - mkdir -p "$BUNDLE/lib" - - cp "$EXE_PATH" "$BUNDLE/Minecraft.Client" - - ldd "$EXE_PATH" \ - | awk '/=>/ { print $3 }' \ - | grep -v '^(' \ - | grep -Ev '/(libc|libm|libdl|libpthread|librt|libgcc_s|libstdc\+\+|ld-linux)[^/]*\.so' \ - | sort -u \ - | while read -r lib; do - [ -f "$lib" ] && cp "$lib" "$BUNDLE/lib/" || true - done - - patchelf --set-rpath '$ORIGIN/lib' "$BUNDLE/Minecraft.Client" - - cat > "$BUNDLE/run.sh" << 'RUNEOF' - #!/usr/bin/env bash - DIR="$(cd "$(dirname "$0")" && pwd)" - if [ ! -d "$DIR/Common" ]; then - echo "ERROR: Missing 'Common' assets folder." - echo "Download LCEWindows64.zip from:" - echo " https://github.com/smartcmd/MinecraftConsoles/releases/tag/nightly" - echo "Extract it and copy the 'Common' folder next to this run.sh file." - exit 1 - fi - cd "$DIR" - exec ./Minecraft.Client "$@" - RUNEOF - chmod +x "$BUNDLE/run.sh" - - cat > "$BUNDLE/README.txt" << 'EOF' - 4JCraft Linux Build - =================== - This bundle contains: - Minecraft.Client - compiled Linux binary - lib/ - all required shared libraries (GL, GLFW, png, zlib, X11 etc.) - run.sh - launcher script - - To run: - 1. Download LCEWindows64.zip from the MinecraftConsoles nightly release: - https://github.com/smartcmd/MinecraftConsoles/releases/tag/nightly - 2. Extract it and copy the "Common" folder into this directory - (so you have Common/ sitting next to run.sh) - 3. Run: ./run.sh [--width W] [--height H] [--fullscreen] - EOF - - echo "Bundle ready: $BUNDLE" - ls -lh "$BUNDLE" "$BUNDLE/lib" - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: minecraft-client-linux-${{ github.sha }} - path: out/minecraft-client-linux-*/ - retention-days: 7 - - build-debug: - runs-on: ubuntu-latest - concurrency: - group: build-linux-debug-${{ github.ref }} - cancel-in-progress: true - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential python3 python3-pip python3-setuptools libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev libpng-dev pkg-config clang lld ccache libssl-dev patchelf - ccache -M 5G || true - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Cache pip packages - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/build-linux.yml') }} - - - name: Install Meson and Ninja - run: | - python -m pip install --upgrade pip - pip install meson ninja - - - name: Restore ccache - uses: actions/cache@v4 - with: - path: ~/.ccache - key: ${{ runner.os }}-ccache-debug-${{ hashFiles('**/meson.build') }} - - - name: Restore meson cache - uses: actions/cache@v4 - with: - path: ~/.cache/meson - key: ${{ runner.os }}-meson-${{ hashFiles('**/meson.build') }} - - - name: Configure Meson - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - mkdir -p "$CCACHE_DIR" - meson setup build_debug --wipe --buildtype=debug - - - name: Build with Ninja - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - export CCACHE_DIR="${{ runner.temp }}/ccache" - ninja -C build_debug -j$(nproc) -v - - - name: Bundle debug executable + libraries - env: - GITHUB_SHA: ${{ github.sha }} - run: | - set -euo pipefail - EXE_PATH=build_debug/Minecraft.Client/Minecraft.Client - if [ ! -f "$EXE_PATH" ]; then - echo "ERROR: expected debug executable at $EXE_PATH" >&2 - ls -la build_debug || true - exit 1 - fi - - SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-8) - BUNDLE=out/minecraft-client-linux-${SHORT_SHA}-debug - mkdir -p "$BUNDLE/lib" - - cp "$EXE_PATH" "$BUNDLE/Minecraft.Client.debug" - - ldd "$EXE_PATH" \ - | awk '/=>/ { print $3 }' \ - | grep -v '^(' \ - | grep -Ev '/(libc|libm|libdl|libpthread|librt|libgcc_s|libstdc\+\+|ld-linux)[^/]*\.so' \ - | sort -u \ - | while read -r lib; do - [ -f "$lib" ] && cp "$lib" "$BUNDLE/lib/" || true - done - - patchelf --set-rpath '$ORIGIN/lib' "$BUNDLE/Minecraft.Client.debug" - - echo "Debug bundle ready: $BUNDLE" - ls -lh "$BUNDLE" "$BUNDLE/lib" - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: minecraft-client-linux-${{ github.sha }}-debug - path: out/minecraft-client-linux-*-debug/ - retention-days: 7 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcfa32d7d..acf60e5bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,49 +1,208 @@ -name: CI Build - +name: CI +# remade the CI now not broken anymore on: push: branches: [ main, master ] + paths: + - '**.cpp' + - '**.h' + - '**.c' + - '**/meson.build' + - 'meson.build' + - '**/CMakeLists.txt' + - 'CMakeLists.txt' + - '.github/workflows/ci.yml' pull_request: branches: [ main, master ] + paths: + - '**.cpp' + - '**.h' + - '**.c' + - '**/meson.build' + - 'meson.build' + - '**/CMakeLists.txt' + - 'CMakeLists.txt' + - '.github/workflows/ci.yml' jobs: - build: + build-release: runs-on: ubuntu-latest + env: + BUILD_DIR: build_meson + BUNDLE_DIR: out/minecraft-client + concurrency: + group: build-linux-release-${{ github.ref }} + cancel-in-progress: true steps: - name: Checkout repository uses: actions/checkout@v4 + - name: Install system dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq build-essential python3 python3-pip \ + libgl1-mesa-dev libglu1-mesa-dev libsdl2-dev libglfw3-dev libpng-dev \ + zlib1g-dev pkg-config clang lld ccache libssl-dev patchelf > /dev/null + - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.x' - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential pkg-config ca-certificates curl git \ - libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev libpng-dev libx11-dev \ - libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libudev-dev + - name: Install Meson and Ninja + run: pip install meson ninja -q - - name: Install Meson and Ninja (pip) - run: | - python -m pip install --upgrade pip - pip install meson ninja + - name: Restore ccache + uses: actions/cache@v4 + with: + path: ~/.ccache + key: ${{ runner.os }}-ccache-release-${{ hashFiles('**/meson.build') }} - name: Configure Meson run: | - meson setup build_meson --wipe --buildtype=release + mkdir -p ${{ runner.temp }}/ccache + CC="ccache clang" CXX="ccache clang++" CCACHE_DIR="${{ runner.temp }}/ccache" \ + meson setup ${{ env.BUILD_DIR }} --buildtype=release -Dwarning_level=0 > /dev/null - - name: Build with Ninja + - name: Build run: | - ninja -C build_meson -v + CCACHE_DIR="${{ runner.temp }}/ccache" \ + ninja -C ${{ env.BUILD_DIR }} -j$(nproc) > /dev/null - - name: Package build output + - name: Prepare bundle directory + run: mkdir -p ${{ env.BUNDLE_DIR }}/lib + + - name: Copy binary + run: cp ${{ env.BUILD_DIR }}/Minecraft.Client/Minecraft.Client ${{ env.BUNDLE_DIR }}/ + + - name: Copy source assets run: | - tar -czf minecraft-binaries.tar.gz -C build_meson . + cp -r Minecraft.Assets/Common ${{ env.BUNDLE_DIR }}/ + cp -r Minecraft.Assets/Common/music ${{ env.BUNDLE_DIR }}/ + cp -r Minecraft.Assets/DurangoMedia/Sound ${{ env.BUNDLE_DIR }}/ + + - name: Copy built assets + run: | + mkdir -p ${{ env.BUNDLE_DIR }}/Common/Media + cp ${{ env.BUILD_DIR }}/Minecraft.Assets/MediaLinux.arc ${{ env.BUNDLE_DIR }}/Common/Media/ + cp ${{ env.BUILD_DIR }}/Minecraft.Assets/languages.loc ${{ env.BUNDLE_DIR }}/Common/ + + - name: Collect shared libraries + run: | + EXE="${{ env.BUNDLE_DIR }}/Minecraft.Client" + ldd "$EXE" | awk '/=>/ { print $3 }' | grep -v '^(' | \ + grep -Ev '/(libc|libm|libdl|libpthread|librt|libgcc_s|libstdc\+\+|ld-linux)[^/]*\.so' | \ + sort -u | xargs -I{} cp -L {} ${{ env.BUNDLE_DIR }}/lib/ || true + + - name: Fix RPATH + run: patchelf --set-rpath '$ORIGIN/lib' ${{ env.BUNDLE_DIR }}/Minecraft.Client + ## useless but still good + - name: Generate launcher and readme + run: | + cat > ${{ env.BUNDLE_DIR }}/run.sh << 'EOF' + #!/usr/bin/env bash + DIR="$(cd "$(dirname "$0")" && pwd)" + cd "$DIR" + export LD_LIBRARY_PATH="$DIR/lib:$LD_LIBRARY_PATH" + exec ./Minecraft.Client "$@" + EOF + chmod +x ${{ env.BUNDLE_DIR }}/run.sh + + echo -e "4JCraft Linux Build\nRun via ./run.sh" > ${{ env.BUNDLE_DIR }}/README.txt - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: minecraft-binaries - path: minecraft-binaries.tar.gz + name: minecraft-client-linux + path: ${{ env.BUNDLE_DIR }} + retention-days: 7 + + build-debug: + runs-on: ubuntu-latest + env: + BUILD_DIR: build_debug + BUNDLE_DIR: out/minecraft-client-debug + concurrency: + group: build-linux-debug-${{ github.ref }} + cancel-in-progress: true + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install system dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq build-essential python3 python3-pip \ + libgl1-mesa-dev libglu1-mesa-dev libsdl2-dev libglfw3-dev libpng-dev \ + zlib1g-dev pkg-config clang lld ccache libssl-dev patchelf > /dev/null + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install Meson and Ninja + run: pip install meson ninja -q + + - name: Restore ccache + uses: actions/cache@v4 + with: + path: ~/.ccache + key: ${{ runner.os }}-ccache-debug-${{ hashFiles('**/meson.build') }} + + - name: Configure Meson + run: | + mkdir -p ${{ runner.temp }}/ccache + CC="ccache clang" CXX="ccache clang++" CCACHE_DIR="${{ runner.temp }}/ccache" \ + meson setup ${{ env.BUILD_DIR }} --buildtype=debug -Dwarning_level=0 > /dev/null + + - name: Build + run: | + CCACHE_DIR="${{ runner.temp }}/ccache" \ + ninja -C ${{ env.BUILD_DIR }} -j$(nproc) > /dev/null + + - name: Prepare bundle directory + run: mkdir -p ${{ env.BUNDLE_DIR }}/lib + + - name: Copy binary + run: cp ${{ env.BUILD_DIR }}/Minecraft.Client/Minecraft.Client ${{ env.BUNDLE_DIR }}/Minecraft.Client.debug + + - name: Copy source assets + run: | + cp -r Minecraft.Assets/Common ${{ env.BUNDLE_DIR }}/ + cp -r Minecraft.Assets/Common/music ${{ env.BUNDLE_DIR }}/ + cp -r Minecraft.Assets/DurangoMedia/Sound ${{ env.BUNDLE_DIR }}/ + + - name: Copy built assets + run: | + mkdir -p ${{ env.BUNDLE_DIR }}/Common/Media + cp ${{ env.BUILD_DIR }}/Minecraft.Assets/MediaLinux.arc ${{ env.BUNDLE_DIR }}/Common/Media/ + cp ${{ env.BUILD_DIR }}/Minecraft.Assets/languages.loc ${{ env.BUNDLE_DIR }}/Common/ + + - name: Collect shared libraries + run: | + EXE="${{ env.BUNDLE_DIR }}/Minecraft.Client.debug" + ldd "$EXE" | awk '/=>/ { print $3 }' | grep -v '^(' | \ + grep -Ev '/(libc|libm|libdl|libpthread|librt|libgcc_s|libstdc\+\+|ld-linux)[^/]*\.so' | \ + sort -u | xargs -I{} cp -L {} ${{ env.BUNDLE_DIR }}/lib/ || true + + - name: Fix RPATH + run: patchelf --set-rpath '$ORIGIN/lib' ${{ env.BUNDLE_DIR }}/Minecraft.Client.debug + + - name: Generate launcher + run: | + cat > ${{ env.BUNDLE_DIR }}/run_debug.sh << 'EOF' + #!/usr/bin/env bash + DIR="$(cd "$(dirname "$0")" && pwd)" + cd "$DIR" + export LD_LIBRARY_PATH="$DIR/lib:$LD_LIBRARY_PATH" + exec ./Minecraft.Client.debug "$@" + EOF + chmod +x ${{ env.BUNDLE_DIR }}/run_debug.sh + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: minecraft-client-linux-debug + path: ${{ env.BUNDLE_DIR }} + retention-days: 7 From 5406648692a51a05949df58ffd119a757f18d01a Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:16:42 -0500 Subject: [PATCH 16/33] fix: implement `CConsoleMinecraftApp::FatalLoadError` on linux --- Minecraft.Client/Platform/Linux/Linux_App.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Minecraft.Client/Platform/Linux/Linux_App.cpp b/Minecraft.Client/Platform/Linux/Linux_App.cpp index 5fcd8017e..a9520f211 100644 --- a/Minecraft.Client/Platform/Linux/Linux_App.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_App.cpp @@ -32,6 +32,8 @@ void CConsoleMinecraftApp::ExitGame() } void CConsoleMinecraftApp::FatalLoadError() { + app.DebugPrintf("CConsoleMinecraftApp::FatalLoadError - asserting 0 and dying...\n"); + assert(0); } void CConsoleMinecraftApp::CaptureSaveThumbnail() From 8a8280b24299ad69efabc7187feb25b406b91f55 Mon Sep 17 00:00:00 2001 From: "Echo J." Date: Tue, 10 Mar 2026 20:00:07 +0200 Subject: [PATCH 17/33] GameNetworkManager: Fix incorrect infinity value redefinition WaitForSingleObject() uses the maximum 32-bit value for specifying infinite timeout (while INFINITY might overflow that sized value back to 0) which caused the server wait event to not actually wait when compiled with Clang (and the game to be forever stuck on a black screen due to abnormal server state) --- .../Platform/Common/Network/GameNetworkManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/Platform/Common/Network/GameNetworkManager.cpp b/Minecraft.Client/Platform/Common/Network/GameNetworkManager.cpp index 5fa598f74..a62be6143 100644 --- a/Minecraft.Client/Platform/Common/Network/GameNetworkManager.cpp +++ b/Minecraft.Client/Platform/Common/Network/GameNetworkManager.cpp @@ -1108,7 +1108,7 @@ int CGameNetworkManager::ChangeSessionTypeThreadProc( void* lpParam ) app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)TRUE); // wait for the server to be in a non-ticking state - pServer->m_serverPausedEvent->WaitForSignal(INFINITY); + pServer->m_serverPausedEvent->WaitForSignal(INFINITE); #if defined(__PS3__) || defined(__ORBIS__) || defined __PSVITA__ // Swap these two messages around as one is too long to display at 480 @@ -1926,7 +1926,7 @@ void CGameNetworkManager::ServerReadyWait() { if (m_hServerReadyEvent != NULL) { - m_hServerReadyEvent->WaitForSignal(INFINITY); + m_hServerReadyEvent->WaitForSignal(INFINITE); } else { @@ -1990,7 +1990,7 @@ void CGameNetworkManager::ServerStoppedWait() { if (m_hServerStoppedEvent != NULL) { - m_hServerStoppedEvent->WaitForSignal(INFINITY); + m_hServerStoppedEvent->WaitForSignal(INFINITE); } else { From af16088015842705bddbab2366d67dbcd00ac5df Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Tue, 10 Mar 2026 20:13:01 +0100 Subject: [PATCH 18/33] improved a change --- Minecraft.World/Network/Packets/AwardStatPacket.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Minecraft.World/Network/Packets/AwardStatPacket.cpp b/Minecraft.World/Network/Packets/AwardStatPacket.cpp index 576cead1b..3ec3113a0 100644 --- a/Minecraft.World/Network/Packets/AwardStatPacket.cpp +++ b/Minecraft.World/Network/Packets/AwardStatPacket.cpp @@ -16,12 +16,13 @@ AwardStatPacket::AwardStatPacket(int statId, int count) { this->statId = statId; - // 4jcraft, changed from new int(count); to new int[1]; - // and initializing the array with count - // reason: .data needs to be delete with delete[] but its - // allocated with new, not new T[] - this->m_paramData.data = (uint8_t *) new int[1]; - ((int*)this->m_paramData.data)[0] = count; + // 4jcraft, changed from (uint8_t*) new int(count); to: + // new uint8_t[sizeof(int)]; + // and memcpy of the integer into the array + // reason: operator missmatch, array is deleted with delete[] + // and typesafety + this->m_paramData.data = new uint8_t[sizeof(int)]; + memcpy(this->m_paramData.data, &count, sizeof(int)); this->m_paramData.length = sizeof(int); } From da3d69b5bde6edd2b73f7430ae1861bd07f6702a Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Tue, 10 Mar 2026 20:27:11 +0100 Subject: [PATCH 19/33] final commit, removed scripts and reset meson.build --- debug.sh | 6 ------ meson.build | 16 ---------------- run.sh | 6 ------ 3 files changed, 28 deletions(-) delete mode 100755 debug.sh delete mode 100755 run.sh diff --git a/debug.sh b/debug.sh deleted file mode 100755 index b5a3c173d..000000000 --- a/debug.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -CC=clang CXX=clang++ meson compile -C build && \ -cd build/Minecraft.Client/ && \ -gdb -tui ./Minecraft.Client && \ -cd ../.. diff --git a/meson.build b/meson.build index 357396709..418d35a0b 100644 --- a/meson.build +++ b/meson.build @@ -12,9 +12,6 @@ pymod = import('python') python = pymod.find_installation('python3', required: true) cc = meson.get_compiler('cpp') -buildtype = get_option('buildtype') -#langs cant sadly be set before calling project -langs = ['cpp', 'c'] # system deps gl_dep = dependency('gl') @@ -24,19 +21,6 @@ png_dep = dependency('libpng') thread_dep = dependency('threads') dl_dep = cc.find_library('dl') -if buildtype.startswith('debug') - strAsan = '-fsanitize=address' - strLsan = '-fsanitize=leak' - if cc.has_argument(strAsan) - add_project_arguments(strAsan, language: langs) - add_project_link_arguments(strAsan, language: langs) - endif - if cc.has_argument(strLsan) - add_project_arguments(strLsan, language: langs) - add_project_link_arguments(strLsan, language: langs) - endif -endif - # compile flags (chagne ts juicey) global_cpp_args = [ '-fpermissive', diff --git a/run.sh b/run.sh deleted file mode 100755 index d261f892b..000000000 --- a/run.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -CC=clang CXX=clang++ meson compile -C build && \ -cd build/Minecraft.Client/ && \ -./Minecraft.Client && \ -cd ../.. From 0ff6f767e5ae91d50169fcab0e34656b902b2abc Mon Sep 17 00:00:00 2001 From: Nikita Edel Date: Tue, 10 Mar 2026 20:30:31 +0100 Subject: [PATCH 20/33] formailize comments --- Minecraft.Client/Platform/Common/Tutorial/LookAtEntityHint.h | 2 +- Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.cpp | 2 +- Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.h | 2 +- Minecraft.Client/Platform/Common/Tutorial/TakeItemHint.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Minecraft.Client/Platform/Common/Tutorial/LookAtEntityHint.h b/Minecraft.Client/Platform/Common/Tutorial/LookAtEntityHint.h index a8c389c37..035a88e68 100644 --- a/Minecraft.Client/Platform/Common/Tutorial/LookAtEntityHint.h +++ b/Minecraft.Client/Platform/Common/Tutorial/LookAtEntityHint.h @@ -14,7 +14,7 @@ private: public: LookAtEntityHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, int titleId, eINSTANCEOF type); - //TODO: LEAK LEKAFDSAKFDJ;SALKDJF;ASLKFJ ITS NEVER IMPLEMENTED + //TODO: 4jcraft added, this was not implemented ~LookAtEntityHint(){}; virtual bool onLookAtEntity(eINSTANCEOF type); diff --git a/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.cpp b/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.cpp index c9ef4ce0e..760d5b54f 100644 --- a/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.cpp +++ b/Minecraft.Client/Platform/Common/Tutorial/LookAtTileHint.cpp @@ -10,7 +10,7 @@ LookAtTileHint::LookAtTileHint(eTutorial_Hint id, Tutorial *tutorial, int tiles[ { m_iTilesCount = tilesLength; - //TODO LEAK LEAK LEAK LEAKS EVERY WHERE AAAAAAAAAAAAAAAAH + //TODO: 4jcraft: allocating but never freeing mem, leak m_iTiles= new int [m_iTilesCount]; for(unsigned int i=0;i item ); From 12ede0691aa4baa214ceb49e71e8e74fb23ae0e0 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:22:37 -0500 Subject: [PATCH 21/33] fix: remove unused console-only asset archive imports --- Minecraft.Client/Platform/Linux/Linux_Minecraft.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Minecraft.Client/Platform/Linux/Linux_Minecraft.cpp b/Minecraft.Client/Platform/Linux/Linux_Minecraft.cpp index 4ab22f581..38717792f 100644 --- a/Minecraft.Client/Platform/Linux/Linux_Minecraft.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_Minecraft.cpp @@ -699,10 +699,6 @@ ProfileManager.Initialise(TITLEID_MINECRAFT, &app.uiGameDefinedDataChangedBitmask ); -byteArray baOptionsIcon = app.getArchiveFile(L"DefaultOptionsImage228x128.png"); -byteArray baSaveThumbnail = app.getArchiveFile(L"DefaultSaveThumbnail64x64.png"); -byteArray baSaveImage = app.getArchiveFile(L"DefaultSaveImage228x128.png"); - // set a function to be called when there's a sign in change, so we can exit a level if the primary player signs out ProfileManager.SetSignInChangeCallback(&CConsoleMinecraftApp::SignInChangeCallback,(LPVOID)&app); From fe631bcff8ae405846c90d9bfe5d7006fea6c201 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:32:53 -0500 Subject: [PATCH 22/33] chore: undo unintentional meson changes, update README --- Minecraft.Client/meson.build | 2 +- README.md | 5 +---- meson.build | 9 ++++----- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Minecraft.Client/meson.build b/Minecraft.Client/meson.build index f786de862..9f4fbaa1b 100644 --- a/Minecraft.Client/meson.build +++ b/Minecraft.Client/meson.build @@ -80,7 +80,7 @@ custom_target('copy_assets_to_client', command : [ python, meson.project_source_root() / 'scripts/copy_assets_to_client.py', meson.project_source_root(), - meson.build_root(), + meson.project_build_root(), meson.current_build_dir(), '@INPUT1@', '@OUTPUT@', diff --git a/README.md b/README.md index 672e052f4..6aa48b2c1 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,9 @@ sudo apt install \ #### Arch/Manjaro ```bash -sudo pacman -S base-devel gcc pkgconf cmake sdl2 mesa openal libvorbis glu +sudo pacman -S base-devel gcc pkgconf cmake sdl2-compat mesa openal libvorbis glu ``` -> [!TIP] -> SDL2 supports both X11 and Wayland backends; no package swap is necessary for Wayland!!!!!!! - #### Fedora/Red Hat/Nobara ```bash diff --git a/meson.build b/meson.build index fa5c4c35f..c1347746f 100644 --- a/meson.build +++ b/meson.build @@ -1,11 +1,10 @@ project('4jcraft-chucklegrounds', ['cpp', 'c'], version : '0.1.0', default_options : [ - 'warning_level=0', - 'buildtype=debug', - 'unity=on', # merge source files per target → fewer compile units - 'unity_size=8', # files per unity batch (tune up for faster full builds) - 'b_pch=true', # precompiled headers + 'buildtype=debug', # for now + 'unity=on', # merge source files per target + 'unity_size=8', # TODO: mess around with this + 'b_pch=true', # precompiled headers ], ) From bce996a2ef9ff70868efbc620acbff982e9a9718 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:49:34 -0500 Subject: [PATCH 23/33] refactor: simplify and update CI pipeline --- .github/workflows/build-linux.yml | 124 ++---------------------------- 1 file changed, 8 insertions(+), 116 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 1a3896803..fceea68b3 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -1,4 +1,4 @@ -name: Build Linux Release +name: Build (Linux, x86_64) on: push: @@ -32,26 +32,10 @@ jobs: - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install -y build-essential python3 python3-pip python3-setuptools libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev libpng-dev pkg-config clang lld ccache libssl-dev + sudo apt-get install -y build-essential python3 ninja-build meson libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev # Set a reasonable ccache size ccache -M 5G || true - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Cache pip packages - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/build-linux.yml') }} - - - name: Install Meson and Ninja (pip) - run: | - python -m pip install --upgrade pip - pip install meson ninja - - name: Restore ccache uses: actions/cache@v4 with: @@ -72,7 +56,7 @@ jobs: run: | mkdir -p "$CCACHE_DIR" export CCACHE_DIR="$CCACHE_DIR" - meson setup build_meson --wipe --buildtype=release + meson setup build_release --wipe --buildtype=release --native-file=./scripts/llvm_native.txt - name: Build with Meson env: @@ -82,92 +66,16 @@ jobs: run: | export CCACHE_DIR="${{ runner.temp }}/ccache" # Use all available cores for faster parallel builds - meson compile -C build_meson -j $(nproc) -v Minecraft.Client + meson compile -C build_release -j $(nproc) -v Minecraft.Client - name: Install patchelf run: sudo apt-get install -y patchelf - - name: Bundle executable + libraries - env: - GITHUB_SHA: ${{ github.sha }} - run: | - set -euo pipefail - EXE_PATH=build_meson/Minecraft.Client/Minecraft.Client - if [ ! -f "$EXE_PATH" ]; then - echo "ERROR: expected executable at $EXE_PATH" >&2 - ls -la build_meson || true - exit 1 - fi - - SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-8) - BUNDLE=out/minecraft-client-linux-${SHORT_SHA} - mkdir -p "$BUNDLE/lib" - - # Copy the binary - cp "$EXE_PATH" "$BUNDLE/Minecraft.Client" - - # Collect non-system shared library dependencies and copy them in. - # Exclude glibc/libstdc++/libgcc — these are ABI-specific and must - # come from the user's system, not from the build runner. - ldd "$EXE_PATH" \ - | awk '/=>/ { print $3 }' \ - | grep -v '^(' \ - | grep -Ev '/(libc|libm|libdl|libpthread|librt|libgcc_s|libstdc\+\+|ld-linux)[^/]*\.so' \ - | sort -u \ - | while read -r lib; do - [ -f "$lib" ] && cp "$lib" "$BUNDLE/lib/" || true - done - - # Patch the binary RPATH so it finds libs in ./lib at runtime - patchelf --set-rpath '$ORIGIN/lib' "$BUNDLE/Minecraft.Client" - - # Write a launcher script - cat > "$BUNDLE/run.sh" << 'RUNEOF' - #!/usr/bin/env bash - # 4JCraft Linux launcher - # ------------------------------------------------------------------- - # IMPORTANT: Before running, copy the "Common" assets folder from - # https://github.com/smartcmd/MinecraftConsoles/releases/tag/nightly - # (LCEWindows64.zip → extract → copy "Common" next to this script) - # ------------------------------------------------------------------- - DIR="$(cd "$(dirname "$0")" && pwd)" - if [ ! -d "$DIR/Common" ]; then - echo "ERROR: Missing 'Common' assets folder." - echo "Download LCEWindows64.zip from:" - echo " https://github.com/smartcmd/MinecraftConsoles/releases/tag/nightly" - echo "Extract it and copy the 'Common' folder next to this run.sh file." - exit 1 - fi - cd "$DIR" - exec ./Minecraft.Client "$@" - RUNEOF - chmod +x "$BUNDLE/run.sh" - - # Write a README - cat > "$BUNDLE/README.txt" << 'EOF' - 4JCraft Linux Build - =================== - This bundle contains: - Minecraft.Client - compiled Linux binary - lib/ - all required shared libraries (GL, GLFW, png, zlib, X11 etc.) - run.sh - launcher script - - To run: - 1. Download LCEWindows64.zip from the MinecraftConsoles nightly release: - https://github.com/smartcmd/MinecraftConsoles/releases/tag/nightly - 2. Extract it and copy the "Common" folder into this directory - (so you have Common/ sitting next to run.sh) - 3. Run: ./run.sh [--width W] [--height H] [--fullscreen] - EOF - - echo "Bundle ready: $BUNDLE" - ls -lh "$BUNDLE" "$BUNDLE/lib" - - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: minecraft-client-linux-${{ github.sha }} - path: out/minecraft-client-linux-*/ + name: minecraft-client-linux-release_exe-${{ github.sha }} + path: build_release/Minecraft.Client/Minecraft.Client retention-days: 7 build-linux-debug: runs-on: ubuntu-latest @@ -178,26 +86,10 @@ jobs: - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install -y build-essential python3 python3-pip python3-setuptools libgl1-mesa-dev libglu1-mesa-dev libglfw3-dev libpng-dev pkg-config clang lld ccache libssl-dev + sudo apt-get install -y build-essential python3 ninja-build meson libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev # Set a reasonable ccache size ccache -M 5G || true - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Cache pip packages - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/build-linux.yml') }} - - - name: Install Meson and Ninja (pip) - run: | - python -m pip install --upgrade pip - pip install meson ninja - - name: Restore ccache uses: actions/cache@v4 with: @@ -218,7 +110,7 @@ jobs: run: | mkdir -p "$CCACHE_DIR" export CCACHE_DIR="$CCACHE_DIR" - meson setup build_debug --wipe --buildtype=debug + meson setup build_debug --wipe --buildtype=debug --native-file=./scripts/llvm_native.txt - name: Build Debug with Meson env: From 52ee114f2430102bd83771e2dec963b228793136 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:57:56 -0500 Subject: [PATCH 24/33] refactor: dont depend on libpng and libdl, update docs --- .devcontainer/Dockerfile | 25 ++++++------------------- README.md | 10 +++------- flake.nix | 5 +---- meson.build | 2 -- 4 files changed, 10 insertions(+), 32 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7ae419ffc..4fb27fb37 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -7,31 +7,18 @@ RUN apt-get update \ && apt-get install -y software-properties-common \ && add-apt-repository -y ppa:ubuntu-toolchain-r/test \ && apt-get update \ - && apt-get install -y \ + && sudo apt-get install -y \ build-essential \ - cmake \ - libglfw3-dev \ + python3 \ + ninja-build \ + meson \ + libsdl2-dev \ libgl-dev \ libglu1-mesa-dev \ - libopenal-dev \ - libvorbis-dev \ - libpng-dev \ - libpthread-stubs0-dev \ - clang \ - lld \ - meson \ - ninja-build \ - gcc-15 \ - g++-15 \ + libpthread-stubs0-dev # Clean up lol && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* -# Set GCC 15 as default -RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-15 100 \ - && update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-15 100 \ - && update-alternatives --install /usr/bin/cc cc /usr/bin/gcc 100 \ - && update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 100 - ENV DEBIAN_FRONTEND=dialog \ No newline at end of file diff --git a/README.md b/README.md index 6aa48b2c1..431b14a2c 100644 --- a/README.md +++ b/README.md @@ -38,23 +38,19 @@ At the moment, we're aiming to support the following platforms: Install the following packages before building (Debian/Ubuntu names shown): ```bash -sudo apt install \ - build-essential cmake \ - libsdl2-dev libgl-dev libglu1-mesa-dev \ - libopenal-dev libvorbis-dev \ - libpng-dev libpthread-stubs0-dev +sudo apt-get install -y build-essential python3 ninja-build meson libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev ``` #### Arch/Manjaro ```bash -sudo pacman -S base-devel gcc pkgconf cmake sdl2-compat mesa openal libvorbis glu +sudo pacman -S base-devel gcc pkgconf cmake sdl2-compat mesa glu ``` #### Fedora/Red Hat/Nobara ```bash -sudo dnf in gcc gcc-c++ make cmake SDL2-devel mesa-libGL-devel mesa-libGLU-devel openal-soft-devel libvorbis-devel libpng-devel openssl-devel +sudo dnf in gcc gcc-c++ make cmake SDL2-devel mesa-libGL-devel mesa-libGLU-devel openssl-devel ``` #### Docker diff --git a/flake.nix b/flake.nix index ba17a0f9d..dc9acfbb7 100644 --- a/flake.nix +++ b/flake.nix @@ -26,13 +26,10 @@ libGL libGLU sdl2 - libpng zlib - openal - libvorbis ]; packages = with pkgs; [ - gcc15 + clang lld cmake gnumake diff --git a/meson.build b/meson.build index c1347746f..7ec53a62e 100644 --- a/meson.build +++ b/meson.build @@ -18,9 +18,7 @@ gl_dep = dependency('gl') glu_dep = dependency('glu') sdl2_dep = dependency('sdl2') # Yes.. i know sdl3 is out, but there's not point upgrading right now except when # someone is gonna ask me "Hey juicey can you make it SDL3?" and i'd be like fuck you and still do it. -png_dep = dependency('libpng') thread_dep = dependency('threads') -dl_dep = cc.find_library('dl') # compile flags (chagne ts juicey) global_cpp_args = [ From 47db746861b3d7dac78206f1ac218187c9c766f8 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:59:43 -0500 Subject: [PATCH 25/33] no png go away shoo --- 4J.Render/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/4J.Render/meson.build b/4J.Render/meson.build index aaf38dc4f..21043a5e3 100644 --- a/4J.Render/meson.build +++ b/4J.Render/meson.build @@ -6,7 +6,7 @@ render_sources = files( lib_render = static_library('4J_Render', render_sources, include_directories : include_directories('.'), - dependencies : [png_dep, sdl2_dep, gl_dep, thread_dep], + dependencies : [sdl2_dep, gl_dep, thread_dep], cpp_args : global_cpp_args + global_cpp_defs + [ '-include', meson.current_source_dir() / 'stdafx.h', ], From c60630280831d15fb32ce385370c3ff6963b1b6f Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 18:09:03 -0500 Subject: [PATCH 26/33] fix: dl dep is gone --- Minecraft.Client/meson.build | 1 - 1 file changed, 1 deletion(-) diff --git a/Minecraft.Client/meson.build b/Minecraft.Client/meson.build index 9f4fbaa1b..94a7e4933 100644 --- a/Minecraft.Client/meson.build +++ b/Minecraft.Client/meson.build @@ -59,7 +59,6 @@ client = executable('Minecraft.Client', glu_dep, thread_dep, thread_dep, - dl_dep, dependency('zlib'), ], cpp_args : global_cpp_args + global_cpp_defs + [ From 6c67565a0bc5ea4c197380e2cbe4febf88d7836e Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 18:10:45 -0500 Subject: [PATCH 27/33] fix: dont invoke apt-get with sudo in dockerfile --- .devcontainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 4fb27fb37..ed043dc98 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -7,7 +7,7 @@ RUN apt-get update \ && apt-get install -y software-properties-common \ && add-apt-repository -y ppa:ubuntu-toolchain-r/test \ && apt-get update \ - && sudo apt-get install -y \ + && apt-get install -y \ build-essential \ python3 \ ninja-build \ From f90d9a2b0d7d80a128d2e67c8bdfbb651a51bf0e Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 18:13:57 -0500 Subject: [PATCH 28/33] docs: don't list meson as a build dep --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 431b14a2c..f09aa64e7 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ At the moment, we're aiming to support the following platforms: Install the following packages before building (Debian/Ubuntu names shown): ```bash -sudo apt-get install -y build-essential python3 ninja-build meson libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev +sudo apt-get install -y build-essential libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev ``` #### Arch/Manjaro From c2728cd2d990d72e1f1b555de529c9d18d3dfaf4 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Wed, 11 Mar 2026 01:52:23 +0100 Subject: [PATCH 29/33] SDL scroll wheel w/ hotbar & numbr support !!!!!! --- 4J.Input/4J_Input.cpp | 171 +++++++++++++++++++++++---------- 4J.Input/4J_Input.h | 2 + Minecraft.Client/Minecraft.cpp | 35 +++++-- 3 files changed, 148 insertions(+), 60 deletions(-) diff --git a/4J.Input/4J_Input.cpp b/4J.Input/4J_Input.cpp index 5f1c319a4..e28c30b68 100644 --- a/4J.Input/4J_Input.cpp +++ b/4J.Input/4J_Input.cpp @@ -18,10 +18,14 @@ static bool s_mouseLeftCurrent = false, s_mouseLeftPrev = false; static bool s_mouseRightCurrent = false, s_mouseRightPrev = false; static bool s_menuDisplayed[4] = {}; static bool s_prevMenuDisplayed = false; -static bool s_snapTaken = false, s_scrollSnapTaken = false; +static bool s_snapTaken = false; static float s_accumRelX = 0, s_accumRelY = 0; static float s_snapRelX = 0, s_snapRelY = 0; -static float s_scrollAccum = 0, s_scrollSnap = 0; + +static int s_scrollTicksForButtonPressed = 0; +static int s_scrollTicksForGetValue = 0; +static int s_scrollTicksSnap = 0; +static bool s_scrollSnapTaken = false; // We set all the watched keys // I don't know if I'll need to change this if we add chat support soon. @@ -36,7 +40,7 @@ static const int s_watchedKeys[] = { SDL_SCANCODE_TAB, SDL_SCANCODE_LCTRL, SDL_SCANCODE_RCTRL, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, SDL_SCANCODE_7, SDL_SCANCODE_8, - SDL_SCANCODE_9, + SDL_SCANCODE_9, SDL_SCANCODE_0, }; static const int s_watchedKeyCount = (int)(sizeof(s_watchedKeys) / sizeof(s_watchedKeys[0])); @@ -51,10 +55,38 @@ static inline bool MouseRDown () { return s_mouseRightCurrent; } static inline bool MouseRPressed () { return s_mouseRightCurrent && !s_mouseRightPrev; } static inline bool MouseRReleased() { return !s_mouseRightCurrent && s_mouseRightPrev; } -// Snap the scroll value so that it doesn't go crazy! -static float ScrollSnap() { - if (!s_scrollSnapTaken) { s_scrollSnap = s_scrollAccum; s_scrollAccum = 0; s_scrollSnapTaken = true; } - return s_scrollSnap; +// get directly into SDL events before the game queue can steal them. +// this took me a while. +static int SDLCALL EventWatcher(void*, SDL_Event* e) { + if (e->type == SDL_MOUSEWHEEL) { + int y = e->wheel.y; + if (e->wheel.direction == SDL_MOUSEWHEEL_FLIPPED) { + y = -y; + } + s_scrollTicksForGetValue += y; + s_scrollTicksForButtonPressed += y; + } else if (e->type == SDL_MOUSEBUTTONDOWN) { + if (e->button.button == 4) { + s_scrollTicksForGetValue++; + s_scrollTicksForButtonPressed++; + } else if (e->button.button == 5) { + s_scrollTicksForGetValue--; + s_scrollTicksForButtonPressed--; + } + } else if (e->type == SDL_MOUSEMOTION) { + s_accumRelX += (float)e->motion.xrel; + s_accumRelY += (float)e->motion.yrel; + } + return 1; +} + +static int ScrollSnap() { + if (!s_scrollSnapTaken) { + s_scrollTicksSnap = s_scrollTicksForButtonPressed; + s_scrollTicksForButtonPressed = 0; + s_scrollSnapTaken = true; + } + return s_scrollTicksSnap; } static void TakeSnapIfNeeded() { @@ -64,11 +96,14 @@ static void TakeSnapIfNeeded() { s_snapTaken = true; } } - // We initialize the SDL input void C_4JInput::Initialise(int, unsigned char, unsigned char, unsigned char) { if (!s_sdlInitialized) { - s_sdlInitialized = SDL_Init(SDL_INIT_VIDEO) == 0; + if (SDL_WasInit(SDL_INIT_VIDEO) == 0) { + SDL_Init(SDL_INIT_VIDEO); + } + SDL_AddEventWatch(EventWatcher, NULL); + s_sdlInitialized = true; } memset(s_keysCurrent, 0, sizeof(s_keysCurrent)); @@ -77,14 +112,13 @@ void C_4JInput::Initialise(int, unsigned char, unsigned char, unsigned char) { s_mouseLeftCurrent = s_mouseLeftPrev = s_mouseRightCurrent = s_mouseRightPrev = false; s_accumRelX = s_accumRelY = s_snapRelX = s_snapRelY = 0; - s_scrollAccum = s_scrollSnap = 0; + // i really gotta name these vars better.. + s_scrollTicksForButtonPressed = s_scrollTicksForGetValue = s_scrollTicksSnap = 0; s_snapTaken = s_scrollSnapTaken = s_prevMenuDisplayed = false; - if (s_sdlInitialized) { + if (s_sdlInitialized) SDL_SetRelativeMouseMode(SDL_TRUE); - } } - // Each tick we update the input state by polling SDL, this is where we get the kbd and mouse state. void C_4JInput::Tick() { if (!s_sdlInitialized) return; @@ -92,41 +126,57 @@ void C_4JInput::Tick() { memcpy(s_keysPrev, s_keysCurrent, sizeof(s_keysCurrent)); s_mouseLeftPrev = s_mouseLeftCurrent; s_mouseRightPrev = s_mouseRightCurrent; - s_snapTaken = s_scrollSnapTaken = false; - s_snapRelX = s_snapRelY = s_scrollSnap = 0; + s_snapTaken = false; + s_scrollSnapTaken = false; + s_snapRelX = s_snapRelY = 0; + s_scrollTicksSnap = 0; SDL_PumpEvents(); - // Keyboard State. + + if (s_menuDisplayed[0]) { + s_scrollTicksForGetValue = 0; + } + const Uint8 *state = SDL_GetKeyboardState(NULL); for (int i = 0; i < s_watchedKeyCount; ++i) { int sc = s_watchedKeys[i]; if (sc > 0 && sc < KEY_COUNT) s_keysCurrent[sc] = state[sc] != 0; } - // Scroll Wheel events. It's a bit weird, but it works. - SDL_Event ev; - while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEWHEEL, SDL_MOUSEWHEEL) > 0) - s_scrollAccum += (float)ev.wheel.y; - - int dx = 0, dy = 0; - while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) { - dx += ev.motion.xrel; dy += ev.motion.yrel; - } - // Mouse Position & Button Events. - if (dx == 0 && dy == 0 && SDL_GetRelativeMouseMode()) - SDL_GetRelativeMouseState(&dx, &dy); - s_accumRelX += (float)dx; - s_accumRelY += (float)dy; Uint32 btns = SDL_GetMouseState(NULL, NULL); s_mouseLeftCurrent = (btns & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; s_mouseRightCurrent = (btns & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; + if (!SDL_GetRelativeMouseMode()) { + s_accumRelX = 0; + s_accumRelY = 0; + } + if (!SDL_GetKeyboardFocus()) { SDL_Window *mf = SDL_GetMouseFocus(); if (mf) { SDL_RaiseWindow(mf); SDL_SetWindowGrab(mf, SDL_TRUE); } } } -// been a while i haven't used cases + +int C_4JInput::GetHotbarSlotPressed(int iPad) { + if (iPad != 0) return -1; + + static const int sc[10] = { + SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, + SDL_SCANCODE_5, SDL_SCANCODE_6, SDL_SCANCODE_7, SDL_SCANCODE_8, + SDL_SCANCODE_9, SDL_SCANCODE_0 + }; + static bool s_wasDown[10] = {}; + + for (int i = 0; i < 10; ++i) { + bool down = KDown(sc[i]); + bool pressed = down && !s_wasDown[i]; + s_wasDown[i] = down; + if (pressed) return i; + } + return -1; +} + #define ACTION_CASES(FN) \ case ACTION_MENU_UP: return FN(SDL_SCANCODE_UP); \ case ACTION_MENU_DOWN: return FN(SDL_SCANCODE_DOWN); \ @@ -153,22 +203,21 @@ case MINECRAFT_ACTION_DPAD_UP: return FN(SDL_SCANCODE_UP); \ case MINECRAFT_ACTION_DPAD_DOWN: return FN(SDL_SCANCODE_DOWN); \ default: return false; -// The part that handles Pressing a button. bool C_4JInput::ButtonDown(int iPad, unsigned char ucAction) { if (iPad != 0) return false; if (ucAction == 255) { for (int i = 0; i < s_watchedKeyCount; ++i) if (s_keysCurrent[s_watchedKeys[i]]) return true; - return s_mouseLeftCurrent || s_mouseRightCurrent; + return s_mouseLeftCurrent || s_mouseRightCurrent; } switch (ucAction) { - case MINECRAFT_ACTION_ACTION: return MouseLDown() || KDown(SDL_SCANCODE_RETURN); - case MINECRAFT_ACTION_USE: return MouseRDown() || KDown(SDL_SCANCODE_F); - case MINECRAFT_ACTION_SNEAK_TOGGLE: return KDown(SDL_SCANCODE_LSHIFT) || KDown(SDL_SCANCODE_RSHIFT) || KDown(SDL_SCANCODE_LCTRL) || KDown(SDL_SCANCODE_RCTRL); + case MINECRAFT_ACTION_ACTION: return MouseLDown() || KDown(SDL_SCANCODE_RETURN); + case MINECRAFT_ACTION_USE: return MouseRDown() || KDown(SDL_SCANCODE_F); + case MINECRAFT_ACTION_SNEAK_TOGGLE: return KDown(SDL_SCANCODE_LSHIFT) || KDown(SDL_SCANCODE_RSHIFT) || KDown(SDL_SCANCODE_LCTRL) || KDown(SDL_SCANCODE_RCTRL); case MINECRAFT_ACTION_LEFT_SCROLL: - case ACTION_MENU_LEFT_SCROLL: return ScrollSnap() > 0.1f; + case ACTION_MENU_LEFT_SCROLL: return ScrollSnap() > 0; case MINECRAFT_ACTION_RIGHT_SCROLL: - case ACTION_MENU_RIGHT_SCROLL: return ScrollSnap() < -0.1f; + case ACTION_MENU_RIGHT_SCROLL: return ScrollSnap() < 0; ACTION_CASES(KDown) } } @@ -176,13 +225,13 @@ bool C_4JInput::ButtonDown(int iPad, unsigned char ucAction) { bool C_4JInput::ButtonPressed(int iPad, unsigned char ucAction) { if (iPad != 0 || ucAction == 255) return false; switch (ucAction) { - case MINECRAFT_ACTION_ACTION: return MouseLPressed() || KPressed(SDL_SCANCODE_RETURN); - case MINECRAFT_ACTION_USE: return MouseRPressed() || KPressed(SDL_SCANCODE_F); - case MINECRAFT_ACTION_SNEAK_TOGGLE: return KPressed(SDL_SCANCODE_LSHIFT) || KPressed(SDL_SCANCODE_RSHIFT) || KPressed(SDL_SCANCODE_LCTRL) || KPressed(SDL_SCANCODE_RCTRL); + case MINECRAFT_ACTION_ACTION: return MouseLPressed() || KPressed(SDL_SCANCODE_RETURN); + case MINECRAFT_ACTION_USE: return MouseRPressed() || KPressed(SDL_SCANCODE_F); + case MINECRAFT_ACTION_SNEAK_TOGGLE: return KPressed(SDL_SCANCODE_LSHIFT) || KPressed(SDL_SCANCODE_RSHIFT) || KPressed(SDL_SCANCODE_LCTRL) || KPressed(SDL_SCANCODE_RCTRL); case MINECRAFT_ACTION_LEFT_SCROLL: - case ACTION_MENU_LEFT_SCROLL: return ScrollSnap() > 0.1f; + case ACTION_MENU_LEFT_SCROLL: return ScrollSnap() > 0; case MINECRAFT_ACTION_RIGHT_SCROLL: - case ACTION_MENU_RIGHT_SCROLL: return ScrollSnap() < -0.1f; + case ACTION_MENU_RIGHT_SCROLL: return ScrollSnap() < 0; ACTION_CASES(KPressed) } } @@ -190,18 +239,35 @@ bool C_4JInput::ButtonPressed(int iPad, unsigned char ucAction) { bool C_4JInput::ButtonReleased(int iPad, unsigned char ucAction) { if (iPad != 0 || ucAction == 255) return false; switch (ucAction) { - case MINECRAFT_ACTION_ACTION: return MouseLReleased() || KReleased(SDL_SCANCODE_RETURN); - case MINECRAFT_ACTION_USE: return MouseRReleased() || KReleased(SDL_SCANCODE_F); - case MINECRAFT_ACTION_SNEAK_TOGGLE: return KReleased(SDL_SCANCODE_LSHIFT) || KReleased(SDL_SCANCODE_RSHIFT) || KReleased(SDL_SCANCODE_LCTRL) || KReleased(SDL_SCANCODE_RCTRL); + case MINECRAFT_ACTION_ACTION: return MouseLReleased() || KReleased(SDL_SCANCODE_RETURN); + case MINECRAFT_ACTION_USE: return MouseRReleased() || KReleased(SDL_SCANCODE_F); + case MINECRAFT_ACTION_SNEAK_TOGGLE: return KReleased(SDL_SCANCODE_LSHIFT) || KReleased(SDL_SCANCODE_RSHIFT) || KReleased(SDL_SCANCODE_LCTRL) || KReleased(SDL_SCANCODE_RCTRL); case MINECRAFT_ACTION_LEFT_SCROLL: case ACTION_MENU_LEFT_SCROLL: case MINECRAFT_ACTION_RIGHT_SCROLL: - case ACTION_MENU_RIGHT_SCROLL: return false; + case ACTION_MENU_RIGHT_SCROLL: return false; ACTION_CASES(KReleased) } } unsigned int C_4JInput::GetValue(int iPad, unsigned char ucAction, bool) { + if (iPad != 0) return 0; + if (ucAction == MINECRAFT_ACTION_LEFT_SCROLL) { + if (s_scrollTicksForGetValue > 0) { + unsigned int v = (unsigned int)s_scrollTicksForGetValue; + s_scrollTicksForGetValue = 0; + return v; + } + return 0u; + } + if (ucAction == MINECRAFT_ACTION_RIGHT_SCROLL) { + if (s_scrollTicksForGetValue < 0) { + unsigned int v = (unsigned int)(-s_scrollTicksForGetValue); + s_scrollTicksForGetValue = 0; + return v; + } + return 0u; + } return ButtonDown(iPad, ucAction) ? 1u : 0u; } // Left stick movement, the one that moves the player around or selects menu options. (Soon be tested.) @@ -211,11 +277,10 @@ float C_4JInput::GetJoypadStick_LX(int, bool) { float C_4JInput::GetJoypadStick_LY(int, bool) { return (KDown(SDL_SCANCODE_W) ? 1.f : 0.f) - (KDown(SDL_SCANCODE_S) ? 1.f : 0.f); } - // We use mouse movement and convert it into a Right Stick output using logarithmic scaling // This is the most important mouse part. Yet it's so small. static float MouseAxis(float raw) { - if (fabsf(raw) < 0.0001f) return 0.f; // from glfw previous code + if (fabsf(raw) < 0.0001f) return 0.f; // from 4j previous code return (raw >= 0.f ? 1.f : -1.f) * sqrtf(fabsf(raw)); } // We apply the Stick movement on the R(Right) X(2D Position) @@ -241,6 +306,12 @@ void C_4JInput::SetMenuDisplayed(int iPad, bool bVal) { s_prevMenuDisplayed = bVal; } +int C_4JInput::GetScrollDelta() { + int v = s_scrollTicksForButtonPressed; + s_scrollTicksForButtonPressed = 0; + return v; +} + void C_4JInput::SetDeadzoneAndMovementRange(unsigned int, unsigned int){} void C_4JInput::SetGameJoypadMaps(unsigned char, unsigned char, unsigned int){} unsigned int C_4JInput::GetGameJoypadMaps(unsigned char, unsigned char){ return 0; } @@ -262,4 +333,4 @@ EKeyboardResult C_4JInput::RequestKeyboard(LPCWSTR, LPCWSTR, DWORD, UINT, void C_4JInput::GetText(uint16_t *s){ if (s) s[0] = 0; } bool C_4JInput::VerifyStrings(WCHAR**, int, int(*)(LPVOID, STRING_VERIFY_RESPONSE*), LPVOID){ return true; } void C_4JInput::CancelQueuedVerifyStrings(int(*)(LPVOID, STRING_VERIFY_RESPONSE*), LPVOID){} -void C_4JInput::CancelAllVerifyInProgress(){} +void C_4JInput::CancelAllVerifyInProgress(){} \ No newline at end of file diff --git a/4J.Input/4J_Input.h b/4J.Input/4J_Input.h index 9ac5c55d1..59a44f60b 100644 --- a/4J.Input/4J_Input.h +++ b/4J.Input/4J_Input.h @@ -106,6 +106,8 @@ public: unsigned char GetJoypadRTrigger(int iPad, bool bCheckMenuDisplay=true); void SetMenuDisplayed(int iPad, bool bVal); + int GetHotbarSlotPressed(int iPad); + int GetScrollDelta(); // EKeyboardResult RequestKeyboard(UINT uiTitle, UINT uiText, UINT uiDesc, DWORD dwPad, WCHAR *pwchResult, UINT uiResultSize,int( *Func)(LPVOID,const bool),LPVOID lpParam,EKeyboardMode eMode,C4JStringTable *pStringTable=NULL); // EKeyboardResult RequestKeyboard(UINT uiTitle, LPCWSTR pwchDefault, UINT uiDesc, DWORD dwPad, WCHAR *pwchResult, UINT uiResultSize,int( *Func)(LPVOID,const bool),LPVOID lpParam, EKeyboardMode eMode,C4JStringTable *pStringTable=NULL); diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 80c75bad5..c87ebac86 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -3179,13 +3179,12 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) ui.SetTooltips( iPad, iA,iB,iX,iY,iLT,iRT,iLB,iRB); int wheel = 0; - if (InputManager.GetValue(iPad, MINECRAFT_ACTION_LEFT_SCROLL, true) > 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL) ) - { - wheel = 1; - } - else if (InputManager.GetValue(iPad, MINECRAFT_ACTION_RIGHT_SCROLL,true) > 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_RIGHT_SCROLL) ) - { - wheel = -1; + unsigned int leftTicks = InputManager.GetValue(iPad, MINECRAFT_ACTION_LEFT_SCROLL, true); + unsigned int rightTicks = InputManager.GetValue(iPad, MINECRAFT_ACTION_RIGHT_SCROLL, true); + if (leftTicks > 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL)) { + wheel = (int)leftTicks; // positive = left + } else if (rightTicks > 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_RIGHT_SCROLL)) { + wheel = -(int)rightTicks; // negative = right } if (wheel != 0) { @@ -3208,7 +3207,6 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) options->flySpeed += wheel * .25f; } } - if( gameMode->isInputAllowed(MINECRAFT_ACTION_ACTION) ) { if((player->ullButtonsPressed&(1LL<ullButtonsPressed&(1LL<ullButtonsPressed&(1LL<ullButtonsPressed; bool selected = false; + #ifdef __PSVITA__ // 4J-PB - use the touchscreen for quickselect SceTouchData* pTouchData = InputManager.GetTouchPadData(iPad,false); @@ -3437,6 +3437,14 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) } } #endif + { + int hotbarSlot = InputManager.GetHotbarSlotPressed(iPad); + if (hotbarSlot >= 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL)) + { + player->inventory->selected = hotbarSlot; + selected = true; + } + } if( selected || wheel != 0 || (player->ullButtonsPressed&(1LL<inventory.swapPaint(wheel); - + { + int hotbarSlot = InputManager.GetHotbarSlotPressed(iPad); + if (hotbarSlot >= 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL)) + { + player->inventory->selected = hotbarSlot; + selected = true; + } + } if (options.isFlying) { if (wheel > 0) wheel = 1; if (wheel < 0) wheel = -1; From 9752dec5e7697ded24be8a25c7395f3a7d132220 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 20:08:01 -0500 Subject: [PATCH 30/33] fix: remove juicey's visionary offhand implementation --- 4J.Input/4J_Input.cpp | 4 ++-- Minecraft.Client/Minecraft.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/4J.Input/4J_Input.cpp b/4J.Input/4J_Input.cpp index e28c30b68..138313454 100644 --- a/4J.Input/4J_Input.cpp +++ b/4J.Input/4J_Input.cpp @@ -40,7 +40,7 @@ static const int s_watchedKeys[] = { SDL_SCANCODE_TAB, SDL_SCANCODE_LCTRL, SDL_SCANCODE_RCTRL, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, SDL_SCANCODE_7, SDL_SCANCODE_8, - SDL_SCANCODE_9, SDL_SCANCODE_0, + SDL_SCANCODE_9, }; static const int s_watchedKeyCount = (int)(sizeof(s_watchedKeys) / sizeof(s_watchedKeys[0])); @@ -164,7 +164,7 @@ int C_4JInput::GetHotbarSlotPressed(int iPad) { static const int sc[10] = { SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, SDL_SCANCODE_7, SDL_SCANCODE_8, - SDL_SCANCODE_9, SDL_SCANCODE_0 + SDL_SCANCODE_9, }; static bool s_wasDown[10] = {}; diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index c87ebac86..9286cb9c0 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -3439,7 +3439,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) #endif { int hotbarSlot = InputManager.GetHotbarSlotPressed(iPad); - if (hotbarSlot >= 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL)) + if (hotbarSlot >= 0 && hotbarSlot <= 9) { player->inventory->selected = hotbarSlot; selected = true; From 304c591512fcdab103ed67671274b0f199c2865a Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 20:08:54 -0500 Subject: [PATCH 31/33] fix: `hotbarSlot` bounds check --- Minecraft.Client/Minecraft.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 9286cb9c0..e64e9289e 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -3439,7 +3439,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) #endif { int hotbarSlot = InputManager.GetHotbarSlotPressed(iPad); - if (hotbarSlot >= 0 && hotbarSlot <= 9) + if (hotbarSlot > 0 && hotbarSlot < 10) { player->inventory->selected = hotbarSlot; selected = true; From 28018e50221a96a8ec8a0119b781e9db1d6cb9f6 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 20:11:35 -0500 Subject: [PATCH 32/33] refactor: die FPS flamegraph --- Minecraft.Client/Minecraft.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index e64e9289e..995d45840 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -1895,7 +1895,6 @@ void Minecraft::run_middle() Sleep(10); } */ - renderFpsMeter(tickDuraction); if (options->renderDebug) { From ca9ac8e7f76c60531687ab60d128b200f9055d41 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Tue, 10 Mar 2026 20:18:03 -0500 Subject: [PATCH 33/33] fix: hotbarSlot is zero-indexed, move max hotbar slots into a constant --- 4J.Input/4J_Input.cpp | 8 +++++--- Minecraft.Client/Minecraft.cpp | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/4J.Input/4J_Input.cpp b/4J.Input/4J_Input.cpp index 138313454..2ed4de5e4 100644 --- a/4J.Input/4J_Input.cpp +++ b/4J.Input/4J_Input.cpp @@ -161,14 +161,16 @@ void C_4JInput::Tick() { int C_4JInput::GetHotbarSlotPressed(int iPad) { if (iPad != 0) return -1; - static const int sc[10] = { + constexpr size_t NUM_HOTBAR_SLOTS = 9; + + static const int sc[NUM_HOTBAR_SLOTS] = { SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, }; - static bool s_wasDown[10] = {}; + static bool s_wasDown[NUM_HOTBAR_SLOTS] = {}; - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < NUM_HOTBAR_SLOTS; ++i) { bool down = KDown(sc[i]); bool pressed = down && !s_wasDown[i]; s_wasDown[i] = down; diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 995d45840..fe9c48fc1 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -3438,7 +3438,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) #endif { int hotbarSlot = InputManager.GetHotbarSlotPressed(iPad); - if (hotbarSlot > 0 && hotbarSlot < 10) + if (hotbarSlot >= 0 && hotbarSlot <= 9) { player->inventory->selected = hotbarSlot; selected = true;