From 58add4e69fe42e4bb34c672e528931dfe3efeeda Mon Sep 17 00:00:00 2001 From: theaddon Date: Mon, 29 Dec 2025 00:07:02 +0100 Subject: [PATCH 1/2] Updated to newer Amethyst format --- .github/workflows/VersionTools.ts | 20 +++ .github/workflows/build.yml | 209 +++++++++++----------- .gitignore | 13 +- .vscode/c_cpp_properties.json | 23 +++ .vscode/settings.json | 5 - CMakeLists.txt | 59 ------ LICENSE | 201 --------------------- README.md | 70 +++++++- data/.gitignore | 2 + data/config.json | 49 +++++ data/packs/BP/.gitkeep | 0 data/packs/RP/manifest.json | 29 +++ data/packs/RP/texts/en_US.lang | 0 data/packs/RP/textures/blocks/.gitkeep | 0 data/packs/RP/textures/entity/.gitkeep | 0 data/packs/RP/textures/item_texture.ts | 30 ++++ data/packs/RP/textures/items/.gitkeep | 0 data/packs/RP/textures/terrain_texture.ts | 29 +++ data/packs/data/.gitkeep | 0 data/packs/data/generated/deno.json | 12 ++ data/packs/data/generated/deno.lock | 48 +++++ mod.json | 17 ++ mod.json.in | 7 - src/ShulkerRenderer.cpp | 142 +++++++-------- src/ShulkerRenderer.h | 22 --- src/ShulkerRenderer.hpp | 22 +++ src/dllmain.cpp | 168 +++++++++-------- src/dllmain.h | 41 ----- src/dllmain.hpp | 41 +++++ xmake.lua | 49 +++++ 30 files changed, 704 insertions(+), 604 deletions(-) create mode 100644 .github/workflows/VersionTools.ts create mode 100644 .vscode/c_cpp_properties.json delete mode 100644 .vscode/settings.json delete mode 100644 CMakeLists.txt delete mode 100644 LICENSE create mode 100644 data/.gitignore create mode 100644 data/config.json create mode 100644 data/packs/BP/.gitkeep create mode 100644 data/packs/RP/manifest.json create mode 100644 data/packs/RP/texts/en_US.lang create mode 100644 data/packs/RP/textures/blocks/.gitkeep create mode 100644 data/packs/RP/textures/entity/.gitkeep create mode 100644 data/packs/RP/textures/item_texture.ts create mode 100644 data/packs/RP/textures/items/.gitkeep create mode 100644 data/packs/RP/textures/terrain_texture.ts create mode 100644 data/packs/data/.gitkeep create mode 100644 data/packs/data/generated/deno.json create mode 100644 data/packs/data/generated/deno.lock create mode 100644 mod.json delete mode 100644 mod.json.in delete mode 100644 src/ShulkerRenderer.h create mode 100644 src/ShulkerRenderer.hpp delete mode 100644 src/dllmain.h create mode 100644 src/dllmain.hpp create mode 100644 xmake.lua diff --git a/.github/workflows/VersionTools.ts b/.github/workflows/VersionTools.ts new file mode 100644 index 0000000..f8070cb --- /dev/null +++ b/.github/workflows/VersionTools.ts @@ -0,0 +1,20 @@ +interface Config { + meta: { + version: string, + } +} + +const filePath = "./mod.json"; +const modConfig: Config = JSON.parse(await Deno.readTextFile(filePath)); + +function bumpVersion(version: string) { + const parts = version.split(".").map(Number); + parts[2]++; + return parts.join("."); +} + +modConfig.meta.version = bumpVersion(modConfig.meta.version); +Deno.writeTextFile(filePath, JSON.stringify(modConfig, null, 4)); + +// Output the new version for GitHub Actions +console.log(modConfig.meta.version); \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9cc0535..c6f3dc8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,111 +1,112 @@ name: Publish Version on: - workflow_dispatch: + workflow_dispatch: env: - MOD_NAME: Better-Inventory + MOD_NAME: Better-Inventory # Replace with your mod name jobs: - build: - runs-on: windows-latest - - steps: - # Clone the current repository - - name: Checkout code - uses: actions/checkout@v2 - - # Clone AmethystAPI into /amethyst - - name: Checkout Amethyst - uses: actions/checkout@v2 - with: - repository: 'FrederoxDev/Amethyst' - path: 'amethyst' - - # Install required tools - - name: Install NASM and GH - run: | - choco install nasm -y - choco install gh -y - shell: cmd - - - name: Setup Visual studio - uses: microsoft/setup-msbuild@v2 - - # Bump up the version number - - name: Extract and increment version number - id: increment_version - run: | - $filePath = "CMakeLists.txt" - $version_line = Select-String -Path $filePath -Pattern 'set\(MOD_VERSION "(.*)"\)' - if ($version_line -match 'set\(MOD_VERSION "(\d+)\.(\d+)\.(\d+)"\)') { - $major = [int]$matches[1] - $minor = [int]$matches[2] - $patch = [int]$matches[3] - - # Increment the minor version - $new_patch = $patch + 1 - $new_version = "$major.$minor.$new_patch" - - # Update the CMakeLists.txt file - (Get-Content $filePath) -replace 'set\(MOD_VERSION ".*"\)', "set(MOD_VERSION `"$new_version`")" | Set-Content $filePath - - echo "NEW_VERSION=$new_version" >> $env:GITHUB_ENV - echo "FILE_PATH=$filePath" >> $env:GITHUB_ENV - } else { - Write-Error "Version line not found or does not match the expected format." - exit 1 - } - shell: pwsh - - - name: Push bumped version - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add $env:FILE_PATH - git commit -m "Bump version to $env:NEW_VERSION" - git push - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: pwsh - - - name: Build Mod - run: | - mkdir build - cd build - cmake -DCI_CD_BUILD=ON .. - msbuild ${{ env.MOD_NAME }}.sln - - - name: Package Build - run: | - $version = $env:NEW_VERSION - $sourcePath = "dist/${{ env.MOD_NAME }}@$version" - $zipPath = "dist/${{ env.MOD_NAME }}@$version.zip" - - if (-Not (Test-Path -Path $sourcePath)) { - Write-Error "Source path does not exist: $sourcePath" - exit 1 - } - - Add-Type -AssemblyName System.IO.Compression.FileSystem - [System.IO.Compression.ZipFile]::CreateFromDirectory($sourcePath, $zipPath) - shell: pwsh - - - name: Create GitHub Release - id: create_release - run: | - $tag_name = "v$env:NEW_VERSION" - $release_name = "Release $env:NEW_VERSION" - gh release create $tag_name --title "$release_name" --notes "Automated release" --target main --repo ${{ github.repository }} --draft=false --prerelease=false - shell: pwsh - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Upload Release Asset - run: | - $asset_path = "dist/${{ env.MOD_NAME }}@$env:NEW_VERSION.zip" - $asset_label = "${{ env.MOD_NAME }}@$env:NEW_VERSION.zip" - gh release upload "v$env:NEW_VERSION" "$asset_path#$asset_label" --repo ${{ github.repository }} - shell: pwsh - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + build: + runs-on: windows-latest + steps: + # Checkout current mod + - name: Checkout code + uses: actions/checkout@v4 + + # Checkout AmethystAPI into /Amethyst + - name: Clone AmethystAPI manually + run: git clone --recurse-submodules https://github.com/FrederoxDev/Amethyst.git Amethyst + + # Build dependencies + - name: Install Build Tools + shell: powershell + run: | + choco install xmake -y + choco install deno -y + + # Bump mod version + - name: Get mod version + id: get_version + shell: powershell + run: | + Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 + refreshenv + $NEW_VERSION = deno run --allow-read --allow-write .github/workflows/VersionTools.ts + echo "NEW_VERSION=$NEW_VERSION" >> $env:GITHUB_ENV + + - name: Push bumped version + shell: powershell + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add mod.json + git commit -m "Bump version to $env:NEW_VERSION" + git push + + - name: Package Resources + shell: powershell + run: | + $url = "https://github.com/Bedrock-OSS/regolith/releases/download/1.5.2/regolith_1.5.2_windows_386.zip" + $zipPath = "$env:TEMP\regolith.zip" + $extractPath = "$env:TEMP\regolith" + + Invoke-WebRequest $url -OutFile $zipPath + Expand-Archive $zipPath -DestinationPath $extractPath + + cd data + + # Ensure deno is in path + Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 + refreshenv + + & "$extractPath\regolith.exe" install-all + & "$extractPath\regolith.exe" run local + + - name: Build + run: | + Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 + refreshenv + + xmake f -y --automated_build=y + xmake + shell: powershell + + - name: Package Mod + shell: powershell + run: | + $zipName = "$env:MOD_NAME@$env:NEW_VERSION.zip" + $distPath = "dist" + + $excluded = Get-ChildItem -Path $distPath -Recurse -File | Where-Object { $_.Extension -in ".lib", ".exp" } + $tempPath = "$env:TEMP\dist_temp" + Remove-Item $tempPath -Recurse -Force -ErrorAction SilentlyContinue + Copy-Item $distPath $tempPath -Recurse + + foreach ($file in $excluded) { + $fileToRemove = $file.FullName.Replace((Resolve-Path $distPath).Path, $tempPath) + Remove-Item $fileToRemove -Force + } + + Compress-Archive -Path "$tempPath\*" -DestinationPath $zipName + + - name: Create GitHub Release + id: create_release + run: | + $tag_name = "v$env:NEW_VERSION" + $release_name = "Release $env:NEW_VERSION" + gh release create $tag_name --title "$release_name" --notes "Automated release" --target main --repo ${{ github.repository }} --draft=false --prerelease=false + shell: pwsh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Release Asset + run: | + $asset_path = "${{ env.MOD_NAME }}@$env:NEW_VERSION.zip" + $asset_label = "${{ env.MOD_NAME }}@$env:NEW_VERSION.zip" + gh release upload "v$env:NEW_VERSION" "$asset_path#$asset_label" --repo ${{ github.repository }} + shell: pwsh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index d163863..009a285 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,12 @@ -build/ \ No newline at end of file +/build +/.xmake +/.vscode/compile_commands.json +/.importer +/vsxmake2022 +/Amethyst +/dist + +!data/packs/RP/textures/blocks +!data/packs/RP/textures/items +!data/packs/RP/textures/entity +!data/packs/BP \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..50eee10 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,23 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**", + "${AMETHYST_SRC}/AmethystAPI/src", + "${AMETHYST_SRC}/AmethystAPI/include" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "windowsSdkVersion": "10.0.26100.0", + "compilerPath": "cl.exe", + "cStandard": "c17", + "cppStandard": "c++23", + "intelliSenseMode": "windows-msvc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 0cba2e6..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "files.associations": { - "iostream": "cpp" - } -} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index ae598e4..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,59 +0,0 @@ -cmake_minimum_required(VERSION 3.12) -project(Better-Inventory CXX ASM_NASM) -set(MOD_VERSION "1.6.1") -set(MOD_AUTHOR "FrederoxDev") - -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /await") - -# Amethyst Minecraft Folder -if (CI_CD_BUILD) - configure_file(mod.json.in "${CMAKE_SOURCE_DIR}/dist/${PROJECT_NAME}@${MOD_VERSION}/mod.json" @ONLY) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_SOURCE_DIR}/dist/${PROJECT_NAME}@${MOD_VERSION}") - set(AMETHYST_SRC "${CMAKE_SOURCE_DIR}/amethyst") -else() - set(AmethystFolder "$ENV{localappdata}/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/amethyst/") - configure_file(mod.json.in "${AmethystFolder}/mods/${PROJECT_NAME}@${MOD_VERSION}/mod.json" @ONLY) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${AmethystFolder}/mods/${PROJECT_NAME}@${MOD_VERSION}") - set(AMETHYST_SRC "$ENV{amethyst_src}/amethyst") -endif() - -# Define only RelWithDebInfo as the available build configuration -set(CMAKE_CXX_STANDARD 23) -set_property(GLOBAL PROPERTY USE_FOLDERS ON) -set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo" CACHE STRING "Build configurations" FORCE) -set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: RelWithDebInfo" FORCE) - -# Project Files -file(GLOB_RECURSE ${PROJECT_NAME}_All - ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/*.asm - ${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h -) - -add_library(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_All}) - -# Pass mod options to the source code -target_compile_definitions(${PROJECT_NAME} PRIVATE MOD_VERSION="${MOD_VERSION}") -target_compile_definitions(${PROJECT_NAME} PRIVATE MOD_AUTHOR="${MOD_AUTHOR}") -target_compile_definitions(${PROJECT_NAME} PRIVATE MOD_NAME="${PROJECT_NAME}") - -# Link libraries -target_link_libraries(${PROJECT_NAME} PRIVATE - AmethystAPI - libhat - ${AMETHYST_SRC}/AmethystAPI/lib/fmt.lib -) - -# Enable multi processor compilation for C++, to make it go brrrrrr -target_compile_options(${PROJECT_NAME} PRIVATE - $<$:/MP> -) - -# EnTT Config Options -target_compile_definitions(${PROJECT_NAME} PUBLIC ENTT_PACKED_PAGE=128) - -# Get the AmethystAPI Lib -add_subdirectory("${AMETHYST_SRC}/AmethystAPI" "build") diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md index d6cccae..7de57a0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,67 @@ -# Better Inventory +# Amethyst-Template -Better Inventory is an inventory improvement mod for Minecraft Bedrock edition, version `1.21.0.3`. The mod is built with [Amethyst](https://github.com/FrederoxDev/Amethyst) and it adds in a shulker box preview, as well as the ability to see extra information about other items, like its identifier, namespace, durability and aux id. +Opinionated template for an Amethyst Mod, will be the base mod used in all amethyst guides! Follow the steps below to build the mod -### Shulker box previewer -![image](https://github.com/FrederoxDev/Better-Inventory/assets/69014593/a6f26fd7-f934-4a9a-95ba-5f03eb950509) +## How to configure -### Item Information -![image](https://github.com/FrederoxDev/Better-Inventory/assets/69014593/97290890-1a12-4c61-a9ac-407bf78289d6) +1. Open `xmake.lua` and configure the mod options: +``` +-- Mod Options +local mod_name = "Amethyst-Template" -- Replace with the name of your mod +``` + +2. Open `data/packs/RP/manifest.json` and replace the name, and generate the two new UUIDv4s [here](https://www.uuidgenerator.net/version4) + +3. Open `.github/build.yml` and replace the mod name +``` +env: + MOD_NAME: Amethyst-Template # Replace with your mod name +``` + +4. Open `mod.json` and fill in all the mod options there too. +``` +{ + "meta": { + "name": "Amethyst-Template", + "version": "1.0.0", + "namespace": "amethyst_template", + "author": "FrederoxDev" + } +} +``` + +5. Open `data/packs/RP/textures/item_texture.ts` and edit the project namespace to match the `mod.json` +``` +const projectNamespace = "amethyst_template"; +``` + +## Building + +1. To generate a visual studio solution run the command: +``` +xmake project -k vsxmake -m "release" +``` + +2. Open the `.sln` file in `./vsxmake2022` + +3. To build your project, either press `Ctrl+Shift+B` in visual studio OR run the `xmake` command + +4. To build your RP/BP run these commands +``` +cd data +rgl watch +``` + +## Mod Build Config + +`extra_deps` - An array of projects to add with `add_deps()` +`extra_links` - An array of projects to link with your mod using `add_links()` +`extra_include_dirs` - An array of extra directories to include +`extra_header_files` - An array of extra header files to include +`extra_files` - An array of extra files to use + +## Additional Information + +Any textures placed into the textures/items will automatically be included into an `item_textures.json` file that is generated by `data/packs/RP/textures/item_texture.ts`. + +The icon identifier is determined by the file name, i.e. `textures/items/example_item.png` will become `amethyst_template:example_item` \ No newline at end of file diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..3f195ca --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,2 @@ +/build +/.regolith \ No newline at end of file diff --git a/data/config.json b/data/config.json new file mode 100644 index 0000000..b38a7ac --- /dev/null +++ b/data/config.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://raw.githubusercontent.com/Bedrock-OSS/regolith-schemas/main/config/v1.4.json", + "author": "FrederoxDev", + "name": "Better-Inventory", + "packs": { + "resourcePack": "./packs/RP", + "behaviorPack": "./packs/BP" + }, + "regolith": { + "dataPath": "./packs/data", + "filterDefinitions": { + "regolith_generators": { + "url": "github.com/FrederoxDev/Regolith-Generators", + "version": "1.0.4" + } + }, + "formatVersion": "1.4.0", + "profiles": { + "default": { + "export": { + "bpPath": "%localappdata%/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/amethyst/mods/Amethyst-Template@0.0.0-dev/behavior_packs/main_bp", + "build": "standard", + "readOnly": false, + "rpPath": "%localappdata%/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/amethyst/mods/Amethyst-Template@0.0.0-dev/resource_packs/main_rp", + "target": "exact" + }, + "filters": [ + { + "filter": "regolith_generators" + } + ] + }, + "local": { + "export": { + "bpPath": "../dist/behavior_packs/main_bp", + "build": "standard", + "readOnly": false, + "rpPath": "../dist/resource_packs/main_rp", + "target": "exact" + }, + "filters": [ + { + "profile": "default" + } + ] + } + } + } +} \ No newline at end of file diff --git a/data/packs/BP/.gitkeep b/data/packs/BP/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/packs/RP/manifest.json b/data/packs/RP/manifest.json new file mode 100644 index 0000000..af3b046 --- /dev/null +++ b/data/packs/RP/manifest.json @@ -0,0 +1,29 @@ +{ + "format_version": 2, + "header": { + "name": "Better-Inventory RP", + "description": "By FrederoxDev", + "uuid": "88e875c6-1d8d-4c12-a815-b1081a6ffadc", + "version": [ + 1, + 0, + 0 + ], + "min_engine_version": [ + 1, + 21, + 0 + ] + }, + "modules": [ + { + "type": "resources", + "uuid": "8152d87d-969e-4091-bdd7-eea3bd85fd62", + "version": [ + 1, + 0, + 0 + ] + } + ] +} \ No newline at end of file diff --git a/data/packs/RP/texts/en_US.lang b/data/packs/RP/texts/en_US.lang new file mode 100644 index 0000000..e69de29 diff --git a/data/packs/RP/textures/blocks/.gitkeep b/data/packs/RP/textures/blocks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/packs/RP/textures/entity/.gitkeep b/data/packs/RP/textures/entity/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/packs/RP/textures/item_texture.ts b/data/packs/RP/textures/item_texture.ts new file mode 100644 index 0000000..7833878 --- /dev/null +++ b/data/packs/RP/textures/item_texture.ts @@ -0,0 +1,30 @@ +import { createFile } from "Regolith-Generators" +import { join, extname, basename } from "jsr:@std/path"; +import { walkSync } from "jsr:@std/fs"; + +// Project namespace no longer needs to be set here! +// File can just be closed! + +const ROOT_DIR = Deno.env.get("ROOT_DIR")!; +const modJsonFilePath = join(ROOT_DIR, "..", "mod.json") +const projectNamespace = JSON.parse(Deno.readTextFileSync(modJsonFilePath)).meta.namespace; + +const itemTexturesFolder = join(Deno.cwd(), "RP", "textures", "items"); + +const textureData: Record = {}; + +for (const entry of walkSync(itemTexturesFolder, { exts: [".png"], includeFiles: true })) { + const textureName = basename(entry.path, extname(entry.path)); + const relativePath = entry.path.replaceAll("\\", "/").split("/RP/")[1]; + const noExtensionPath = relativePath.replace(extname(relativePath), ""); + + textureData[`${projectNamespace}:${textureName}`] = { + "textures": noExtensionPath + } +} + +createFile({ + resource_pack_name: "Better-Inventory RP", + texture_name: 'atlas.items', + texture_data: textureData, +}); \ No newline at end of file diff --git a/data/packs/RP/textures/items/.gitkeep b/data/packs/RP/textures/items/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/packs/RP/textures/terrain_texture.ts b/data/packs/RP/textures/terrain_texture.ts new file mode 100644 index 0000000..35197cd --- /dev/null +++ b/data/packs/RP/textures/terrain_texture.ts @@ -0,0 +1,29 @@ +import { createFile } from "Regolith-Generators" +import { join, extname, basename } from "jsr:@std/path"; +import { walkSync } from "jsr:@std/fs"; + +const ROOT_DIR = Deno.env.get("ROOT_DIR")!; +const modJsonFilePath = join(ROOT_DIR, "..", "mod.json") +const projectNamespace = JSON.parse(Deno.readTextFileSync(modJsonFilePath)).meta.namespace; + +const texturesFolder = join(Deno.cwd(), "RP", "textures", "blocks"); + +const textureData: Record = {}; + +for (const entry of walkSync(texturesFolder, { exts: [".png"], includeFiles: true })) { + const textureName = basename(entry.path, extname(entry.path)); + const relativePath = entry.path.replaceAll("\\", "/").split("/RP/")[1]; + const noExtensionPath = relativePath.replace(extname(relativePath), ""); + + textureData[`${projectNamespace}:${textureName}`] = { + "textures": noExtensionPath + } +} + +createFile({ + "num_mip_levels": 4, + "padding": 8, + "resource_pack_name": "pack.name", + "texture_name": "atlas.terrain", + texture_data: textureData, +}); \ No newline at end of file diff --git a/data/packs/data/.gitkeep b/data/packs/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/packs/data/generated/deno.json b/data/packs/data/generated/deno.json new file mode 100644 index 0000000..f9c3666 --- /dev/null +++ b/data/packs/data/generated/deno.json @@ -0,0 +1,12 @@ +{ + "imports": { + "Regolith-Generators": "jsr:@frederoxdev/regolith-generators", + "@/RP/": "../../RP/", + "@/BP/": "../../BP/" + }, + "compilerOptions": { + "jsx": "react", + "jsxFactory": "createMinecraftElement", + "jsxFragmentFactory": "createMinecraftFragment" + } +} \ No newline at end of file diff --git a/data/packs/data/generated/deno.lock b/data/packs/data/generated/deno.lock new file mode 100644 index 0000000..3356034 --- /dev/null +++ b/data/packs/data/generated/deno.lock @@ -0,0 +1,48 @@ +{ + "version": "5", + "specifiers": { + "jsr:@frederoxdev/regolith-generators@*": "1.2.4", + "jsr:@std/fs@*": "1.0.19", + "jsr:@std/fs@1.0.6": "1.0.6", + "jsr:@std/internal@^1.0.9": "1.0.10", + "jsr:@std/path@*": "1.1.1", + "jsr:@std/path@^1.0.8": "1.1.1", + "jsr:@std/path@^1.1.1": "1.1.1" + }, + "jsr": { + "@frederoxdev/regolith-generators@1.2.4": { + "integrity": "8a867f04560c6cd87fe1130ec0d2aec1867bf0e947e280db33d79d053c7319c1", + "dependencies": [ + "jsr:@std/fs@1.0.6", + "jsr:@std/path@^1.0.8" + ] + }, + "@std/fs@1.0.6": { + "integrity": "42b56e1e41b75583a21d5a37f6a6a27de9f510bcd36c0c85791d685ca0b85fa2", + "dependencies": [ + "jsr:@std/path@^1.0.8" + ] + }, + "@std/fs@1.0.19": { + "integrity": "051968c2b1eae4d2ea9f79a08a3845740ef6af10356aff43d3e2ef11ed09fb06", + "dependencies": [ + "jsr:@std/internal", + "jsr:@std/path@^1.1.1" + ] + }, + "@std/internal@1.0.10": { + "integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7" + }, + "@std/path@1.1.1": { + "integrity": "fe00026bd3a7e6a27f73709b83c607798be40e20c81dde655ce34052fd82ec76", + "dependencies": [ + "jsr:@std/internal" + ] + } + }, + "workspace": { + "dependencies": [ + "jsr:@frederoxdev/regolith-generators@*" + ] + } +} diff --git a/mod.json b/mod.json new file mode 100644 index 0000000..02b8649 --- /dev/null +++ b/mod.json @@ -0,0 +1,17 @@ +{ + "format_version": "1.1.0", + "meta": { + "name": "Better-Inventory", + "version": "1.7.0", + "namespace": "better_inventory", + "author": "FrederoxDev", + "uuid": "08de8928-9158-4b83-a05a-e02a92460da0", + "dependencies": [ + { + "dependency_uuid": "e56c3987-fd4a-4590-9923-a07fc4be7250", + "dependency_namespace": "amethyst", + "version_range": ">=2.0.0 <2.1.0" + } + ] + } +} diff --git a/mod.json.in b/mod.json.in deleted file mode 100644 index 5bfa393..0000000 --- a/mod.json.in +++ /dev/null @@ -1,7 +0,0 @@ -{ - "meta": { - "name": "@PROJECT_NAME@", - "version": "@MOD_VERSION@", - "author": "@MOD_AUTHOR@" - } -} \ No newline at end of file diff --git a/src/ShulkerRenderer.cpp b/src/ShulkerRenderer.cpp index a498ac1..64e62a8 100644 --- a/src/ShulkerRenderer.cpp +++ b/src/ShulkerRenderer.cpp @@ -1,7 +1,7 @@ -#include "ShulkerRenderer.h" -#include -#include -#include +#include "ShulkerRenderer.hpp" +#include +#include +#include extern ItemStack shulkerInventory[SHULKER_CACHE_SIZE][27]; @@ -20,100 +20,100 @@ glm::tvec2 itemSlotUvSize(22.0f / 256.0f, 22.0f / 256.0f); Amethyst::NinesliceHelper backgroundNineslice(16, 16, 4, 4); int countNewlines(const std::string& str) { - int newlineCount = 0; + int newlineCount = 0; - for (char c : str) { - if (c == '\n') { - newlineCount++; - } - } + for (char c : str) { + if (c == '\n') { + newlineCount++; + } + } - return newlineCount; + return newlineCount; } -void ShulkerRenderer::Render(UIRenderContext* ctx, HoverRenderer* hoverRenderer, int index) { +void ShulkerRenderer::Render(MinecraftUIRenderContext* ctx, HoverRenderer* hoverRenderer, int index) { // Only load inventory resources once if (!hasLoadedTexture) { - mBackgroundTexture = ctx->getTexture("textures/ui/purpleBorder", true); - mItemSlotTexture = ctx->getTexture("textures/gui/gui", true); + mBackgroundTexture = ctx->getTexture("textures/ui/purpleBorder", true); + mItemSlotTexture = ctx->getTexture("textures/gui/gui", true); - hasLoadedTexture = true; + hasLoadedTexture = true; } - float textHeight = (countNewlines(hoverRenderer->mFilteredContent) + 1) * 10.0f; - float panelWidth = slotSize * 9; - float panelHeight = slotSize * 3 + textHeight; + float textHeight = (countNewlines(hoverRenderer->mFilteredContent) + 1) * 10.0f; + float panelWidth = slotSize * 9; + float panelHeight = slotSize * 3 + textHeight; - float panelX = hoverRenderer->mCursorPosition.x + hoverRenderer->mOffset.x; - float panelY = hoverRenderer->mCursorPosition.y + hoverRenderer->mOffset.y; + float panelX = hoverRenderer->mCursorPosition.x + hoverRenderer->mOffset.x; + float panelY = hoverRenderer->mCursorPosition.y + hoverRenderer->mOffset.y; - // Draw the background panel - RectangleArea background = {panelX - 4, panelX + panelWidth + 4, panelY - 4, panelY + panelHeight + 4}; - backgroundNineslice.Draw(background, &mBackgroundTexture, ctx); - ctx->flushImages(mce::Color::WHITE, 1.0f, flushString); + // Draw the background panel + RectangleArea background = { panelX - 4, panelX + panelWidth + 4, panelY - 4, panelY + panelHeight + 4 }; + backgroundNineslice.Draw(background, &mBackgroundTexture, ctx); + ctx->flushImages(mce::Color::WHITE, 1.0f, flushString); - // Draw the item slots + // Draw the item slots for (int x = 0; x < 9; x++) { - for (int y = 0; y < 3; y++) { - glm::tvec2 size(slotSize, slotSize); - glm::tvec2 position(panelX + slotSize * x, panelY + textHeight + slotSize * y); + for (int y = 0; y < 3; y++) { + glm::tvec2 size(slotSize, slotSize); + glm::tvec2 position(panelX + slotSize * x, panelY + textHeight + slotSize * y); - ctx->drawImage(mItemSlotTexture, &position, &size, &itemSlotUvPos, &itemSlotUvSize, 0); - } - } + ctx->drawImage(mItemSlotTexture, position, size, itemSlotUvPos, itemSlotUvSize, 0); + } + } - // It's possible to tint the background here - ctx->flushImages(mce::Color::WHITE, 1.0f, flushString); + // It's possible to tint the background here + ctx->flushImages(mce::Color::WHITE, 1.0f, flushString); - // Draw the item icons - BaseActorRenderContext renderCtxPtr = BaseActorRenderContext(ctx->mScreenContext, ctx->mClient, ctx->mClient->minecraftGame); + // Draw the item icons ScreenContext& screenContext, ClientInstance& clientInstance, IMinecraftGame& minecraftGame + BaseActorRenderContext renderCtxPtr = BaseActorRenderContext(*ctx->mScreenContext, *ctx->mClient, *ctx->mClient->mMinecraftGame); - for (int x = 0; x < 9; x++) { - for (int y = 0; y < 3; y++) { - const ItemStack* itemStack = &shulkerInventory[index][y * 9 + x]; - if (itemStack->mItem == nullptr) continue; + for (int x = 0; x < 9; x++) { + for (int y = 0; y < 3; y++) { + const ItemStack* itemStack = &shulkerInventory[index][y * 9 + x]; + if (itemStack->mItem == nullptr) continue; - float xPos = (x * slotSize) + borderSize + panelX; - float yPos = (y * slotSize) + borderSize + textHeight + panelY; + float xPos = (x * slotSize) + borderSize + panelX; + float yPos = (y * slotSize) + borderSize + textHeight + panelY; - renderCtxPtr.itemRenderer->renderGuiItemNew(&renderCtxPtr, itemStack, 0, xPos, yPos, false, 1.f, 1.f, 1.f); - } - } + renderCtxPtr.itemRenderer.renderGuiItemNew(&renderCtxPtr, itemStack, 0, xPos, yPos, false, 1.f, 1.f, 1.f); + } + } - ctx->flushImages(mce::Color::WHITE, 1.0f, flushString); + ctx->flushImages(mce::Color::WHITE, 1.0f, flushString); - // Draw Item Counts - TextMeasureData textData; - memset(&textData, 0, sizeof(TextMeasureData)); - textData.fontSize = 1.0f; + // Draw Item Counts + TextMeasureData textData; + memset(&textData, 0, sizeof(TextMeasureData)); + textData.fontSize = 1.0f; - CaretMeasureData caretData; - memset(&caretData, 1, sizeof(CaretMeasureData)); + CaretMeasureData caretData; + memset(&caretData, 1, sizeof(CaretMeasureData)); - // Draw the item counts - for (int x = 0; x < 9; x++) { - for (int y = 0; y < 3; y++) { - ItemStack* itemStack = &shulkerInventory[index][y * 9 + x]; - if (itemStack->mItem == nullptr) continue; - if (itemStack->mCount == 1) continue; + // Draw the item counts + for (int x = 0; x < 9; x++) { + for (int y = 0; y < 3; y++) { + ItemStack* itemStack = &shulkerInventory[index][y * 9 + x]; + if (itemStack->mItem == nullptr) continue; + if (itemStack->mCount == 1) continue; - float top = (y * slotSize) + borderSize + textHeight + panelY; - float bottom = top + 16.f; + float top = (y * slotSize) + borderSize + textHeight + panelY; + float bottom = top + 16.f; - float left = (x * slotSize) + borderSize + panelX; - float right = left + 16.f; + float left = (x * slotSize) + borderSize + panelX; + float right = left + 16.f; - std::string text = fmt::format("{}", itemStack->mCount); - RectangleArea rect = { left, right, top + 7.f, bottom}; + std::string text = std::format("{}", itemStack->mCount); + RectangleArea rect = { left, right, top + 7.f, bottom }; - ctx->drawDebugText(&rect, &text, &mce::Color::WHITE, 1.0f, ui::Left, &textData, &caretData); - } - } + ctx->drawDebugText(rect, text, mce::Color::WHITE, 1.0f, ui::Left, textData, caretData); + } + } - ctx->flushText(0.0f); + ctx->flushText(0.0f); - RectangleArea textArea = { panelX, panelX + panelWidth, panelY, panelY + textHeight }; - RectangleArea rect = { panelX, panelX + panelWidth, panelY, panelY + panelHeight }; - ctx->drawDebugText(&textArea, &hoverRenderer->mFilteredContent, &mce::Color::WHITE, 1.0f, ui::Left, &textData, &caretData); - ctx->flushText(0.0f); + RectangleArea textArea = { panelX, panelX + panelWidth, panelY, panelY + textHeight }; + RectangleArea rect = { panelX, panelX + panelWidth, panelY, panelY + panelHeight }; + ctx->drawDebugText(textArea, hoverRenderer->mFilteredContent, mce::Color::WHITE, 1.0f, ui::Left, textData, caretData); + ctx->flushText(0.0f); } \ No newline at end of file diff --git a/src/ShulkerRenderer.h b/src/ShulkerRenderer.h deleted file mode 100644 index 86ab5e4..0000000 --- a/src/ShulkerRenderer.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SHULKER_CACHE_SIZE 16 - -class ShulkerRenderer { -private: - mce::TexturePtr mBackgroundTexture; - mce::TexturePtr mItemSlotTexture; - -public: - void Render(UIRenderContext* ctx, HoverRenderer* hoverRenderer, int index); -}; \ No newline at end of file diff --git a/src/ShulkerRenderer.hpp b/src/ShulkerRenderer.hpp new file mode 100644 index 0000000..735b97f --- /dev/null +++ b/src/ShulkerRenderer.hpp @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SHULKER_CACHE_SIZE 16 + +class ShulkerRenderer { +private: + mce::TexturePtr mBackgroundTexture; + mce::TexturePtr mItemSlotTexture; + +public: + void Render(MinecraftUIRenderContext* ctx, HoverRenderer* hoverRenderer, int index); +}; \ No newline at end of file diff --git a/src/dllmain.cpp b/src/dllmain.cpp index 88247a6..18fced9 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -1,118 +1,112 @@ -#include "dllmain.h" +#include "dllmain.hpp" ClientInstance* client; ShulkerRenderer shulkerRenderer; -ItemStack shulkerInventory[SHULKER_CACHE_SIZE][27]; +ItemStack shulkerInventory[SHULKER_CACHE_SIZE][27]; -SafetyHookInline _Item_appendFormattedHoverText; -SafetyHookInline _Shulker_appendFormattedHoverText; -SafetyHookInline _HoverRenderer__renderHoverBox; +Amethyst::InlineHook _Item_appendFormattedHovertext; +Amethyst::InlineHook _ShulkerBoxBlockItem_appendFormattedHovertext; +Amethyst::InlineHook _HoverRenderer__renderHoverBox; static void Item_appendFormattedHovertext(Item* self, const ItemStackBase& itemStack, Level& level, std::string& text, uint8_t a5) { - _Item_appendFormattedHoverText.call(self, itemStack, level, text, a5); + _Item_appendFormattedHovertext(self, itemStack, level, text, a5); - Item* item = itemStack.mItem; - uint64_t max = item->getMaxDamage(); + Item* item = itemStack.mItem; + uint64_t maxDamage = item->getMaxDamage(); - if (max != 0) { - uint64_t current = max - item->getDamageValue(itemStack.mUserData); - text.append(fmt::format("\n{}7Durability: {} / {}{}r", "\xc2\xa7", current, max, "\xc2\xa7")); - } - - std::string rawNameId = std::string(item->mRawNameId.c_str()); + if (maxDamage != 0) { + uint64_t current = maxDamage - item->getDamageValue(itemStack.mUserData); + text.append(std::format("\n{}7Durability: {} / {}{}r", "\xc2\xa7", current, maxDamage, "\xc2\xa7")); + } - if (rawNameId == "bee_nest" || rawNameId == "beehive") { - AppendBeeNestInformation(text, itemStack); - } + std::string rawNameId = std::string(item->mRawNameId.c_str()); - if (rawNameId.find("shulker_box") != std::string::npos) { - // Don't append the id for shulker boxes (makes it too long) - text.append(fmt::format("\n{}8{}:{}{}r", "\xc2\xa7", item->mNamespace, rawNameId, "\xc2\xa7")); - return; - } + if (rawNameId == "bee_nest" || rawNameId == "beehive") { + AppendBeeNestInformation(text, itemStack); + } - text.append(fmt::format("\n{}8{}:{} ({}){}r", "\xc2\xa7", item->mNamespace, rawNameId, item->mId, "\xc2\xa7")); + if (rawNameId.find("shulker_box") != std::string::npos) { + // Don't append the id for shulker boxes (makes it too long) + text.append(std::format("\n{}8{}:{}{}r", "\xc2\xa7", item->mNamespace, rawNameId, "\xc2\xa7")); + return; + } + + text.append(std::format("\n{}8{}:{} ({}){}r", "\xc2\xa7", item->mNamespace, rawNameId, item->mId, "\xc2\xa7")); } static void AppendBeeNestInformation(std::string& text, const ItemStackBase& itemStack) { - CompoundTag* userData = itemStack.mUserData; - - // There are no bees in the bee nest - if (userData == nullptr || !userData->contains("Occupants")) { - text.append(fmt::format("\n{}5Contains 0 bees", "\xc2\xa7")); - return; - }; - - ListTag* occupants = userData->getList("Occupants"); - text.append(fmt::format("\n{}5Contains {:d} bee{}", "\xc2\xa7", occupants->size(), occupants->size() > 1 ? "s" : "")); + CompoundTag* userData = itemStack.mUserData; + + // There are no bees in the bee nest + if (userData == nullptr || !userData->contains("Occupants")) { + text.append(std::format("\n{}5Contains 0 bees", "\xc2\xa7")); + return; + }; + + ListTag* occupants = userData->getList("Occupants"); + text.append(std::format("\n{}5Contains {:d} bee{}", "\xc2\xa7", occupants->size(), occupants->size() > 1 ? "s" : "")); } int index = 0; -static void Shulker_appendFormattedHovertext(ShulkerBoxBlockItem* self, const ItemStackBase& itemStack, Level& level, std::string& text, uint8_t someBool) { - // Use the appendFormattedHovertext for regular items, we don't want the list of items - Item_appendFormattedHovertext(self, itemStack, level, text, someBool); - - index++; - if (index >= SHULKER_CACHE_SIZE) index = 0; - - // Hide a secret index in the shulker name - // We do this because appendFormattedHovertext gets called for the neightboring items so if there is a shulker - // to the right of this one then its preview will get overriden, so we keep track of multiple at once using a rolling identifier - text += fmt::format("{}{:x}", "\xc2\xa7", index); - int thisIndex = index; - - // Reset all the currrent item stacks - for (auto& itemStack : shulkerInventory[index]) { - itemStack = ItemStack(); - } - - if (!itemStack.mUserData) return; - if (!itemStack.mUserData->contains("Items")) return; - - const ListTag* items = itemStack.mUserData->getList("Items"); - - for (int i = 0; i < items->size(); i++) { - const CompoundTag* itemCompound = items->getCompound(i); - byte slot = itemCompound->getByte("Slot"); - shulkerInventory[thisIndex][slot]._loadItem(itemCompound); - } -} +static void ShulkerBoxBlockItem_appendFormattedHovertext(ShulkerBoxBlockItem* self, const ItemStackBase& itemStack, Level& level, std::string& text, uint8_t someBool) { + // Use the appendFormattedHovertext for regular items, we don't want the list of items + Item_appendFormattedHovertext(self, itemStack, level, text, someBool); -static void HoverRenderer__renderHoverBox(HoverRenderer* self, MinecraftUIRenderContext* ctx, IClientInstance* client, RectangleArea* aabb, float someFloat) { - // This is really bad code, it is relying on the fact that I have also hooked appendFormattedHovertext for items to append the item identifier - // I have no idea where the currently hovered item is stored in the game! I can't find any references to it, so it might be set in some weird place? + index++; + if (index >= SHULKER_CACHE_SIZE) index = 0; + + // Hide a secret index in the shulker name + // We do this because appendFormattedHovertext gets called for the neightboring items so if there is a shulker + // to the right of this one then its preview will get overriden, so we keep track of multiple at once using a rolling identifier + text += std::format("{}{:x}", "\xc2\xa7", index); + int thisIndex = index; - if (self->mFilteredContent.find("shulker_box") != std::string::npos) { - std::string cachedIndex = self->mFilteredContent.substr(self->mFilteredContent.size() - 7, 1); + // Reset all the currrent item stacks + for (auto& itemStack : shulkerInventory[index]) { + itemStack = ItemStack(); + } - try { - int index = std::stoi(cachedIndex, nullptr, 16); - shulkerRenderer.Render(ctx, self, index); - } - catch (...) { - Log::Warning("There was an issue reading the shulker box! No id found, instead got: {}", cachedIndex); - return; - } + if (!itemStack.mUserData) return; + if (!itemStack.mUserData->contains("Items")) return; - return; - } + const ListTag* items = itemStack.mUserData->getList("Items"); - _HoverRenderer__renderHoverBox.thiscall(self, ctx, client, aabb, someFloat); + for (int i = 0; i < items->size(); i++) { + const CompoundTag* itemCompound = items->getCompound(i); + byte slot = itemCompound->getByte("Slot"); + shulkerInventory[thisIndex][slot]._loadItem(itemCompound); + } } -ModFunction void Initialize(AmethystContext& ctx) { - Amethyst::InitializeAmethystMod(ctx); +static void HoverRenderer__renderHoverBox(HoverRenderer* self, MinecraftUIRenderContext* ctx, IClientInstance* client, RectangleArea* aabb, float someFloat) { + // This is really bad code, it is relying on the fact that I have also hooked appendFormattedHovertext for items to append the item identifier + // I have no idea where the currently hovered item is stored in the game! I can't find any references to it, so it might be set in some weird place? + + if (self->mFilteredContent.find("shulker_box") != std::string::npos) { + std::string cachedIndex = self->mFilteredContent.substr(self->mFilteredContent.size() - 7, 1); + + try { + int index = std::stoi(cachedIndex, nullptr, 16); + shulkerRenderer.Render(ctx, self, index); + } + catch (...) { + return; + } - auto& hooks = Amethyst::GetHookManager(); + return; + } + + _HoverRenderer__renderHoverBox(self, ctx, client, aabb, someFloat); +} - hooks.RegisterFunction<&Item::appendFormattedHovertext>("40 55 53 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 ? 49 8B F1 4C 89 44 24 ? 4C 8B F2 48 8B D9"); - hooks.CreateHook<&Item::appendFormattedHovertext>(_Item_appendFormattedHoverText, &Item_appendFormattedHovertext); +ModFunction void Initialize(AmethystContext& ctx, const Amethyst::Mod& mod) { + Amethyst::InitializeAmethystMod(ctx, mod); - hooks.RegisterFunction<&ShulkerBoxBlockItem::appendFormattedHovertext>("40 53 55 56 57 41 56 41 57 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 44 24 ? 4D 8B F9 48 8B DA"); - hooks.CreateHook<&ShulkerBoxBlockItem::appendFormattedHovertext>(_Shulker_appendFormattedHoverText, &Shulker_appendFormattedHovertext); + auto& hooks = Amethyst::GetHookManager(); - hooks.RegisterFunction<&HoverRenderer::_renderHoverBox>("48 8B C4 48 89 58 ? 48 89 70 ? 48 89 78 ? 4C 89 70 ? 55 48 8D 68 ? 48 81 EC ? ? ? ? 0F 29 70 ? 0F 29 78 ? 44 0F 29 40 ? 49 8B D9"); - hooks.CreateHook<&HoverRenderer::_renderHoverBox>(_HoverRenderer__renderHoverBox, &HoverRenderer__renderHoverBox); + VHOOK(Item, appendFormattedHovertext, this); + VHOOK(ShulkerBoxBlockItem, appendFormattedHovertext, this); + HOOK(HoverRenderer, _renderHoverBox, this); } \ No newline at end of file diff --git a/src/dllmain.h b/src/dllmain.h deleted file mode 100644 index d000550..0000000 --- a/src/dllmain.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ShulkerRenderer.h" - -#define ModFunction extern "C" __declspec(dllexport) - -BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { - return TRUE; -} - -static void AppendBeeNestInformation(std::string& text, const ItemStackBase& itemStack); \ No newline at end of file diff --git a/src/dllmain.hpp b/src/dllmain.hpp new file mode 100644 index 0000000..2b895eb --- /dev/null +++ b/src/dllmain.hpp @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ShulkerRenderer.hpp" + +#define ModFunction extern "C" __declspec(dllexport) + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { + return TRUE; +} + +static void AppendBeeNestInformation(std::string& text, const ItemStackBase& itemStack); \ No newline at end of file diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 0000000..b1b0421 --- /dev/null +++ b/xmake.lua @@ -0,0 +1,49 @@ +-- Mod Options +local mod_name = "Better-Inventory" -- Replace with the name of your mod +local targetMajor, targetMinor, targetPatch = 1, 21, 3 -- 1.21.0.3 (Other versions not supported by Amethyst) +local config_options = {} -- Any additional options, see: https://github.com/AmethystAPI/Amethyst-Template/blob/main/README.md + +-- Anything below here should not need to be changed +-- To update your build script if its outdated, replace everything below these comments +-- The latest version can be found here: https://github.com/AmethystAPI/Amethyst-Template/blob/main/xmake.lua +local MOD_BUILD_SCRIPT_VERSION = 2 + +option("automated_build") + set_default(false) + set_showmenu(true) + set_description("Flag to indicate this is an automated build") +option_end() + +option("platform") + set_default("win-client") + set_showmenu(true) + set_values("win-client", "win-server") + set_description("The platform to target building too") +option_end() + +local automated = is_config("automated_build", true) +local platform = get_config("platform") + +local build_script_path +if automated then + build_script_path = path.join("Amethyst", "AmethystAPI", "mod_build.lua") +else + build_script_path = path.join(os.getenv(("AMETHYST_SRC")), "AmethystAPI", "mod_build.lua") +end + +if not os.isfile(build_script_path) then + print("Failed to find build script!" .. build_script_path) +else + includes(build_script_path) + + local build_config = { + MOD_BUILD_SCRIPT_VERSION = MOD_BUILD_SCRIPT_VERSION, + platform = platform, + } + + for k, v in pairs(config_options) do + build_config[k] = v + end + + build_mod(mod_name, targetMajor, targetMinor, targetPatch, automated, build_config) +end \ No newline at end of file From 7e9ce0e70f91dddb33b879a86d57021bd9f32005 Mon Sep 17 00:00:00 2001 From: theaddon Date: Mon, 29 Dec 2025 16:43:33 +0100 Subject: [PATCH 2/2] Add support for shulkers with nbt data --- src/dllmain.cpp | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/dllmain.cpp b/src/dllmain.cpp index 18fced9..c928d13 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -81,21 +81,50 @@ static void ShulkerBoxBlockItem_appendFormattedHovertext(ShulkerBoxBlockItem* se } static void HoverRenderer__renderHoverBox(HoverRenderer* self, MinecraftUIRenderContext* ctx, IClientInstance* client, RectangleArea* aabb, float someFloat) { + // Freddie: // This is really bad code, it is relying on the fact that I have also hooked appendFormattedHovertext for items to append the item identifier // I have no idea where the currently hovered item is stored in the game! I can't find any references to it, so it might be set in some weird place? + // + // Lucy: + // No clue either but this should be more solid if (self->mFilteredContent.find("shulker_box") != std::string::npos) { - std::string cachedIndex = self->mFilteredContent.substr(self->mFilteredContent.size() - 7, 1); - - try { - int index = std::stoi(cachedIndex, nullptr, 16); - shulkerRenderer.Render(ctx, self, index); - } - catch (...) { - return; + // Find the position right after "shulker_box§r" + size_t shulkerPos = self->mFilteredContent.find("shulker_box"); + if (shulkerPos != std::string::npos) { + // Look for the hex index after "shulker_box§r" + // The pattern is: "shulker_box§r§X" where X is our hex digit + size_t searchStart = shulkerPos + 11; // length of "shulker_box" + + // Skip the "§r" that follows + if (searchStart + 2 < self->mFilteredContent.size() && + self->mFilteredContent[searchStart] == '\xc2' && + self->mFilteredContent[searchStart + 1] == '\xa7' && + self->mFilteredContent[searchStart + 2] == 'r') { + searchStart += 3; + } + + // Now look for our index marker "§X" where X is the hex digit + if (searchStart + 2 < self->mFilteredContent.size() && + self->mFilteredContent[searchStart] == '\xc2' && + self->mFilteredContent[searchStart + 1] == '\xa7') { + + char hexChar = self->mFilteredContent[searchStart + 2]; + + try { + std::string hexStr(1, hexChar); + int index = std::stoi(hexStr, nullptr, 16); + + if (index >= 0 && index < SHULKER_CACHE_SIZE) { + shulkerRenderer.Render(ctx, self, index); + return; + } + } + catch (...) { + // Fall through to default rendering + } + } } - - return; } _HoverRenderer__renderHoverBox(self, ctx, client, aabb, someFloat);