From 6c994a54f0d0c7d9b26679c1cd28e57d4691bf2a Mon Sep 17 00:00:00 2001 From: Vitaly Date: Tue, 22 Oct 2024 23:41:19 +0300 Subject: [PATCH 001/753] autodeploy PRs (#218) --- .github/workflows/preview.yml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 526705e9..564fc189 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -6,22 +6,38 @@ env: on: issue_comment: types: [created] + pull_request: jobs: deploy: runs-on: ubuntu-latest # todo skip already created deploys on that commit if: >- - github.event.issue.pull_request != '' && ( - contains(github.event.comment.body, '/deploy') + github.event.issue.pull_request != '' && + ( + contains(github.event.comment.body, '/deploy') + ) + ) || + ( + github.event_name == 'pull_request' && ( + env.AUTO_DEPLOY_PRS contains $'\n' + toString(github.event.pull_request.number) + $'\n' || + startsWith(env.AUTO_DEPLOY_PRS, toString(github.event.pull_request.number) + $'\n') || + endsWith(env.AUTO_DEPLOY_PRS, $'\n' + toString(github.event.pull_request.number)) || + env.AUTO_DEPLOY_PRS == toString(github.event.pull_request.number) + ) ) permissions: pull-requests: write steps: - - name: Checkout + - name: Checkout PR (comment) uses: actions/checkout@v2 + if: github.event_name == 'issue_comment' with: ref: refs/pull/${{ github.event.issue.number }}/head + - name: Checkout PR (pull_request) + uses: actions/checkout@v2 + if: github.event_name == 'pull_request' + - run: npm i -g pnpm@9.0.4 - uses: actions/setup-node@v4 with: @@ -59,7 +75,7 @@ jobs: id: alias env: ALIASES: ${{ env.ALIASES }} - PULL_URL: ${{ github.event.issue.pull_request.url }} + PULL_URL: ${{ github.event.issue.pull_request.url || github.event.pull_request.url }} - name: Set deployment alias if: ${{ steps.alias.outputs.alias != '' && steps.alias.outputs.alias != 'mcraft.fun' && steps.alias.outputs.alias != 's.mcraft.fun' }} run: vercel alias set ${{ steps.deploy.outputs.stdout }} ${{ steps.alias.outputs.alias }} --token=${{ secrets.VERCEL_TOKEN }} --scope=zaro From 95c185fc0b50b318620aa1a791f6f36fb5fad776 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 23 Oct 2024 00:35:33 +0300 Subject: [PATCH 002/753] fix: correct syntax for checking PR numbers in AUTO_DEPLOY_PRS in preview.yml --- .github/workflows/preview.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 564fc189..2e7f933f 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -20,7 +20,7 @@ jobs: ) || ( github.event_name == 'pull_request' && ( - env.AUTO_DEPLOY_PRS contains $'\n' + toString(github.event.pull_request.number) + $'\n' || + contains(env.AUTO_DEPLOY_PRS, $'\n' + toString(github.event.pull_request.number)) + $'\n' || startsWith(env.AUTO_DEPLOY_PRS, toString(github.event.pull_request.number) + $'\n') || endsWith(env.AUTO_DEPLOY_PRS, $'\n' + toString(github.event.pull_request.number)) || env.AUTO_DEPLOY_PRS == toString(github.event.pull_request.number) From 6354ba6bb8e68398531e37bf6222a8b32be3514e Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 23 Oct 2024 00:36:58 +0300 Subject: [PATCH 003/753] ci: update variable name from env to vars in AUTO_DEPLOY_PRS check in preview.yml --- .github/workflows/preview.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 2e7f933f..c5bb47dc 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -20,10 +20,10 @@ jobs: ) || ( github.event_name == 'pull_request' && ( - contains(env.AUTO_DEPLOY_PRS, $'\n' + toString(github.event.pull_request.number)) + $'\n' || - startsWith(env.AUTO_DEPLOY_PRS, toString(github.event.pull_request.number) + $'\n') || - endsWith(env.AUTO_DEPLOY_PRS, $'\n' + toString(github.event.pull_request.number)) || - env.AUTO_DEPLOY_PRS == toString(github.event.pull_request.number) + contains(vars.AUTO_DEPLOY_PRS, $'\n' + toString(github.event.pull_request.number)) + $'\n' || + startsWith(vars.AUTO_DEPLOY_PRS, toString(github.event.pull_request.number) + $'\n') || + endsWith(vars.AUTO_DEPLOY_PRS, $'\n' + toString(github.event.pull_request.number)) || + vars.AUTO_DEPLOY_PRS == toString(github.event.pull_request.number) ) ) permissions: From f900d6933c11d3dd31b45ecaabfbe192694f1e51 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 23 Oct 2024 00:42:04 +0300 Subject: [PATCH 004/753] ci: simplify PR number check logic using fromJSON in AUTO_DEPLOY_PRS in preview.yml --- .github/workflows/preview.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index c5bb47dc..ebbdefeb 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -19,12 +19,7 @@ jobs: ) ) || ( - github.event_name == 'pull_request' && ( - contains(vars.AUTO_DEPLOY_PRS, $'\n' + toString(github.event.pull_request.number)) + $'\n' || - startsWith(vars.AUTO_DEPLOY_PRS, toString(github.event.pull_request.number) + $'\n') || - endsWith(vars.AUTO_DEPLOY_PRS, $'\n' + toString(github.event.pull_request.number)) || - vars.AUTO_DEPLOY_PRS == toString(github.event.pull_request.number) - ) + github.event_name == 'pull_request' && fromJSON(env.AUTO_DEPLOY_PRS).includes(github.event.pull_request.number) ) permissions: pull-requests: write From e35873e1061464e5e9e1943ac3a9aef2aa5f78c6 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 23 Oct 2024 00:43:43 +0300 Subject: [PATCH 005/753] ci: refactor variable usage from env to vars for PR check in preview.yml --- .github/workflows/preview.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index ebbdefeb..1c8e3057 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -19,7 +19,7 @@ jobs: ) ) || ( - github.event_name == 'pull_request' && fromJSON(env.AUTO_DEPLOY_PRS).includes(github.event.pull_request.number) + github.event_name == 'pull_request' && fromJSON(vars.AUTO_DEPLOY_PRS).includes(github.event.pull_request.number) ) permissions: pull-requests: write From 9a7a13c2dd66dc972fc6b088a4bf2a39bd2923ae Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 23 Oct 2024 01:00:34 +0300 Subject: [PATCH 006/753] ci: enhance trigger conditions for deployment in preview.yml using variables for issue comments and pull requests --- .github/workflows/preview.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 1c8e3057..e7a7dcfd 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -3,6 +3,8 @@ env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} ALIASES: ${{ vars.ALIASES }} + COMMENT_TRIGGER_OK: github.event_name == 'issue_comment' && contains(github.event.comment.body, '/deploy') && github.event.issue.pull_request != null + PULL_TRIGGER_OK: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository on: issue_comment: types: [created] @@ -11,16 +13,7 @@ jobs: deploy: runs-on: ubuntu-latest # todo skip already created deploys on that commit - if: >- - ( - github.event.issue.pull_request != '' && - ( - contains(github.event.comment.body, '/deploy') - ) - ) || - ( - github.event_name == 'pull_request' && fromJSON(vars.AUTO_DEPLOY_PRS).includes(github.event.pull_request.number) - ) + if: vars.COMMENT_TRIGGER_OK || vars.PULL_TRIGGER_OK permissions: pull-requests: write steps: From 42cc0bd818bd8b908373ba7ec5f6e5ff8bb8c78b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 23 Oct 2024 01:38:55 +0300 Subject: [PATCH 007/753] ci: add push event trigger and refine deployment conditions in preview.yml for improved deployment handling --- .github/workflows/preview.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index e7a7dcfd..b0b27522 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -3,17 +3,26 @@ env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} ALIASES: ${{ vars.ALIASES }} - COMMENT_TRIGGER_OK: github.event_name == 'issue_comment' && contains(github.event.comment.body, '/deploy') && github.event.issue.pull_request != null - PULL_TRIGGER_OK: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository on: issue_comment: types: [created] pull_request: + push: jobs: deploy: runs-on: ubuntu-latest - # todo skip already created deploys on that commit - if: vars.COMMENT_TRIGGER_OK || vars.PULL_TRIGGER_OK + if: >- + ( + ( + github.event_name == 'issue_comment' && + contains(github.event.comment.body, '/deploy') && + github.event.issue.pull_request != null + ) || + ( + github.event_name == 'pull_request' && + contains(fromJSON(vars.AUTO_DEPLOY_PRS), github.event.pull_request.number) + ) + ) permissions: pull-requests: write steps: From 427ec21213161f97fa498df23782de493c54d1a2 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 23 Oct 2024 02:03:05 +0300 Subject: [PATCH 008/753] check that! --- .github/workflows/preview.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index b0b27522..2e7773fa 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -6,8 +6,7 @@ env: on: issue_comment: types: [created] - pull_request: - push: + pull_request_target: jobs: deploy: runs-on: ubuntu-latest @@ -34,6 +33,8 @@ jobs: - name: Checkout PR (pull_request) uses: actions/checkout@v2 if: github.event_name == 'pull_request' + with: + ref: refs/pull/${{ github.event.pull_request.number }}/head - run: npm i -g pnpm@9.0.4 - uses: actions/setup-node@v4 From 8955075d75d99a2d90d7cb5c669ba4187cb5d1be Mon Sep 17 00:00:00 2001 From: Vitaly Date: Wed, 23 Oct 2024 02:11:11 +0300 Subject: [PATCH 009/753] ci: trying to fix auto preview workflow! (#221) --- .github/workflows/preview.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 2e7773fa..738f834e 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -8,6 +8,13 @@ on: types: [created] pull_request_target: jobs: + check-trigger: + runs-on: ubuntu-latest + steps: + - run: echo ${{ vars.AUTO_DEPLOY_PRS }} + - run: echo ${{ fromJson(vars.AUTO_DEPLOY_PRS) }} + - run: echo ${{ toJson(contains(fromJson(vars.AUTO_DEPLOY_PRS), github.event.pull_request.number)) }} + - run: echo ${{ toJson(contains(fromJson(vars.AUTO_DEPLOY_PRS), 120)) }} deploy: runs-on: ubuntu-latest if: >- @@ -19,7 +26,7 @@ jobs: ) || ( github.event_name == 'pull_request' && - contains(fromJSON(vars.AUTO_DEPLOY_PRS), github.event.pull_request.number) + contains(fromJson(vars.AUTO_DEPLOY_PRS), github.event.pull_request.number) ) ) permissions: From ece59e174435ad2a889c5bab1438478d3c54b1d1 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 23 Oct 2024 02:20:44 +0300 Subject: [PATCH 010/753] ci: update event trigger from pull_request to pull_request_target in preview.yml (fix) --- .github/workflows/preview.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 738f834e..fd424620 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -25,7 +25,7 @@ jobs: github.event.issue.pull_request != null ) || ( - github.event_name == 'pull_request' && + github.event_name == 'pull_request_target' && contains(fromJson(vars.AUTO_DEPLOY_PRS), github.event.pull_request.number) ) ) @@ -39,7 +39,7 @@ jobs: ref: refs/pull/${{ github.event.issue.number }}/head - name: Checkout PR (pull_request) uses: actions/checkout@v2 - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request_target' with: ref: refs/pull/${{ github.event.pull_request.number }}/head From ebb5056540a1796c2936ec9decb33f8be686cc70 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 23 Oct 2024 02:31:04 +0300 Subject: [PATCH 011/753] ci: refactor deployment workflow in preview.yml by removing unused checks and adding alias retrieval step --- .github/workflows/preview.yml | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index fd424620..ddb386b7 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -8,13 +8,6 @@ on: types: [created] pull_request_target: jobs: - check-trigger: - runs-on: ubuntu-latest - steps: - - run: echo ${{ vars.AUTO_DEPLOY_PRS }} - - run: echo ${{ fromJson(vars.AUTO_DEPLOY_PRS) }} - - run: echo ${{ toJson(contains(fromJson(vars.AUTO_DEPLOY_PRS), github.event.pull_request.number)) }} - - run: echo ${{ toJson(contains(fromJson(vars.AUTO_DEPLOY_PRS), 120)) }} deploy: runs-on: ubuntu-latest if: >- @@ -32,6 +25,16 @@ jobs: permissions: pull-requests: write steps: + - name: Checkout Base To Temp + uses: actions/checkout@v2 + with: + path: temp-base-repo + - name: Get deployment alias + run: node temp-base-repo/scripts/githubActions.mjs getAlias + id: alias + env: + ALIASES: ${{ env.ALIASES }} + PULL_URL: ${{ github.event.issue.pull_request.url || github.event.pull_request.url }} - name: Checkout PR (comment) uses: actions/checkout@v2 if: github.event_name == 'issue_comment' @@ -68,6 +71,7 @@ jobs: run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} id: deploy - uses: mshick/add-pr-comment@v2 + # if: github.event_name == 'issue_comment' with: allow-repeats: true message: | @@ -75,12 +79,6 @@ jobs: [Playground](${{ steps.deploy.outputs.stdout }}/playground/) [Storybook](${{ steps.deploy.outputs.stdout }}/storybook/) # - run: git checkout next scripts/githubActions.mjs - - name: Get deployment alias - run: node scripts/githubActions.mjs getAlias - id: alias - env: - ALIASES: ${{ env.ALIASES }} - PULL_URL: ${{ github.event.issue.pull_request.url || github.event.pull_request.url }} - name: Set deployment alias if: ${{ steps.alias.outputs.alias != '' && steps.alias.outputs.alias != 'mcraft.fun' && steps.alias.outputs.alias != 's.mcraft.fun' }} run: vercel alias set ${{ steps.deploy.outputs.stdout }} ${{ steps.alias.outputs.alias }} --token=${{ secrets.VERCEL_TOKEN }} --scope=zaro From 5fa019e7b374259759792d1fa2faf682cfca4b75 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Thu, 24 Oct 2024 14:36:44 +0300 Subject: [PATCH 012/753] =?UTF-8?q?fix:=20don=E2=80=99t=20display=20advanc?= =?UTF-8?q?ed=20stats=20on=20prod=20deploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/optionsStorage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index b6ecb7dd..01610cf2 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -76,7 +76,7 @@ const defaultOptions = { autoJump: 'auto' as 'auto' | 'always' | 'never', autoParkour: false, vrSupport: true, // doesn't directly affect the VR mode, should only disable the button which is annoying to android users - renderDebug: (isDev ? 'basic' : 'advanced') as 'none' | 'advanced' | 'basic', + renderDebug: (isDev ? 'advanced' : 'basic') as 'none' | 'advanced' | 'basic', // advanced bot options autoRespawn: false, From 6b23eb6badf1dbd0cd01b865300d558f4ad8883f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 28 Oct 2024 05:07:52 +0300 Subject: [PATCH 013/753] fix: never get stuck in unloaded chunks! @sa2urami feat: fully supported spectator mode & basic creative fly fixes --- package.json | 1 + pnpm-lock.yaml | 30 ++++++++++++++++-------------- src/controls.ts | 2 ++ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 6f5924ba..af800edc 100644 --- a/package.json +++ b/package.json @@ -174,6 +174,7 @@ "prismarine-world": "github:zardoy/prismarine-world#next-era", "minecraft-data": "3.76.0", "prismarine-provider-anvil": "github:zardoy/prismarine-provider-anvil#everything", + "prismarine-physics": "github:zardoy/prismarine-physics", "minecraft-protocol": "github:PrismarineJS/node-minecraft-protocol#master", "react": "^18.2.0", "prismarine-chunk": "github:zardoy/prismarine-chunk", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76203c73..97d8e889 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,6 +13,7 @@ overrides: prismarine-world: github:zardoy/prismarine-world#next-era minecraft-data: 3.76.0 prismarine-provider-anvil: github:zardoy/prismarine-provider-anvil#everything + prismarine-physics: github:zardoy/prismarine-physics minecraft-protocol: github:PrismarineJS/node-minecraft-protocol#master react: ^18.2.0 prismarine-chunk: github:zardoy/prismarine-chunk @@ -7545,8 +7546,9 @@ packages: prismarine-nbt@2.5.0: resolution: {integrity: sha512-F0/8UAa9SDDnAGrBYqZc4nG8h2zj5cE2eAJU5xlDR/IsQQ3moVxkOjE3h3nMv6SbvZrvAcgX7waA/nd9LLHYdA==} - prismarine-physics@1.8.0: - resolution: {integrity: sha512-gbM+S+bmVtOKVv+Z0WGaHMeEeBHISIDsRDRlv8sr0dex3ZJRhuq8djA02CBreguXtI18ZKh6q3TSj2qDr45NHA==} + prismarine-physics@https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b: + resolution: {tarball: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b} + version: 1.9.0 prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699: resolution: {tarball: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699} @@ -9851,7 +9853,7 @@ snapshots: '@babel/core': 7.22.11 '@babel/helper-compilation-targets': 7.22.10 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7 lodash.debounce: 4.0.8 resolve: 1.22.4 transitivePeerDependencies: @@ -13150,7 +13152,7 @@ snapshots: dependencies: '@typescript-eslint/types': 6.1.0 '@typescript-eslint/visitor-keys': 6.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7 globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.0 @@ -13178,7 +13180,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.0.0 '@typescript-eslint/visitor-keys': 8.0.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -13522,7 +13524,7 @@ snapshots: agent-base@7.1.0: dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -14903,7 +14905,7 @@ snapshots: detect-port@1.5.1: dependencies: address: 1.2.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -16558,7 +16560,7 @@ snapshots: https-proxy-agent@4.0.0: dependencies: agent-base: 5.1.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -16573,7 +16575,7 @@ snapshots: https-proxy-agent@7.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -17647,7 +17649,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7 decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 @@ -17792,7 +17794,7 @@ snapshots: prismarine-entity: 2.3.1 prismarine-item: 1.15.0 prismarine-nbt: 2.2.1 - prismarine-physics: 1.8.0 + prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b vec3: 0.1.8 mineflayer@4.22.0(encoding@0.1.13): @@ -17806,7 +17808,7 @@ snapshots: prismarine-entity: 2.3.1 prismarine-item: 1.15.0 prismarine-nbt: 2.5.0 - prismarine-physics: 1.8.0 + prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b prismarine-recipe: 1.3.1(prismarine-registry@1.9.0) prismarine-registry: 1.9.0 prismarine-windows: 2.9.0 @@ -17829,7 +17831,7 @@ snapshots: prismarine-entity: 2.3.1 prismarine-item: 1.15.0 prismarine-nbt: 2.5.0 - prismarine-physics: 1.8.0 + prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b prismarine-recipe: 1.3.1(prismarine-registry@1.9.0) prismarine-registry: 1.9.0 prismarine-windows: 2.9.0 @@ -18705,7 +18707,7 @@ snapshots: dependencies: protodef: 1.17.0 - prismarine-physics@1.8.0: + prismarine-physics@https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b: dependencies: minecraft-data: 3.76.0 prismarine-nbt: 2.5.0 diff --git a/src/controls.ts b/src/controls.ts index 6effdcd4..d29d141f 100644 --- a/src/controls.ts +++ b/src/controls.ts @@ -603,6 +603,7 @@ const patchedSetControlState = (action, state) => { } const startFlying = (sendAbilities = true) => { + bot.entity['creativeFly'] = true if (sendAbilities) { bot._client.write('abilities', { flags: 2, @@ -616,6 +617,7 @@ const startFlying = (sendAbilities = true) => { } const endFlying = (sendAbilities = true) => { + bot.entity['creativeFly'] = false if (bot.physics.gravity !== 0) return if (sendAbilities) { bot._client.write('abilities', { From d497299235952543d990e44c2ec9049055323a39 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 28 Oct 2024 05:48:51 +0300 Subject: [PATCH 014/753] feat: 1.21.1 support --- package.json | 8 +- pnpm-lock.yaml | 274 +++++++++++++++++++++++-------------------------- 2 files changed, 130 insertions(+), 152 deletions(-) diff --git a/package.json b/package.json index af800edc..c458474b 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "google-drive-browserfs": "github:zardoy/browserfs#google-drive", "jszip": "^3.10.1", "lodash-es": "^4.17.21", - "minecraft-data": "3.76.0", + "minecraft-data": "3.78.0", "minecraft-protocol": "github:PrismarineJS/node-minecraft-protocol#master", "mineflayer-item-map-downloader": "github:zardoy/mineflayer-item-map-downloader", "mojangson": "^2.0.4", @@ -172,12 +172,12 @@ "diamond-square": "github:zardoy/diamond-square", "prismarine-block": "github:zardoy/prismarine-block#next-era", "prismarine-world": "github:zardoy/prismarine-world#next-era", - "minecraft-data": "3.76.0", + "minecraft-data": "3.78.0", "prismarine-provider-anvil": "github:zardoy/prismarine-provider-anvil#everything", "prismarine-physics": "github:zardoy/prismarine-physics", "minecraft-protocol": "github:PrismarineJS/node-minecraft-protocol#master", "react": "^18.2.0", - "prismarine-chunk": "github:zardoy/prismarine-chunk", + "prismarine-chunk": "github:zardoy/prismarine-chunk#master", "prismarine-item": "latest" }, "updateConfig": { @@ -187,7 +187,7 @@ "three@0.154.0": "patches/three@0.154.0.patch", "pixelarticons@1.8.1": "patches/pixelarticons@1.8.1.patch", "mineflayer-item-map-downloader@1.2.0": "patches/mineflayer-item-map-downloader@1.2.0.patch", - "minecraft-protocol@1.49.0": "patches/minecraft-protocol@1.49.0.patch" + "minecraft-protocol@1.50.0": "patches/minecraft-protocol@1.49.0.patch" } }, "packageManager": "pnpm@9.0.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97d8e889..6eff7655 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,16 +11,16 @@ overrides: diamond-square: github:zardoy/diamond-square prismarine-block: github:zardoy/prismarine-block#next-era prismarine-world: github:zardoy/prismarine-world#next-era - minecraft-data: 3.76.0 + minecraft-data: 3.78.0 prismarine-provider-anvil: github:zardoy/prismarine-provider-anvil#everything prismarine-physics: github:zardoy/prismarine-physics minecraft-protocol: github:PrismarineJS/node-minecraft-protocol#master react: ^18.2.0 - prismarine-chunk: github:zardoy/prismarine-chunk + prismarine-chunk: github:zardoy/prismarine-chunk#master prismarine-item: latest patchedDependencies: - minecraft-protocol@1.49.0: + minecraft-protocol@1.50.0: hash: 7sh5krubuk2vjuogjioaktvwzi path: patches/minecraft-protocol@1.49.0.patch mineflayer-item-map-downloader@1.2.0: @@ -134,11 +134,11 @@ importers: specifier: ^4.17.21 version: 4.17.21 minecraft-data: - specifier: 3.76.0 - version: 3.76.0 + specifier: 3.78.0 + version: 3.78.0 minecraft-protocol: specifier: github:PrismarineJS/node-minecraft-protocol#master - version: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/11e8594bb43dff4fe89f84462ed37d593ee17380(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13) + version: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/590dc33fed2100e77ef58e7db716dfc45eb61159(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13) mineflayer-item-map-downloader: specifier: github:zardoy/mineflayer-item-map-downloader version: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/642fd4f7023a98a96da4caf8f993f8e19361a1e7(patch_hash=bck55yjvd4wrgz46x7o4vfur5q)(encoding@0.1.13) @@ -162,7 +162,7 @@ importers: version: 6.1.1 prismarine-provider-anvil: specifier: github:zardoy/prismarine-provider-anvil#everything - version: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699(minecraft-data@3.76.0) + version: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/d807fc339a3d95a7aef91468d4d64d367e7c682a(minecraft-data@3.78.0) prosemirror-example-setup: specifier: ^1.2.2 version: 1.2.2 @@ -350,7 +350,7 @@ importers: version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/75e940a4cd50d89e0ba03db3733d5d704917a3c8(@types/react@18.2.20)(react@18.2.0) mineflayer: specifier: github:zardoy/mineflayer - version: https://codeload.github.com/zardoy/mineflayer/tar.gz/f5fb0bcdfebe79fc74adfd7325ac80f2b2bce425(encoding@0.1.13) + version: https://codeload.github.com/zardoy/mineflayer/tar.gz/519acba455d3387414b81fcaa12e550dcdc4f88e(encoding@0.1.13) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -425,10 +425,10 @@ importers: version: 1.3.6 prismarine-block: specifier: github:zardoy/prismarine-block#next-era - version: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 + version: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05 prismarine-chunk: - specifier: github:zardoy/prismarine-chunk - version: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.76.0) + specifier: github:zardoy/prismarine-chunk#master + version: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb(minecraft-data@3.78.0) prismarine-schematic: specifier: ^1.2.0 version: 1.2.3 @@ -472,7 +472,7 @@ importers: dependencies: vite: specifier: ^4.4.9 - version: 4.4.10(@types/node@20.12.8)(terser@5.31.3) + version: 4.4.10(@types/node@22.8.1)(terser@5.31.3) packages: @@ -3053,6 +3053,9 @@ packages: '@types/node@20.8.0': resolution: {integrity: sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==} + '@types/node@22.8.1': + resolution: {integrity: sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==} + '@types/normalize-package-data@2.4.2': resolution: {integrity: sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==} @@ -6763,8 +6766,8 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - minecraft-data@3.76.0: - resolution: {integrity: sha512-aqWonoe5h7to5iEfjUmG5r5F0K3FwrnF9c1YzxbALyOyRUujm2a/G5GJ4TfuoEslVp9dJ7OYVHLJfOS9aD/Twg==} + minecraft-data@3.78.0: + resolution: {integrity: sha512-Ssks8QD31lsoxqa7LySTqeP9romsfAbfsSGiUHiGMeqfxRi/PtOxGLyKD1BXB8V/tXLztFcbQYqzIhprDkPguw==} minecraft-folder-path@1.2.0: resolution: {integrity: sha512-qaUSbKWoOsH9brn0JQuBhxNAzTDMwrOXorwuRxdJKKKDYvZhtml+6GVCUrY5HRiEsieBEjCUnhVpDuQiKsiFaw==} @@ -6773,9 +6776,9 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/75e940a4cd50d89e0ba03db3733d5d704917a3c8} version: 1.0.1 - minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/11e8594bb43dff4fe89f84462ed37d593ee17380: - resolution: {tarball: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/11e8594bb43dff4fe89f84462ed37d593ee17380} - version: 1.49.0 + minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/590dc33fed2100e77ef58e7db716dfc45eb61159: + resolution: {tarball: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/590dc33fed2100e77ef58e7db716dfc45eb61159} + version: 1.50.0 engines: {node: '>=14'} minecraft-wrap@1.5.1: @@ -6792,13 +6795,13 @@ packages: mineflayer-pathfinder@2.4.4: resolution: {integrity: sha512-HAXakZrJRb1UC+5dv8EaDrqjW3ZnBnBk3nkb6x/YWyhHCUKn/E7VU0FO+UN9whuqPlkSaVumEdXJdydE6lSYxQ==} - mineflayer@4.22.0: - resolution: {integrity: sha512-fjIcFtmyDOyeZp64S5Af1tlVAgEfu4E8O4TyqnbVPq0RZXZe8MinhR3yE1eML3iAhcswElnSQjijgx3Y8C8h3A==} + mineflayer@4.23.0: + resolution: {integrity: sha512-wSchhS59hK+oPs8tFg847H82YEvxU7zYKdDKj4e5FVo3CxJ74eXJVT+JcFwEvoqFO7kXiQlhJITxEvO13GOSKA==} engines: {node: '>=18'} - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/f5fb0bcdfebe79fc74adfd7325ac80f2b2bce425: - resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/f5fb0bcdfebe79fc74adfd7325ac80f2b2bce425} - version: 4.22.0 + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/519acba455d3387414b81fcaa12e550dcdc4f88e: + resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/519acba455d3387414b81fcaa12e550dcdc4f88e} + version: 4.23.0 engines: {node: '>=18'} minimalistic-assert@1.0.1: @@ -7509,29 +7512,19 @@ packages: prismarine-biome@1.3.0: resolution: {integrity: sha512-GY6nZxq93mTErT7jD7jt8YS1aPrOakbJHh39seYsJFXvueIOdHAmW16kYQVrTVMW5MlWLQVxV/EquRwOgr4MnQ==} peerDependencies: - minecraft-data: 3.76.0 + minecraft-data: 3.78.0 prismarine-registry: ^1.1.0 - prismarine-block@https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819: - resolution: {tarball: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819} + prismarine-block@https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05: + resolution: {tarball: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05} version: 1.17.1 prismarine-chat@1.10.1: resolution: {integrity: sha512-XukYcuueuhDxzEXG7r8BZyt6jOObrPPB4JESCgb+/XenB9nExoSHF8eTQWWj8faKPLqm1dRQaYwFJlNBlJZJUw==} - prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/28305fcc2c180b8d82211d0651d4a5eaed489ae4: - resolution: {tarball: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/28305fcc2c180b8d82211d0651d4a5eaed489ae4} - version: 1.35.0 - engines: {node: '>=14'} - - prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/5cae250670175a6cc1be92184d3cb5ca6d235a30: - resolution: {tarball: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/5cae250670175a6cc1be92184d3cb5ca6d235a30} - version: 1.35.0 - engines: {node: '>=14'} - - prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef: - resolution: {tarball: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef} - version: 1.35.0 + prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb: + resolution: {tarball: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb} + version: 1.36.0 engines: {node: '>=14'} prismarine-entity@2.3.1: @@ -7550,8 +7543,8 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b} version: 1.9.0 - prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699: - resolution: {tarball: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699} + prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/d807fc339a3d95a7aef91468d4d64d367e7c682a: + resolution: {tarball: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/d807fc339a3d95a7aef91468d4d64d367e7c682a} version: 2.8.0 prismarine-realms@1.3.2: @@ -7562,12 +7555,12 @@ packages: peerDependencies: prismarine-registry: ^1.4.0 + prismarine-registry@1.10.0: + resolution: {integrity: sha512-6TYQiZHtsJ87HsB2E0yamCFp77ZyyLT16OmW5iXB5V30yCPflhHvR5TR2IqISmmiRc2093BkHfiIhsOZaMatmQ==} + prismarine-registry@1.7.0: resolution: {integrity: sha512-yyva0FpWI078nNeMhx8ekVza5uUTYhEv+C+ADu3wUQXiG8qhXkvrf0uzsnhTgZL8BLdsi2axgCEiKw9qSKIuxQ==} - prismarine-registry@1.9.0: - resolution: {integrity: sha512-RZ5PvedMGA/uEwZOsnmC9MYq1CupH2CHwm9UT4Ara87Vf7QMfxxPTZZ4GXq7EJmmxe5qodhDYoq6Z8mz11oXlQ==} - prismarine-schematic@1.2.3: resolution: {integrity: sha512-Mwpn43vEHhm3aw3cPhJjWqztkW+nX+QLajDHlTask8lEOTGl1WmpvFja4iwiws4GIvaC8x0Foptf4uvDsnjrAg==} @@ -9053,6 +9046,9 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici@5.25.4: resolution: {integrity: sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==} engines: {node: '>=14.0'} @@ -9853,7 +9849,7 @@ snapshots: '@babel/core': 7.22.11 '@babel/helper-compilation-targets': 7.22.10 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.7 + debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.4 transitivePeerDependencies: @@ -12968,6 +12964,11 @@ snapshots: '@types/node@20.8.0': {} + '@types/node@22.8.1': + dependencies: + undici-types: 6.19.8 + optional: true + '@types/normalize-package-data@2.4.2': {} '@types/offscreencanvas@2019.7.2': {} @@ -13152,7 +13153,7 @@ snapshots: dependencies: '@typescript-eslint/types': 6.1.0 '@typescript-eslint/visitor-keys': 6.1.0 - debug: 4.3.7 + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.0 @@ -13180,7 +13181,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.0.0 '@typescript-eslint/visitor-keys': 8.0.0 - debug: 4.3.7 + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -13424,16 +13425,16 @@ snapshots: exit-hook: 2.2.1 flatmap: 0.0.3 long: 5.2.3 - minecraft-data: 3.76.0 - minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/11e8594bb43dff4fe89f84462ed37d593ee17380(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13) + minecraft-data: 3.78.0 + minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/590dc33fed2100e77ef58e7db716dfc45eb61159(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13) mkdirp: 2.1.6 node-gzip: 1.1.2 node-rsa: 1.1.1 - prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/5cae250670175a6cc1be92184d3cb5ca6d235a30(minecraft-data@3.76.0) + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb(minecraft-data@3.78.0) prismarine-entity: 2.3.1 prismarine-item: 1.15.0 prismarine-nbt: 2.5.0 - prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699(minecraft-data@3.76.0) + prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/d807fc339a3d95a7aef91468d4d64d367e7c682a(minecraft-data@3.78.0) prismarine-windows: 2.9.0 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/6221e049e2ad0f508edc23c7f5bda7fd6d9566be rambda: 9.2.0 @@ -13524,7 +13525,7 @@ snapshots: agent-base@7.1.0: dependencies: - debug: 4.3.7 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -14905,7 +14906,7 @@ snapshots: detect-port@1.5.1: dependencies: address: 1.2.2 - debug: 4.3.7 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -14915,9 +14916,9 @@ snapshots: diamond-square@https://codeload.github.com/zardoy/diamond-square/tar.gz/cfaad2d1d5909fdfa63c8cc7bc05fb5e87782d71: dependencies: - minecraft-data: 3.76.0 - prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/28305fcc2c180b8d82211d0651d4a5eaed489ae4(minecraft-data@3.76.0) - prismarine-registry: 1.9.0 + minecraft-data: 3.78.0 + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb(minecraft-data@3.78.0) + prismarine-registry: 1.10.0 random-seed: 0.3.0 vec3: 0.1.8 @@ -16560,7 +16561,7 @@ snapshots: https-proxy-agent@4.0.0: dependencies: agent-base: 5.1.1 - debug: 4.3.7 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -16575,7 +16576,7 @@ snapshots: https-proxy-agent@7.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.7 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -17649,7 +17650,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.7 + debug: 4.3.4(supports-color@8.1.1) decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 @@ -17720,7 +17721,7 @@ snapshots: min-indent@1.0.1: {} - minecraft-data@3.76.0: {} + minecraft-data@3.78.0: {} minecraft-folder-path@1.2.0: {} @@ -17731,7 +17732,7 @@ snapshots: - '@types/react' - react - minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/11e8594bb43dff4fe89f84462ed37d593ee17380(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13): + minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/590dc33fed2100e77ef58e7db716dfc45eb61159(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13): dependencies: '@types/readable-stream': 4.0.12 aes-js: 3.1.2 @@ -17740,7 +17741,7 @@ snapshots: endian-toggle: 0.0.0 lodash.get: 4.4.2 lodash.merge: 4.6.2 - minecraft-data: 3.76.0 + minecraft-data: 3.78.0 minecraft-folder-path: 1.2.0 node-fetch: 2.7.0(encoding@0.1.13) node-rsa: 0.4.2 @@ -17781,7 +17782,7 @@ snapshots: mineflayer-item-map-downloader@https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/642fd4f7023a98a96da4caf8f993f8e19361a1e7(patch_hash=bck55yjvd4wrgz46x7o4vfur5q)(encoding@0.1.13): dependencies: - mineflayer: 4.22.0(encoding@0.1.13) + mineflayer: 4.23.0(encoding@0.1.13) sharp: 0.30.7 transitivePeerDependencies: - encoding @@ -17789,28 +17790,28 @@ snapshots: mineflayer-pathfinder@2.4.4: dependencies: - minecraft-data: 3.76.0 - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 + minecraft-data: 3.78.0 + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05 prismarine-entity: 2.3.1 prismarine-item: 1.15.0 prismarine-nbt: 2.2.1 prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b vec3: 0.1.8 - mineflayer@4.22.0(encoding@0.1.13): + mineflayer@4.23.0(encoding@0.1.13): dependencies: - minecraft-data: 3.76.0 - minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/11e8594bb43dff4fe89f84462ed37d593ee17380(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13) - prismarine-biome: 1.3.0(minecraft-data@3.76.0)(prismarine-registry@1.9.0) - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 + minecraft-data: 3.78.0 + minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/590dc33fed2100e77ef58e7db716dfc45eb61159(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13) + prismarine-biome: 1.3.0(minecraft-data@3.78.0)(prismarine-registry@1.10.0) + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05 prismarine-chat: 1.10.1 - prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/5cae250670175a6cc1be92184d3cb5ca6d235a30(minecraft-data@3.76.0) + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb(minecraft-data@3.78.0) prismarine-entity: 2.3.1 prismarine-item: 1.15.0 prismarine-nbt: 2.5.0 prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b - prismarine-recipe: 1.3.1(prismarine-registry@1.9.0) - prismarine-registry: 1.9.0 + prismarine-recipe: 1.3.1(prismarine-registry@1.10.0) + prismarine-registry: 1.10.0 prismarine-windows: 2.9.0 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/6221e049e2ad0f508edc23c7f5bda7fd6d9566be protodef: 1.17.0 @@ -17820,20 +17821,20 @@ snapshots: - encoding - supports-color - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/f5fb0bcdfebe79fc74adfd7325ac80f2b2bce425(encoding@0.1.13): + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/519acba455d3387414b81fcaa12e550dcdc4f88e(encoding@0.1.13): dependencies: - minecraft-data: 3.76.0 - minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/11e8594bb43dff4fe89f84462ed37d593ee17380(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13) - prismarine-biome: 1.3.0(minecraft-data@3.76.0)(prismarine-registry@1.9.0) - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 + minecraft-data: 3.78.0 + minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/590dc33fed2100e77ef58e7db716dfc45eb61159(patch_hash=7sh5krubuk2vjuogjioaktvwzi)(encoding@0.1.13) + prismarine-biome: 1.3.0(minecraft-data@3.78.0)(prismarine-registry@1.10.0) + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05 prismarine-chat: 1.10.1 - prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/5cae250670175a6cc1be92184d3cb5ca6d235a30(minecraft-data@3.76.0) + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb(minecraft-data@3.78.0) prismarine-entity: 2.3.1 prismarine-item: 1.15.0 prismarine-nbt: 2.5.0 prismarine-physics: https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b - prismarine-recipe: 1.3.1(prismarine-registry@1.9.0) - prismarine-registry: 1.9.0 + prismarine-recipe: 1.3.1(prismarine-registry@1.10.0) + prismarine-registry: 1.10.0 prismarine-windows: 2.9.0 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/6221e049e2ad0f508edc23c7f5bda7fd6d9566be protodef: 1.17.0 @@ -18623,20 +18624,20 @@ snapshots: - encoding - supports-color - prismarine-biome@1.3.0(minecraft-data@3.76.0)(prismarine-registry@1.7.0): + prismarine-biome@1.3.0(minecraft-data@3.78.0)(prismarine-registry@1.10.0): dependencies: - minecraft-data: 3.76.0 + minecraft-data: 3.78.0 + prismarine-registry: 1.10.0 + + prismarine-biome@1.3.0(minecraft-data@3.78.0)(prismarine-registry@1.7.0): + dependencies: + minecraft-data: 3.78.0 prismarine-registry: 1.7.0 - prismarine-biome@1.3.0(minecraft-data@3.76.0)(prismarine-registry@1.9.0): + prismarine-block@https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05: dependencies: - minecraft-data: 3.76.0 - prismarine-registry: 1.9.0 - - prismarine-block@https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819: - dependencies: - minecraft-data: 3.76.0 - prismarine-biome: 1.3.0(minecraft-data@3.76.0)(prismarine-registry@1.7.0) + minecraft-data: 3.78.0 + prismarine-biome: 1.3.0(minecraft-data@3.78.0)(prismarine-registry@1.7.0) prismarine-chat: 1.10.1 prismarine-item: 1.15.0 prismarine-nbt: 2.5.0 @@ -18646,40 +18647,14 @@ snapshots: dependencies: mojangson: 2.0.4 prismarine-nbt: 2.5.0 - prismarine-registry: 1.9.0 + prismarine-registry: 1.10.0 - prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/28305fcc2c180b8d82211d0651d4a5eaed489ae4(minecraft-data@3.76.0): + prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb(minecraft-data@3.78.0): dependencies: - prismarine-biome: 1.3.0(minecraft-data@3.76.0)(prismarine-registry@1.9.0) - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 + prismarine-biome: 1.3.0(minecraft-data@3.78.0)(prismarine-registry@1.10.0) + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05 prismarine-nbt: 2.5.0 - prismarine-registry: 1.9.0 - smart-buffer: 4.2.0 - uint4: 0.1.2 - vec3: 0.1.8 - xxhash-wasm: 0.4.2 - transitivePeerDependencies: - - minecraft-data - - prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/5cae250670175a6cc1be92184d3cb5ca6d235a30(minecraft-data@3.76.0): - dependencies: - prismarine-biome: 1.3.0(minecraft-data@3.76.0)(prismarine-registry@1.9.0) - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 - prismarine-nbt: 2.5.0 - prismarine-registry: 1.9.0 - smart-buffer: 4.2.0 - uint4: 0.1.2 - vec3: 0.1.8 - xxhash-wasm: 0.4.2 - transitivePeerDependencies: - - minecraft-data - - prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.76.0): - dependencies: - prismarine-biome: 1.3.0(minecraft-data@3.76.0)(prismarine-registry@1.7.0) - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 - prismarine-nbt: 2.5.0 - prismarine-registry: 1.7.0 + prismarine-registry: 1.10.0 smart-buffer: 4.2.0 uint4: 0.1.2 vec3: 0.1.8 @@ -18691,13 +18666,13 @@ snapshots: dependencies: prismarine-chat: 1.10.1 prismarine-item: 1.15.0 - prismarine-registry: 1.9.0 + prismarine-registry: 1.10.0 vec3: 0.1.8 prismarine-item@1.15.0: dependencies: prismarine-nbt: 2.5.0 - prismarine-registry: 1.9.0 + prismarine-registry: 1.10.0 prismarine-nbt@2.2.1: dependencies: @@ -18709,14 +18684,14 @@ snapshots: prismarine-physics@https://codeload.github.com/zardoy/prismarine-physics/tar.gz/353e25b800149393f40539ec381218be44cbb03b: dependencies: - minecraft-data: 3.76.0 + minecraft-data: 3.78.0 prismarine-nbt: 2.5.0 vec3: 0.1.8 - prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699(minecraft-data@3.76.0): + prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/d807fc339a3d95a7aef91468d4d64d367e7c682a(minecraft-data@3.78.0): dependencies: - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 - prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.76.0) + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05 + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/416dd49bec42f4cc9f50ccf79527e6e4c01cebcb(minecraft-data@3.78.0) prismarine-nbt: 2.5.0 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/6221e049e2ad0f508edc23c7f5bda7fd6d9566be uint4: 0.1.2 @@ -18732,24 +18707,24 @@ snapshots: - encoding - supports-color - prismarine-recipe@1.3.1(prismarine-registry@1.9.0): + prismarine-recipe@1.3.1(prismarine-registry@1.10.0): dependencies: - prismarine-registry: 1.9.0 + prismarine-registry: 1.10.0 + + prismarine-registry@1.10.0: + dependencies: + minecraft-data: 3.78.0 + prismarine-nbt: 2.5.0 prismarine-registry@1.7.0: dependencies: - minecraft-data: 3.76.0 - prismarine-nbt: 2.5.0 - - prismarine-registry@1.9.0: - dependencies: - minecraft-data: 3.76.0 + minecraft-data: 3.78.0 prismarine-nbt: 2.5.0 prismarine-schematic@1.2.3: dependencies: - minecraft-data: 3.76.0 - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 + minecraft-data: 3.78.0 + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/23849d4d24af91f45a5bd38781a6f82d40316c05 prismarine-nbt: 2.2.1 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/6221e049e2ad0f508edc23c7f5bda7fd6d9566be vec3: 0.1.8 @@ -18757,7 +18732,7 @@ snapshots: prismarine-windows@2.9.0: dependencies: prismarine-item: 1.15.0 - prismarine-registry: 1.9.0 + prismarine-registry: 1.10.0 typed-emitter: 2.1.0 prismarine-world@https://codeload.github.com/zardoy/prismarine-world/tar.gz/6221e049e2ad0f508edc23c7f5bda7fd6d9566be: @@ -20596,6 +20571,9 @@ snapshots: undici-types@5.26.5: {} + undici-types@6.19.8: + optional: true + undici@5.25.4: dependencies: '@fastify/busboy': 2.0.0 @@ -20884,16 +20862,6 @@ snapshots: - supports-color - terser - vite@4.4.10(@types/node@20.12.8)(terser@5.31.3): - dependencies: - esbuild: 0.18.20 - postcss: 8.4.31 - rollup: 3.29.4 - optionalDependencies: - '@types/node': 20.12.8 - fsevents: 2.3.3 - terser: 5.31.3 - vite@4.4.10(@types/node@20.8.0)(terser@5.31.3): dependencies: esbuild: 0.18.20 @@ -20904,6 +20872,16 @@ snapshots: fsevents: 2.3.3 terser: 5.31.3 + vite@4.4.10(@types/node@22.8.1)(terser@5.31.3): + dependencies: + esbuild: 0.18.20 + postcss: 8.4.31 + rollup: 3.29.4 + optionalDependencies: + '@types/node': 22.8.1 + fsevents: 2.3.3 + terser: 5.31.3 + vite@4.5.3(@types/node@20.8.0)(terser@5.31.3): dependencies: esbuild: 0.18.20 From 153101fa6f3a5503a81f5ffbdd2aa360780a43fa Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 28 Oct 2024 05:49:27 +0300 Subject: [PATCH 015/753] ci: fix lint --- prismarine-viewer/examples/baseScene.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prismarine-viewer/examples/baseScene.ts b/prismarine-viewer/examples/baseScene.ts index ec2db6e8..52b9e781 100644 --- a/prismarine-viewer/examples/baseScene.ts +++ b/prismarine-viewer/examples/baseScene.ts @@ -117,7 +117,7 @@ export class BasePlaygroundScene { const block = properties ? this.Block.fromProperties(loadedData.blocksByName[blockName].id, properties ?? {}, 0) : - this.Block.fromStateId(loadedData.blocksByName[blockName].defaultState!, 0) + this.Block.fromStateId(loadedData.blocksByName[blockName].defaultState, 0) this.world.setBlock(this.targetPos.offset(xOffset, yOffset, zOffset), block) } From dbd4058912c9ce47cc4de68e0bcb96b2b633acc9 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 28 Oct 2024 17:57:40 +0300 Subject: [PATCH 016/753] should fix publishing to npm --- .github/workflows/publish.yml | 4 ++++ scripts/buildNpmReact.ts | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fa1f0281..c1c6af29 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -47,6 +47,10 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: .vercel/output/static force_orphan: true + - name: Set publishing config + run: pnpm config set '//registry.npmjs.org/:_authToken' "${NODE_AUTH_TOKEN}" + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: pnpm tsx scripts/buildNpmReact.ts ${{ steps.release.outputs.tag }} if: steps.release.outputs.tag env: diff --git a/scripts/buildNpmReact.ts b/scripts/buildNpmReact.ts index f23a37cc..9d0b3484 100644 --- a/scripts/buildNpmReact.ts +++ b/scripts/buildNpmReact.ts @@ -148,6 +148,13 @@ fs.promises.readdir(path.resolve(__dirname, '../src/react')).then(async (files) fs.copyFileSync(path.resolve(__dirname, '../README.NPM.MD'), path.resolve(__dirname, '../dist-npm/README.md')) if (version !== '0.0.0-dev') { - execSync('npm publish', { cwd: path.resolve(__dirname, '../dist-npm') }) + execSync('npm publish', { + cwd: path.resolve(__dirname, '../dist-npm'), + env: { + ...process.env, + NPM_TOKEN: process.env.NPM_TOKEN, + NODE_AUTH_TOKEN: process.env.NPM_TOKEN + } + }) } }) From 900bcb0b56196ef3ac5105d665f286614582367f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 30 Oct 2024 07:02:33 +0300 Subject: [PATCH 017/753] fix: don't crash and conflict with g663 spyware installed --- index.html | 9 +++++++++ package.json | 2 +- pnpm-lock.yaml | 20 +++++++++++++------- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index 304ee0a6..d662cbb6 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,15 @@ diff --git a/experiments/state.ts b/experiments/state.ts new file mode 100644 index 00000000..b01523fc --- /dev/null +++ b/experiments/state.ts @@ -0,0 +1,37 @@ +import { SmoothSwitcher } from '../renderer/viewer/lib/smoothSwitcher' + +const div = document.createElement('div') +div.style.width = '100px' +div.style.height = '100px' +div.style.backgroundColor = 'red' +document.body.appendChild(div) + +const pos = {x: 0, y: 0} + +const positionSwitcher = new SmoothSwitcher(() => pos, (key, value) => { + pos[key] = value +}) +globalThis.positionSwitcher = positionSwitcher + +document.body.addEventListener('keydown', e => { + if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') { + const to = { + x: e.code === 'ArrowLeft' ? -100 : 100 + } + console.log(pos, to) + positionSwitcher.transitionTo(to, e.code === 'ArrowLeft' ? 'Left' : 'Right', () => { + console.log('Switched to ', e.code === 'ArrowLeft' ? 'Left' : 'Right') + }) + } + if (e.code === 'Space') { + pos.x = 200 + } +}) + +const render = () => { + positionSwitcher.update() + div.style.transform = `translate(${pos.x}px, ${pos.y}px)` + requestAnimationFrame(render) +} + +render() diff --git a/renderer/viewer/lib/mesher/world.ts b/renderer/viewer/lib/mesher/world.ts index b914e8cf..2106e179 100644 --- a/renderer/viewer/lib/mesher/world.ts +++ b/renderer/viewer/lib/mesher/world.ts @@ -144,7 +144,7 @@ export class World { if (!this.blockCache[cacheKey]) { const b = column.getBlock(locInChunk) as unknown as WorldBlock if (modelOverride) { - b.name = modelOverride + // b.name = modelOverride } b.isCube = isCube(b.shapes) this.blockCache[cacheKey] = b @@ -228,6 +228,11 @@ export class World { } block.models = null } + + if (block.models && modelOverride) { + const model = block.models[0] + block.transparent = model['transparent'] ?? block.transparent + } } catch (err) { this.erroredBlockModel ??= blockProvider.getAllResolvedModels0_1({ name: 'errored', properties: {} }) block.models ??= this.erroredBlockModel diff --git a/src/customChannels.ts b/src/customChannels.ts index 33bc8fcd..71dc7067 100644 --- a/src/customChannels.ts +++ b/src/customChannels.ts @@ -41,7 +41,6 @@ customEvents.on('mineflayerBotCreated', async () => { bot._client.on(CHANNEL_NAME as any, (data) => { const { worldName, x, y, z, model } = data - console.debug('Received model data:', { worldName, x, y, z, model }) const chunkX = Math.floor(x / 16) * 16 const chunkZ = Math.floor(z / 16) * 16 From 0b2d676d9373ecd76e74ac3061e8fecc22b5065b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 25 Feb 2025 00:01:56 +0300 Subject: [PATCH 277/753] fix: fix a lof of bugs in base transition classes and fix bug with possibly wrong item display in hand --- package.json | 2 +- pnpm-lock.yaml | 10 ++--- renderer/viewer/lib/animationController.ts | 16 ++++++-- renderer/viewer/lib/holdingBlock.ts | 45 ++++++++++++++++------ renderer/viewer/lib/mesher/world.ts | 7 ++-- renderer/viewer/lib/smoothSwitcher.ts | 5 +-- 6 files changed, 59 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index aff91168..f3f2f644 100644 --- a/package.json +++ b/package.json @@ -145,7 +145,7 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", - "mc-assets": "^0.2.36", + "mc-assets": "^0.2.37", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", "mineflayer-pathfinder": "^2.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e58b6da..7060521d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -350,8 +350,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 mc-assets: - specifier: ^0.2.36 - version: 0.2.36 + specifier: ^0.2.37 + version: 0.2.37 minecraft-inventory-gui: specifier: github:zardoy/minecraft-inventory-gui#next version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/75e940a4cd50d89e0ba03db3733d5d704917a3c8(@types/react@18.2.20)(react@18.2.0) @@ -6248,8 +6248,8 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mc-assets@0.2.36: - resolution: {integrity: sha512-JBi9frIACmzmpKL38YudZJpml+tWP3UuCeb8ko5iJRHpmCmmChE+X3xzVEbEYnYBI2dMiO7915/5eYnKUVys3Q==} + mc-assets@0.2.37: + resolution: {integrity: sha512-49tk3shwxsDoV0PrrbORZEKg613vUQPULgusWjXNl8JEma5y41LEo57D6q4aHliXsV3Gb9ThrkFf6hIb0WlY1Q==} engines: {node: '>=18.0.0'} mcraft-fun-mineflayer@0.1.6: @@ -16686,7 +16686,7 @@ snapshots: math-intrinsics@1.1.0: {} - mc-assets@0.2.36: + mc-assets@0.2.37: dependencies: apl-image-packer: 1.1.0 zod: 3.24.1 diff --git a/renderer/viewer/lib/animationController.ts b/renderer/viewer/lib/animationController.ts index bfccc72f..329d0e24 100644 --- a/renderer/viewer/lib/animationController.ts +++ b/renderer/viewer/lib/animationController.ts @@ -5,9 +5,10 @@ export class AnimationController { private isAnimating = false private cancelRequested = false private completionCallbacks: Array<() => void> = [] + private currentCancelCallback: (() => void) | null = null /** Main method */ - async startAnimation (createAnimation: () => tweenJs.Group): Promise { + async startAnimation (createAnimation: () => tweenJs.Group, onCancelled?: () => void): Promise { if (this.isAnimating) { await this.cancelCurrentAnimation() } @@ -15,6 +16,7 @@ export class AnimationController { return new Promise((resolve) => { this.isAnimating = true this.cancelRequested = false + this.currentCancelCallback = onCancelled ?? null this.currentAnimation = createAnimation() this.completionCallbacks.push(() => { @@ -29,6 +31,12 @@ export class AnimationController { async cancelCurrentAnimation (): Promise { if (!this.isAnimating) return + if (this.currentCancelCallback) { + const callback = this.currentCancelCallback + this.currentCancelCallback = null + callback() + } + return new Promise((resolve) => { this.cancelRequested = true this.completionCallbacks.push(() => { @@ -41,7 +49,7 @@ export class AnimationController { if (this.cancelRequested) this.forceFinish() } - forceFinish () { + forceFinish (callComplete = true) { if (!this.isAnimating) return if (this.currentAnimation) { @@ -55,7 +63,9 @@ export class AnimationController { const callbacks = [...this.completionCallbacks] this.completionCallbacks = [] - for (const cb of callbacks) cb() + if (callComplete) { + for (const cb of callbacks) cb() + } } /** Required method */ diff --git a/renderer/viewer/lib/holdingBlock.ts b/renderer/viewer/lib/holdingBlock.ts index c9fdc5f5..3dcc0d5c 100644 --- a/renderer/viewer/lib/holdingBlock.ts +++ b/renderer/viewer/lib/holdingBlock.ts @@ -235,7 +235,7 @@ export default class HoldingBlock { // new tweenJs.Tween(group.rotation).to({ z: THREE.MathUtils.degToRad(90) }, 1000).yoyo(true).repeat(Infinity).start() // } - async playBlockSwapAnimation (forceState?: 'appeared' | 'disappeared') { + async playBlockSwapAnimation (forceState: 'appeared' | 'disappeared') { this.blockSwapAnimation ??= { switcher: new SmoothSwitcher( () => ({ @@ -250,23 +250,29 @@ export default class HoldingBlock { ) } - const newState = this.blockSwapAnimation.switcher.currentStateName === 'disappeared' ? 'appeared' : 'disappeared' - if (forceState && newState !== forceState) throw new Error(`forceState does not match current state ${newState} !== ${forceState}`) + const newState = forceState + // if (forceState && newState !== forceState) { + // throw new Error(`forceState does not match current state ${forceState} !== ${newState}`) + // } const targetY = this.objectInnerGroup.position.y + (this.objectInnerGroup.scale.y * 1.5 * (newState === 'appeared' ? 1 : -1)) - if (newState === this.blockSwapAnimation.switcher.transitioningToStateName) { - return false - } + // if (newState === this.blockSwapAnimation.switcher.transitioningToStateName) { + // return false + // } + let cancelled = false return new Promise((resolve) => { this.blockSwapAnimation!.switcher.transitionTo( { y: targetY }, newState, () => { - resolve(true) + if (!cancelled) { + resolve(true) + } }, () => { + cancelled = true resolve(false) } ) @@ -274,7 +280,18 @@ export default class HoldingBlock { } isDifferentItem (block: HandItemBlock | undefined) { - return !this.lastHeldItem || (this.lastHeldItem.name !== block?.name || JSON.stringify(this.lastHeldItem.fullItem) !== JSON.stringify(block?.fullItem ?? '{}')) + if (!this.lastHeldItem) { + return true + } + if (this.lastHeldItem.name !== block?.name) { + return true + } + // eslint-disable-next-line sonarjs/prefer-single-boolean-return + if (JSON.stringify(this.lastHeldItem.fullItem) !== JSON.stringify(block?.fullItem ?? '{}')) { + return true + } + + return false } updateCameraGroup () { @@ -339,6 +356,9 @@ export default class HoldingBlock { } async replaceItemModel (handItem?: HandItemBlock): Promise { + // if switch animation is in progress, do not replace the item + if (this.blockSwapAnimation?.switcher.isTransitioning) return + if (!handItem) { this.holdingBlock?.removeFromParent() this.holdingBlock = undefined @@ -359,19 +379,21 @@ export default class HoldingBlock { } + switchRequest = 0 async setNewItem (handItem?: HandItemBlock) { if (!this.isDifferentItem(handItem)) return + const switchRequest = ++this.switchRequest + this.lastHeldItem = handItem let playAppearAnimation = false if (this.holdingBlock) { // play disappear animation playAppearAnimation = true - const result = await this.playBlockSwapAnimation() + const result = await this.playBlockSwapAnimation('disappeared') if (!result) return this.holdingBlock?.removeFromParent() this.holdingBlock = undefined } - this.lastHeldItem = handItem if (!handItem) { this.swingAnimator?.stopSwing() this.swingAnimator = undefined @@ -380,8 +402,9 @@ export default class HoldingBlock { return } + if (switchRequest !== this.switchRequest) return const result = await this.createItemModel(handItem) - if (!result) return + if (!result || switchRequest !== this.switchRequest) return const blockOuterGroup = new THREE.Group() this.holdingBlockInnerGroup.removeFromParent() diff --git a/renderer/viewer/lib/mesher/world.ts b/renderer/viewer/lib/mesher/world.ts index 2106e179..e18f8201 100644 --- a/renderer/viewer/lib/mesher/world.ts +++ b/renderer/viewer/lib/mesher/world.ts @@ -172,7 +172,7 @@ export class World { } } - const block = this.blockCache[cacheKey] + const block: WorldBlock = this.blockCache[cacheKey] if (block.models === undefined && blockProvider) { if (!attr) throw new Error('attr is required') @@ -222,7 +222,7 @@ export class World { }) } - if (!block.models!.length) { + if (!block.models.length) { if (block.name !== 'water' && block.name !== 'lava' && !INVISIBLE_BLOCKS.has(block.name)) { console.debug('[mesher] block to render not found', block.name, props) } @@ -231,7 +231,7 @@ export class World { if (block.models && modelOverride) { const model = block.models[0] - block.transparent = model['transparent'] ?? block.transparent + block.transparent = model[0]?.['transparent'] ?? block.transparent } } catch (err) { this.erroredBlockModel ??= blockProvider.getAllResolvedModels0_1({ name: 'errored', properties: {} }) @@ -245,6 +245,7 @@ export class World { if (block.name === 'flowing_lava') block.name = 'lava' if (block.name === 'bubble_column') block.name = 'water' // TODO need to distinguish between water and bubble column // block.position = loc // it overrides position of all currently loaded blocks + //@ts-expect-error block.biome = this.biomeCache[column.getBiome(locInChunk)] ?? this.biomeCache[1] ?? this.biomeCache[0] if (block.name === 'redstone_ore') block.transparent = false return block diff --git a/renderer/viewer/lib/smoothSwitcher.ts b/renderer/viewer/lib/smoothSwitcher.ts index 925affec..74eb1171 100644 --- a/renderer/viewer/lib/smoothSwitcher.ts +++ b/renderer/viewer/lib/smoothSwitcher.ts @@ -88,8 +88,7 @@ export class SmoothSwitcher { onCancelled?: () => void ): void { if (this.isTransitioning) { - onCancelled?.() - this.animationController.forceFinish() + this.animationController.forceFinish(false) } this.transitioningToStateName = stateName ?? '' @@ -116,7 +115,7 @@ export class SmoothSwitcher { }) .start() return group - }) + }, onCancelled) } /** From 077dc9df26dba3476c0d186bd6ce4644debaad14 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 25 Feb 2025 00:02:35 +0300 Subject: [PATCH 278/753] fix: fix hand performance because of unnecessary texture rewrites --- renderer/viewer/lib/entities.ts | 9 ++++++-- renderer/viewer/lib/holdingBlock.ts | 36 +++++++++++++++++++++-------- src/index.ts | 3 ++- src/inventoryWindows.ts | 3 ++- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/renderer/viewer/lib/entities.ts b/renderer/viewer/lib/entities.ts index c4664489..b60a5fef 100644 --- a/renderer/viewer/lib/entities.ts +++ b/renderer/viewer/lib/entities.ts @@ -225,10 +225,11 @@ export class Entities extends EventEmitter { su?: number; sv?: number; size?: number; + modelName?: string; } | { resolvedModel: BlockModel modelName: string - }) + } | undefined) get entitiesByName (): Record { const byName: Record = {} @@ -498,8 +499,10 @@ export class Entities extends EventEmitter { return typeof component === 'string' ? component : component.text ?? '' } - getItemMesh (item, specificProps: ItemSpecificContextProperties) { + getItemMesh (item, specificProps: ItemSpecificContextProperties, previousModel?: string) { const textureUv = this.getItemUv?.(item, specificProps) + if (previousModel && previousModel === textureUv?.modelName) return undefined + if (textureUv && 'resolvedModel' in textureUv) { const mesh = getBlockMeshFromModel(this.viewer.world.material, textureUv.resolvedModel, textureUv.modelName) if (specificProps['minecraft:display_context'] === 'ground') { @@ -514,6 +517,7 @@ export class Entities extends EventEmitter { isBlock: true, itemsTexture: null, itemsTextureFlipped: null, + modelName: textureUv.modelName, } } @@ -555,6 +559,7 @@ export class Entities extends EventEmitter { isBlock: false, itemsTexture, itemsTextureFlipped, + modelName: textureUv.modelName, } } } diff --git a/renderer/viewer/lib/holdingBlock.ts b/renderer/viewer/lib/holdingBlock.ts index 3dcc0d5c..084953fd 100644 --- a/renderer/viewer/lib/holdingBlock.ts +++ b/renderer/viewer/lib/holdingBlock.ts @@ -320,31 +320,37 @@ export default class HoldingBlock { this.objectOuterGroup.scale.set(scale, scale, scale) } + lastItemModelName: string | undefined private async createItemModel (handItem: HandItemBlock): Promise<{ model: THREE.Object3D; type: 'hand' | 'block' | 'item' } | undefined> { this.lastUpdate = performance.now() if (!handItem || (handItem.type === 'hand' && !this.playerHand)) return undefined - let blockInner: THREE.Object3D + let blockInner: THREE.Object3D | undefined if (handItem.type === 'item' || handItem.type === 'block') { - const { mesh: itemMesh, isBlock } = viewer.entities.getItemMesh({ + const result = viewer.entities.getItemMesh({ ...handItem.fullItem, itemId: handItem.id, }, { 'minecraft:display_context': 'firstperson', 'minecraft:use_duration': this.playerState.getItemUsageTicks?.(), 'minecraft:using_item': !!this.playerState.getItemUsageTicks?.(), - })! - if (isBlock) { - blockInner = itemMesh - handItem.type = 'block' - } else { - itemMesh.position.set(0.5, 0.5, 0.5) - blockInner = itemMesh - handItem.type = 'item' + }, this.lastItemModelName) + if (result) { + const { mesh: itemMesh, isBlock, modelName } = result + if (isBlock) { + blockInner = itemMesh + handItem.type = 'block' + } else { + itemMesh.position.set(0.5, 0.5, 0.5) + blockInner = itemMesh + handItem.type = 'item' + } + this.lastItemModelName = modelName } } else { blockInner = this.playerHand! } + if (!blockInner) return blockInner.name = 'holdingBlock' const rotationDeg = this.getHandHeld3d().rotation @@ -379,9 +385,19 @@ export default class HoldingBlock { } + testUnknownBlockSwitch () { + void this.setNewItem({ + type: 'item', + name: 'minecraft:some-unknown-block', + id: 0, + fullItem: {} + }) + } + switchRequest = 0 async setNewItem (handItem?: HandItemBlock) { if (!this.isDifferentItem(handItem)) return + this.lastItemModelName = undefined const switchRequest = ++this.switchRequest this.lastHeldItem = handItem let playAppearAnimation = false diff --git a/src/index.ts b/src/index.ts index c0721cc6..6c46d0e9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -214,7 +214,8 @@ viewer.entities.getItemUv = (item, specificProps) => { const [u, v, su, sv] = [x / img.width, y / img.height, (w / img.width), (h / img.height)] return { u, v, su, sv, - texture: textureThree + texture: textureThree, + modelName: renderInfo.modelName } } diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index bdcdef3c..471f5be4 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -207,7 +207,8 @@ export const renderSlot = (slot: GeneralInputItem, debugIsQuickbar = false, full // is item return { texture: itemTexture.type, - slice: itemTexture.slice + slice: itemTexture.slice, + modelName: itemModelName } } else { // is block From 2833b33b4ed24a5400b7441701a57eb2b73e13e5 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 25 Feb 2025 00:29:34 +0300 Subject: [PATCH 279/753] fix: display err when sound mappings not found --- src/sounds/botSoundSystem.ts | 4 ++++ src/sounds/soundsMap.ts | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/sounds/botSoundSystem.ts b/src/sounds/botSoundSystem.ts index 8a22082e..aa33db60 100644 --- a/src/sounds/botSoundSystem.ts +++ b/src/sounds/botSoundSystem.ts @@ -7,6 +7,7 @@ import { miscUiState } from '../globalState' import { options } from '../optionsStorage' import { loadOrPlaySound } from '../basicSounds' import { getActiveResourcepackBasePath, resourcePackState } from '../resourcePack' +import { showNotification } from '../react/NotificationProvider' import { createSoundMap, SoundMap } from './soundsMap' import { musicSystem } from './musicSystem' @@ -31,6 +32,9 @@ subscribeKey(miscUiState, 'gameLoaded', async () => { soundMap = createSoundMap(bot.version) ?? undefined globalThis.soundMap = soundMap if (!soundMap) return + if (soundMap.noVersionIdMapping) { + showNotification('No sound ID mapping for this version', undefined, true) + } void updateResourcePack() startMusicSystem() diff --git a/src/sounds/soundsMap.ts b/src/sounds/soundsMap.ts index 7cdfe74d..c461dd8d 100644 --- a/src/sounds/soundsMap.ts +++ b/src/sounds/soundsMap.ts @@ -52,13 +52,27 @@ export class SoundMap { private readonly existingResourcePackPaths: Set private activeResourcePackBasePath: string | undefined private activeResourcePackSoundsJson: ResourcePackSoundsJson | undefined + noVersionIdMapping = false constructor ( private readonly soundData: SoundMapData, private readonly version: string ) { - const allSoundsMajor = versionsMapToMajor(soundData.allSoundsMap) - const soundsMap = allSoundsMajor[versionToMajor(version)] ?? Object.values(allSoundsMajor)[0] + // First try exact version match + let soundsMap = soundData.allSoundsMap[this.version] + + if (!soundsMap) { + // If no exact match, try major version + const allSoundsMajor = versionsMapToMajor(soundData.allSoundsMap) + soundsMap = allSoundsMajor[versionToMajor(version)] + + if (!soundsMap) { + // If still no match, use first available mapping + soundsMap = Object.values(allSoundsMajor)[0] + } + + this.noVersionIdMapping = true + } // Create both mappings this.soundsIdToName = {} From b7560f716a86d84e2ec1f889035a52b4d9e12a2a Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 25 Feb 2025 23:11:02 +0300 Subject: [PATCH 280/753] fix custom block models regression --- renderer/viewer/lib/mesher/world.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderer/viewer/lib/mesher/world.ts b/renderer/viewer/lib/mesher/world.ts index e18f8201..e13bf760 100644 --- a/renderer/viewer/lib/mesher/world.ts +++ b/renderer/viewer/lib/mesher/world.ts @@ -144,7 +144,7 @@ export class World { if (!this.blockCache[cacheKey]) { const b = column.getBlock(locInChunk) as unknown as WorldBlock if (modelOverride) { - // b.name = modelOverride + b.name = modelOverride } b.isCube = isCube(b.shapes) this.blockCache[cacheKey] = b From acd8144d7640188bd5854cbe344ac53a540d88ac Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 03:29:10 +0300 Subject: [PATCH 281/753] feat: initial config.json is now bundled on build step, which is required for defaultSettings feat: allow to specify default and locked settings in config.json feat: allow to specify default app params in config.json feat: rework how loading progress is reported in app on connect feat: add setting to wait for chunks to load before starting rendering (which is highly recommended to enable), however not enabled by default because functionality is top priority of the client out of the box, not pleasent ux, so pls enable yourself --- renderer/viewer/lib/viewerWrapper.ts | 4 +- rsbuild.config.ts | 11 ++- scripts/prepareSounds.mjs | 10 +- src/appParams.ts | 12 ++- src/browserfs.ts | 3 +- src/controls.ts | 9 +- src/downloadAndOpenFile.ts | 3 +- src/globalState.ts | 23 +++++ src/index.ts | 142 ++++++++++++++++++--------- src/optionsGuiScheme.tsx | 4 +- src/optionsStorage.ts | 13 ++- src/react/OptionsItems.tsx | 4 +- src/resourcePack.ts | 95 +++++++++--------- src/supportedVersions.mjs | 2 + 14 files changed, 218 insertions(+), 117 deletions(-) diff --git a/renderer/viewer/lib/viewerWrapper.ts b/renderer/viewer/lib/viewerWrapper.ts index 2e08da1d..6b2fb562 100644 --- a/renderer/viewer/lib/viewerWrapper.ts +++ b/renderer/viewer/lib/viewerWrapper.ts @@ -1,5 +1,6 @@ import * as THREE from 'three' import { statsEnd, statsStart } from '../../../src/topRightStats' +import { activeModalStack } from '../../../src/globalState' // wrapper for now export class ViewerWrapper { @@ -77,8 +78,8 @@ export class ViewerWrapper { postRender = () => { } render (time: DOMHighResTimeStamp) { if (this.globalObject.stopLoop) return - for (const fn of beforeRenderFrame) fn() this.globalObject.requestAnimationFrame(this.render.bind(this)) + if (activeModalStack.some(m => m.reactType === 'app-status')) return if (!viewer || this.globalObject.stopRender || this.renderer?.xr.isPresenting || (this.stopRenderOnBlur && !this.windowFocused)) return const renderInterval = (this.windowFocused ? this.renderInterval : this.renderIntervalUnfocused) ?? this.renderInterval if (renderInterval) { @@ -91,6 +92,7 @@ export class ViewerWrapper { return } } + for (const fn of beforeRenderFrame) fn() this.preRender() statsStart() // ios bug: viewport dimensions are updated after the resize event diff --git a/rsbuild.config.ts b/rsbuild.config.ts index a93e2c84..5fd9316c 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -33,6 +33,11 @@ if (fs.existsSync('./assets/release.json')) { releaseChangelog = releaseJson.changelog?.replace(//, '') } +const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8')) +if (dev) { + configJson.defaultProxy = ':8080' +} + // base options are in ./renderer/rsbuildSharedConfig.ts const appConfig = defineConfig({ html: { @@ -64,6 +69,7 @@ const appConfig = defineConfig({ 'process.env.RELEASE_LINK': JSON.stringify(releaseLink), 'process.env.RELEASE_CHANGELOG': JSON.stringify(releaseChangelog), 'process.env.DISABLE_SERVICE_WORKER': JSON.stringify(disableServiceWorker), + 'process.env.INLINED_APP_CONFIG': JSON.stringify(configJson), }, }, server: { @@ -99,14 +105,11 @@ const appConfig = defineConfig({ if (fs.existsSync('./assets/release.json')) { fs.copyFileSync('./assets/release.json', './dist/release.json') } - const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8')) let configLocalJson = {} try { configLocalJson = JSON.parse(fs.readFileSync('./config.local.json', 'utf8')) } catch (err) {} - if (dev) { - configJson.defaultProxy = ':8080' - } + fs.writeFileSync('./dist/config.json', JSON.stringify({ ...configJson, ...configLocalJson }), 'utf8') if (fs.existsSync('./generated/sounds.js')) { fs.copyFileSync('./generated/sounds.js', './dist/sounds.js') diff --git a/scripts/prepareSounds.mjs b/scripts/prepareSounds.mjs index f9b8cd60..f80ace4e 100644 --- a/scripts/prepareSounds.mjs +++ b/scripts/prepareSounds.mjs @@ -7,10 +7,11 @@ import { fileURLToPath } from 'url' import { exec } from 'child_process' import { promisify } from 'util' import { build } from 'esbuild' +import supportedVersions from '../src/supportedVersions.mjs' const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url))) -const targetedVersions = ['1.21.1', '1.20.6', '1.20.1', '1.19.2', '1.18.2', '1.17.1', '1.16.5', '1.15.2', '1.14.4', '1.13.2', '1.12.2', '1.11.2', '1.10.2', '1.9.4', '1.8.9'] +const targetedVersions = supportedVersions.reverse() /** @type {{name, size, hash}[]} */ let prevSounds = null @@ -145,6 +146,7 @@ const convertSounds = async () => { const convertSound = async (i) => { const proc = promisify(exec)(`${ffmpegExec} -i "${toConvert[i]}" -y -codec:a libmp3lame ${maintainBitrate ? '-qscale:a 2' : ''} "${toConvert[i].replace('.ogg', '.mp3')}"`) // pipe stdout to the console + //@ts-ignore proc.child.stdout.pipe(process.stdout) await proc console.log('converted to mp3', i, '/', toConvert.length, toConvert[i]) @@ -173,7 +175,11 @@ const writeSoundsMap = async () => { // const localTargetedVersions = targetedVersions.slice(0, 2) const localTargetedVersions = targetedVersions for (const targetedVersion of localTargetedVersions) { - const burgerData = await fetch(burgerDataUrl(targetedVersion)).then((r) => r.json()) + const burgerData = await fetch(burgerDataUrl(targetedVersion)).then((r) => r.json()).catch((err) => { + console.error('error fetching burger data', targetedVersion, err) + return null + }) + if (!burgerData) continue const allSoundsMap = getSoundsMap(burgerData) // console.log(Object.keys(sounds).length, 'ids') const outputIdMap = {} diff --git a/src/appParams.ts b/src/appParams.ts index 6e9a2d50..50fbf055 100644 --- a/src/appParams.ts +++ b/src/appParams.ts @@ -1,3 +1,5 @@ +import type { AppConfig } from './globalState' + const qsParams = new URLSearchParams(window.location?.search ?? '') export type AppQsParams = { @@ -61,12 +63,16 @@ type AppQsParamsArrayTransformed = { [k in keyof AppQsParamsArray]: string[] } +const initialAppConfig = process.env.INLINED_APP_CONFIG as AppConfig ?? {} + export const appQueryParams = new Proxy({} as AppQsParams, { get (target, property) { if (typeof property !== 'string') { return undefined } - return qsParams.get(property) + const qsParam = qsParams.get(property) + if (qsParam) return qsParam + return initialAppConfig.appParams?.[property] }, }) @@ -75,7 +81,9 @@ export const appQueryParamsArray = new Proxy({} as AppQsParamsArrayTransformed, if (typeof property !== 'string') { return null } - return qsParams.getAll(property) + const qsParam = qsParams.getAll(property) + if (qsParam.length) return qsParam + return initialAppConfig.appParams?.[property] ?? [] }, }) diff --git a/src/browserfs.ts b/src/browserfs.ts index 76733e64..d38b13a2 100644 --- a/src/browserfs.ts +++ b/src/browserfs.ts @@ -13,6 +13,7 @@ import { setLoadingScreenStatus } from './appStatus' import { VALID_REPLAY_EXTENSIONS, openFile } from './packetsReplay/replayPackets' import { getFixedFilesize } from './downloadAndOpenFile' import { packetsReplayState } from './react/state/packetsReplayState' +import { createFullScreenProgressReporter } from './core/progressReporter' const { GoogleDriveFileSystem } = require('google-drive-browserfs/src/backends/GoogleDrive') browserfs.install(window) @@ -635,7 +636,7 @@ export const openFilePicker = (specificCase?: 'resourcepack') => { const doContinue = confirm(`Are you sure ${file.name.slice(-20)} is .zip file? ONLY .zip files are supported. Continue?`) if (!doContinue) return } - void installResourcepackPack(file).catch((err) => { + void installResourcepackPack(file, createFullScreenProgressReporter()).catch((err) => { setLoadingScreenStatus(err.message, true) }) } else { diff --git a/src/controls.ts b/src/controls.ts index be5028aa..a3a54ffb 100644 --- a/src/controls.ts +++ b/src/controls.ts @@ -20,10 +20,11 @@ import { showOptionsModal } from './react/SelectOption' import widgets from './react/widgets' import { getItemFromBlock } from './chatUtils' import { gamepadUiCursorState, moveGamepadCursorByPx } from './react/GamepadUiCursor' -import { completeTexturePackInstall, copyServerResourcePackToRegular, resourcePackState } from './resourcePack' +import { completeResourcepackPackInstall, copyServerResourcePackToRegular, resourcePackState } from './resourcePack' import { showNotification } from './react/NotificationProvider' import { lastConnectOptions } from './react/AppStatusProvider' import { onCameraMove, onControInit } from './cameraRotationControls' +import { createNotificationProgressReporter } from './core/progressReporter' export const customKeymaps = proxy(JSON.parse(localStorage.keymap || '{}')) as UserOverridesConfig @@ -627,7 +628,7 @@ export const f3Keybinds: Array<{ // TODO! if (resourcePackState.resourcePackInstalled || gameAdditionalState.usingServerResourcePack) { showNotification('Reloading textures...') - await completeTexturePackInstall('default', 'default', gameAdditionalState.usingServerResourcePack) + await completeResourcepackPackInstall('default', 'default', gameAdditionalState.usingServerResourcePack, createNotificationProgressReporter()) } }, mobileTitle: 'Reload Textures' @@ -668,8 +669,8 @@ export const f3Keybinds: Array<{ const proxyPing = await bot['pingProxy']() void showOptionsModal(`${username}: last known total latency (ping): ${playerPing}. Connected to ${lastConnectOptions.value?.proxy} with current ping ${proxyPing}. Player UUID: ${uuid}`, []) }, - mobileTitle: 'Show Proxy & Ping Details', - enabled: () => !!lastConnectOptions.value?.proxy + mobileTitle: 'Show Player & Ping Details', + enabled: () => !lastConnectOptions.value?.singleplayer && !!bot.player }, { action () { diff --git a/src/downloadAndOpenFile.ts b/src/downloadAndOpenFile.ts index 2cbb3aad..870a70b1 100644 --- a/src/downloadAndOpenFile.ts +++ b/src/downloadAndOpenFile.ts @@ -4,6 +4,7 @@ import { getResourcePackNames, installResourcepackPack, resourcePackState, updat import { setLoadingScreenStatus } from './appStatus' import { appQueryParams, appQueryParamsArray } from './appParams' import { VALID_REPLAY_EXTENSIONS, openFile } from './packetsReplay/replayPackets' +import { createFullScreenProgressReporter } from './core/progressReporter' export const getFixedFilesize = (bytes: number) => { return prettyBytes(bytes, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) @@ -92,7 +93,7 @@ const inner = async () => { })).arrayBuffer() if (texturepack) { const name = mapUrl.slice(mapUrl.lastIndexOf('/') + 1).slice(-30) - await installResourcepackPack(buffer, name) + await installResourcepackPack(buffer, createFullScreenProgressReporter(), name) } else { await openWorldZip(buffer) } diff --git a/src/globalState.ts b/src/globalState.ts index 412dd31b..66959198 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -4,6 +4,7 @@ import { proxy, ref, subscribe } from 'valtio' import type { WorldWarp } from 'flying-squid/dist/lib/modules/warps' import type { OptionsGroupType } from './optionsGuiScheme' import { appQueryParams } from './appParams' +import { options, disabledSettings } from './optionsStorage' // todo: refactor structure with support of hideNext=false @@ -120,7 +121,11 @@ export type AppConfig = { promoteServers?: Array<{ ip, description, version? }> mapsProvider?: string + appParams?: Record // query string params + defaultSettings?: Record + forceSettings?: Record + // hideSettings?: Record allowAutoConnect?: boolean } @@ -145,6 +150,24 @@ export const miscUiState = proxy({ displayFullmap: false }) +export const loadAppConfig = (appConfig: AppConfig) => { + if (miscUiState.appConfig) { + Object.assign(miscUiState.appConfig, appConfig) + } else { + miscUiState.appConfig = appConfig + } + + if (appConfig.forceSettings) { + for (const [key, value] of Object.entries(appConfig.forceSettings)) { + if (value) { + disabledSettings.value.delete(key) + } else { + disabledSettings.value.add(key) + } + } + } +} + export const isGameActive = (foregroundCheck: boolean) => { if (foregroundCheck && activeModalStack.length) return false return miscUiState.gameLoaded diff --git a/src/index.ts b/src/index.ts index 6c46d0e9..1fb318b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -57,7 +57,8 @@ import { isGameActive, miscUiState, showModal, - gameAdditionalState + gameAdditionalState, + loadAppConfig } from './globalState' import { parseServerAddress } from './parseServerAddress' @@ -114,6 +115,7 @@ import ping from './mineflayer/plugins/ping' import { LocalServer } from './customServer' import { startLocalReplayServer } from './packetsReplay/replayPackets' import { localRelayServerPlugin } from './mineflayer/plugins/localRelay' +import { createFullScreenProgressReporter } from './core/progressReporter' window.debug = debug window.THREE = THREE @@ -318,8 +320,9 @@ export async function connect (connectOptions: ConnectOptions) { console.log('using player username', username) hideCurrentScreens() + const progress = createFullScreenProgressReporter() const loggingInMsg = connectOptions.server ? 'Connecting to server' : 'Logging in' - setLoadingScreenStatus(loggingInMsg) + progress.beginStage('connect', loggingInMsg) let ended = false let bot!: typeof __type_bot @@ -405,38 +408,43 @@ export async function connect (connectOptions: ConnectOptions) { try { const serverOptions = defaultsDeep({}, connectOptions.serverOverrides ?? {}, options.localServerOptions, defaultServerOptions) Object.assign(serverOptions, connectOptions.serverOverridesFlat ?? {}) - setLoadingScreenStatus('Downloading minecraft data') - await Promise.all([ - downloadAllMinecraftData(), // download mc data before we can use minecraft-data at all - downloadOtherGameData() - ]) - setLoadingScreenStatus(loggingInMsg) + + await progress.executeWithMessage('Downloading minecraft data', 'download-mcdata', async () => { + await Promise.all([ + downloadAllMinecraftData(), + downloadOtherGameData() + ]) + }) + let dataDownloaded = false const downloadMcData = async (version: string) => { if (dataDownloaded) return dataDownloaded = true - // if (connectOptions.authenticatedAccount && (versionToNumber(version) < versionToNumber('1.19.4') || versionToNumber(version) >= versionToNumber('1.21'))) { - // // todo support it (just need to fix .export crash) - // throw new UserError('Microsoft authentication is only supported on 1.19.4 - 1.20.6 (at least for now)') - // } - await downloadMcDataOnConnect(version) - try { - // TODO! reload only after login packet (delay viewer display) so no unecessary reload after server one is isntalled - await resourcepackReload(version) - } catch (err) { - console.error(err) - const doContinue = confirm('Failed to apply texture pack. See errors in the console. Continue?') - if (!doContinue) { - throw err + await progress.executeWithMessage( + 'Applying user-installed resource pack', + async () => { + await downloadMcDataOnConnect(version) + try { + await resourcepackReload(version) + } catch (err) { + console.error(err) + const doContinue = confirm('Failed to apply texture pack. See errors in the console. Continue?') + if (!doContinue) { + throw err + } + } } - } - const oldStatus = appStatusState.status - setLoadingScreenStatus('Loading minecraft assets') - viewer.world.blockstatesModels = await import('mc-assets/dist/blockStatesModels.json') - void viewer.setVersion(version, options.useVersionsTextures === 'latest' ? version : options.useVersionsTextures) - miscUiState.loadedDataVersion = version - setLoadingScreenStatus(oldStatus) + ) + + await progress.executeWithMessage( + 'Loading minecraft models', + async () => { + viewer.world.blockstatesModels = await import('mc-assets/dist/blockStatesModels.json') + void viewer.setVersion(version, options.useVersionsTextures === 'latest' ? version : options.useVersionsTextures) + miscUiState.loadedDataVersion = version + } + ) } let finalVersion = connectOptions.botVersion || (singleplayer ? serverOptions.version : undefined) @@ -463,21 +471,24 @@ export async function connect (connectOptions: ConnectOptions) { // Client (class) of flying-squid (in server/login.js of mc-protocol): onLogin handler: skip most logic & go to loginClient() which assigns uuid and sends 'success' back to client (onLogin handler) and emits 'login' on the server (login.js in flying-squid handler) // flying-squid: 'login' -> player.login -> now sends 'login' event to the client (handled in many plugins in mineflayer) -> then 'update_health' is sent which emits 'spawn' in mineflayer - setLoadingScreenStatus('Starting local server') localServer = window.localServer = window.server = startLocalServer(serverOptions) // todo need just to call quit if started // loadingScreen.maybeRecoverable = false // init world, todo: do it for any async plugins if (!localServer.pluginsReady) { - await new Promise(resolve => { - localServer.once('pluginsReady', resolve) - }) + await progress.executeWithMessage( + 'Starting local server', + async () => { + await new Promise(resolve => { + localServer.once('pluginsReady', resolve) + }) + } + ) } localServer.on('newPlayer', (player) => { - // it's you! player.on('loadingStatus', (newStatus) => { - setLoadingScreenStatus(newStatus, false, false, true) + progress.beginStage('flying-squid', newStatus) }) }) flyingSquidEvents() @@ -754,6 +765,17 @@ export async function connect (connectOptions: ConnectOptions) { window.pathfinder = pathfinder }) + const start = Date.now() + let worldWasReady = false + void viewer.world.renderUpdateEmitter.on('update', () => { + // todo might not emit as servers simply don't send chunk if it's empty + if (!viewer.world.allChunksFinished || worldWasReady) return + worldWasReady = true + console.log('All chunks done and ready! Time from renderer open to ready', (Date.now() - start) / 1000, 's') + viewer.render() // ensure the last state is rendered + document.dispatchEvent(new Event('cypress-world-ready')) + }) + const spawnEarlier = !singleplayer && !p2pMultiplayer // don't use spawn event, player can be dead bot.once(spawnEarlier ? 'forcedMove' : 'health', async () => { @@ -769,11 +791,7 @@ export async function connect (connectOptions: ConnectOptions) { window.focus?.() errorAbortController.abort() - miscUiState.gameLoaded = true - miscUiState.loadedServerIndex = connectOptions.serverIndex ?? '' - customEvents.emit('gameLoaded') if (p2pConnectTimeout) clearTimeout(p2pConnectTimeout) - playerState.onlineMode = !!connectOptions.authenticatedAccount setLoadingScreenStatus('Placing blocks (starting viewer)') @@ -827,6 +845,36 @@ export async function connect (connectOptions: ConnectOptions) { onGameLoad(() => {}) if (appStatusState.isError) return + + const waitForChunks = async () => { + const waitForChunks = options.waitForChunksRender === 'sp-only' ? !!singleplayer : options.waitForChunksRender + if (viewer.world.allChunksFinished || !waitForChunks) { + return + } + + await progress.executeWithMessage( + 'Loading chunks', + 'chunks', + async () => { + await new Promise(resolve => { + let wasFinished = false + void viewer.world.renderUpdateEmitter.on('update', () => { + if (wasFinished) return + if (viewer.world.allChunksFinished) { + wasFinished = true + resolve() + } else { + const perc = Math.round(Object.keys(viewer.world.finishedChunks).length / viewer.world.chunksLength * 100) + progress.reportProgress('chunks', perc / 100) + } + }) + }) + } + ) + } + + await waitForChunks() + setTimeout(() => { if (appQueryParams.suggest_save) { showNotification('Suggestion', 'Save the world to keep your progress!', false, undefined, async () => { @@ -840,17 +888,11 @@ export async function connect (connectOptions: ConnectOptions) { } }, 600) + miscUiState.gameLoaded = true + miscUiState.loadedServerIndex = connectOptions.serverIndex ?? '' + customEvents.emit('gameLoaded') setLoadingScreenStatus(undefined) - const start = Date.now() - let done = false - void viewer.world.renderUpdateEmitter.on('update', () => { - // todo might not emit as servers simply don't send chunk if it's empty - if (!viewer.world.allChunksFinished || done) return - done = true - console.log('All chunks done and ready! Time from renderer open to ready', (Date.now() - start) / 1000, 's') - viewer.render() // ensure the last state is rendered - document.dispatchEvent(new Event('cypress-world-ready')) - }) + progress.end() }) if (singleplayer && connectOptions.serverOverrides.worldFolder) { @@ -934,11 +976,13 @@ document.body.addEventListener('touchstart', (e) => { }, { passive: false }) // #endregion +loadAppConfig(process.env.INLINED_APP_CONFIG as AppConfig ?? {}) +// load maybe updated config on the server with updated params (just in case) void window.fetch('config.json').then(async res => res.json()).then(c => c, (error) => { console.warn('Failed to load optional app config.json', error) return {} }).then((config: AppConfig | {}) => { - miscUiState.appConfig = config + loadAppConfig(config) }) // qs open actions diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index 8b994507..cb2d9be8 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -10,7 +10,7 @@ import Slider from './react/Slider' import { getScreenRefreshRate } from './utils' import { setLoadingScreenStatus } from './appStatus' import { openFilePicker, resetLocalStorageWithoutWorld } from './browserfs' -import { completeTexturePackInstall, getResourcePackNames, resourcePackState, uninstallResourcePack } from './resourcePack' +import { completeResourcepackPackInstall, getResourcePackNames, resourcePackState, uninstallResourcePack } from './resourcePack' import { downloadPacketsReplay, packetsReplaceSessionState } from './packetsReplay/packetsReplayLegacy' import { showOptionsModal } from './react/SelectOption' import supportedVersions from './supportedVersions.mjs' @@ -181,7 +181,7 @@ export const guiOptionsScheme: { } if (choice === 'Enable') { options.enabledResourcepack = name - await completeTexturePackInstall(name, name, false) + await completeResourcepackPackInstall(name, name, false) return } if (choice === 'Uninstall') { diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index b82d6bfb..6dc1608b 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -5,8 +5,10 @@ import { proxy, subscribe } from 'valtio/vanilla' import { subscribeKey } from 'valtio/utils' import { omitObj } from '@zardoy/utils' import { appQueryParamsArray } from './appParams' +import type { AppConfig } from './globalState' const isDev = process.env.NODE_ENV === 'development' +const initialAppConfig = process.env.INLINED_APP_CONFIG as AppConfig ?? {} const defaultOptions = { renderDistance: 3, keepChunksDistance: 1, @@ -58,6 +60,8 @@ const defaultOptions = { serversAutoVersionSelect: 'auto' as 'auto' | 'latest' | '1.20.4' | string, customChannels: false, packetsReplayAutoStart: false, + // todo ui setting, maybe enable by default? + waitForChunksRender: 'sp-only' as 'sp-only' | boolean, // antiAliasing: false, @@ -133,6 +137,11 @@ export const qsOptions = Object.fromEntries(qsOptionsRaw.map(o => { return [key, JSON.parse(value)] })) +// Track which settings are disabled (controlled by QS or forced by config) +export const disabledSettings = proxy({ + value: new Set(Object.keys(qsOptions)) +}) + const migrateOptions = (options: Partial>) => { if (options.highPerformanceGpu) { options.gpuPreference = 'high-performance' @@ -155,6 +164,7 @@ export type AppOptions = typeof defaultOptions export const options: AppOptions = proxy({ ...defaultOptions, + ...initialAppConfig.defaultSettings, ...migrateOptions(JSON.parse(localStorage.options || '{}')), ...qsOptions }) @@ -172,7 +182,8 @@ Object.defineProperty(window, 'debugChangedOptions', { }) subscribe(options, () => { - const saveOptions = omitObj(options, ...Object.keys(qsOptions) as [any]) + // Don't save disabled settings to localStorage + const saveOptions = omitObj(options, [...disabledSettings.value] as any) localStorage.options = JSON.stringify(saveOptions) }) diff --git a/src/react/OptionsItems.tsx b/src/react/OptionsItems.tsx index 9879aeb4..4759b3d7 100644 --- a/src/react/OptionsItems.tsx +++ b/src/react/OptionsItems.tsx @@ -2,7 +2,7 @@ import { useSnapshot } from 'valtio' import { noCase } from 'change-case' import { titleCase } from 'title-case' import { useMemo } from 'react' -import { options, qsOptions } from '../optionsStorage' +import { disabledSettings, options, qsOptions } from '../optionsStorage' import { hideAllModals, miscUiState } from '../globalState' import Button from './Button' import Slider from './Slider' @@ -39,7 +39,7 @@ export type OptionMeta = GeneralItem & ({ // todo not reactive const isLocked = (item: GeneralItem) => { - return Object.keys(qsOptions).includes(item.id!) + return disabledSettings.value.has(item.id!) } const useCommonComponentsProps = (item: OptionMeta) => { diff --git a/src/resourcePack.ts b/src/resourcePack.ts index fdb36d45..aef23e8f 100644 --- a/src/resourcePack.ts +++ b/src/resourcePack.ts @@ -6,7 +6,6 @@ import { proxy, subscribe } from 'valtio' import { WorldRendererThree } from 'renderer/viewer/lib/worldrendererThree' import { armorTextures } from 'renderer/viewer/lib/entity/armorModels' import { collectFilesToCopy, copyFilesAsyncWithProgress, mkdirRecursive, removeFileRecursiveAsync } from './browserfs' -import { setLoadingScreenStatus } from './appStatus' import { showNotification } from './react/NotificationProvider' import { options } from './optionsStorage' import { showOptionsModal } from './react/SelectOption' @@ -14,6 +13,7 @@ import { appStatusState } from './react/AppStatusProvider' import { appReplacableResources, resourcesContentOriginal } from './generated/resources' import { gameAdditionalState, miscUiState } from './globalState' import { watchUnloadForCleanup } from './gameUnload' +import { createConsoleLogProgressReporter, createFullScreenProgressReporter, ProgressReporter } from './core/progressReporter' export const resourcePackState = proxy({ resourcePackInstalled: false, @@ -31,13 +31,13 @@ const getLoadedImage = async (url: string) => { return img } -const texturePackBasePath = '/data/resourcePacks/' +const resourcepackPackBasePath = '/data/resourcePacks/' export const uninstallResourcePack = async (name = 'default') => { if (await existsAsync('/resourcepack/pack.mcmeta')) { await removeFileRecursiveAsync('/resourcepack') gameAdditionalState.usingServerResourcePack = false } - const basePath = texturePackBasePath + name + const basePath = resourcepackPackBasePath + name if (!(await existsAsync(basePath))) return await removeFileRecursiveAsync(basePath) options.enabledResourcepack = null @@ -47,7 +47,7 @@ export const uninstallResourcePack = async (name = 'default') => { export const getResourcePackNames = async () => { // TODO try { - return { [await fs.promises.readFile(join(texturePackBasePath, 'default', 'name.txt'), 'utf8')]: true } + return { [await fs.promises.readFile(join(resourcepackPackBasePath, 'default', 'name.txt'), 'utf8')]: true } } catch (err) { return {} } @@ -59,7 +59,7 @@ export const fromTexturePackPath = (path) => { export const updateTexturePackInstalledState = async () => { try { - resourcePackState.resourcePackInstalled = await existsAsync(texturePackBasePath + 'default') + resourcePackState.resourcePackInstalled = await existsAsync(resourcepackPackBasePath + 'default') } catch { } } @@ -70,19 +70,18 @@ export const installTexturePackFromHandle = async () => { // await completeTexturePackInstall() } -export const installResourcepackPack = async (file: File | ArrayBuffer, displayName = file['name'], name = 'default', isServer = false) => { +export const installResourcepackPack = async (file: File | ArrayBuffer, progressReporter: ProgressReporter, displayName = file['name'], name = 'default', isServer = false) => { console.time('processResourcePack') - const installPath = isServer ? '/resourcepack/' : texturePackBasePath + name + const installPath = isServer ? '/resourcepack/' : resourcepackPackBasePath + name try { - await uninstallResourcePack(name) + await progressReporter.executeWithMessage('Uninstalling resource pack', async () => { + await uninstallResourcePack(name) + }) } catch (err) { } - const showLoader = !isServer const status = 'Installing resource pack: copying all files' + progressReporter.beginStage('copy-files-resourcepack', status) - if (showLoader) { - setLoadingScreenStatus(status) - } // extract the zip and write to fs every file in it const zip = new JSZip() const zipFile = await zip.loadAsync(file) @@ -93,9 +92,7 @@ export const installResourcepackPack = async (file: File | ArrayBuffer, displayN .filter(([path]) => !path.startsWith('.') && !path.startsWith('_') && !path.startsWith('/')) // ignore dot files and __MACOSX let done = 0 const upStatus = () => { - if (showLoader) { - setLoadingScreenStatus(`${status} ${Math.round(done / allFilesArr.length * 100)}%`) - } + progressReporter.reportProgress('copy-files-resourcepack', done / allFilesArr.length) } const createdDirs = new Set() const copyTasks = [] as Array> @@ -119,25 +116,24 @@ export const installResourcepackPack = async (file: File | ArrayBuffer, displayN upStatus() })) console.timeEnd('resourcePackCopy') - await completeTexturePackInstall(displayName, name, isServer) + await completeResourcepackPackInstall(displayName, name, isServer, progressReporter) console.log('resource pack install done') console.timeEnd('processResourcePack') } // or enablement -export const completeTexturePackInstall = async (displayName: string | undefined, name: string, isServer: boolean) => { - const basePath = isServer ? '/resourcepack/' : texturePackBasePath + name +export const completeResourcepackPackInstall = async (displayName: string | undefined, name: string, isServer: boolean, progressReporter: ProgressReporter) => { + const basePath = isServer ? '/resourcepack/' : resourcepackPackBasePath + name if (displayName) { await fs.promises.writeFile(join(basePath, 'name.txt'), displayName, 'utf8') } - await updateTextures() - setLoadingScreenStatus(undefined) + await updateTextures(progressReporter) if (currentErrors.length > 0) { - showNotification(`Texturepack installed & enabled with ${currentErrors.length} errors`) - console.error('Texturepack installed & enabled with errors:', currentErrors) + showNotification(`Resource pack installed & enabled with ${currentErrors.length} errors`) + console.error('Resource pack installed & enabled with errors:', currentErrors) } else { - showNotification('Texturepack installed & enabled') + showNotification('Resource pack installed & enabled') } await updateTexturePackInstalledState() if (isServer) { @@ -145,6 +141,8 @@ export const completeTexturePackInstall = async (displayName: string | undefined } else { options.enabledResourcepack = name } + + progressReporter.end() } const existsAsync = async (path) => { @@ -211,14 +209,13 @@ const getFilesMapFromDir = async (dir: string) => { let currentErrors = [] as string[] -export const getResourcepackTiles = async (type: 'blocks' | 'items' | 'armor', existingTextures: string[]) => { +export const getResourcepackTiles = async (type: 'blocks' | 'items' | 'armor', existingTextures: string[], progressReporter: ProgressReporter) => { const basePath = await getActiveResourcepackBasePath() if (!basePath) return let firstTextureSize: number | undefined const namespaces = await fs.promises.readdir(join(basePath, 'assets')) - if (appStatusState.status) { - setLoadingScreenStatus(`Generating atlas texture for ${type}`) - } + progressReporter.beginStage(`generate-atlas-texture-${type}`, `Generating atlas texture for ${type}`) + const textures = {} as Record let path switch (type) { @@ -314,15 +311,13 @@ export const getResourcepackTiles = async (type: 'blocks' | 'items' | 'armor', e } } -const prepareBlockstatesAndModels = async () => { +const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) => { viewer.world.customBlockStates = {} viewer.world.customModels = {} const usedTextures = new Set() const basePath = await getActiveResourcepackBasePath() if (!basePath) return - if (appStatusState.status) { - setLoadingScreenStatus('Reading resource pack blockstates and models') - } + progressReporter.beginStage('read-resource-pack-blockstates-and-models', 'Reading resource pack blockstates and models') const readModelData = async (path: string, type: 'models' | 'blockstates', namespaceDir: string) => { if (!(await existsAsync(path))) return @@ -377,11 +372,12 @@ const prepareBlockstatesAndModels = async () => { return { usedTextures } } -const downloadAndUseResourcePack = async (url: string): Promise => { +const downloadAndUseResourcePack = async (url: string, progressReporter: ProgressReporter): Promise => { + progressReporter.beginStage('install-resource-pack', 'Installing server resource pack') try { resourcePackState.isServerInstalling = true resourcePackState.isServerDownloading = true - if (!miscUiState.gameLoaded) setLoadingScreenStatus('Downloading resource pack') + progressReporter.beginStage('download-resource-pack', 'Downloading server resource pack') console.log('Downloading server resource pack', url) console.time('downloadServerResourcePack') const response = await fetch(url).catch((err) => { @@ -391,7 +387,6 @@ const downloadAndUseResourcePack = async (url: string): Promise => { }) console.timeEnd('downloadServerResourcePack') if (!response) return - if (!miscUiState.gameLoaded) setLoadingScreenStatus('Installing resource pack') const contentLength = response.headers.get('Content-Length') const total = contentLength ? parseInt(contentLength, 10) : 0 @@ -410,18 +405,19 @@ const downloadAndUseResourcePack = async (url: string): Promise => { if (total) { const progress = Math.round((loaded / total) * 100) - if (!miscUiState.gameLoaded) setLoadingScreenStatus(`Downloading resource pack: ${progress}%`) + progressReporter.reportProgress('download-resource-pack', progress / 100) } } resourcePackState.isServerDownloading = false const resourcePackData = await new Blob(chunks).arrayBuffer() - showNotification('Installing resource pack...') - await installResourcepackPack(resourcePackData, undefined, undefined, true).catch((err) => { + progressReporter.endStage('install-resource-pack') + await installResourcepackPack(resourcePackData, progressReporter, undefined, undefined, true).catch((err) => { console.error(err) showNotification('Failed to install resource pack: ' + err.message) }) } finally { + progressReporter.endStage('download-resource-pack') resourcePackState.isServerInstalling = false resourcePackState.isServerDownloading = false } @@ -465,7 +461,7 @@ export const onAppLoad = () => { console.log('accepting resource pack') bot.acceptResourcePack() if (choice === true || choice === 'Download & Install (recommended)') { - await downloadAndUseResourcePack(packet.url).catch((err) => { + await downloadAndUseResourcePack(packet.url, createFullScreenProgressReporter()).catch((err) => { console.error(err) showNotification('Failed to download resource pack: ' + err.message) }) @@ -516,15 +512,15 @@ const updateAllReplacableTextures = async () => { const repeatArr = (arr, i) => Array.from({ length: i }, () => arr) -const updateTextures = async () => { +const updateTextures = async (progressReporter = createConsoleLogProgressReporter()) => { currentErrors = [] const origBlocksFiles = Object.keys(viewer.world.sourceData.blocksAtlases.latest.textures) const origItemsFiles = Object.keys(viewer.world.sourceData.itemsAtlases.latest.textures) const origArmorFiles = Object.keys(armorTextures) - const { usedTextures: extraBlockTextures = new Set() } = await prepareBlockstatesAndModels() ?? {} - const blocksData = await getResourcepackTiles('blocks', [...origBlocksFiles, ...extraBlockTextures]) - const itemsData = await getResourcepackTiles('items', origItemsFiles) - const armorData = await getResourcepackTiles('armor', origArmorFiles) + const { usedTextures: extraBlockTextures = new Set() } = await prepareBlockstatesAndModels(progressReporter) ?? {} + const blocksData = await getResourcepackTiles('blocks', [...origBlocksFiles, ...extraBlockTextures], progressReporter) + const itemsData = await getResourcepackTiles('items', origItemsFiles, progressReporter) + const armorData = await getResourcepackTiles('armor', origArmorFiles, progressReporter) await updateAllReplacableTextures() viewer.world.customTextures = {} if (blocksData) { @@ -573,13 +569,16 @@ export const copyServerResourcePackToRegular = async (name = 'default') => { } // Copy all files from server resource pack to regular location - const destPath = texturePackBasePath + name + const destPath = resourcepackPackBasePath + name await mkdirRecursive(destPath) - setLoadingScreenStatus('Copying server resource pack to regular location') - await copyFilesAsyncWithProgress('/resourcepack', destPath, true, ' (server -> regular)') + const reporter = createFullScreenProgressReporter() + reporter.setMessage('Copying server resource pack to user location') + await copyFilesAsyncWithProgress('/resourcepack', destPath, true, ' (server -> user)') // Complete the installation - await completeTexturePackInstall(displayName, name, false) - showNotification('Server resource pack copied to regular location') + await completeResourcepackPackInstall(displayName, name, false, reporter) + showNotification('Server resource pack copied to user location') + + reporter.end() } diff --git a/src/supportedVersions.mjs b/src/supportedVersions.mjs index 39fd38d1..87dd5ea1 100644 --- a/src/supportedVersions.mjs +++ b/src/supportedVersions.mjs @@ -5,4 +5,6 @@ export const ignoredVersionsRegex = /(^0\.30c$)|w|-pre|-rc/ /** @type {string[]} */ const versionsFromProtocol = Object.values(postNettyVersionsByProtocolVersion.pc).flat().filter(x => !ignoredVersionsRegex.test(x.minecraftVersion)).map(x => x.minecraftVersion) +export const notTestedVersions = '1.19.3 1.20 1.19.1 1.19 1.18.1 1.15.1 1.14.1'.split(' ') + export default versionsFromProtocol.filter(x => x !== '1.7' && !x.startsWith('1.7.')) From e8d980b790e5c56bbd79faea8ea24476af0f7eb2 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 03:31:02 +0300 Subject: [PATCH 282/753] add brand new progress reporter --- src/core/progressReporter.ts | 195 +++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 src/core/progressReporter.ts diff --git a/src/core/progressReporter.ts b/src/core/progressReporter.ts new file mode 100644 index 00000000..db1958da --- /dev/null +++ b/src/core/progressReporter.ts @@ -0,0 +1,195 @@ +import { setLoadingScreenStatus } from '../appStatus' +import { appStatusState } from '../react/AppStatusProvider' +import { hideNotification, showNotification } from '../react/NotificationProvider' + +export interface ProgressReporter { + currentMessage: string | undefined + beginStage (stage: string, title: string): void + endStage (stage: string): void + setSubStage (stage: string, subStageTitle: string): void + reportProgress (stage: string, progress: number): void + executeWithMessage(message: string, fn: () => Promise): Promise + executeWithMessage(message: string, stage: string, fn: () => Promise): Promise + + setMessage (message: string): void + + end (): void +} + +interface ReporterDisplayImplementation { + setMessage (message: string): void + end (): void +} + +interface StageInfo { + title: string + subStage?: string + progress?: number +} + +const NO_STAGES_ACTION_END = false + +const createProgressReporter = (implementation: ReporterDisplayImplementation): ProgressReporter => { + const stages = new Map() + let currentMessage: string | undefined + let ended = false + + const end = () => { + if (ended) return + ended = true + stages.clear() + implementation.end() + } + + const updateStatus = () => { + if (ended) return + const activeStages = [...stages.entries()] + if (activeStages.length === 0) { + if (NO_STAGES_ACTION_END) { + end() + } else { + implementation.setMessage('Waiting for tasks') + } + return + } + + const [currentStage, info] = activeStages.at(-1)! + let message = info.title + if (info.subStage) { + message += ` - ${info.subStage}` + } + if (info.progress !== undefined) { + const num = Math.round(info.progress * 100) + if (isFinite(num)) { + message += `: ${num}%` + } + } + + currentMessage = message + implementation.setMessage(message) + } + + const reporter = { + beginStage (stage: string, title: string) { + if (stages.has(stage)) { + throw new Error(`Stage ${stage} already is running`) + } + stages.set(stage, { title }) + updateStatus() + }, + + endStage (stage: string) { + stages.delete(stage) + updateStatus() + }, + + setSubStage (stage: string, subStageTitle: string) { + const info = stages.get(stage) + if (info) { + info.subStage = subStageTitle + updateStatus() + } + }, + + reportProgress (stage: string, progress: number) { + const info = stages.get(stage) + if (info) { + info.progress = progress + updateStatus() + } + }, + + async executeWithMessage(...args: any[]): Promise { + const message = args[0] + const stage = typeof args[1] === 'string' ? args[1] : undefined + const fn = typeof args[1] === 'string' ? args[2] : args[1] + + const tempStage = stage ?? 'temp-' + Math.random().toString(36).slice(2) + reporter.beginStage(tempStage, message) + try { + const result = await fn() + return result + } finally { + reporter.endStage(tempStage) + } + }, + + end (): void { + end() + }, + + setMessage (message: string): void { + implementation.setMessage(message) + }, + + get currentMessage () { + return currentMessage + } + } + + return reporter +} + +const fullScreenReporters = [] as ProgressReporter[] +export const createFullScreenProgressReporter = (): ProgressReporter => { + const reporter = createProgressReporter({ + setMessage (message: string) { + if (appStatusState.isError) return + setLoadingScreenStatus(message) + }, + end () { + fullScreenReporters.splice(fullScreenReporters.indexOf(reporter), 1) + if (fullScreenReporters.length === 0) { + setLoadingScreenStatus(undefined) + } else { + setLoadingScreenStatus(fullScreenReporters.at(-1)!.currentMessage) + } + } + }) + fullScreenReporters.push(reporter) + return reporter +} + +export const createNotificationProgressReporter = (endMessage?: string): ProgressReporter => { + return createProgressReporter({ + setMessage (message: string) { + showNotification(`${message}...`, '', false, '', undefined, true) + }, + end () { + if (endMessage) { + showNotification(endMessage, '', false, '', undefined, true) + } else { + hideNotification() + } + } + }) +} + +export const createConsoleLogProgressReporter = (): ProgressReporter => { + return createProgressReporter({ + setMessage (message: string) { + console.log(message) + }, + end () { + console.log('done') + } + }) +} + +export const createWrappedProgressReporter = (reporter: ProgressReporter, message?: string) => { + const stage = `wrapped-${message}` + if (message) { + reporter.beginStage(stage, message) + } + + return createProgressReporter({ + setMessage (message: string) { + reporter.setMessage(message) + }, + end () { + if (message) { + reporter.endStage(stage) + } + } + }) +} From 59cb4422259aa56bb752ca77d83b958d4519be0d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 03:33:49 +0300 Subject: [PATCH 283/753] fix: display notification on user resourecepack enable --- src/optionsGuiScheme.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index cb2d9be8..6f4d4fea 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -15,6 +15,7 @@ import { downloadPacketsReplay, packetsReplaceSessionState } from './packetsRepl import { showOptionsModal } from './react/SelectOption' import supportedVersions from './supportedVersions.mjs' import { getVersionAutoSelect } from './connect' +import { createNotificationProgressReporter } from './core/progressReporter' export const guiOptionsScheme: { [t in OptionsGroupType]: Array<{ [K in keyof AppOptions]?: Partial> } & { custom? }> @@ -181,7 +182,7 @@ export const guiOptionsScheme: { } if (choice === 'Enable') { options.enabledResourcepack = name - await completeResourcepackPackInstall(name, name, false) + await completeResourcepackPackInstall(name, name, false, createNotificationProgressReporter()) return } if (choice === 'Uninstall') { From ecf55727bc4d340591378903cc167297ea3d9271 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 03:52:32 +0300 Subject: [PATCH 284/753] stop publishing UI to npm since no one uses it --- .github/workflows/ci.yml | 2 +- .github/workflows/publish.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc54da4e..92b7e7f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - run: pnpm build-storybook - run: pnpm test-unit - run: pnpm lint - - run: pnpm tsx scripts/buildNpmReact.ts + # - run: pnpm tsx scripts/buildNpmReact.ts - run: nohup pnpm prod-start & - run: nohup pnpm test-mc-server & - uses: cypress-io/github-action@v5 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ce7f56b9..eb765648 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -60,7 +60,7 @@ jobs: run: pnpm config set '//registry.npmjs.org/:_authToken' "${NODE_AUTH_TOKEN}" env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - run: pnpm tsx scripts/buildNpmReact.ts ${{ steps.release.outputs.tag }} - if: steps.release.outputs.tag - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + # - run: pnpm tsx scripts/buildNpmReact.ts ${{ steps.release.outputs.tag }} + # if: steps.release.outputs.tag + # env: + # NPM_TOKEN: ${{ secrets.NPM_TOKEN }} From deedcda4675f78c1d8e31978512a206696749167 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 03:54:25 +0300 Subject: [PATCH 285/753] correctly merge local config when building --- rsbuild.config.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 5fd9316c..4147ce7d 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -34,6 +34,9 @@ if (fs.existsSync('./assets/release.json')) { } const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8')) +try { + Object.assign(configJson, JSON.parse(fs.readFileSync('./config.local.json', 'utf8'))) +} catch (err) {} if (dev) { configJson.defaultProxy = ':8080' } @@ -105,12 +108,8 @@ const appConfig = defineConfig({ if (fs.existsSync('./assets/release.json')) { fs.copyFileSync('./assets/release.json', './dist/release.json') } - let configLocalJson = {} - try { - configLocalJson = JSON.parse(fs.readFileSync('./config.local.json', 'utf8')) - } catch (err) {} - fs.writeFileSync('./dist/config.json', JSON.stringify({ ...configJson, ...configLocalJson }), 'utf8') + fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8') if (fs.existsSync('./generated/sounds.js')) { fs.copyFileSync('./generated/sounds.js', './dist/sounds.js') } From 89fd5dde71292f7a1a83ba17cb828d6f91e5f31c Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 04:36:15 +0300 Subject: [PATCH 286/753] add external folder for forks code (ext functionality) --- src/external/index.ts | 0 src/index.ts | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 src/external/index.ts diff --git a/src/external/index.ts b/src/external/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/index.ts b/src/index.ts index 1fb318b8..01912e94 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import './mineflayer/maps' import './mineflayer/cameraShake' import './shims/patchShims' import './mineflayer/java-tester/index' +import './external' import { getServerInfo } from './mineflayer/mc-protocol' import { onGameLoad, renderSlot } from './inventoryWindows' import { RenderItem } from './mineflayer/items' @@ -488,7 +489,7 @@ export async function connect (connectOptions: ConnectOptions) { localServer.on('newPlayer', (player) => { player.on('loadingStatus', (newStatus) => { - progress.beginStage('flying-squid', newStatus) + progress.setMessage(newStatus) }) }) flyingSquidEvents() From 322e2f9b4496cece3da35356b65ba0a45ff430a1 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 22:18:51 +0300 Subject: [PATCH 287/753] fix sounds --- scripts/prepareSounds.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/prepareSounds.mjs b/scripts/prepareSounds.mjs index f80ace4e..7ff614a2 100644 --- a/scripts/prepareSounds.mjs +++ b/scripts/prepareSounds.mjs @@ -246,6 +246,7 @@ const writeSoundsMap = async () => { const makeSoundsBundle = async () => { const allSoundsMap = JSON.parse(fs.readFileSync('./generated/sounds.json', 'utf8')) const allSoundsVersionedMap = JSON.parse(fs.readFileSync('./generated/soundsPathVersionsRemap.json', 'utf8')) + if (!process.env.REPO_SLUG) throw new Error('REPO_SLUG is not set') const allSoundsMeta = { format: 'mp3', From 8ff05924dd76cec13b86b237675bd489692886fa Mon Sep 17 00:00:00 2001 From: Max Lee Date: Wed, 26 Feb 2025 20:31:22 +0100 Subject: [PATCH 288/753] feat: add config option for pause screen links (#288) --- config.json | 10 ++++++++++ src/globalState.ts | 1 + src/react/DiscordButton.tsx | 8 ++++---- src/react/PauseScreen.tsx | 27 ++++++++++++++++++++++----- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/config.json b/config.json index d6d68b94..6aa86397 100644 --- a/config.json +++ b/config.json @@ -17,5 +17,15 @@ "version": "1.20.3", "description": "Very nice a polite server. Must try for everyone!" } + ], + "pauseLinks": [ + [ + { + "type": "github" + }, + { + "type": "discord" + } + ] ] } diff --git a/src/globalState.ts b/src/globalState.ts index 66959198..cc9cd127 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -127,6 +127,7 @@ export type AppConfig = { forceSettings?: Record // hideSettings?: Record allowAutoConnect?: boolean + pauseLinks?: Array>> } export const miscUiState = proxy({ diff --git a/src/react/DiscordButton.tsx b/src/react/DiscordButton.tsx index 3824c4c1..e8278e95 100644 --- a/src/react/DiscordButton.tsx +++ b/src/react/DiscordButton.tsx @@ -4,7 +4,7 @@ import { CSSProperties, useState } from 'react' import Button from './Button' import PixelartIcon, { pixelartIcons } from './PixelartIcon' -export const DiscordButton = () => { +export const DiscordButton = ({ text, style }: { text?: string, style?: Record }) => { const links: DropdownButtonItem[] = [ { text: 'Support Official Server (mcraft.fun)', @@ -16,7 +16,7 @@ export const DiscordButton = () => { } ] - return + return } export type DropdownButtonItem = { @@ -24,7 +24,7 @@ export type DropdownButtonItem = { clickHandler: () => void } -export const DropdownButton = ({ text, links }: { text: string, links: DropdownButtonItem[] }) => { +export const DropdownButton = ({ text, style, links }: { text: string, style?: Record, links: DropdownButtonItem[] }) => { const [isOpen, setIsOpen] = useState(false) const { refs, floatingStyles } = useFloating({ open: isOpen, @@ -50,7 +50,7 @@ export const DropdownButton = ({ text, links }: { text: string, links: DropdownB return <> ) + } else if (button.type === 'url' && button.text) { + rowButtons.push() + } + } + pauseLinks.push(
{rowButtons}
) + } + } + return -
- - -
+ {pauseLinks} {singleplayer ? (
From 52ae41a78d24b1bb95e26eb24d9b4b3b5cb977f0 Mon Sep 17 00:00:00 2001 From: Max Lee Date: Wed, 26 Feb 2025 20:33:50 +0100 Subject: [PATCH 289/753] Add better chat link prompt screen (#290) --- src/react/MessageFormatted.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/react/MessageFormatted.tsx b/src/react/MessageFormatted.tsx index 95204b26..554d5a9b 100644 --- a/src/react/MessageFormatted.tsx +++ b/src/react/MessageFormatted.tsx @@ -6,6 +6,7 @@ import { openURL } from 'renderer/viewer/lib/simpleUtils' import { MessageFormatPart } from '../chatUtils' import { chatInputValueGlobal } from './Chat' import './MessageFormatted.css' +import { showOptionsModal } from './SelectOption' const hoverItemToText = (hoverEvent: MessageFormatPart['hoverEvent']) => { try { @@ -42,17 +43,21 @@ const clickEventToProps = (clickEvent: MessageFormatPart['clickEvent']) => { } } } - if (clickEvent.action === 'open_url') { + if (clickEvent.action === 'open_url' || clickEvent.action === 'open_file') { return { - onClick () { - const confirm = window.confirm(`Open ${clickEvent.value}?`) - if (confirm) { + async onClick () { + const promptMessageText = `Open "${clickEvent.value}"?` + const confirm = await showOptionsModal(promptMessageText, ['Open', 'Copy'], { + cancel: true + }) + if (confirm === 'Open') { openURL(clickEvent.value) + } else if (confirm === 'Copy') { + void navigator.clipboard.writeText(clickEvent.value) } } } } - //@ts-expect-error todo if (clickEvent.action === 'copy_to_clipboard') { return { onClick () { @@ -71,6 +76,7 @@ export const MessagePart = ({ part, ...props }: { part: MessageFormatPart } & Co const hoverItemText = hoverMessageRaw && typeof hoverMessageRaw !== 'string' ? render(hoverMessageRaw).children.map(child => child.component.text).join('') : hoverMessageRaw const applyStyles = [ + clickProps && messageFormatStylesMap.clickEvent, color ? colorF(color.toLowerCase()) + `; text-shadow: 1px 1px 0px ${getColorShadow(colorF(color.toLowerCase()).replace('color:', ''))}` : messageFormatStylesMap.white, italic && messageFormatStylesMap.italic, bold && messageFormatStylesMap.bold, @@ -80,7 +86,7 @@ export const MessagePart = ({ part, ...props }: { part: MessageFormatPart } & Co obfuscated && messageFormatStylesMap.obfuscated ].filter(a => a !== false && a !== undefined).filter(Boolean) - return {text} + return {text} } export default ({ parts, className }: { parts: readonly MessageFormatPart[], className?: string }) => { @@ -138,4 +144,5 @@ export const messageFormatStylesMap = { underlined: 'text-decoration:underline', italic: 'font-style:italic', obfuscated: 'filter:blur(2px)', + clickEvent: 'cursor:pointer', } From edad57a225c98809c7877039ffee01fafa1df516 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 22:56:02 +0300 Subject: [PATCH 290/753] feat: allow to load client without free space on device (or no write permissions) --- src/browserfs.ts | 25 ++++++++++++++++++++++--- src/dragndrop.ts | 2 +- src/globalState.ts | 1 + src/react/MainMenu.tsx | 7 +++++-- src/react/MainMenuRenderApp.tsx | 3 ++- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/browserfs.ts b/src/browserfs.ts index d38b13a2..41608e30 100644 --- a/src/browserfs.ts +++ b/src/browserfs.ts @@ -14,22 +14,41 @@ import { VALID_REPLAY_EXTENSIONS, openFile } from './packetsReplay/replayPackets import { getFixedFilesize } from './downloadAndOpenFile' import { packetsReplayState } from './react/state/packetsReplayState' import { createFullScreenProgressReporter } from './core/progressReporter' +import { showNotification } from './react/NotificationProvider' const { GoogleDriveFileSystem } = require('google-drive-browserfs/src/backends/GoogleDrive') browserfs.install(window) const defaultMountablePoints = { - '/world': { fs: 'LocalStorage' }, // will be removed in future '/data': { fs: 'IndexedDB' }, '/resourcepack': { fs: 'InMemory' }, // temporary storage for currently loaded resource pack + '/temp': { fs: 'InMemory' } +} +const fallbackMountablePoints = { + '/resourcepack': { fs: 'InMemory' }, // temporary storage for downloaded server resource pack + '/temp': { fs: 'InMemory' } } browserfs.configure({ fs: 'MountableFileSystem', options: defaultMountablePoints, }, async (e) => { - // todo disable singleplayer button - if (e) throw e + if (e) { + browserfs.configure({ + fs: 'MountableFileSystem', + options: fallbackMountablePoints, + }, async (e2) => { + if (e2) { + showNotification('Unknown FS error, cannot continue', e2.message, true) + throw e2 + } + showNotification('Failed to access device storage', `Check you have free space. ${e.message}`, true) + miscUiState.appLoaded = true + miscUiState.singleplayerAvailable = false + }) + return + } await updateTexturePackInstalledState() miscUiState.appLoaded = true + miscUiState.singleplayerAvailable = true }) export const forceCachedDataPaths = {} diff --git a/src/dragndrop.ts b/src/dragndrop.ts index 6c8af856..6be90551 100644 --- a/src/dragndrop.ts +++ b/src/dragndrop.ts @@ -64,7 +64,7 @@ async function handleDroppedFile (file: File) { return } if (file.name.endsWith('.mca')) { - const tempPath = '/data/temp.mca' + const tempPath = '/temp/temp.mca' try { await fs.promises.writeFile(tempPath, Buffer.from(await file.arrayBuffer()) as any) const region = new RegionFile(tempPath) diff --git a/src/globalState.ts b/src/globalState.ts index cc9cd127..74e6c3ff 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -145,6 +145,7 @@ export const miscUiState = proxy({ /** currently trying to load or loaded mc version, after all data is loaded */ loadedDataVersion: null as string | null, appLoaded: false, + singleplayerAvailable: false, usingGamepadInput: false, appConfig: null as AppConfig | null, displaySearchInput: false, diff --git a/src/react/MainMenu.tsx b/src/react/MainMenu.tsx index fc770ad1..09214af2 100644 --- a/src/react/MainMenu.tsx +++ b/src/react/MainMenu.tsx @@ -24,6 +24,7 @@ interface Props { bottomRightLinks?: string versionText?: string onVersionTextClick?: () => void + singleplayerAvailable?: boolean } const httpsRegex = /^https?:\/\// @@ -41,7 +42,8 @@ export default ({ versionStatus, versionTitle, onVersionStatusClick, - bottomRightLinks + bottomRightLinks, + singleplayerAvailable = true }: Props) => { if (!bottomRightLinks?.trim()) bottomRightLinks = undefined // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion @@ -107,6 +109,7 @@ export default ({ style={{ width: 150 }} {...singleplayerLongPress} data-test-id='singleplayer-button' + disabled={!singleplayerAvailable} initialTooltip={{ content: 'Create worlds and play offline', placement: 'left', @@ -183,7 +186,7 @@ export default ({
})} - A Minecraft client in the browser! + A Minecraft client clone in the browser! diff --git a/src/react/MainMenuRenderApp.tsx b/src/react/MainMenuRenderApp.tsx index b12d9ba8..e06ced52 100644 --- a/src/react/MainMenuRenderApp.tsx +++ b/src/react/MainMenuRenderApp.tsx @@ -75,7 +75,7 @@ export const mainMenuState = proxy({ let disableAnimation = false export default () => { const haveModals = useSnapshot(activeModalStack).length - const { gameLoaded, appLoaded, appConfig } = useSnapshot(miscUiState) + const { gameLoaded, appLoaded, appConfig, singleplayerAvailable } = useSnapshot(miscUiState) const noDisplay = haveModals || gameLoaded || !appLoaded @@ -118,6 +118,7 @@ export default () => { return {(state) =>
showModal({ reactType: 'serversList' })} singleplayerAction={async () => { const oldFormatSave = fs.existsSync('./world/level.dat') From 2414111b9cf24ebbe75948234ed33c20cc4c6e50 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 23:29:18 +0300 Subject: [PATCH 291/753] feat: add packets recording control to pause menu, display packets view after recording was started for in real time server packets debug, fix auto captured packets display --- package.json | 2 +- pnpm-lock.yaml | 12 ++--- src/mineflayer/plugins/localRelay.ts | 59 +++++++++++++++++++++--- src/packetsReplay/packetsReplayLegacy.ts | 10 ++-- src/react/AppStatusProvider.tsx | 2 +- src/react/PauseScreen.tsx | 25 ++++++++-- 6 files changed, 86 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index f3f2f644..b67a2b03 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "mojangson": "^2.0.4", "net-browserify": "github:zardoy/prismarinejs-net-browserify", "node-gzip": "^1.1.2", - "mcraft-fun-mineflayer": "^0.1.6", + "mcraft-fun-mineflayer": "^0.1.7", "peerjs": "^1.5.0", "pixelarticons": "^1.8.1", "pretty-bytes": "^6.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7060521d..3386ac03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,8 +135,8 @@ importers: specifier: ^4.17.21 version: 4.17.21 mcraft-fun-mineflayer: - specifier: ^0.1.6 - version: 0.1.6(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) + specifier: ^0.1.7 + version: 0.1.7(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) minecraft-data: specifier: 3.83.1 version: 3.83.1 @@ -6252,9 +6252,9 @@ packages: resolution: {integrity: sha512-49tk3shwxsDoV0PrrbORZEKg613vUQPULgusWjXNl8JEma5y41LEo57D6q4aHliXsV3Gb9ThrkFf6hIb0WlY1Q==} engines: {node: '>=18.0.0'} - mcraft-fun-mineflayer@0.1.6: - resolution: {integrity: sha512-ifaIL//NJtkGcYasEULy0KcJjwUA8BwcmC/KoIpTTj6Xmk5o8AYEVdUnR9jrir4kpiLBOIbHgG1QhY1Wbofl1g==} - version: 0.1.6 + mcraft-fun-mineflayer@0.1.7: + resolution: {integrity: sha512-DJPpp1YFwztoscdIOwfqBV9lbotva621F9GEep3BlqG3l06UdTzX2ElkvwyTR0IurFFBX/YKsNfxwL5WtLytgw==} + version: 0.1.7 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: '@roamhq/wrtc': '*' @@ -16691,7 +16691,7 @@ snapshots: apl-image-packer: 1.1.0 zod: 3.24.1 - mcraft-fun-mineflayer@0.1.6(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): + mcraft-fun-mineflayer@0.1.7(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): dependencies: '@zardoy/flying-squid': 0.0.49(encoding@0.1.13) exit-hook: 2.2.1 diff --git a/src/mineflayer/plugins/localRelay.ts b/src/mineflayer/plugins/localRelay.ts index e6c8cfe5..ab8527e5 100644 --- a/src/mineflayer/plugins/localRelay.ts +++ b/src/mineflayer/plugins/localRelay.ts @@ -3,9 +3,22 @@ import { PACKETS_REPLAY_FILE_EXTENSION, WORLD_STATE_FILE_EXTENSION } from 'mcraf import { Bot } from 'mineflayer' import CircularBuffer from 'flying-squid/dist/circularBuffer' import { PacketsLogger } from 'mcraft-fun-mineflayer/build/packetsLogger' +import { subscribe } from 'valtio' import { lastConnectOptions } from '../../react/AppStatusProvider' +import { packetsReplaceSessionState } from '../../packetsReplay/packetsReplayLegacy' +import { packetsReplayState } from '../../react/state/packetsReplayState' + +const AUTO_CAPTURE_PACKETS_COUNT = 30 +let circularBuffer: CircularBuffer | undefined +let lastConnectVersion = '' export const localRelayServerPlugin = (bot: Bot) => { + lastConnectVersion = bot.version + let ended = false + bot.on('end', () => { + ended = true + }) + bot.loadPlugin( viewerConnector({ tcpEnabled: false, @@ -28,28 +41,60 @@ export const localRelayServerPlugin = (bot: Bot) => { } circularBuffer = new CircularBuffer(AUTO_CAPTURE_PACKETS_COUNT) + let position = 0 bot._client.on('writePacket' as any, (name, params) => { - circularBuffer!.add({ name, params, isFromServer: false }) + circularBuffer!.add({ name, state: bot._client.state, params, isFromServer: false, timestamp: Date.now() }) + if (packetsReplaceSessionState.active) { + packetsReplayState.packetsPlayback.push({ + name, + data: params, + isFromClient: true, + isUpcoming: false, + position: position++, + timestamp: Date.now(), + }) + } }) bot._client.on('packet', (data, { name }) => { - circularBuffer!.add({ name, params: data, isFromServer: true }) + if (name === 'map_chunk') data = { x: data.x, z: data.z } + circularBuffer!.add({ name, state: bot._client.state, params: data, isFromServer: true, timestamp: Date.now() }) + if (packetsReplaceSessionState.active) { + packetsReplayState.packetsPlayback.push({ + name, + data, + isFromClient: false, + isUpcoming: false, + position: position++, + timestamp: Date.now(), + }) + } }) + + upPacketsReplayPanel() } +const upPacketsReplayPanel = () => { + if (packetsReplaceSessionState.active && bot) { + packetsReplayState.isOpen = true + packetsReplayState.replayName = 'Recording all packets for ' + bot.username + } +} + +subscribe(packetsReplaceSessionState, () => { + upPacketsReplayPanel() +}) + declare module 'mineflayer' { interface Bot { downloadCurrentWorldState: () => void } } -const AUTO_CAPTURE_PACKETS_COUNT = 30 -let circularBuffer: CircularBuffer | undefined - export const getLastAutoCapturedPackets = () => circularBuffer?.size export const downloadAutoCapturedPackets = () => { - const logger = new PacketsLogger() + const logger = new PacketsLogger({ minecraftVersion: lastConnectVersion }) for (const packet of circularBuffer?.getLastElements() ?? []) { - logger.log(packet.isFromServer, packet.name, packet.params) + logger.log(packet.isFromServer, { name: packet.name, state: packet.state, time: packet.timestamp }, packet.params) } const textContents = logger.contents const blob = new Blob([textContents], { type: 'text/plain' }) diff --git a/src/packetsReplay/packetsReplayLegacy.ts b/src/packetsReplay/packetsReplayLegacy.ts index 0db97012..478bbc5d 100644 --- a/src/packetsReplay/packetsReplayLegacy.ts +++ b/src/packetsReplay/packetsReplayLegacy.ts @@ -7,7 +7,8 @@ export const packetsReplaceSessionState = proxy({ hasRecordedPackets: false }) -export const replayLogger = new PacketsLogger() +// eslint-disable-next-line import/no-mutable-exports +export let replayLogger: PacketsLogger | undefined const isBufferData = (data: any): boolean => { if (Buffer.isBuffer(data) || data instanceof Uint8Array) return true @@ -35,13 +36,14 @@ const processPacketData = (data: any): any => { export default () => { customEvents.on('mineflayerBotCreated', () => { + replayLogger = new PacketsLogger({ minecraftVersion: bot.version }) replayLogger.contents = '' packetsReplaceSessionState.hasRecordedPackets = false const handleServerPacket = (data, { name, state = bot._client.state }) => { if (!packetsReplaceSessionState.active) { return } - replayLogger.log(true, { name, state }, processPacketData(data)) + replayLogger!.log(true, { name, state }, processPacketData(data)) packetsReplaceSessionState.hasRecordedPackets = true } bot._client.on('packet', handleServerPacket) @@ -53,7 +55,7 @@ export default () => { if (!packetsReplaceSessionState.active) { return } - replayLogger.log(false, { name, state: bot._client.state }, processPacketData(data)) + replayLogger!.log(false, { name, state: bot._client.state }, processPacketData(data)) packetsReplaceSessionState.hasRecordedPackets = true }) }) @@ -61,7 +63,7 @@ export default () => { export const downloadPacketsReplay = async () => { const a = document.createElement('a') - a.href = `data:text/plain;charset=utf-8,${encodeURIComponent(replayLogger.contents)}` + a.href = `data:text/plain;charset=utf-8,${encodeURIComponent(replayLogger!.contents)}` a.download = `packets-replay-${new Date().toISOString()}.txt` a.click() } diff --git a/src/react/AppStatusProvider.tsx b/src/react/AppStatusProvider.tsx index a3ef4c9d..e83416c1 100644 --- a/src/react/AppStatusProvider.tsx +++ b/src/react/AppStatusProvider.tsx @@ -147,7 +147,7 @@ export default () => { <> {displayAuthButton &&
error
}>
From dffadbb06c56ef9288d0ed61758f63724dd3d7e6 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 23:33:13 +0300 Subject: [PATCH 292/753] wip jei channel --- src/customChannels.ts | 36 +++++++++++++++++++++++++++++- src/packetsReplay/replayPackets.ts | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/customChannels.ts b/src/customChannels.ts index 71dc7067..35922101 100644 --- a/src/customChannels.ts +++ b/src/customChannels.ts @@ -8,7 +8,10 @@ customEvents.on('mineflayerBotCreated', async () => { resolve(true) }) }) + registerBlockModelsChannel() +}) +const registerBlockModelsChannel = () => { const CHANNEL_NAME = 'minecraft-web-client:blockmodels' const packetStructure = [ @@ -72,4 +75,35 @@ customEvents.on('mineflayerBotCreated', async () => { }) console.debug(`registered custom channel ${CHANNEL_NAME} channel`) -}) +} + +const registeredJeiChannel = () => { + const CHANNEL_NAME = 'minecraft-web-client:jei' + // id - string, categoryTitle - string, items - string (json array) + const packetStructure = [ + 'container', + [ + { + name: 'id', + type: 'pstring', + }, + { + name: 'categoryTitle', + type: 'pstring', + }, + { + name: 'items', + type: 'pstring', + }, + ] + ] + + bot._client.registerChannel(CHANNEL_NAME, packetStructure, true) + + bot._client.on(CHANNEL_NAME as any, (data) => { + const { id, categoryTitle, items } = data + // ... + }) + + console.debug(`registered custom channel ${CHANNEL_NAME} channel`) +} diff --git a/src/packetsReplay/replayPackets.ts b/src/packetsReplay/replayPackets.ts index e3935258..9e777b38 100644 --- a/src/packetsReplay/replayPackets.ts +++ b/src/packetsReplay/replayPackets.ts @@ -1,7 +1,7 @@ /* eslint-disable no-await-in-loop */ import { createServer, ServerClient } from 'minecraft-protocol' import { ParsedReplayPacket, parseReplayContents } from 'mcraft-fun-mineflayer/build/packetsLogger' -import { WorldStateHeader, PACKETS_REPLAY_FILE_EXTENSION, WORLD_STATE_FILE_EXTENSION } from 'mcraft-fun-mineflayer/build/worldState' +import { PACKETS_REPLAY_FILE_EXTENSION, WORLD_STATE_FILE_EXTENSION } from 'mcraft-fun-mineflayer/build/worldState' import MinecraftData from 'minecraft-data' import { LocalServer } from '../customServer' import { UserError } from '../mineflayer/userError' From d348a44bb86cb1678f7727b92c2018acbc0eaf51 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Feb 2025 04:48:03 +0300 Subject: [PATCH 293/753] add a way to disable recording button on pause menu, refactor --- src/index.ts | 2 +- .../{localRelay.ts => packetsRecording.ts} | 10 +++--- src/optionsGuiScheme.tsx | 8 ++--- src/optionsStorage.ts | 2 +- src/packetsReplay/packetsReplayLegacy.ts | 12 +++---- src/react/AppStatusProvider.tsx | 6 ++-- src/react/Chat.tsx | 1 + src/react/PauseScreen.tsx | 34 +++++++++++-------- 8 files changed, 41 insertions(+), 34 deletions(-) rename src/mineflayer/plugins/{localRelay.ts => packetsRecording.ts} (92%) diff --git a/src/index.ts b/src/index.ts index 01912e94..249fbe9d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -115,7 +115,7 @@ import { UserError } from './mineflayer/userError' import ping from './mineflayer/plugins/ping' import { LocalServer } from './customServer' import { startLocalReplayServer } from './packetsReplay/replayPackets' -import { localRelayServerPlugin } from './mineflayer/plugins/localRelay' +import { localRelayServerPlugin } from './mineflayer/plugins/packetsRecording' import { createFullScreenProgressReporter } from './core/progressReporter' window.debug = debug diff --git a/src/mineflayer/plugins/localRelay.ts b/src/mineflayer/plugins/packetsRecording.ts similarity index 92% rename from src/mineflayer/plugins/localRelay.ts rename to src/mineflayer/plugins/packetsRecording.ts index ab8527e5..f1ff18cf 100644 --- a/src/mineflayer/plugins/localRelay.ts +++ b/src/mineflayer/plugins/packetsRecording.ts @@ -5,7 +5,7 @@ import CircularBuffer from 'flying-squid/dist/circularBuffer' import { PacketsLogger } from 'mcraft-fun-mineflayer/build/packetsLogger' import { subscribe } from 'valtio' import { lastConnectOptions } from '../../react/AppStatusProvider' -import { packetsReplaceSessionState } from '../../packetsReplay/packetsReplayLegacy' +import { packetsRecordingState } from '../../packetsReplay/packetsReplayLegacy' import { packetsReplayState } from '../../react/state/packetsReplayState' const AUTO_CAPTURE_PACKETS_COUNT = 30 @@ -44,7 +44,7 @@ export const localRelayServerPlugin = (bot: Bot) => { let position = 0 bot._client.on('writePacket' as any, (name, params) => { circularBuffer!.add({ name, state: bot._client.state, params, isFromServer: false, timestamp: Date.now() }) - if (packetsReplaceSessionState.active) { + if (packetsRecordingState.active) { packetsReplayState.packetsPlayback.push({ name, data: params, @@ -58,7 +58,7 @@ export const localRelayServerPlugin = (bot: Bot) => { bot._client.on('packet', (data, { name }) => { if (name === 'map_chunk') data = { x: data.x, z: data.z } circularBuffer!.add({ name, state: bot._client.state, params: data, isFromServer: true, timestamp: Date.now() }) - if (packetsReplaceSessionState.active) { + if (packetsRecordingState.active) { packetsReplayState.packetsPlayback.push({ name, data, @@ -74,13 +74,13 @@ export const localRelayServerPlugin = (bot: Bot) => { } const upPacketsReplayPanel = () => { - if (packetsReplaceSessionState.active && bot) { + if (packetsRecordingState.active && bot) { packetsReplayState.isOpen = true packetsReplayState.replayName = 'Recording all packets for ' + bot.username } } -subscribe(packetsReplaceSessionState, () => { +subscribe(packetsRecordingState, () => { upPacketsReplayPanel() }) diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index 6f4d4fea..7032e644 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -11,7 +11,7 @@ import { getScreenRefreshRate } from './utils' import { setLoadingScreenStatus } from './appStatus' import { openFilePicker, resetLocalStorageWithoutWorld } from './browserfs' import { completeResourcepackPackInstall, getResourcePackNames, resourcePackState, uninstallResourcePack } from './resourcePack' -import { downloadPacketsReplay, packetsReplaceSessionState } from './packetsReplay/packetsReplayLegacy' +import { downloadPacketsReplay, packetsRecordingState } from './packetsReplay/packetsReplayLegacy' import { showOptionsModal } from './react/SelectOption' import supportedVersions from './supportedVersions.mjs' import { getVersionAutoSelect } from './connect' @@ -462,18 +462,18 @@ export const guiOptionsScheme: { }, { custom () { - const { active } = useSnapshot(packetsReplaceSessionState) + const { active } = useSnapshot(packetsRecordingState) return }, }, { custom () { - const { active, hasRecordedPackets } = useSnapshot(packetsReplaceSessionState) + const { active, hasRecordedPackets } = useSnapshot(packetsRecordingState) return
error
}> From dec93c2b64c185b251ebb092c3b3519d206fb890 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Feb 2025 04:48:16 +0300 Subject: [PATCH 294/753] fix react warning --- src/react/PauseScreen.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index d7aa2201..4c4dec03 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -227,19 +227,19 @@ export default () => { if (!isModalActive) return null - const pauseLinks: any[] = [] + const pauseLinks: React.ReactNode[] = [] const pauseLinksConfig = miscUiState.appConfig?.pauseLinks if (pauseLinksConfig) { - for (const row of pauseLinksConfig) { - const rowButtons: any[] = [] + for (const [i, row] of pauseLinksConfig.entries()) { + const rowButtons: React.ReactNode[] = [] for (const button of row) { const style = { width: (204 / row.length - (row.length > 1 ? 4 : 0)) + 'px' } if (button.type === 'discord') { - rowButtons.push() + rowButtons.push() } else if (button.type === 'github') { - rowButtons.push() + rowButtons.push() } else if (button.type === 'url' && button.text) { - rowButtons.push() + rowButtons.push() } } pauseLinks.push(
{rowButtons}
) From fa9c0813c3214cceea51de13ce844b6b86006260 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Feb 2025 05:17:18 +0300 Subject: [PATCH 295/753] fix: seagrass and kelp are always waterlogged --- renderer/viewer/lib/mesher/models.ts | 15 ++++++++++----- renderer/viewer/lib/mesher/world.ts | 13 ------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/renderer/viewer/lib/mesher/models.ts b/renderer/viewer/lib/mesher/models.ts index 0dec4015..dd2952fb 100644 --- a/renderer/viewer/lib/mesher/models.ts +++ b/renderer/viewer/lib/mesher/models.ts @@ -43,10 +43,6 @@ function prepareTints (tints) { }) } -function mod (x: number, n: number) { - return ((x % n) + n) % n -} - const calculatedBlocksEntries = Object.entries(legacyJson.clientCalculatedBlocks) export function preflatBlockCalculation (block: Block, world: World, position: Vec3) { const type = calculatedBlocksEntries.find(([name, blocks]) => blocks.includes(block.name))?.[0] @@ -439,7 +435,16 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: } } -const isBlockWaterlogged = (block: Block) => block.getProperties().waterlogged === true || block.getProperties().waterlogged === 'true' +const ALWAYS_WATERLOGGED = new Set([ + 'seagrass', + 'tall_seagrass', + 'kelp', + 'kelp_plant', + 'bubble_column' +]) +const isBlockWaterlogged = (block: Block) => { + return block.getProperties().waterlogged === true || block.getProperties().waterlogged === 'true' || ALWAYS_WATERLOGGED.has(block.name) +} let unknownBlockModel: BlockModelPartsResolved export function getSectionGeometry (sx, sy, sz, world: World) { diff --git a/renderer/viewer/lib/mesher/world.ts b/renderer/viewer/lib/mesher/world.ts index e13bf760..f2757ae6 100644 --- a/renderer/viewer/lib/mesher/world.ts +++ b/renderer/viewer/lib/mesher/world.ts @@ -10,14 +10,6 @@ import { INVISIBLE_BLOCKS } from './worldConstants' const ignoreAoBlocks = Object.keys(moreBlockDataGeneratedJson.noOcclusions) -const ALWAYS_WATERLOGGED = new Set([ - 'seagrass', - 'tall_seagrass', - 'kelp', - 'kelp_plant', - 'bubble_column' -]) - function columnKey (x, z) { return `${x},${z}` } @@ -178,11 +170,6 @@ export class World { if (!attr) throw new Error('attr is required') const props = block.getProperties() - // Patch waterlogged property for ocean plants - if (ALWAYS_WATERLOGGED.has(block.name)) { - props.waterlogged = 'true' - } - try { // fixme if (this.preflat) { From ceb4cb0b66644962da2afbbf31d4faf6ec35cd76 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Thu, 27 Feb 2025 15:26:38 +0300 Subject: [PATCH 296/753] feat: Refactor mouse controls, fixing all false entity/item interaction issues (#286) --- package.json | 1 + pnpm-lock.yaml | 858 ++++++++++++++++++++++++++- src/cameraRotationControls.ts | 14 +- src/devtools.ts | 5 +- src/globals.d.ts | 2 +- src/index.ts | 11 +- src/mineflayer/plugins/mouse.ts | 204 +++++++ src/optionsStorage.ts | 1 + src/react/GameInteractionOverlay.tsx | 3 +- src/react/TouchAreasControls.tsx | 10 +- src/worldInteractions.ts | 551 ----------------- 11 files changed, 1066 insertions(+), 594 deletions(-) create mode 100644 src/mineflayer/plugins/mouse.ts delete mode 100644 src/worldInteractions.ts diff --git a/package.json b/package.json index b67a2b03..b0c5ee20 100644 --- a/package.json +++ b/package.json @@ -145,6 +145,7 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", + "mineflayer-mouse": "^0.0.4", "mc-assets": "^0.2.37", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3386ac03..be7ce2e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -282,7 +282,7 @@ importers: version: 7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) '@storybook/react-vite': specifier: ^7.4.6 - version: 7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@2.79.1)(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3)) + version: 7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@2.79.1)(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1)) '@types/diff-match-patch': specifier: ^1.0.36 version: 1.0.36 @@ -358,6 +358,9 @@ importers: mineflayer: specifier: github:zardoy/mineflayer version: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13) + mineflayer-mouse: + specifier: ^0.0.4 + version: 0.0.4(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -1265,6 +1268,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.18.20': resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} engines: {node: '>=12'} @@ -1277,6 +1286,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.18.20': resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} engines: {node: '>=12'} @@ -1289,6 +1304,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.18.20': resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} engines: {node: '>=12'} @@ -1301,6 +1322,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.18.20': resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} engines: {node: '>=12'} @@ -1313,6 +1340,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.18.20': resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} engines: {node: '>=12'} @@ -1325,6 +1358,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.18.20': resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} engines: {node: '>=12'} @@ -1337,6 +1376,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.18.20': resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} engines: {node: '>=12'} @@ -1349,6 +1394,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.18.20': resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} engines: {node: '>=12'} @@ -1361,6 +1412,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.18.20': resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} engines: {node: '>=12'} @@ -1373,6 +1430,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.18.20': resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} engines: {node: '>=12'} @@ -1385,6 +1448,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.18.20': resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} engines: {node: '>=12'} @@ -1397,6 +1466,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.18.20': resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} engines: {node: '>=12'} @@ -1409,6 +1484,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.18.20': resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} engines: {node: '>=12'} @@ -1421,6 +1502,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.18.20': resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} engines: {node: '>=12'} @@ -1433,6 +1520,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.18.20': resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} engines: {node: '>=12'} @@ -1445,6 +1538,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.18.20': resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} engines: {node: '>=12'} @@ -1457,6 +1556,18 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.18.20': resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} engines: {node: '>=12'} @@ -1469,6 +1580,18 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.18.20': resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} engines: {node: '>=12'} @@ -1481,6 +1604,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.18.20': resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} engines: {node: '>=12'} @@ -1493,6 +1622,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.18.20': resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} engines: {node: '>=12'} @@ -1505,6 +1640,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.18.20': resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} engines: {node: '>=12'} @@ -1517,6 +1658,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.18.20': resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} engines: {node: '>=12'} @@ -1529,6 +1676,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1813,6 +1966,9 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} @@ -2244,6 +2400,101 @@ packages: rollup: optional: true + '@rollup/rollup-android-arm-eabi@4.34.8': + resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.34.8': + resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.34.8': + resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.34.8': + resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.34.8': + resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.34.8': + resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.34.8': + resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.34.8': + resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.34.8': + resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.34.8': + resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.34.8': + resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.34.8': + resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.34.8': + resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.34.8': + resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.34.8': + resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.34.8': + resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} + cpu: [x64] + os: [win32] + '@rsbuild/core@1.0.1-beta.9': resolution: {integrity: sha512-F9npL47TFmNVhPBqoE6jBvKGxXEKNszBA7skhbi3opskmX7Ako9vfXvtgi2W2jQjq837/WUL8gG/ua9zRqKFEQ==} engines: {node: '>=16.7.0'} @@ -2722,6 +2973,9 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/express-serve-static-core@4.17.37': resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==} @@ -3023,18 +3277,47 @@ packages: '@vitest/expect@0.34.6': resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + '@vitest/expect@3.0.7': + resolution: {integrity: sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==} + + '@vitest/mocker@3.0.7': + resolution: {integrity: sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.0.7': + resolution: {integrity: sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==} + '@vitest/runner@0.34.6': resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + '@vitest/runner@3.0.7': + resolution: {integrity: sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==} + '@vitest/snapshot@0.34.6': resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + '@vitest/snapshot@3.0.7': + resolution: {integrity: sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==} + '@vitest/spy@0.34.6': resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + '@vitest/spy@3.0.7': + resolution: {integrity: sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==} + '@vitest/utils@0.34.6': resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + '@vitest/utils@3.0.7': + resolution: {integrity: sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==} + '@webassemblyjs/ast@1.12.1': resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} @@ -3418,6 +3701,10 @@ packages: assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + assign-symbols@1.0.0: resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} engines: {node: '>=0.10.0'} @@ -3777,6 +4064,10 @@ packages: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3795,6 +4086,9 @@ packages: change-case@5.1.2: resolution: {integrity: sha512-CAtbGEDulyjzs05RXy3uKcwqeztz/dMEuAc1Xu9NQBsbrhuGMneL0u9Dj5SoutLKBFYun8txxYIwhjtLNfUmCA==} + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} @@ -3804,6 +4098,10 @@ packages: check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + check-more-types@2.24.0: resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} engines: {node: '>= 0.8.0'} @@ -4220,6 +4518,10 @@ packages: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -4541,6 +4843,9 @@ packages: es-module-lexer@1.5.4: resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -4586,6 +4891,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -4794,6 +5104,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -4849,6 +5162,10 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} + exponential-backoff@3.1.1: resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} @@ -6161,6 +6478,9 @@ packages: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} deprecated: Please upgrade to 2.3.7 which fixes GHSA-4q6p-r6v2-jvc5 + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -6189,6 +6509,9 @@ packages: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.4: resolution: {integrity: sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==} engines: {node: '>=12'} @@ -6478,6 +6801,10 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824} version: 1.2.0 + mineflayer-mouse@0.0.4: + resolution: {integrity: sha512-55GqQhJWCXnOnm30uOjtI7nsawPb0kA3cAv6a5n1NJjTWFR6hzMkiRT6xGLYrvYhdf6Er3nsE2Ok/Aysa/jtFQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + mineflayer-pathfinder@2.4.4: resolution: {integrity: sha512-HAXakZrJRb1UC+5dv8EaDrqjW3ZnBnBk3nkb6x/YWyhHCUKn/E7VU0FO+UN9whuqPlkSaVumEdXJdydE6lSYxQ==} @@ -6615,6 +6942,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanomatch@1.2.13: resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} engines: {node: '>=0.10.0'} @@ -7006,9 +7338,16 @@ packages: pathe@1.1.1: resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} @@ -7122,6 +7461,10 @@ packages: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + potpack@1.0.2: resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} @@ -7810,6 +8153,11 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true + rollup@4.34.8: + resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rope-sequence@1.3.4: resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} @@ -8199,6 +8547,9 @@ packages: std-env@3.4.3: resolution: {integrity: sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==} + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + store2@2.14.2: resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==} @@ -8441,17 +8792,35 @@ packages: tinybench@2.5.1: resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinypool@0.7.0: resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} engines: {node: '>=14.0.0'} + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + tinyspy@2.2.0: resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} engines: {node: '>=14.0.0'} + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + title-case@3.0.3: resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} @@ -8906,6 +9275,11 @@ packages: engines: {node: '>=v14.18.0'} hasBin: true + vite-node@3.0.7: + resolution: {integrity: sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + vite@4.5.3: resolution: {integrity: sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -8934,6 +9308,46 @@ packages: terser: optional: true + vite@6.2.0: + resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitest@0.34.6: resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} engines: {node: '>=v14.18.0'} @@ -8965,6 +9379,34 @@ packages: webdriverio: optional: true + vitest@3.0.7: + resolution: {integrity: sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.0.7 + '@vitest/ui': 3.0.7 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} @@ -9067,6 +9509,11 @@ packages: engines: {node: '>=8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} @@ -10255,138 +10702,213 @@ snapshots: '@esbuild/aix-ppc64@0.19.11': optional: true + '@esbuild/aix-ppc64@0.25.0': + optional: true + '@esbuild/android-arm64@0.18.20': optional: true '@esbuild/android-arm64@0.19.11': optional: true + '@esbuild/android-arm64@0.25.0': + optional: true + '@esbuild/android-arm@0.18.20': optional: true '@esbuild/android-arm@0.19.11': optional: true + '@esbuild/android-arm@0.25.0': + optional: true + '@esbuild/android-x64@0.18.20': optional: true '@esbuild/android-x64@0.19.11': optional: true + '@esbuild/android-x64@0.25.0': + optional: true + '@esbuild/darwin-arm64@0.18.20': optional: true '@esbuild/darwin-arm64@0.19.11': optional: true + '@esbuild/darwin-arm64@0.25.0': + optional: true + '@esbuild/darwin-x64@0.18.20': optional: true '@esbuild/darwin-x64@0.19.11': optional: true + '@esbuild/darwin-x64@0.25.0': + optional: true + '@esbuild/freebsd-arm64@0.18.20': optional: true '@esbuild/freebsd-arm64@0.19.11': optional: true + '@esbuild/freebsd-arm64@0.25.0': + optional: true + '@esbuild/freebsd-x64@0.18.20': optional: true '@esbuild/freebsd-x64@0.19.11': optional: true + '@esbuild/freebsd-x64@0.25.0': + optional: true + '@esbuild/linux-arm64@0.18.20': optional: true '@esbuild/linux-arm64@0.19.11': optional: true + '@esbuild/linux-arm64@0.25.0': + optional: true + '@esbuild/linux-arm@0.18.20': optional: true '@esbuild/linux-arm@0.19.11': optional: true + '@esbuild/linux-arm@0.25.0': + optional: true + '@esbuild/linux-ia32@0.18.20': optional: true '@esbuild/linux-ia32@0.19.11': optional: true + '@esbuild/linux-ia32@0.25.0': + optional: true + '@esbuild/linux-loong64@0.18.20': optional: true '@esbuild/linux-loong64@0.19.11': optional: true + '@esbuild/linux-loong64@0.25.0': + optional: true + '@esbuild/linux-mips64el@0.18.20': optional: true '@esbuild/linux-mips64el@0.19.11': optional: true + '@esbuild/linux-mips64el@0.25.0': + optional: true + '@esbuild/linux-ppc64@0.18.20': optional: true '@esbuild/linux-ppc64@0.19.11': optional: true + '@esbuild/linux-ppc64@0.25.0': + optional: true + '@esbuild/linux-riscv64@0.18.20': optional: true '@esbuild/linux-riscv64@0.19.11': optional: true + '@esbuild/linux-riscv64@0.25.0': + optional: true + '@esbuild/linux-s390x@0.18.20': optional: true '@esbuild/linux-s390x@0.19.11': optional: true + '@esbuild/linux-s390x@0.25.0': + optional: true + '@esbuild/linux-x64@0.18.20': optional: true '@esbuild/linux-x64@0.19.11': optional: true + '@esbuild/linux-x64@0.25.0': + optional: true + + '@esbuild/netbsd-arm64@0.25.0': + optional: true + '@esbuild/netbsd-x64@0.18.20': optional: true '@esbuild/netbsd-x64@0.19.11': optional: true + '@esbuild/netbsd-x64@0.25.0': + optional: true + + '@esbuild/openbsd-arm64@0.25.0': + optional: true + '@esbuild/openbsd-x64@0.18.20': optional: true '@esbuild/openbsd-x64@0.19.11': optional: true + '@esbuild/openbsd-x64@0.25.0': + optional: true + '@esbuild/sunos-x64@0.18.20': optional: true '@esbuild/sunos-x64@0.19.11': optional: true + '@esbuild/sunos-x64@0.25.0': + optional: true + '@esbuild/win32-arm64@0.18.20': optional: true '@esbuild/win32-arm64@0.19.11': optional: true + '@esbuild/win32-arm64@0.25.0': + optional: true + '@esbuild/win32-ia32@0.18.20': optional: true '@esbuild/win32-ia32@0.19.11': optional: true + '@esbuild/win32-ia32@0.25.0': + optional: true + '@esbuild/win32-x64@0.18.20': optional: true '@esbuild/win32-x64@0.19.11': optional: true + '@esbuild/win32-x64@0.25.0': + optional: true + '@eslint-community/eslint-utils@4.4.0(eslint@8.50.0)': dependencies: eslint: 8.50.0 @@ -10814,13 +11336,13 @@ snapshots: regenerator-runtime: 0.13.11 optional: true - '@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1))': dependencies: glob: 7.2.3 glob-promise: 4.2.2(glob@7.2.3) magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.5.4) - vite: 4.5.3(@types/node@22.8.1)(terser@5.31.3) + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) optionalDependencies: typescript: 5.5.4 @@ -10841,6 +11363,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.1 @@ -11299,6 +11823,63 @@ snapshots: optionalDependencies: rollup: 2.79.1 + '@rollup/rollup-android-arm-eabi@4.34.8': + optional: true + + '@rollup/rollup-android-arm64@4.34.8': + optional: true + + '@rollup/rollup-darwin-arm64@4.34.8': + optional: true + + '@rollup/rollup-darwin-x64@4.34.8': + optional: true + + '@rollup/rollup-freebsd-arm64@4.34.8': + optional: true + + '@rollup/rollup-freebsd-x64@4.34.8': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.34.8': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.34.8': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-x64-musl@4.34.8': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.34.8': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.34.8': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.34.8': + optional: true + '@rsbuild/core@1.0.1-beta.9': dependencies: '@rspack/core': 1.0.0-beta.1(@swc/helpers@0.5.11) @@ -11696,7 +12277,7 @@ snapshots: - encoding - supports-color - '@storybook/builder-vite@7.4.6(encoding@0.1.13)(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3))': + '@storybook/builder-vite@7.4.6(encoding@0.1.13)(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1))': dependencies: '@storybook/channels': 7.4.6 '@storybook/client-logger': 7.4.6 @@ -11717,7 +12298,7 @@ snapshots: remark-external-links: 8.0.0 remark-slug: 6.1.0 rollup: 3.29.4 - vite: 4.5.3(@types/node@22.8.1)(terser@5.31.3) + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -12003,19 +12584,19 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@storybook/react-vite@7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@2.79.1)(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3))': + '@storybook/react-vite@7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@2.79.1)(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1)) '@rollup/pluginutils': 5.0.5(rollup@2.79.1) - '@storybook/builder-vite': 7.4.6(encoding@0.1.13)(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3)) + '@storybook/builder-vite': 7.4.6(encoding@0.1.13)(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1)) '@storybook/react': 7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) - '@vitejs/plugin-react': 3.1.0(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3)) + '@vitejs/plugin-react': 3.1.0(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1)) ast-types: 0.14.2 magic-string: 0.30.4 react: 18.2.0 react-docgen: 6.0.0-alpha.3 react-dom: 18.2.0(react@18.2.0) - vite: 4.5.3(@types/node@22.8.1)(terser@5.31.3) + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -12237,6 +12818,8 @@ snapshots: '@types/estree@1.0.5': {} + '@types/estree@1.0.6': {} + '@types/express-serve-static-core@4.17.37': dependencies: '@types/node': 22.8.1 @@ -12582,14 +13165,14 @@ snapshots: '@typescript-eslint/types': 8.0.0 eslint-visitor-keys: 3.4.3 - '@vitejs/plugin-react@3.1.0(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3))': + '@vitejs/plugin-react@3.1.0(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1))': dependencies: '@babel/core': 7.22.11 '@babel/plugin-transform-react-jsx-self': 7.22.5(@babel/core@7.22.11) '@babel/plugin-transform-react-jsx-source': 7.22.5(@babel/core@7.22.11) magic-string: 0.27.0 react-refresh: 0.14.2 - vite: 4.5.3(@types/node@22.8.1)(terser@5.31.3) + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) transitivePeerDependencies: - supports-color @@ -12599,28 +13182,68 @@ snapshots: '@vitest/utils': 0.34.6 chai: 4.3.10 + '@vitest/expect@3.0.7': + dependencies: + '@vitest/spy': 3.0.7 + '@vitest/utils': 3.0.7 + chai: 5.2.0 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.0.7(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1))': + dependencies: + '@vitest/spy': 3.0.7 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + + '@vitest/pretty-format@3.0.7': + dependencies: + tinyrainbow: 2.0.0 + '@vitest/runner@0.34.6': dependencies: '@vitest/utils': 0.34.6 p-limit: 4.0.0 pathe: 1.1.1 + '@vitest/runner@3.0.7': + dependencies: + '@vitest/utils': 3.0.7 + pathe: 2.0.3 + '@vitest/snapshot@0.34.6': dependencies: magic-string: 0.30.4 pathe: 1.1.1 pretty-format: 29.7.0 + '@vitest/snapshot@3.0.7': + dependencies: + '@vitest/pretty-format': 3.0.7 + magic-string: 0.30.17 + pathe: 2.0.3 + '@vitest/spy@0.34.6': dependencies: tinyspy: 2.2.0 + '@vitest/spy@3.0.7': + dependencies: + tinyspy: 3.0.2 + '@vitest/utils@0.34.6': dependencies: diff-sequences: 29.6.3 loupe: 2.3.6 pretty-format: 29.7.0 + '@vitest/utils@3.0.7': + dependencies: + '@vitest/pretty-format': 3.0.7 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + '@webassemblyjs/ast@1.12.1': dependencies: '@webassemblyjs/helper-numbers': 1.11.6 @@ -13123,6 +13746,8 @@ snapshots: assertion-error@1.1.0: {} + assertion-error@2.0.1: {} + assign-symbols@1.0.0: {} ast-types@0.14.2: @@ -13603,6 +14228,14 @@ snapshots: pathval: 1.1.1 type-detect: 4.0.8 + chai@5.2.0: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.3 + pathval: 2.0.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -13633,6 +14266,8 @@ snapshots: change-case@5.1.2: {} + change-case@5.4.4: {} + character-entities@2.0.2: {} charenc@0.0.2: {} @@ -13641,6 +14276,8 @@ snapshots: dependencies: get-func-name: 2.0.2 + check-error@2.1.1: {} + check-more-types@2.24.0: optional: true @@ -14154,6 +14791,8 @@ snapshots: dependencies: type-detect: 4.0.8 + deep-eql@5.0.2: {} + deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -14600,6 +15239,8 @@ snapshots: es-module-lexer@1.5.4: {} + es-module-lexer@1.6.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -14693,6 +15334,34 @@ snapshots: '@esbuild/win32-ia32': 0.19.11 '@esbuild/win32-x64': 0.19.11 + esbuild@0.25.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 + escalade@3.1.2: {} escape-html@1.0.3: {} @@ -14968,6 +15637,10 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + esutils@2.0.3: {} etag@1.8.1: {} @@ -15045,6 +15718,8 @@ snapshots: expand-template@2.0.3: {} + expect-type@1.1.0: {} + exponential-backoff@3.1.1: optional: true @@ -16586,6 +17261,8 @@ snapshots: dependencies: get-func-name: 2.0.2 + loupe@3.1.3: {} + lower-case@2.0.2: dependencies: tslib: 2.6.2 @@ -16613,6 +17290,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + magic-string@0.30.4: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -17061,6 +17742,33 @@ snapshots: - encoding - supports-color + mineflayer-mouse@0.0.4(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + dependencies: + change-case: 5.4.4 + debug: 4.4.0(supports-color@8.1.1) + prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/ab2146c9933eef3247c3f64446de4ccc2c484c7c + vitest: 3.0.7(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + transitivePeerDependencies: + - '@edge-runtime/vm' + - '@types/debug' + - '@types/node' + - '@vitest/browser' + - '@vitest/ui' + - happy-dom + - jiti + - jsdom + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + mineflayer-pathfinder@2.4.4: dependencies: minecraft-data: 3.83.1 @@ -17254,6 +17962,8 @@ snapshots: nanoid@3.3.7: {} + nanoid@3.3.8: {} + nanomatch@1.2.13: dependencies: arr-diff: 4.0.0 @@ -17712,8 +18422,12 @@ snapshots: pathe@1.1.1: {} + pathe@2.0.3: {} + pathval@1.1.1: {} + pathval@2.0.0: {} + pause-stream@0.0.11: dependencies: through: 2.3.8 @@ -17821,6 +18535,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.3: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + potpack@1.0.2: {} prebuild-install@7.1.1: @@ -18703,6 +19423,31 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + rollup@4.34.8: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.34.8 + '@rollup/rollup-android-arm64': 4.34.8 + '@rollup/rollup-darwin-arm64': 4.34.8 + '@rollup/rollup-darwin-x64': 4.34.8 + '@rollup/rollup-freebsd-arm64': 4.34.8 + '@rollup/rollup-freebsd-x64': 4.34.8 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 + '@rollup/rollup-linux-arm-musleabihf': 4.34.8 + '@rollup/rollup-linux-arm64-gnu': 4.34.8 + '@rollup/rollup-linux-arm64-musl': 4.34.8 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 + '@rollup/rollup-linux-riscv64-gnu': 4.34.8 + '@rollup/rollup-linux-s390x-gnu': 4.34.8 + '@rollup/rollup-linux-x64-gnu': 4.34.8 + '@rollup/rollup-linux-x64-musl': 4.34.8 + '@rollup/rollup-win32-arm64-msvc': 4.34.8 + '@rollup/rollup-win32-ia32-msvc': 4.34.8 + '@rollup/rollup-win32-x64-msvc': 4.34.8 + fsevents: 2.3.3 + rope-sequence@1.3.4: {} rtl-css-js@1.16.1: @@ -19251,6 +19996,8 @@ snapshots: std-env@3.4.3: {} + std-env@3.8.0: {} + store2@2.14.2: {} storybook@7.4.6(encoding@0.1.13): @@ -19537,13 +20284,23 @@ snapshots: tinybench@2.5.1: {} + tinybench@2.9.0: {} + tinycolor2@1.6.0: optional: true + tinyexec@0.3.2: {} + tinypool@0.7.0: {} + tinypool@1.0.2: {} + + tinyrainbow@2.0.0: {} + tinyspy@2.2.0: {} + tinyspy@3.0.2: {} + title-case@3.0.3: dependencies: tslib: 2.6.2 @@ -20014,6 +20771,27 @@ snapshots: - supports-color - terser + vite-node@3.0.7(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + dependencies: + cac: 6.7.14 + debug: 4.4.0(supports-color@8.1.1) + es-module-lexer: 1.6.0 + pathe: 2.0.3 + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vite@4.5.3(@types/node@22.8.1)(terser@5.31.3): dependencies: esbuild: 0.18.20 @@ -20024,6 +20802,18 @@ snapshots: fsevents: 2.3.3 terser: 5.31.3 + vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + dependencies: + esbuild: 0.25.0 + postcss: 8.5.3 + rollup: 4.34.8 + optionalDependencies: + '@types/node': 22.8.1 + fsevents: 2.3.3 + terser: 5.31.3 + tsx: 4.7.0 + yaml: 2.4.1 + vitest@0.34.6(terser@5.31.3): dependencies: '@types/chai': 4.3.6 @@ -20059,6 +20849,45 @@ snapshots: - supports-color - terser + vitest@3.0.7(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + dependencies: + '@vitest/expect': 3.0.7 + '@vitest/mocker': 3.0.7(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1)) + '@vitest/pretty-format': 3.0.7 + '@vitest/runner': 3.0.7 + '@vitest/snapshot': 3.0.7 + '@vitest/spy': 3.0.7 + '@vitest/utils': 3.0.7 + chai: 5.2.0 + debug: 4.4.0(supports-color@8.1.1) + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 2.0.3 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + vite-node: 3.0.7(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.8.1 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vm-browserify@1.1.2: {} w3c-keyname@2.2.8: {} @@ -20212,6 +21041,11 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + wide-align@1.1.5: dependencies: string-width: 4.2.3 diff --git a/src/cameraRotationControls.ts b/src/cameraRotationControls.ts index b9bc5fe9..0c222dc6 100644 --- a/src/cameraRotationControls.ts +++ b/src/cameraRotationControls.ts @@ -3,20 +3,10 @@ import { activeModalStack, isGameActive, miscUiState, showModal } from './global import { options } from './optionsStorage' import { hideNotification, notificationProxy } from './react/NotificationProvider' import { pointerLock } from './utils' -import worldInteractions from './worldInteractions' import { updateMotion, initMotionTracking } from './react/uiMotion' let lastMouseMove: number -const MOTION_DAMPING = 0.92 -const MAX_MOTION_OFFSET = 30 -const motionVelocity = { x: 0, y: 0 } -const lastUpdate = performance.now() - -export const updateCursor = () => { - worldInteractions.update() -} - export type CameraMoveEvent = { movementX: number movementY: number @@ -30,7 +20,7 @@ export function onCameraMove (e: MouseEvent | CameraMoveEvent) { e.stopPropagation?.() const now = performance.now() // todo: limit camera movement for now to avoid unexpected jumps - if (now - lastMouseMove < 4) return + if (now - lastMouseMove < 4 && !options.preciseMouseInput) return lastMouseMove = now let { mouseSensX, mouseSensY } = options if (mouseSensY === -1) mouseSensY = mouseSensX @@ -38,7 +28,7 @@ export function onCameraMove (e: MouseEvent | CameraMoveEvent) { x: e.movementX * mouseSensX * 0.0001, y: e.movementY * mouseSensY * 0.0001 }) - updateCursor() + bot.mouse.update() updateMotion() } diff --git a/src/devtools.ts b/src/devtools.ts index 5d0be20b..617a4440 100644 --- a/src/devtools.ts +++ b/src/devtools.ts @@ -3,7 +3,6 @@ import fs from 'fs' import { WorldRendererThree } from 'renderer/viewer/lib/worldrendererThree' import { enable, disable, enabled } from 'debug' -import { getEntityCursor } from './worldInteractions' window.cursorBlockRel = (x = 0, y = 0, z = 0) => { const newPos = bot.blockAtCursor(5)?.position.offset(x, y, z) @@ -11,8 +10,8 @@ window.cursorBlockRel = (x = 0, y = 0, z = 0) => { return bot.world.getBlock(newPos) } -window.cursorEntity = () => { - return getEntityCursor() +window.entityCursor = () => { + return bot.mouse.getCursorState().entity } // wanderer diff --git a/src/globals.d.ts b/src/globals.d.ts index 96b32916..6b2c6640 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -21,7 +21,7 @@ declare const loadedData: import('minecraft-data').IndexedData & { sounds: Recor declare const customEvents: import('typed-emitter').default<{ /** Singleplayer load requested */ singleplayer (): void - digStart () + digStart (): void gameLoaded (): void mineflayerBotCreated (): void search (q: string): void diff --git a/src/index.ts b/src/index.ts index 249fbe9d..beb86aea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,8 +40,6 @@ import { WorldDataEmitter, Viewer } from 'renderer/viewer' import pathfinder from 'mineflayer-pathfinder' import { Vec3 } from 'vec3' -import worldInteractions from './worldInteractions' - import * as THREE from 'three' import MinecraftData from 'minecraft-data' import debug from 'debug' @@ -106,13 +104,12 @@ import { parseFormattedMessagePacket } from './botUtils' import { getViewerVersionData, getWsProtocolStream, handleCustomChannel } from './viewerConnector' import { getWebsocketStream } from './mineflayer/websocket-core' import { appQueryParams, appQueryParamsArray } from './appParams' -import { updateCursor } from './cameraRotationControls' -import { pingServerVersion } from './mineflayer/minecraft-protocol-extra' import { playerState, PlayerStateManager } from './mineflayer/playerState' import { states } from 'minecraft-protocol' import { initMotionTracking } from './react/uiMotion' import { UserError } from './mineflayer/userError' import ping from './mineflayer/plugins/ping' +import mouse from './mineflayer/plugins/mouse' import { LocalServer } from './customServer' import { startLocalReplayServer } from './packetsReplay/replayPackets' import { localRelayServerPlugin } from './mineflayer/plugins/packetsRecording' @@ -120,7 +117,6 @@ import { createFullScreenProgressReporter } from './core/progressReporter' window.debug = debug window.THREE = THREE -window.worldInteractions = worldInteractions window.beforeRenderFrame = [] // ACTUAL CODE @@ -705,6 +701,7 @@ export async function connect (connectOptions: ConnectOptions) { if (connectOptions.server) { bot.loadPlugin(ping) } + bot.loadPlugin(mouse) if (!localReplaySession) { bot.loadPlugin(localRelayServerPlugin) } @@ -754,8 +751,6 @@ export async function connect (connectOptions: ConnectOptions) { onBotCreate() bot.once('login', () => { - worldInteractions.initBot() - setLoadingScreenStatus('Loading world') const mcData = MinecraftData(bot.version) @@ -815,8 +810,6 @@ export async function connect (connectOptions: ConnectOptions) { const worldView = window.worldView = new WorldDataEmitter(bot.world, renderDistance, center) watchOptionsAfterWorldViewInit() - bot.on('physicsTick', () => updateCursor()) - void initVR() initMotionTracking() diff --git a/src/mineflayer/plugins/mouse.ts b/src/mineflayer/plugins/mouse.ts new file mode 100644 index 00000000..e5b5e283 --- /dev/null +++ b/src/mineflayer/plugins/mouse.ts @@ -0,0 +1,204 @@ +import { createMouse } from 'mineflayer-mouse' +import * as THREE from 'three' +import { Bot } from 'mineflayer' +import { Block } from 'prismarine-block' +import { Vec3 } from 'vec3' +import { LineMaterial } from 'three-stdlib' +import { subscribeKey } from 'valtio/utils' +import { disposeObject } from 'renderer/viewer/lib/threeJsUtils' +import { isGameActive, showModal } from '../../globalState' + +// wouldn't better to create atlas instead? +import destroyStage0 from '../../../assets/destroy_stage_0.png' +import destroyStage1 from '../../../assets/destroy_stage_1.png' +import destroyStage2 from '../../../assets/destroy_stage_2.png' +import destroyStage3 from '../../../assets/destroy_stage_3.png' +import destroyStage4 from '../../../assets/destroy_stage_4.png' +import destroyStage5 from '../../../assets/destroy_stage_5.png' +import destroyStage6 from '../../../assets/destroy_stage_6.png' +import destroyStage7 from '../../../assets/destroy_stage_7.png' +import destroyStage8 from '../../../assets/destroy_stage_8.png' +import destroyStage9 from '../../../assets/destroy_stage_9.png' +import { options } from '../../optionsStorage' +import { isCypress } from '../../standaloneUtils' +import { playerState } from '../playerState' + +function createDisplayManager (bot: Bot, scene: THREE.Scene, renderer: THREE.WebGLRenderer) { + // State + const state = { + blockBreakMesh: null as THREE.Mesh | null, + breakTextures: [] as THREE.Texture[], + } + + // Initialize break mesh and textures + const loader = new THREE.TextureLoader() + const destroyStagesImages = [ + destroyStage0, destroyStage1, destroyStage2, destroyStage3, destroyStage4, + destroyStage5, destroyStage6, destroyStage7, destroyStage8, destroyStage9 + ] + + for (let i = 0; i < 10; i++) { + const texture = loader.load(destroyStagesImages[i]) + texture.magFilter = THREE.NearestFilter + texture.minFilter = THREE.NearestFilter + state.breakTextures.push(texture) + } + + const breakMaterial = new THREE.MeshBasicMaterial({ + transparent: true, + blending: THREE.MultiplyBlending, + alphaTest: 0.5, + }) + state.blockBreakMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), breakMaterial) + state.blockBreakMesh.visible = false + state.blockBreakMesh.renderOrder = 999 + state.blockBreakMesh.name = 'blockBreakMesh' + scene.add(state.blockBreakMesh) + + // Update functions + function updateLineMaterial () { + const inCreative = bot.game.gameMode === 'creative' + const pixelRatio = viewer.renderer.getPixelRatio() + + viewer.world.threejsCursorLineMaterial = new LineMaterial({ + color: (() => { + switch (options.highlightBlockColor) { + case 'blue': + return 0x40_80_ff + case 'classic': + return 0x00_00_00 + default: + return inCreative ? 0x40_80_ff : 0x00_00_00 + } + })(), + linewidth: Math.max(pixelRatio * 0.7, 1) * 2, + // dashed: true, + // dashSize: 5, + }) + } + + function updateDisplay () { + if (viewer.world.threejsCursorLineMaterial) { + const { renderer } = viewer + viewer.world.threejsCursorLineMaterial.resolution.set(renderer.domElement.width, renderer.domElement.height) + viewer.world.threejsCursorLineMaterial.dashOffset = performance.now() / 750 + } + } + beforeRenderFrame.push(updateDisplay) + + // Update cursor line material on game mode change + bot.on('game', updateLineMaterial) + // Update material when highlight color setting changes + subscribeKey(options, 'highlightBlockColor', updateLineMaterial) + + function updateBreakAnimation (block: Block | undefined, stage: number | null) { + hideBreakAnimation() + if (!state.blockBreakMesh) return // todo + if (stage === null || !block) return + + const mergedShape = bot.mouse.getMergedCursorShape(block) + if (!mergedShape) return + const { position, width, height, depth } = bot.mouse.getDataFromShape(mergedShape) + state.blockBreakMesh.scale.set(width * 1.001, height * 1.001, depth * 1.001) + position.add(block.position) + state.blockBreakMesh.position.set(position.x, position.y, position.z) + state.blockBreakMesh.visible = true + + //@ts-expect-error + state.blockBreakMesh.material.map = state.breakTextures[stage] ?? state.breakTextures.at(-1) + //@ts-expect-error + state.blockBreakMesh.material.needsUpdate = true + } + + function hideBreakAnimation () { + if (state.blockBreakMesh) { + state.blockBreakMesh.visible = false + } + } + + function updateCursorBlock (data?: { block: Block }) { + if (!data?.block) { + viewer.world.setHighlightCursorBlock(null) + return + } + + const { block } = data + viewer.world.setHighlightCursorBlock(block.position, bot.mouse.getBlockCursorShapes(block).map(shape => { + return bot.mouse.getDataFromShape(shape) + })) + } + + bot.on('highlightCursorBlock', updateCursorBlock) + + bot.on('blockBreakProgressStage', updateBreakAnimation) + + bot.on('end', () => { + disposeObject(state.blockBreakMesh!, true) + scene.remove(state.blockBreakMesh!) + viewer.world.setHighlightCursorBlock(null) + }) +} + +export default (bot: Bot) => { + bot.loadPlugin(createMouse({})) + + domListeners(bot) + createDisplayManager(bot, viewer.scene, viewer.renderer) + + otherListeners() +} + +const otherListeners = () => { + bot.on('startDigging', (block) => { + customEvents.emit('digStart') + }) + + bot.on('goingToSleep', () => { + showModal({ reactType: 'bed' }) + }) + + bot.on('botArmSwingStart', (hand) => { + viewer.world.changeHandSwingingState(true, hand === 'left') + }) + + bot.on('botArmSwingEnd', (hand) => { + viewer.world.changeHandSwingingState(false, hand === 'left') + }) + + bot.on('startUsingItem', (item, slot, isOffhand, duration) => { + customEvents.emit('activateItem', item, isOffhand ? 45 : bot.quickBarSlot, isOffhand) + playerState.startUsingItem() + }) + + bot.on('stopUsingItem', () => { + playerState.stopUsingItem() + }) +} + +const domListeners = (bot: Bot) => { + document.addEventListener('mousedown', (e) => { + if (e.isTrusted && !document.pointerLockElement && !isCypress()) return + if (!isGameActive(true)) return + + if (e.button === 0) { + bot.leftClickStart() + } else if (e.button === 2) { + bot.rightClickStart() + } + }) + + document.addEventListener('mouseup', (e) => { + if (e.button === 0) { + bot.leftClickEnd() + } else if (e.button === 2) { + bot.rightClickEnd() + } + }) + + bot.mouse.beforeUpdateChecks = () => { + if (!document.hasFocus()) { + // deactive all buttons + bot.mouse.buttons.fill(false) + } + } +} diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index 47109979..b88b71c4 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -60,6 +60,7 @@ const defaultOptions = { serversAutoVersionSelect: 'auto' as 'auto' | 'latest' | '1.20.4' | string, customChannels: false, packetsReplayAutoStart: false, + preciseMouseInput: false, // todo ui setting, maybe enable by default? waitForChunksRender: 'sp-only' as 'sp-only' | boolean, diff --git a/src/react/GameInteractionOverlay.tsx b/src/react/GameInteractionOverlay.tsx index 503ca8b4..cb7a39f8 100644 --- a/src/react/GameInteractionOverlay.tsx +++ b/src/react/GameInteractionOverlay.tsx @@ -3,7 +3,6 @@ import { subscribe, useSnapshot } from 'valtio' import { useUtilsEffect } from '@zardoy/react-util' import { options } from '../optionsStorage' import { activeModalStack, isGameActive, miscUiState } from '../globalState' -import worldInteractions from '../worldInteractions' import { onCameraMove, CameraMoveEvent } from '../cameraRotationControls' import { pointerLock, isInRealGameSession } from '../utils' import { handleMovementStickDelta, joystickPointer } from './TouchAreasControls' @@ -152,7 +151,7 @@ function GameInteractionOverlayInner ({ virtualClickActive = false } else if (!capturedPointer.active.activateCameraMove && (Date.now() - capturedPointer.active.time < touchStartBreakingBlockMs)) { document.dispatchEvent(new MouseEvent('mousedown', { button: 2 })) - worldInteractions.update() + bot.mouse.update() document.dispatchEvent(new MouseEvent('mouseup', { button: 2 })) } diff --git a/src/react/TouchAreasControls.tsx b/src/react/TouchAreasControls.tsx index 981ebebb..6b34f9bc 100644 --- a/src/react/TouchAreasControls.tsx +++ b/src/react/TouchAreasControls.tsx @@ -1,7 +1,6 @@ import { CSSProperties, PointerEvent, useEffect, useRef } from 'react' import { proxy, ref, useSnapshot } from 'valtio' import { contro } from '../controls' -import worldInteractions from '../worldInteractions' import { options } from '../optionsStorage' import PixelartIcon from './PixelartIcon' import Button from './Button' @@ -73,8 +72,9 @@ export default ({ setupActive, closeButtonsSetup, foregroundGameActive }: Props) }[name] const holdDown = { action () { + if (!bot) return document.dispatchEvent(new MouseEvent('mousedown', { button: 2 })) - worldInteractions.update() + bot.mouse.update() }, sneak () { void contro.emit('trigger', { @@ -84,8 +84,9 @@ export default ({ setupActive, closeButtonsSetup, foregroundGameActive }: Props) active = bot?.getControlState('sneak') }, break () { + if (!bot) return document.dispatchEvent(new MouseEvent('mousedown', { button: 0 })) - worldInteractions.update() + bot.mouse.update() active = true }, jump () { @@ -108,8 +109,9 @@ export default ({ setupActive, closeButtonsSetup, foregroundGameActive }: Props) active = bot?.getControlState('sneak') }, break () { + if (!bot) return document.dispatchEvent(new MouseEvent('mouseup', { button: 0 })) - worldInteractions.update() + bot.mouse.update() active = false }, jump () { diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts deleted file mode 100644 index 5b03184c..00000000 --- a/src/worldInteractions.ts +++ /dev/null @@ -1,551 +0,0 @@ -//@ts-check - -import * as THREE from 'three' - -// wouldn't better to create atlas instead? -import { Vec3 } from 'vec3' -import { LineMaterial } from 'three-stdlib' -import { Entity } from 'prismarine-entity' -import { Block } from 'prismarine-block' -import { subscribeKey } from 'valtio/utils' -import destroyStage0 from '../assets/destroy_stage_0.png' -import destroyStage1 from '../assets/destroy_stage_1.png' -import destroyStage2 from '../assets/destroy_stage_2.png' -import destroyStage3 from '../assets/destroy_stage_3.png' -import destroyStage4 from '../assets/destroy_stage_4.png' -import destroyStage5 from '../assets/destroy_stage_5.png' -import destroyStage6 from '../assets/destroy_stage_6.png' -import destroyStage7 from '../assets/destroy_stage_7.png' -import destroyStage8 from '../assets/destroy_stage_8.png' -import destroyStage9 from '../assets/destroy_stage_9.png' - -import { hideCurrentModal, isGameActive, showModal } from './globalState' -import { assertDefined } from './utils' -import { options } from './optionsStorage' -import { itemBeingUsed } from './react/Crosshair' -import { isCypress } from './standaloneUtils' -import { displayClientChat } from './botUtils' -import { playerState } from './mineflayer/playerState' - -function getViewDirection (pitch, yaw) { - const csPitch = Math.cos(pitch) - const snPitch = Math.sin(pitch) - const csYaw = Math.cos(yaw) - const snYaw = Math.sin(yaw) - return new Vec3(-snYaw * csPitch, snPitch, -csYaw * csPitch) -} - -class WorldInteraction { - ready = false - cursorBlock: Block | null = null - prevBreakState: number | null = null - currentDigTime: number | null = null - prevOnGround: boolean | null = null - lastBlockPlaced: number - lastSwing = 0 - buttons = [false, false, false] - lastButtons = [false, false, false] - breakStartTime: number | undefined = 0 - lastDugBlock: Vec3 | null = null - blockBreakMesh: THREE.Mesh - breakTextures: THREE.Texture[] - lastDigged: number - debugDigStatus: string - currentBreakBlock: { block: any, stage: number } | null = null - swingTimeout: any = null - - oneTimeInit () { - const loader = new THREE.TextureLoader() - this.breakTextures = [] - const destroyStagesImages = [ - destroyStage0, - destroyStage1, - destroyStage2, - destroyStage3, - destroyStage4, - destroyStage5, - destroyStage6, - destroyStage7, - destroyStage8, - destroyStage9 - ] - for (let i = 0; i < 10; i++) { - const texture = loader.load(destroyStagesImages[i]) - texture.magFilter = THREE.NearestFilter - texture.minFilter = THREE.NearestFilter - this.breakTextures.push(texture) - } - const breakMaterial = new THREE.MeshBasicMaterial({ - transparent: true, - blending: THREE.MultiplyBlending, - alphaTest: 0.5, - }) - this.blockBreakMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), breakMaterial) - this.blockBreakMesh.visible = false - this.blockBreakMesh.renderOrder = 999 - this.blockBreakMesh.name = 'blockBreakMesh' - viewer.scene.add(this.blockBreakMesh) - - // Setup events - document.addEventListener('mouseup', (e) => { - this.buttons[e.button] = false - }) - - this.lastBlockPlaced = 4 // ticks since last placed - document.addEventListener('mousedown', (e) => { - if (e.isTrusted && !document.pointerLockElement && !isCypress()) return - if (!isGameActive(true)) return - this.buttons[e.button] = true - - const entity = getEntityCursor() - - if (entity) { - if (e.button === 0) { // left click - bot.attack(entity) - } else if (e.button === 2) { // right click - this.activateEntity(entity) - } - } - }) - document.addEventListener('blur', (e) => { - this.buttons = [false, false, false] - }) - - beforeRenderFrame.push(() => { - if (viewer.world.threejsCursorLineMaterial) { - const { renderer } = viewer - viewer.world.threejsCursorLineMaterial.resolution.set(renderer.domElement.width, renderer.domElement.height) - viewer.world.threejsCursorLineMaterial.dashOffset = performance.now() / 750 - } - }) - } - - initBot () { - if (!this.ready) { - this.ready = true - this.oneTimeInit() - } - assertDefined(viewer) - bot.on('physicsTick', () => { if (this.lastBlockPlaced < 4) this.lastBlockPlaced++ }) - bot.on('diggingCompleted', (block) => { - this.breakStartTime = undefined - this.lastDugBlock = block.position - // TODO: If the tool and enchantments immediately exceed the hardness times 30, the block breaks with no delay; SO WE NEED TO CHECK THAT - // TODO: Any blocks with a breaking time of 0.05 - this.lastDigged = Date.now() - this.debugDigStatus = 'done' - this.stopBreakAnimation() - }) - bot.on('diggingAborted', (block) => { - if (!viewer.world.cursorBlock?.equals(block.position)) return - this.debugDigStatus = 'aborted' - this.breakStartTime = undefined - if (this.buttons[0]) { - this.buttons[0] = false - this.update() - this.buttons[0] = true // trigger again - } - this.lastDugBlock = null - this.stopBreakAnimation() - }) - bot.on('heldItemChanged' as any, () => { - itemBeingUsed.name = null - }) - - // Add new event listeners for block breaking and swinging - bot.on('entitySwingArm', (entity: Entity) => { - if (entity.id === bot.entity.id) { - if (this.swingTimeout) { - clearTimeout(this.swingTimeout) - } - bot.swingArm('right') - viewer.world.changeHandSwingingState(true, false) - this.swingTimeout = setTimeout(() => { - viewer.world.changeHandSwingingState(false, false) - this.swingTimeout = null - }, 250) - } - }) - - //@ts-expect-error mineflayer types are wrong - bot.on('blockBreakProgressObserved', (block: Block, destroyStage: number, entity: Entity) => { - if (this.cursorBlock?.position.equals(block.position) && entity.id === bot.entity.id) { - if (!this.buttons[0]) { - // Simulate left mouse button press - this.buttons[0] = true - this.update() - } - // this.setBreakState(block, destroyStage) - } - }) - - //@ts-expect-error mineflayer types are wrong - bot.on('blockBreakProgressEnd', (block: Block, entity: Entity) => { - if (this.currentBreakBlock?.block.position.equals(block.position) && entity.id === bot.entity.id) { - if (!this.buttons[0]) { - // Simulate left mouse button press - this.buttons[0] = false - this.update() - } - // this.stopBreakAnimation() - } - }) - - // Handle acknowledge_player_digging packet - bot._client.on('acknowledge_player_digging', (data: { location: { x: number, y: number, z: number }, block: number, status: number, successful: boolean } | { sequenceId: number }) => { - if ('location' in data && !data.successful) { - const packetPos = new Vec3(data.location.x, data.location.y, data.location.z) - if (this.cursorBlock?.position.equals(packetPos)) { - this.buttons[0] = false - this.update() - this.stopBreakAnimation() - } - } - }) - - const upLineMaterial = () => { - const inCreative = bot.game.gameMode === 'creative' - const pixelRatio = viewer.renderer.getPixelRatio() - viewer.world.threejsCursorLineMaterial = new LineMaterial({ - color: (() => { - switch (options.highlightBlockColor) { - case 'blue': - return 0x40_80_ff - case 'classic': - return 0x00_00_00 - default: - return inCreative ? 0x40_80_ff : 0x00_00_00 - } - })(), - linewidth: Math.max(pixelRatio * 0.7, 1) * 2, - // dashed: true, - // dashSize: 5, - }) - } - upLineMaterial() - // todo use gamemode update only - bot.on('game', upLineMaterial) - // Update material when highlight color setting changes - subscribeKey(options, 'highlightBlockColor', upLineMaterial) - } - - activateEntity (entity: Entity) { - // mineflayer has completely wrong implementation of this action - if (bot.supportFeature('armAnimationBeforeUse')) { - bot.swingArm('right') - } - bot._client.write('use_entity', { - target: entity.id, - mouse: 2, - // todo do not fake - x: 0.581_012_585_759_162_9, - y: 0.581_012_585_759_162_9, - z: 0.581_012_585_759_162_9, - // x: raycastPosition.x - entity.position.x, - // y: raycastPosition.y - entity.position.y, - // z: raycastPosition.z - entity.position.z - sneaking: bot.getControlState('sneak'), - hand: 0 - }) - bot._client.write('use_entity', { - target: entity.id, - mouse: 0, - sneaking: bot.getControlState('sneak'), - hand: 0 - }) - if (!bot.supportFeature('armAnimationBeforeUse')) { - bot.swingArm('right') - } - } - - beforeUpdateChecks () { - if (!document.hasFocus()) { - // deactive all buttson - this.buttons.fill(false) - } - } - - // todo this shouldnt be done in the render loop, migrate the code to dom events to avoid delays on lags - update () { - this.beforeUpdateChecks() - const inSpectator = bot.game.gameMode === 'spectator' - const inAdventure = bot.game.gameMode === 'adventure' - const entity = getEntityCursor() - let _cursorBlock = inSpectator && !options.showCursorBlockInSpectator ? null : bot.blockAtCursor(5) - if (entity) { - _cursorBlock = null - } - this.cursorBlock = _cursorBlock - const { cursorBlock } = this - - let cursorBlockDiggable = cursorBlock - if (cursorBlock && (!bot.canDigBlock(cursorBlock) || inAdventure) && bot.game.gameMode !== 'creative') cursorBlockDiggable = null - - const cursorChanged = cursorBlock && viewer.world.cursorBlock ? !viewer.world.cursorBlock.equals(cursorBlock.position) : viewer.world.cursorBlock !== cursorBlock - - // Place / interact / activate - if (this.buttons[2] && this.lastBlockPlaced >= 4) { - const activatableItems = (itemName: string) => { - return ['egg', 'fishing_rod', 'firework_rocket', - 'fire_charge', 'snowball', 'ender_pearl', 'experience_bottle', 'potion', - 'glass_bottle', 'bucket', 'water_bucket', 'lava_bucket', 'milk_bucket', - 'minecart', 'boat', 'tnt_minecart', 'chest_minecart', 'hopper_minecart', - 'command_block_minecart', 'armor_stand', 'lead', 'name_tag', - // - 'writable_book', 'written_book', 'compass', 'clock', 'filled_map', 'empty_map', 'map', - 'shears', 'carrot_on_a_stick', 'warped_fungus_on_a_stick', - 'spawn_egg', 'trident', 'crossbow', 'elytra', 'shield', 'turtle_helmet', 'bow', 'crossbow', 'bucket_of_cod', - ...loadedData.foodsArray.map((f) => f.name), - ].includes(itemName) - } - const activate = bot.heldItem && activatableItems(bot.heldItem.name) - let stop = false - if (!bot.controlState.sneak) { - if (cursorBlock?.name === 'bed' || cursorBlock?.name.endsWith('_bed')) { - stop = true - showModal({ reactType: 'bed' }) - let cancelSleep = true - void bot.sleep(cursorBlock).catch((e) => { - if (cancelSleep) { - hideCurrentModal() - } - // if (e.message === 'bot is not sleeping') return - displayClientChat(e.message) - }) - setTimeout(() => { - cancelSleep = false - }) - } - } - // todo placing with offhand - if (cursorBlock && !activate && !stop) { - const vecArray = [new Vec3(0, -1, 0), new Vec3(0, 1, 0), new Vec3(0, 0, -1), new Vec3(0, 0, 1), new Vec3(-1, 0, 0), new Vec3(1, 0, 0)] - //@ts-expect-error - const delta = cursorBlock.intersect.minus(cursorBlock.position) - - if (bot.heldItem) { - //@ts-expect-error todo - bot._placeBlockWithOptions(cursorBlock, vecArray[cursorBlock.face], { delta, forceLook: 'ignore' }).catch(console.warn) - } else { - // https://discord.com/channels/413438066984747026/413438150594265099/1198724637572477098 - const oldLookAt = bot.lookAt - //@ts-expect-error - bot.lookAt = (pos) => { } - //@ts-expect-error - // TODO it still must 1. fire block place 2. swing arm (right) - bot.activateBlock(cursorBlock, vecArray[cursorBlock.face], delta).finally(() => { - bot.lookAt = oldLookAt - }).catch(console.warn) - } - viewer.world.changeHandSwingingState(true, false) - viewer.world.changeHandSwingingState(false, false) - } else if (!stop) { - const offhand = activate ? false : activatableItems(bot.inventory.slots[45]?.name ?? '') - bot.activateItem(offhand) // todo offhand - const item = offhand ? bot.inventory.slots[45] : bot.heldItem - if (item) { - customEvents.emit('activateItem', item, offhand ? 45 : bot.quickBarSlot, offhand) - } - playerState.startUsingItem() - itemBeingUsed.name = (offhand ? bot.inventory.slots[45]?.name : bot.heldItem?.name) ?? null - itemBeingUsed.hand = offhand ? 1 : 0 - } - this.lastBlockPlaced = 0 - } - // stop using activated item (cancel) - if (itemBeingUsed.name && !this.buttons[2]) { - itemBeingUsed.name = null - // "only foods and bow can be deactivated" - not true, shields also can be deactivated and client always sends this - // if (bot.heldItem && (loadedData.foodsArray.map((f) => f.name).includes(bot.heldItem.name) || bot.heldItem.name === 'bow')) { - bot.deactivateItem() - playerState.stopUsingItem() - // } - } - - // Stop break - if ((!this.buttons[0] && this.lastButtons[0]) || cursorChanged) { - try { - bot.stopDigging() // this shouldnt throw anything... - } catch (e) { } // to be reworked in mineflayer, then remove the try here - } - // We stopped breaking - if ((!this.buttons[0] && this.lastButtons[0])) { - this.lastDugBlock = null - this.breakStartTime = undefined - this.debugDigStatus = 'cancelled' - this.stopBreakAnimation() - } - - const onGround = bot.entity.onGround || bot.game.gameMode === 'creative' - this.prevOnGround ??= onGround // todo this should be fixed in mineflayer to involve correct calculations when this changes as this is very important when mining straight down // todo this should be fixed in mineflayer to involve correct calculations when this changes as this is very important when mining straight down // todo this should be fixed in mineflayer to involve correct calculations when this changes as this is very important when mining straight down - // Start break - // todo last check doesnt work as cursorChanged happens once (after that check is false) - if ( - this.buttons[0] - ) { - if (cursorBlockDiggable - && (!this.lastButtons[0] || ((cursorChanged || (this.lastDugBlock && !this.lastDugBlock.equals(cursorBlock!.position))) && Date.now() - (this.lastDigged ?? 0) > 300) || onGround !== this.prevOnGround) - && onGround) { - this.lastDugBlock = null - this.debugDigStatus = 'breaking' - this.currentDigTime = bot.digTime(cursorBlockDiggable) - this.breakStartTime = performance.now() - const vecArray = [new Vec3(0, -1, 0), new Vec3(0, 1, 0), new Vec3(0, 0, -1), new Vec3(0, 0, 1), new Vec3(-1, 0, 0), new Vec3(1, 0, 0)] - bot.dig( - //@ts-expect-error - cursorBlockDiggable, 'ignore', vecArray[cursorBlockDiggable.face] - ).catch((err) => { - if (err.message === 'Digging aborted') return - throw err - }) - customEvents.emit('digStart') - this.lastDigged = Date.now() - viewer.world.changeHandSwingingState(true, false) - } else if (performance.now() - this.lastSwing > 200) { - bot.swingArm('right') - this.lastSwing = performance.now() - } - } - if (!this.buttons[0] && this.lastButtons[0]) { - viewer.world.changeHandSwingingState(false, false) - } - this.prevOnGround = onGround - - // Show cursor - const allShapes = [...cursorBlock?.shapes ?? [], ...cursorBlock?.['interactionShapes'] ?? []] - if (cursorBlock) { - // BREAK MESH - // union of all values - const breakShape = allShapes.reduce((acc, cur) => { - return [ - Math.min(acc[0], cur[0]), - Math.min(acc[1], cur[1]), - Math.min(acc[2], cur[2]), - Math.max(acc[3], cur[3]), - Math.max(acc[4], cur[4]), - Math.max(acc[5], cur[5]) - ] - }) - const { position, width, height, depth } = getDataFromShape(breakShape) - this.blockBreakMesh.scale.set(width * 1.001, height * 1.001, depth * 1.001) - position.add(cursorBlock.position) - this.blockBreakMesh.position.set(position.x, position.y, position.z) - } - - // Show break animation - if (cursorBlockDiggable && this.breakStartTime && bot.game.gameMode !== 'creative') { - const elapsed = performance.now() - this.breakStartTime - const time = bot.digTime(cursorBlockDiggable) - if (time !== this.currentDigTime) { - console.warn('dig time changed! cancelling!', time, 'from', this.currentDigTime) // todo - try { bot.stopDigging() } catch { } - } - const state = Math.floor((elapsed / time) * 10) - if (state !== this.prevBreakState) { - this.setBreakState(cursorBlockDiggable, Math.min(state, 9)) - } - this.prevBreakState = state - } else { - this.blockBreakMesh.visible = false - } - - // Update state - if (cursorChanged) { - viewer.world.setHighlightCursorBlock(cursorBlock?.position ?? null, allShapes.map(shape => { - return getDataFromShape(shape) - })) - } - this.lastButtons[0] = this.buttons[0] - this.lastButtons[1] = this.buttons[1] - this.lastButtons[2] = this.buttons[2] - } - - setBreakState (block: Block, stage: number) { - this.currentBreakBlock = { block, stage } - this.blockBreakMesh.visible = true - //@ts-expect-error - this.blockBreakMesh.material.map = this.breakTextures[stage] ?? this.breakTextures.at(-1) - //@ts-expect-error - this.blockBreakMesh.material.needsUpdate = true - } - - stopBreakAnimation () { - this.currentBreakBlock = null - this.blockBreakMesh.visible = false - } -} - -const getDataFromShape = (shape) => { - const width = shape[3] - shape[0] - const height = shape[4] - shape[1] - const depth = shape[5] - shape[2] - const centerX = (shape[3] + shape[0]) / 2 - const centerY = (shape[4] + shape[1]) / 2 - const centerZ = (shape[5] + shape[2]) / 2 - const position = new Vec3(centerX, centerY, centerZ) - return { position, width, height, depth } -} - -// Blocks that can be interacted with in adventure mode -const activatableBlockPatterns = [ - // Containers - /^(chest|barrel|hopper|dispenser|dropper)$/, - /^.*shulker_box$/, - /^.*(furnace|smoker)$/, - /^(brewing_stand|beacon)$/, - // Crafting - /^.*table$/, - /^(grindstone|stonecutter|loom)$/, - /^.*anvil$/, - // Redstone - /^(lever|repeater|comparator|daylight_detector|observer|note_block|jukebox|bell)$/, - // Buttons - /^.*button$/, - // Doors and trapdoors - /^.*door$/, - /^.*trapdoor$/, - // Functional blocks - /^(enchanting_table|lectern|composter|respawn_anchor|lodestone|conduit)$/, - /^.*bee.*$/, - // Beds - /^.*bed$/, - // Misc - /^(cake|decorated_pot|crafter|trial_spawner|vault)$/ -] - -function isBlockActivatable (blockName: string) { - return activatableBlockPatterns.some(pattern => pattern.test(blockName)) -} - -function isLookingAtActivatableBlock (block: Block) { - return isBlockActivatable(block.name) -} - -export const getEntityCursor = () => { - const entity = bot.nearestEntity((e) => { - if (e.position.distanceTo(bot.entity.position) <= (bot.game.gameMode === 'creative' ? 5 : 3)) { - const dir = getViewDirection(bot.entity.pitch, bot.entity.yaw) - const { width, height } = e - const { x: eX, y: eY, z: eZ } = e.position - const { x: bX, y: bY, z: bZ } = bot.entity.position - const box = new THREE.Box3( - new THREE.Vector3(eX - width / 2, eY, eZ - width / 2), - new THREE.Vector3(eX + width / 2, eY + height, eZ + width / 2) - ) - - const r = new THREE.Raycaster( - new THREE.Vector3(bX, bY + 1.52, bZ), - new THREE.Vector3(dir.x, dir.y, dir.z) - ) - const int = r.ray.intersectBox(box, new THREE.Vector3(eX, eY, eZ)) - return int !== null - } - - return false - }) - return entity -} - -const worldInteraction = new WorldInteraction() -globalThis.worldInteraction = worldInteraction -export default worldInteraction From 10f17063c06a5c45106c1cec01af9b0a4727ac51 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 14:19:38 +0300 Subject: [PATCH 297/753] fix: fix whole pipeline of rendering custom items from rp: add them to atlas and update texture propertly. align behavior blocks vs items and gui vs hand/floor --- renderer/viewer/lib/viewer.ts | 7 +---- renderer/viewer/lib/worldrendererCommon.ts | 12 ++++++-- src/index.ts | 17 ++++-------- src/inventoryWindows.ts | 32 +++++++++------------- src/mineflayer/items.ts | 1 + src/resourcePack.ts | 23 +++++++++++----- src/resourcesManager.ts | 23 ++++++++++++++++ 7 files changed, 70 insertions(+), 45 deletions(-) create mode 100644 src/resourcesManager.ts diff --git a/renderer/viewer/lib/viewer.ts b/renderer/viewer/lib/viewer.ts index 7b951438..941f2182 100644 --- a/renderer/viewer/lib/viewer.ts +++ b/renderer/viewer/lib/viewer.ts @@ -86,12 +86,7 @@ export class Viewer { console.log('[viewer] Using version:', userVersion, 'textures:', texturesVersion) this.entities.clear() // this.primitives.clear() - return this.world.setVersion(userVersion, texturesVersion).then(async () => { - return new THREE.TextureLoader().loadAsync(this.world.itemsAtlasParser!.latestImage) - }).then((texture) => { - this.entities.itemsTexture = texture - this.world.renderUpdateEmitter.emit('itemsTextureDownloaded') - }) + return this.world.setVersion(userVersion, texturesVersion) } addColumn (x, z, chunk, isLightUpdate = false) { diff --git a/renderer/viewer/lib/worldrendererCommon.ts b/renderer/viewer/lib/worldrendererCommon.ts index 3ca100a2..a54f9fe8 100644 --- a/renderer/viewer/lib/worldrendererCommon.ts +++ b/renderer/viewer/lib/worldrendererCommon.ts @@ -365,6 +365,7 @@ export abstract class WorldRendererCommon } const customBlockTextures = Object.keys(this.customTextures.blocks?.textures ?? {}) + const customItemTextures = Object.keys(this.customTextures.items?.textures ?? {}) console.time('createBlocksAtlas') const { atlas: blocksAtlas, canvas: blocksCanvas } = await blocksAssetsParser.makeNewAtlas(this.texturesVersion ?? this.version ?? 'latest', (textureName) => { const texture = this.customTextures?.blocks?.textures[textureName] @@ -376,7 +377,7 @@ export abstract class WorldRendererCommon const texture = this.customTextures?.items?.textures[textureName] if (!texture) return return texture - }, this.customTextures?.items?.tileSize) + }, this.customTextures?.items?.tileSize, undefined, customItemTextures) console.timeEnd('createItemsAtlas') this.blocksAtlasParser = new AtlasParser({ latest: blocksAtlas }, blocksCanvas.toDataURL()) this.itemsAtlasParser = new AtlasParser({ latest: itemsAtlas }, itemsCanvas.toDataURL()) @@ -417,8 +418,15 @@ export abstract class WorldRendererCommon config: this.mesherConfig, }) } + const itemsTexture = await new THREE.TextureLoader().loadAsync(this.itemsAtlasParser.latestImage) + itemsTexture.magFilter = THREE.NearestFilter + itemsTexture.minFilter = THREE.NearestFilter + itemsTexture.flipY = false + viewer.entities.itemsTexture = itemsTexture + this.renderUpdateEmitter.emit('textureDownloaded') - console.log('texture loaded') + this.renderUpdateEmitter.emit('itemsTextureDownloaded') + console.log('textures loaded') } async downloadDebugAtlas (isItems = false) { diff --git a/src/index.ts b/src/index.ts index beb86aea..8e586a8f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,7 @@ import './mineflayer/java-tester/index' import './external' import { getServerInfo } from './mineflayer/mc-protocol' import { onGameLoad, renderSlot } from './inventoryWindows' -import { RenderItem } from './mineflayer/items' +import { GeneralInputItem, RenderItem } from './mineflayer/items' import initCollisionShapes from './getCollisionInteractionShapes' import protocolMicrosoftAuth from 'minecraft-protocol/src/client/microsoftAuth' import microsoftAuthflow from './microsoftAuthflow' @@ -114,6 +114,7 @@ import { LocalServer } from './customServer' import { startLocalReplayServer } from './packetsReplay/replayPackets' import { localRelayServerPlugin } from './mineflayer/plugins/packetsRecording' import { createFullScreenProgressReporter } from './core/progressReporter' +import { getItemModelName } from './resourcesManager' window.debug = debug window.THREE = THREE @@ -181,19 +182,13 @@ viewer.entities.getItemUv = (item, specificProps) => { const name = typeof idOrName === 'number' ? loadedData.items[idOrName]?.name : idOrName if (!name) throw new Error(`Item not found: ${idOrName}`) - const itemSelector = playerState.getItemSelector({ - ...specificProps - }) - const model = getItemDefinition(viewer.world.itemsDefinitionsStore, { + const model = getItemModelName({ + ...item, name, - version: viewer.world.texturesVersion!, - properties: itemSelector - })?.model ?? name + } as GeneralInputItem, specificProps) const renderInfo = renderSlot({ - ...item, - nbt: null, - name: model, + modelName: model, }, false, true) if (!renderInfo) throw new Error(`Failed to get render info for item ${name}`) diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 471f5be4..d473f5be 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -20,6 +20,7 @@ import { currentScaling } from './scaleInterface' import { getItemDescription } from './itemsDescriptions' import { MessageFormatPart } from './chatUtils' import { GeneralInputItem, getItemMetadata, getItemNameRaw, RenderItem } from './mineflayer/items' +import { getItemModelName } from './resourcesManager' const loadedImagesCache = new Map() const cleanLoadedImagesCache = () => { @@ -174,14 +175,18 @@ const getImage = ({ path = undefined as string | undefined, texture = undefined return loadedImagesCache.get(loadPath) } -export const renderSlot = (slot: GeneralInputItem, debugIsQuickbar = false, fullBlockModelSupport = false): { +export type ResolvedItemModelRender = { + modelName: string, +} + +export const renderSlot = (model: ResolvedItemModelRender, debugIsQuickbar = false, fullBlockModelSupport = false): { texture: string, blockData?: Record & { resolvedModel: BlockModel }, scale?: number, slice?: number[], modelName?: string } | undefined => { - let itemModelName = slot.name + let itemModelName = model.modelName const originalItemName = itemModelName const isItem = loadedData.itemsByName[itemModelName] @@ -190,15 +195,12 @@ export const renderSlot = (slot: GeneralInputItem, debugIsQuickbar = false, full // #endregion - const { customModel } = getItemMetadata(slot) - if (customModel) { - itemModelName = customModel - } - let itemTexture try { assertDefined(viewer.world.itemsRenderer) - itemTexture = viewer.world.itemsRenderer.getItemTexture(itemModelName, {}, false, fullBlockModelSupport) ?? viewer.world.itemsRenderer.getItemTexture('item/missing_texture')! + itemTexture = + viewer.world.itemsRenderer.getItemTexture(itemModelName, {}, false, fullBlockModelSupport) + ?? viewer.world.itemsRenderer.getItemTexture('item/missing_texture')! } catch (err) { inGameError(`Failed to render item ${itemModelName} (original: ${originalItemName}) on ${bot.version} (resourcepack: ${options.enabledResourcepack}): ${err.stack}`) itemTexture = viewer.world.itemsRenderer!.getItemTexture('block/errored')! @@ -228,23 +230,15 @@ const getItemName = (slot: Item | RenderItem | null) => { return text.join('') } -export const renderSlotExternal = (slot) => { - const data = renderSlot(slot) - if (!data) return - return { - imageDataUrl: data.texture === 'invsprite' ? undefined : getImage({ path: data.texture })?.src, - sprite: data.slice && data.texture !== 'invsprite' ? data.slice.map(x => x * 2) : data.slice, - displayName: getItemName(slot) ?? slot.displayName, - } -} - const mapSlots = (slots: Array) => { return slots.map((slot, i) => { // todo stateid if (!slot) return try { - const slotCustomProps = renderSlot(slot, i === bot.inventory.hotbarStart + bot.quickBarSlot) + const debugIsQuickbar = i === bot.inventory.hotbarStart + bot.quickBarSlot + const modelName = getItemModelName(slot, { 'minecraft:display_context': 'gui', }) + const slotCustomProps = renderSlot({ modelName }, debugIsQuickbar) const itemCustomName = getItemName(slot) Object.assign(slot, { ...slotCustomProps, displayName: itemCustomName ?? slot.displayName }) //@ts-expect-error diff --git a/src/mineflayer/items.ts b/src/mineflayer/items.ts index 2f1b2f8d..bb437ef4 100644 --- a/src/mineflayer/items.ts +++ b/src/mineflayer/items.ts @@ -19,6 +19,7 @@ export type RenderItem = Pick & { components?: RenderSlotComponent[], displayName?: string + modelResolved?: boolean } type JsonString = string diff --git a/src/resourcePack.ts b/src/resourcePack.ts index aef23e8f..6e9c28a4 100644 --- a/src/resourcePack.ts +++ b/src/resourcePack.ts @@ -314,7 +314,8 @@ export const getResourcepackTiles = async (type: 'blocks' | 'items' | 'armor', e const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) => { viewer.world.customBlockStates = {} viewer.world.customModels = {} - const usedTextures = new Set() + const usedBlockTextures = new Set() + const usedItemTextures = new Set() const basePath = await getActiveResourcepackBasePath() if (!basePath) return progressReporter.beginStage('read-resource-pack-blockstates-and-models', 'Reading resource pack blockstates and models') @@ -328,8 +329,9 @@ const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) = if (file.endsWith('.json')) { const contents = await fs.promises.readFile(filePath, 'utf8') let name = file.replace('.json', '') + const isBlock = path.endsWith('block') if (type === 'models') { - name = `${path.endsWith('block') ? 'block' : 'item'}/${name}` + name = `${isBlock ? 'block' : 'item'}/${name}` } const parsed = JSON.parse(contents) if (namespaceDir === 'minecraft') { @@ -341,7 +343,11 @@ const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) = if (typeof texturePath !== 'string') continue if (texturePath.startsWith('#')) continue if (!texturePath.includes(':')) texturePath = `minecraft:${texturePath}` - usedTextures.add(texturePath as string) + if (isBlock) { + usedBlockTextures.add(texturePath as string) + } else { + usedItemTextures.add(texturePath as string) + } } } } @@ -369,7 +375,10 @@ const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) = viewer.world.customBlockStates = undefined viewer.world.customModels = undefined } - return { usedTextures } + return { + usedBlockTextures, + usedItemTextures + } } const downloadAndUseResourcePack = async (url: string, progressReporter: ProgressReporter): Promise => { @@ -517,9 +526,9 @@ const updateTextures = async (progressReporter = createConsoleLogProgressReporte const origBlocksFiles = Object.keys(viewer.world.sourceData.blocksAtlases.latest.textures) const origItemsFiles = Object.keys(viewer.world.sourceData.itemsAtlases.latest.textures) const origArmorFiles = Object.keys(armorTextures) - const { usedTextures: extraBlockTextures = new Set() } = await prepareBlockstatesAndModels(progressReporter) ?? {} - const blocksData = await getResourcepackTiles('blocks', [...origBlocksFiles, ...extraBlockTextures], progressReporter) - const itemsData = await getResourcepackTiles('items', origItemsFiles, progressReporter) + const { usedBlockTextures, usedItemTextures } = await prepareBlockstatesAndModels(progressReporter) ?? {} + const blocksData = await getResourcepackTiles('blocks', [...origBlocksFiles, ...usedBlockTextures ?? []], progressReporter) + const itemsData = await getResourcepackTiles('items', [...origItemsFiles, ...usedItemTextures ?? []], progressReporter) const armorData = await getResourcepackTiles('armor', origArmorFiles, progressReporter) await updateAllReplacableTextures() viewer.world.customTextures = {} diff --git a/src/resourcesManager.ts b/src/resourcesManager.ts new file mode 100644 index 00000000..fd92e58b --- /dev/null +++ b/src/resourcesManager.ts @@ -0,0 +1,23 @@ +import { Item } from 'prismarine-item' +import { ItemSpecificContextProperties } from 'renderer/viewer/lib/basePlayerState' +import { getItemDefinition } from 'mc-assets/dist/itemDefinitions' +import { playerState } from './mineflayer/playerState' +import { GeneralInputItem, getItemMetadata } from './mineflayer/items' + +export const getItemModelName = (item: GeneralInputItem, specificProps: ItemSpecificContextProperties) => { + let itemModelName = item.name + const { customModel } = getItemMetadata(item) + if (customModel) { + itemModelName = customModel + } + + const itemSelector = playerState.getItemSelector({ + ...specificProps + }) + const model = getItemDefinition(viewer.world.itemsDefinitionsStore, { + name: itemModelName, + version: viewer.world.texturesVersion!, + properties: itemSelector + })?.model ?? itemModelName + return model +} From b0da1e41d690d081636d320b8d29a1a80da58991 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 15:31:25 +0300 Subject: [PATCH 298/753] fix: fix crashes on packets logging recording fix: make replay panel minmizable --- package.json | 4 +- pnpm-lock.yaml | 22 ++-- src/appParams.ts | 3 +- src/core/progressReporter.ts | 1 + src/index.ts | 3 +- src/optionsStorage.ts | 2 +- src/react/PauseScreen.tsx | 9 +- src/react/ReplayPanel.tsx | 119 +++++++++++++++------ src/react/components/replay/PacketList.tsx | 8 +- 9 files changed, 114 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index b0c5ee20..7cacae56 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "mojangson": "^2.0.4", "net-browserify": "github:zardoy/prismarinejs-net-browserify", "node-gzip": "^1.1.2", - "mcraft-fun-mineflayer": "^0.1.7", + "mcraft-fun-mineflayer": "^0.1.8", "peerjs": "^1.5.0", "pixelarticons": "^1.8.1", "pretty-bytes": "^6.1.1", @@ -145,7 +145,7 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", - "mineflayer-mouse": "^0.0.4", + "mineflayer-mouse": "^0.0.5", "mc-assets": "^0.2.37", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be7ce2e5..dbbac60e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,8 +135,8 @@ importers: specifier: ^4.17.21 version: 4.17.21 mcraft-fun-mineflayer: - specifier: ^0.1.7 - version: 0.1.7(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) + specifier: ^0.1.8 + version: 0.1.8(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) minecraft-data: specifier: 3.83.1 version: 3.83.1 @@ -359,8 +359,8 @@ importers: specifier: github:zardoy/mineflayer version: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13) mineflayer-mouse: - specifier: ^0.0.4 - version: 0.0.4(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + specifier: ^0.0.5 + version: 0.0.5(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -6575,9 +6575,9 @@ packages: resolution: {integrity: sha512-49tk3shwxsDoV0PrrbORZEKg613vUQPULgusWjXNl8JEma5y41LEo57D6q4aHliXsV3Gb9ThrkFf6hIb0WlY1Q==} engines: {node: '>=18.0.0'} - mcraft-fun-mineflayer@0.1.7: - resolution: {integrity: sha512-DJPpp1YFwztoscdIOwfqBV9lbotva621F9GEep3BlqG3l06UdTzX2ElkvwyTR0IurFFBX/YKsNfxwL5WtLytgw==} - version: 0.1.7 + mcraft-fun-mineflayer@0.1.8: + resolution: {integrity: sha512-jyJTihNHfeToBPwVs3QMKBlVcaCABJ25YN2eoIBQEVTRVFzaXh13XRpElphLzTMj1Q5XFYqufHtMoR4tsb08qQ==} + version: 0.1.8 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: '@roamhq/wrtc': '*' @@ -6801,8 +6801,8 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824} version: 1.2.0 - mineflayer-mouse@0.0.4: - resolution: {integrity: sha512-55GqQhJWCXnOnm30uOjtI7nsawPb0kA3cAv6a5n1NJjTWFR6hzMkiRT6xGLYrvYhdf6Er3nsE2Ok/Aysa/jtFQ==} + mineflayer-mouse@0.0.5: + resolution: {integrity: sha512-0r/AOGTq+wZH9vrBcW93jH2dGRSlwlO6xc1Z67VJUFlZZ8oBefAOpiZq7LIGc7ROVbpcKEKjROdNv/iCFmzXYA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} mineflayer-pathfinder@2.4.4: @@ -17372,7 +17372,7 @@ snapshots: apl-image-packer: 1.1.0 zod: 3.24.1 - mcraft-fun-mineflayer@0.1.7(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): + mcraft-fun-mineflayer@0.1.8(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): dependencies: '@zardoy/flying-squid': 0.0.49(encoding@0.1.13) exit-hook: 2.2.1 @@ -17742,7 +17742,7 @@ snapshots: - encoding - supports-color - mineflayer-mouse@0.0.4(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + mineflayer-mouse@0.0.5(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): dependencies: change-case: 5.4.4 debug: 4.4.0(supports-color@8.1.1) diff --git a/src/appParams.ts b/src/appParams.ts index 50fbf055..962eb168 100644 --- a/src/appParams.ts +++ b/src/appParams.ts @@ -63,7 +63,8 @@ type AppQsParamsArrayTransformed = { [k in keyof AppQsParamsArray]: string[] } -const initialAppConfig = process.env.INLINED_APP_CONFIG as AppConfig ?? {} +globalThis.process ??= {} as any +const initialAppConfig = process?.env?.INLINED_APP_CONFIG as AppConfig ?? {} export const appQueryParams = new Proxy({} as AppQsParams, { get (target, property) { diff --git a/src/core/progressReporter.ts b/src/core/progressReporter.ts index db1958da..e2bdf5bc 100644 --- a/src/core/progressReporter.ts +++ b/src/core/progressReporter.ts @@ -138,6 +138,7 @@ export const createFullScreenProgressReporter = (): ProgressReporter => { setLoadingScreenStatus(message) }, end () { + if (appStatusState.isError) return fullScreenReporters.splice(fullScreenReporters.indexOf(reporter), 1) if (fullScreenReporters.length === 0) { setLoadingScreenStatus(undefined) diff --git a/src/index.ts b/src/index.ts index 8e586a8f..7269781d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -322,6 +322,7 @@ export async function connect (connectOptions: ConnectOptions) { if (ended) return ended = true viewer.resetAll() + progress.end() localServer = window.localServer = window.server = undefined gameAdditionalState.viewerConnection = false @@ -692,6 +693,7 @@ export async function connect (connectOptions: ConnectOptions) { } catch (err) { handleError(err) } + if (!bot) return if (connectOptions.server) { bot.loadPlugin(ping) @@ -700,7 +702,6 @@ export async function connect (connectOptions: ConnectOptions) { if (!localReplaySession) { bot.loadPlugin(localRelayServerPlugin) } - if (!bot) return const p2pConnectTimeout = p2pMultiplayer ? setTimeout(() => { throw new UserError('Spawn timeout. There might be error on the other side, check console.') }, 20_000) : undefined diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index b88b71c4..904b0a29 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -8,7 +8,7 @@ import { appQueryParamsArray } from './appParams' import type { AppConfig } from './globalState' const isDev = process.env.NODE_ENV === 'development' -const initialAppConfig = process.env.INLINED_APP_CONFIG as AppConfig ?? {} +const initialAppConfig = process.env?.INLINED_APP_CONFIG as AppConfig ?? {} const defaultOptions = { renderDistance: 3, keepChunksDistance: 1, diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index 4c4dec03..35d873ea 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -232,14 +232,15 @@ export default () => { if (pauseLinksConfig) { for (const [i, row] of pauseLinksConfig.entries()) { const rowButtons: React.ReactNode[] = [] - for (const button of row) { + for (const [k, button] of row.entries()) { + const key = `${i}-${k}` const style = { width: (204 / row.length - (row.length > 1 ? 4 : 0)) + 'px' } if (button.type === 'discord') { - rowButtons.push() + rowButtons.push() } else if (button.type === 'github') { - rowButtons.push() + rowButtons.push() } else if (button.type === 'url' && button.text) { - rowButtons.push() + rowButtons.push() } } pauseLinks.push(
{rowButtons}
) diff --git a/src/react/ReplayPanel.tsx b/src/react/ReplayPanel.tsx index 59950555..fd4082af 100644 --- a/src/react/ReplayPanel.tsx +++ b/src/react/ReplayPanel.tsx @@ -41,31 +41,100 @@ export default function ReplayPanel ({ style }: Props) { const [filter, setFilter] = useState(defaultFilter) + const [isMinimized, setIsMinimized] = useState(false) const { filtered: filteredPackets, hiddenCount } = filterPackets(packets.slice(-500), filter) useEffect(() => { onFilterChange(filter) }, [filter, onFilterChange]) + const handlePlayPauseClick = () => { + if (isMinimized) { + setIsMinimized(false) + } else { + onPlayPause?.(!isPlaying) + } + } + + const playPauseButton = ( + + ) + + const baseContainerStyle = { + position: 'fixed', + top: 18, + right: 0, + zIndex: 1000, + background: DARK_COLORS.bg, + padding: '16px', + borderRadius: '0 0 8px 0', + boxShadow: '0 2px 8px rgba(0,0,0,0.3)', + display: 'flex', + flexDirection: 'column', + gap: '12px', + color: DARK_COLORS.text, + ...style + } as const + + if (isMinimized) { + return ( +
+ {playPauseButton} +
+ ) + } + return (
-
{replayName || 'Unnamed Replay'}
+
+
{replayName || 'Unnamed Replay'}
+ +
+
Integrated server emulation. Testing client...
- - + {playPauseButton}
diff --git a/src/react/components/replay/PacketList.tsx b/src/react/components/replay/PacketList.tsx index 484c42c9..5183bf3f 100644 --- a/src/react/components/replay/PacketList.tsx +++ b/src/react/components/replay/PacketList.tsx @@ -1,4 +1,5 @@ import { useRef, useState } from 'react' +import { processPacketDataForLogging } from 'mcraft-fun-mineflayer/build/packetsLogger' import { PacketData } from '../../ReplayPanel' import { useScrollBehavior } from '../../hooks/useScrollBehavior' import { ClientOnMap } from '../../../generatedServerPackets' @@ -12,6 +13,7 @@ const formatters: Record string> = { const blockEntitiesCount = data.blockEntities?.length return `x:${data.x} z:${data.z} C:${sizeOfChunk} E:${blockEntitiesCount}` }, + default: (data) => processPacketDataForLogging(data) } const getPacketIcon = (name: string): string => { @@ -115,7 +117,7 @@ export default function PacketList ({ packets, filter, maxHeight = 300 }: Props) {packet.name} - {formatters[packet.name]?.(packet.data) ?? JSON.stringify(packet.data)} + {formatters[packet.name]?.(packet.data) ?? formatters.default(packet.data)}
{expandedPacket === packet.position && ( @@ -123,14 +125,14 @@ export default function PacketList ({ packets, filter, maxHeight = 300 }: Props)
Data:
-                        {JSON.stringify(packet.data, null, 2)}
+                        {JSON.stringify(JSON.parse(formatters.default(packet.data)), null, 2)}
                       
{packet.actualVersion && (
Actual Version:
-                          {JSON.stringify(packet.actualVersion, null, 2)}
+                          {JSON.stringify(JSON.parse(formatters.default(packet.actualVersion)), null, 2)}
                         
)} From 2619e5da89fc270297d3cc379758a95e13e01e0a Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 17:42:01 +0300 Subject: [PATCH 299/753] fix: was not possible to click notification, make error routing more strict & obvious --- src/core/progressReporter.ts | 25 ++++++++++++++++++++++++- src/react/NotificationProvider.tsx | 11 +++++++---- src/reactUi.tsx | 2 +- src/resourcePack.ts | 4 +++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/core/progressReporter.ts b/src/core/progressReporter.ts index e2bdf5bc..6ef6044f 100644 --- a/src/core/progressReporter.ts +++ b/src/core/progressReporter.ts @@ -13,12 +13,14 @@ export interface ProgressReporter { setMessage (message: string): void - end (): void + end(): void + error(message: string): void } interface ReporterDisplayImplementation { setMessage (message: string): void end (): void + error(message: string): void } interface StageInfo { @@ -124,6 +126,10 @@ const createProgressReporter = (implementation: ReporterDisplayImplementation): get currentMessage () { return currentMessage + }, + + error (message: string): void { + implementation.error(message) } } @@ -145,6 +151,11 @@ export const createFullScreenProgressReporter = (): ProgressReporter => { } else { setLoadingScreenStatus(fullScreenReporters.at(-1)!.currentMessage) } + }, + + error (message: string): void { + if (appStatusState.isError) return + setLoadingScreenStatus(message, true) } }) fullScreenReporters.push(reporter) @@ -162,6 +173,10 @@ export const createNotificationProgressReporter = (endMessage?: string): Progres } else { hideNotification() } + }, + + error (message: string): void { + showNotification(message, '', true, '', undefined, true) } }) } @@ -173,6 +188,10 @@ export const createConsoleLogProgressReporter = (): ProgressReporter => { }, end () { console.log('done') + }, + + error (message: string): void { + console.error(message) } }) } @@ -191,6 +210,10 @@ export const createWrappedProgressReporter = (reporter: ProgressReporter, messag if (message) { reporter.endStage(stage) } + }, + + error (message: string): void { + reporter.error(message) } }) } diff --git a/src/react/NotificationProvider.tsx b/src/react/NotificationProvider.tsx index 4c9661e0..6460c0e9 100644 --- a/src/react/NotificationProvider.tsx +++ b/src/react/NotificationProvider.tsx @@ -60,10 +60,13 @@ export default () => { // }, []) const scale = useAppScale() - return
+ return
{ +
@@ -214,7 +215,6 @@ const App = () => {
-
diff --git a/src/resourcePack.ts b/src/resourcePack.ts index 6e9c28a4..fd01168a 100644 --- a/src/resourcePack.ts +++ b/src/resourcePack.ts @@ -392,7 +392,7 @@ const downloadAndUseResourcePack = async (url: string, progressReporter: Progres const response = await fetch(url).catch((err) => { console.log(`Ensure server on ${url} support CORS which is not required for regular client, but is required for the web client`) console.error(err) - showNotification('Failed to download resource pack: ' + err.message) + progressReporter.error('Failed to download resource pack: ' + err.message) }) console.timeEnd('downloadServerResourcePack') if (!response) return @@ -425,6 +425,8 @@ const downloadAndUseResourcePack = async (url: string, progressReporter: Progres console.error(err) showNotification('Failed to install resource pack: ' + err.message) }) + } catch (err) { + progressReporter.error('Could not install resource pack: ' + err.message) } finally { progressReporter.endStage('download-resource-pack') resourcePackState.isServerInstalling = false From 2a8f5140957be1e832a8b480c262cec5963f514e Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 18:24:06 +0300 Subject: [PATCH 300/753] add build zip workflow --- .github/workflows/build-zip.yml | 43 +++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/build-zip.yml diff --git a/.github/workflows/build-zip.yml b/.github/workflows/build-zip.yml new file mode 100644 index 00000000..9f8a6292 --- /dev/null +++ b/.github/workflows/build-zip.yml @@ -0,0 +1,43 @@ +name: Build and Bundle + +on: + workflow_dispatch: + +jobs: + build-and-bundle: + runs-on: ubuntu-latest + permissions: write-all + steps: + - name: Checkout repository + uses: actions/checkout@master + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install dependencies + run: pnpm install + + - name: Build project + run: pnpm build + + - name: Bundle server.js + run: | + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js + + - name: Create distribution package + run: | + mkdir -p package + cp -r dist package/ + cp bundled-server.js package/server.js + cd package + zip -r ../dist-package.zip . + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: dist-package + path: dist-package.zip From 874cafc75e605688e9a7d745eed09859ff820154 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 18:42:08 +0300 Subject: [PATCH 301/753] add self host zip publishing with release --- .github/workflows/build-zip.yml | 10 +++++----- .github/workflows/publish.yml | 31 ++++++++++++++++++++++++------- package.json | 3 +++ server.js | 2 +- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-zip.yml b/.github/workflows/build-zip.yml index 9f8a6292..e6ed334c 100644 --- a/.github/workflows/build-zip.yml +++ b/.github/workflows/build-zip.yml @@ -1,4 +1,4 @@ -name: Build and Bundle +name: Make Self Host Zip on: workflow_dispatch: @@ -26,7 +26,7 @@ jobs: - name: Bundle server.js run: | - pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define=process.env.NODE_ENV="production" - name: Create distribution package run: | @@ -34,10 +34,10 @@ jobs: cp -r dist package/ cp bundled-server.js package/server.js cd package - zip -r ../dist-package.zip . + zip -r ../self-host.zip . - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: dist-package - path: dist-package.zip + name: self-host + path: self-host.zip diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index eb765648..958aa50e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -43,19 +43,36 @@ jobs: with: run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} --prod id: deploy - - run: | - pnpx zardoy-release node --footer "This release URL: ${{ steps.deploy.outputs.stdout }}" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # has possible output: tag - id: release - # has output + # publish to github - run: cp vercel.json .vercel/output/static/vercel.json - uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: .vercel/output/static force_orphan: true + + - name: Build self-host version + run: pnpm build + - name: Bundle server.js + run: | + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define=process.env.NODE_ENV="production" + + - name: Create zip package + run: | + mkdir -p package + cp -r dist package/ + cp bundled-server.js package/server.js + cd package + zip -r ../self-host.zip . + + - run: | + pnpx zardoy-release node --footer "This release URL: ${{ steps.deploy.outputs.stdout }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # has possible output: tag + id: release + + # has output - name: Set publishing config run: pnpm config set '//registry.npmjs.org/:_authToken' "${NODE_AUTH_TOKEN}" env: diff --git a/package.json b/package.json index 7cacae56..0e0aa6a8 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,9 @@ "web", "client" ], + "release": { + "attachReleaseFiles": "self-host.zip" + }, "publish": { "preset": { "publishOnlyIfChanged": true, diff --git a/server.js b/server.js index 2dbb05b3..20e66051 100644 --- a/server.js +++ b/server.js @@ -15,7 +15,7 @@ try { // Create our app const app = express() -const isProd = process.argv.includes('--prod') +const isProd = process.argv.includes('--prod') || process.env.NODE_ENV === 'production' app.use(compression()) app.use(cors()) app.use(netApi({ allowOrigin: '*' })) From 1d4dc0ddaa8fe203197c6c819f0223b551bcaeb9 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 18:45:25 +0300 Subject: [PATCH 302/753] fix define in arg build --- .github/workflows/build-zip.yml | 2 +- .github/workflows/publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-zip.yml b/.github/workflows/build-zip.yml index e6ed334c..2e7e886d 100644 --- a/.github/workflows/build-zip.yml +++ b/.github/workflows/build-zip.yml @@ -26,7 +26,7 @@ jobs: - name: Bundle server.js run: | - pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define=process.env.NODE_ENV="production" + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="production" - name: Create distribution package run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 958aa50e..7e0af8d2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -55,7 +55,7 @@ jobs: run: pnpm build - name: Bundle server.js run: | - pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define=process.env.NODE_ENV="production" + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="production" - name: Create zip package run: | From 4b54be637d1917b87e51f8c8c03938141fbc3bfe Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 18:48:27 +0300 Subject: [PATCH 303/753] ci: adjust esbuild build arg syntax for prod --- .github/workflows/build-zip.yml | 2 +- .github/workflows/publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-zip.yml b/.github/workflows/build-zip.yml index 2e7e886d..cc472476 100644 --- a/.github/workflows/build-zip.yml +++ b/.github/workflows/build-zip.yml @@ -26,7 +26,7 @@ jobs: - name: Bundle server.js run: | - pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="production" + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="'production'" - name: Create distribution package run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7e0af8d2..4fbff15d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -55,7 +55,7 @@ jobs: run: pnpm build - name: Bundle server.js run: | - pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="production" + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="'production'" - name: Create zip package run: | From 1c700aac1e3b7d8592c3a0a60e1f27ad5ad80756 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Tue, 4 Mar 2025 19:00:20 +0300 Subject: [PATCH 304/753] feat(config-json): Only either bundle or load from remote (#291) --- .github/workflows/next-deploy.yml | 2 ++ .github/workflows/preview.yml | 2 ++ .github/workflows/publish.yml | 2 ++ Dockerfile | 5 +-- rsbuild.config.ts | 12 +++++-- scripts/dockerPrepare.mjs | 22 ++++++++++-- src/appConfig.ts | 59 +++++++++++++++++++++++++++++++ src/appParams.ts | 2 +- src/browserfs.ts | 4 +-- src/customChannels.ts | 6 ++-- src/globalState.ts | 41 ++------------------- src/index.ts | 17 +++------ src/optionsStorage.ts | 19 +++++++--- src/panorama.ts | 2 +- src/react/MainMenuRenderApp.tsx | 6 ++-- 15 files changed, 127 insertions(+), 74 deletions(-) create mode 100644 src/appConfig.ts diff --git a/.github/workflows/next-deploy.yml b/.github/workflows/next-deploy.yml index 665abb30..042302a4 100644 --- a/.github/workflows/next-deploy.yml +++ b/.github/workflows/next-deploy.yml @@ -32,6 +32,8 @@ jobs: echo "{\"latestTag\": \"$(git rev-parse --short $GITHUB_SHA)\", \"isCommit\": true}" > assets/release.json - name: Build Project Artifacts run: vercel build --token=${{ secrets.VERCEL_TOKEN }} + env: + CONFIG_JSON_SOURCE: BUNDLED - run: pnpm build-storybook - name: Copy playground files run: | diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 18c80e8c..6408c86a 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -61,6 +61,8 @@ jobs: echo "{\"latestTag\": \"$(git rev-parse --short ${{ github.event.pull_request.head.sha }})\", \"isCommit\": true}" > assets/release.json - name: Build Project Artifacts run: vercel build --token=${{ secrets.VERCEL_TOKEN }} + env: + CONFIG_JSON_SOURCE: BUNDLED - run: pnpm build-storybook - name: Copy playground files run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4fbff15d..5af8abab 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -30,6 +30,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: vercel build --token=${{ secrets.VERCEL_TOKEN }} --prod + env: + CONFIG_JSON_SOURCE: BUNDLED - run: pnpm build-storybook - name: Copy playground files run: | diff --git a/Dockerfile b/Dockerfile index 4769141f..34641353 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,7 @@ RUN npm i -g pnpm@9.0.4 # Build arguments ARG DOWNLOAD_SOUNDS=false ARG DISABLE_SERVICE_WORKER=false +ARG CONFIG_JSON_SOURCE=REMOTE # TODO need flat --no-root-optional RUN node ./scripts/dockerPrepare.mjs RUN pnpm i @@ -22,8 +23,8 @@ RUN if [ "$DOWNLOAD_SOUNDS" = "true" ] ; then node scripts/downloadSoundsMap.mjs # ENTRYPOINT ["pnpm", "run", "run-all"] # only for prod -RUN GITHUB_REPOSITORY=zardoy/minecraft-web-client \ - DISABLE_SERVICE_WORKER=$DISABLE_SERVICE_WORKER \ +RUN DISABLE_SERVICE_WORKER=$DISABLE_SERVICE_WORKER \ + CONFIG_JSON_SOURCE=$CONFIG_JSON_SOURCE \ pnpm run build # ---- Run Stage ---- diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 4147ce7d..875d0e0c 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -25,12 +25,14 @@ const disableServiceWorker = process.env.DISABLE_SERVICE_WORKER === 'true' let releaseTag let releaseLink let releaseChangelog +let githubRepositoryFallback if (fs.existsSync('./assets/release.json')) { const releaseJson = JSON.parse(fs.readFileSync('./assets/release.json', 'utf8')) releaseTag = releaseJson.latestTag releaseLink = releaseJson.isCommit ? `/commit/${releaseJson.latestTag}` : `/releases/${releaseJson.latestTag}` releaseChangelog = releaseJson.changelog?.replace(//, '') + githubRepositoryFallback = releaseJson.repository } const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8')) @@ -41,6 +43,8 @@ if (dev) { configJson.defaultProxy = ':8080' } +const configSource = process.env.CONFIG_JSON_SOURCE || 'REMOTE' + // base options are in ./renderer/rsbuildSharedConfig.ts const appConfig = defineConfig({ html: { @@ -66,13 +70,13 @@ const appConfig = defineConfig({ 'process.env.BUILD_VERSION': JSON.stringify(!dev ? buildingVersion : 'undefined'), 'process.env.MAIN_MENU_LINKS': JSON.stringify(process.env.MAIN_MENU_LINKS), 'process.env.GITHUB_URL': - JSON.stringify(`https://github.com/${process.env.GITHUB_REPOSITORY || `${process.env.VERCEL_GIT_REPO_OWNER}/${process.env.VERCEL_GIT_REPO_SLUG}`}`), + JSON.stringify(`https://github.com/${process.env.GITHUB_REPOSITORY || `${process.env.VERCEL_GIT_REPO_OWNER}/${process.env.VERCEL_GIT_REPO_SLUG}` || githubRepositoryFallback}`), 'process.env.DEPS_VERSIONS': JSON.stringify({}), 'process.env.RELEASE_TAG': JSON.stringify(releaseTag), 'process.env.RELEASE_LINK': JSON.stringify(releaseLink), 'process.env.RELEASE_CHANGELOG': JSON.stringify(releaseChangelog), 'process.env.DISABLE_SERVICE_WORKER': JSON.stringify(disableServiceWorker), - 'process.env.INLINED_APP_CONFIG': JSON.stringify(configJson), + 'process.env.INLINED_APP_CONFIG': JSON.stringify(configSource === 'BUNDLED' ? configJson : null), }, }, server: { @@ -109,7 +113,9 @@ const appConfig = defineConfig({ fs.copyFileSync('./assets/release.json', './dist/release.json') } - fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8') + if (configSource === 'REMOTE') { + fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8') + } if (fs.existsSync('./generated/sounds.js')) { fs.copyFileSync('./generated/sounds.js', './dist/sounds.js') } diff --git a/scripts/dockerPrepare.mjs b/scripts/dockerPrepare.mjs index 37e57d01..62a4f5e4 100644 --- a/scripts/dockerPrepare.mjs +++ b/scripts/dockerPrepare.mjs @@ -4,9 +4,27 @@ import path from 'path' import { fileURLToPath } from 'url' import { execSync } from 'child_process' -// write release tag +// Get repository from git config +const getGitRepository = () => { + try { + const gitConfig = fs.readFileSync('.git/config', 'utf8') + const originUrlMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url = .*?github\.com[:/](.*?)(\.git)?\n/m) + if (originUrlMatch) { + return originUrlMatch[1] + } + } catch (err) { + console.warn('Failed to read git repository from config:', err) + } + return null +} + +// write release tag and repository info const commitShort = execSync('git rev-parse --short HEAD').toString().trim() -fs.writeFileSync('./assets/release.json', JSON.stringify({ latestTag: `${commitShort} (docker)` }), 'utf8') +const repository = getGitRepository() +fs.writeFileSync('./assets/release.json', JSON.stringify({ + latestTag: `${commitShort} (docker)`, + repository +}), 'utf8') const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8')) delete packageJson.optionalDependencies diff --git a/src/appConfig.ts b/src/appConfig.ts new file mode 100644 index 00000000..3d6d8f93 --- /dev/null +++ b/src/appConfig.ts @@ -0,0 +1,59 @@ +import { disabledSettings, options, qsOptions } from './optionsStorage' +import { miscUiState } from './globalState' +import { setLoadingScreenStatus } from './appStatus' + +export type AppConfig = { + // defaultHost?: string + // defaultHostSave?: string + defaultProxy?: string + // defaultProxySave?: string + // defaultVersion?: string + peerJsServer?: string + peerJsServerFallback?: string + promoteServers?: Array<{ ip, description, version? }> + mapsProvider?: string + + appParams?: Record // query string params + + defaultSettings?: Record + forceSettings?: Record + // hideSettings?: Record + allowAutoConnect?: boolean + pauseLinks?: Array>> +} + +export const loadAppConfig = (appConfig: AppConfig) => { + if (miscUiState.appConfig) { + Object.assign(miscUiState.appConfig, appConfig) + } else { + miscUiState.appConfig = appConfig + } + + if (appConfig.forceSettings) { + for (const [key, value] of Object.entries(appConfig.forceSettings)) { + if (value) { + disabledSettings.value.add(key) + // since the setting is forced, we need to set it to that value + if (appConfig.defaultSettings?.[key] && !qsOptions[key]) { + options[key] = appConfig.defaultSettings[key] + } + } else { + disabledSettings.value.delete(key) + } + } + } +} + +export const isBundledConfigUsed = !!process.env.INLINED_APP_CONFIG + +if (isBundledConfigUsed) { + loadAppConfig(process.env.INLINED_APP_CONFIG as AppConfig ?? {}) +} else { + void window.fetch('config.json').then(async res => res.json()).then(c => c, (error) => { + // console.warn('Failed to load optional app config.json', error) + // return {} + setLoadingScreenStatus('Failed to load app config.json', true) + }).then((config: AppConfig) => { + loadAppConfig(config) + }) +} diff --git a/src/appParams.ts b/src/appParams.ts index 962eb168..98d6ff62 100644 --- a/src/appParams.ts +++ b/src/appParams.ts @@ -1,4 +1,4 @@ -import type { AppConfig } from './globalState' +import type { AppConfig } from './appConfig' const qsParams = new URLSearchParams(window.location?.search ?? '') diff --git a/src/browserfs.ts b/src/browserfs.ts index 41608e30..0f4579b8 100644 --- a/src/browserfs.ts +++ b/src/browserfs.ts @@ -41,13 +41,13 @@ browserfs.configure({ throw e2 } showNotification('Failed to access device storage', `Check you have free space. ${e.message}`, true) - miscUiState.appLoaded = true + miscUiState.fsReady = true miscUiState.singleplayerAvailable = false }) return } await updateTexturePackInstalledState() - miscUiState.appLoaded = true + miscUiState.fsReady = true miscUiState.singleplayerAvailable = true }) diff --git a/src/customChannels.ts b/src/customChannels.ts index 35922101..ff0f8a32 100644 --- a/src/customChannels.ts +++ b/src/customChannels.ts @@ -85,15 +85,15 @@ const registeredJeiChannel = () => { [ { name: 'id', - type: 'pstring', + type: ['pstring', { countType: 'i16' }] }, { name: 'categoryTitle', - type: 'pstring', + type: ['pstring', { countType: 'i16' }] }, { name: 'items', - type: 'pstring', + type: ['pstring', { countType: 'i16' }] }, ] ] diff --git a/src/globalState.ts b/src/globalState.ts index 74e6c3ff..0ee8671d 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -5,6 +5,7 @@ import type { WorldWarp } from 'flying-squid/dist/lib/modules/warps' import type { OptionsGroupType } from './optionsGuiScheme' import { appQueryParams } from './appParams' import { options, disabledSettings } from './optionsStorage' +import { AppConfig } from './appConfig' // todo: refactor structure with support of hideNext=false @@ -110,26 +111,6 @@ export const showContextmenu = (items: ContextMenuItem[], { clientX, clientY }) // --- -export type AppConfig = { - // defaultHost?: string - // defaultHostSave?: string - defaultProxy?: string - // defaultProxySave?: string - // defaultVersion?: string - peerJsServer?: string - peerJsServerFallback?: string - promoteServers?: Array<{ ip, description, version? }> - mapsProvider?: string - - appParams?: Record // query string params - - defaultSettings?: Record - forceSettings?: Record - // hideSettings?: Record - allowAutoConnect?: boolean - pauseLinks?: Array>> -} - export const miscUiState = proxy({ currentDisplayQr: null as string | null, currentTouch: null as boolean | null, @@ -144,7 +125,7 @@ export const miscUiState = proxy({ loadedServerIndex: '', /** currently trying to load or loaded mc version, after all data is loaded */ loadedDataVersion: null as string | null, - appLoaded: false, + fsReady: false, singleplayerAvailable: false, usingGamepadInput: false, appConfig: null as AppConfig | null, @@ -152,24 +133,6 @@ export const miscUiState = proxy({ displayFullmap: false }) -export const loadAppConfig = (appConfig: AppConfig) => { - if (miscUiState.appConfig) { - Object.assign(miscUiState.appConfig, appConfig) - } else { - miscUiState.appConfig = appConfig - } - - if (appConfig.forceSettings) { - for (const [key, value] of Object.entries(appConfig.forceSettings)) { - if (value) { - disabledSettings.value.delete(key) - } else { - disabledSettings.value.add(key) - } - } - } -} - export const isGameActive = (foregroundCheck: boolean) => { if (foregroundCheck && activeModalStack.length) return false return miscUiState.gameLoaded diff --git a/src/index.ts b/src/index.ts index 7269781d..b8727395 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import './mineflayer/cameraShake' import './shims/patchShims' import './mineflayer/java-tester/index' import './external' +import './appConfig' import { getServerInfo } from './mineflayer/mc-protocol' import { onGameLoad, renderSlot } from './inventoryWindows' import { GeneralInputItem, RenderItem } from './mineflayer/items' @@ -48,7 +49,6 @@ import initializePacketsReplay from './packetsReplay/packetsReplayLegacy' import { initVR } from './vr' import { - AppConfig, activeModalStack, activeModalStacks, hideModal, @@ -57,7 +57,6 @@ import { miscUiState, showModal, gameAdditionalState, - loadAppConfig } from './globalState' import { parseServerAddress } from './parseServerAddress' @@ -904,8 +903,9 @@ export async function connect (connectOptions: ConnectOptions) { const reconnectOptions = sessionStorage.getItem('reconnectOptions') ? JSON.parse(sessionStorage.getItem('reconnectOptions')!) : undefined listenGlobalEvents() -watchValue(miscUiState, async s => { - if (s.appLoaded) { // fs ready +const unsubscribe = watchValue(miscUiState, async s => { + if (s.fsReady && s.appConfig) { + unsubscribe() if (reconnectOptions) { sessionStorage.removeItem('reconnectOptions') if (Date.now() - reconnectOptions.timestamp < 1000 * 60 * 2) { @@ -966,15 +966,6 @@ document.body.addEventListener('touchstart', (e) => { }, { passive: false }) // #endregion -loadAppConfig(process.env.INLINED_APP_CONFIG as AppConfig ?? {}) -// load maybe updated config on the server with updated params (just in case) -void window.fetch('config.json').then(async res => res.json()).then(c => c, (error) => { - console.warn('Failed to load optional app config.json', error) - return {} -}).then((config: AppConfig | {}) => { - loadAppConfig(config) -}) - // qs open actions if (!reconnectOptions) { downloadAndOpenFile().then((downloadAction) => { diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index 904b0a29..79748679 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -5,7 +5,7 @@ import { proxy, subscribe } from 'valtio/vanilla' import { subscribeKey } from 'valtio/utils' import { omitObj } from '@zardoy/utils' import { appQueryParamsArray } from './appParams' -import type { AppConfig } from './globalState' +import type { AppConfig } from './appConfig' const isDev = process.env.NODE_ENV === 'development' const initialAppConfig = process.env?.INLINED_APP_CONFIG as AppConfig ?? {} @@ -188,7 +188,7 @@ subscribe(options, () => { localStorage.options = JSON.stringify(saveOptions) }) -type WatchValue = >(proxy: T, callback: (p: T, isChanged: boolean) => void) => void +type WatchValue = >(proxy: T, callback: (p: T, isChanged: boolean) => void) => () => void export const watchValue: WatchValue = (proxy, callback) => { const watchedProps = new Set() @@ -198,10 +198,19 @@ export const watchValue: WatchValue = (proxy, callback) => { return Reflect.get(target, p, receiver) }, }), false) + const unsubscribes = [] as Array<() => void> for (const prop of watchedProps) { - subscribeKey(proxy, prop, () => { - callback(proxy, true) - }) + unsubscribes.push( + subscribeKey(proxy, prop, () => { + callback(proxy, true) + }) + ) + } + + return () => { + for (const unsubscribe of unsubscribes) { + unsubscribe() + } } } diff --git a/src/panorama.ts b/src/panorama.ts index efc06e16..3c888246 100644 --- a/src/panorama.ts +++ b/src/panorama.ts @@ -48,7 +48,7 @@ const updateResourcePackSupportPanorama = async () => { } watchValue(miscUiState, m => { - if (m.appLoaded) { + if (m.fsReady) { // Also adds panorama on app load here watchValue(resourcePackState, async (s) => { const oldState = panoramaUsesResourcePack diff --git a/src/react/MainMenuRenderApp.tsx b/src/react/MainMenuRenderApp.tsx index e06ced52..30fd3047 100644 --- a/src/react/MainMenuRenderApp.tsx +++ b/src/react/MainMenuRenderApp.tsx @@ -75,12 +75,12 @@ export const mainMenuState = proxy({ let disableAnimation = false export default () => { const haveModals = useSnapshot(activeModalStack).length - const { gameLoaded, appLoaded, appConfig, singleplayerAvailable } = useSnapshot(miscUiState) + const { gameLoaded, fsReady, appConfig, singleplayerAvailable } = useSnapshot(miscUiState) - const noDisplay = haveModals || gameLoaded || !appLoaded + const noDisplay = haveModals || gameLoaded || !fsReady useEffect(() => { - if (noDisplay && appLoaded) disableAnimation = true + if (noDisplay && fsReady) disableAnimation = true }, [noDisplay]) const [versionStatus, setVersionStatus] = useState('') From 465ce35e8314a0ddda93a39e733243b7763c49f3 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 13:02:55 +0300 Subject: [PATCH 305/753] feat: display motd/players info for ws servers (still no icon sadly) add new server --- config.json | 4 ++++ src/mineflayer/minecraft-protocol-extra.ts | 2 +- src/react/ServersListProvider.tsx | 6 +++++- src/react/Singleplayer.tsx | 4 +++- src/react/singleplayer.module.css | 3 +++ 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/config.json b/config.json index 6aa86397..e48d758b 100644 --- a/config.json +++ b/config.json @@ -6,6 +6,10 @@ "peerJsServer": "", "peerJsServerFallback": "https://p2p.mcraft.fun", "promoteServers": [ + { + "ip": "ws://mcraft.ryzyn.xyz", + "version": "1.19.4" + }, { "ip": "ws://play.mcraft.fun" }, diff --git a/src/mineflayer/minecraft-protocol-extra.ts b/src/mineflayer/minecraft-protocol-extra.ts index ef1bfbf2..e8216a00 100644 --- a/src/mineflayer/minecraft-protocol-extra.ts +++ b/src/mineflayer/minecraft-protocol-extra.ts @@ -13,7 +13,7 @@ export const pingServerVersion = async (ip: string, port?: number, mergeOptions: ...mergeOptions, } let latency = 0 - let fullInfo = null + let fullInfo: any = null fakeClient.autoVersionHooks = [(res) => { latency = res.latency fullInfo = res diff --git a/src/react/ServersListProvider.tsx b/src/react/ServersListProvider.tsx index 16c5ae8b..56ec75b6 100644 --- a/src/react/ServersListProvider.tsx +++ b/src/react/ServersListProvider.tsx @@ -17,6 +17,7 @@ import { useCopyKeybinding } from './simpleHooks' import { AuthenticatedAccount, getInitialServersList, getServerConnectionHistory, setNewServersList, StoreServerItem } from './serversStorage' type AdditionalDisplayData = { + textNameRightGrayed: string formattedText: string textNameRight: string icon?: string @@ -143,9 +144,11 @@ const Inner = ({ hidden, customServersList }: { hidden?: boolean, customServersL let data if (isWebSocket) { const pingResult = await getServerInfo(server.ip, undefined, undefined, true) + console.log('pingResult.fullInfo.description', pingResult.fullInfo.description) data = { - formattedText: `${pingResult.version} server with a direct websocket connection`, + formattedText: pingResult.fullInfo.description, textNameRight: `ws ${pingResult.latency}ms`, + textNameRightGrayed: `${pingResult.fullInfo.players?.online ?? '??'}/${pingResult.fullInfo.players?.max ?? '??'}`, offline: false } } else { @@ -364,6 +367,7 @@ const Inner = ({ hidden, customServersList }: { hidden?: boolean, customServersL detail: (server.versionOverride ?? '') + ' ' + (server.usernameOverride ?? ''), formattedTextOverride: additional?.formattedText, worldNameRight: additional?.textNameRight ?? '', + worldNameRightGrayed: additional?.textNameRightGrayed ?? '', iconSrc: additional?.icon, offline: additional?.offline } diff --git a/src/react/Singleplayer.tsx b/src/react/Singleplayer.tsx index 5effc269..6d7d6b0e 100644 --- a/src/react/Singleplayer.tsx +++ b/src/react/Singleplayer.tsx @@ -24,13 +24,14 @@ export interface WorldProps { detail?: string formattedTextOverride?: string worldNameRight?: string + worldNameRightGrayed?: string onFocus?: (name: string) => void onInteraction?(interaction: 'enter' | 'space') elemRef?: React.Ref offline?: boolean } -const World = ({ name, isFocused, title, lastPlayed, size, detail = '', onFocus, onInteraction, iconSrc, formattedTextOverride, worldNameRight, elemRef, offline }: WorldProps & { ref?: React.Ref }) => { +const World = ({ name, isFocused, title, lastPlayed, size, detail = '', onFocus, onInteraction, iconSrc, formattedTextOverride, worldNameRight, worldNameRightGrayed, elemRef, offline }: WorldProps & { ref?: React.Ref }) => { const timeRelativeFormatted = useMemo(() => { if (!lastPlayed) return '' const formatter = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }) @@ -63,6 +64,7 @@ const World = ({ name, isFocused, title, lastPlayed, size, detail = '', onFocus,
{title}
+ {worldNameRightGrayed && {worldNameRightGrayed}} {offline ? ( diff --git a/src/react/singleplayer.module.css b/src/react/singleplayer.module.css index 0e69ea46..0de91735 100644 --- a/src/react/singleplayer.module.css +++ b/src/react/singleplayer.module.css @@ -36,6 +36,9 @@ .world_title_right { color: #999; font-size: 9px; + display: flex; + align-items: end; + gap: 1px; } .world_info { margin-left: 3px; From 998f0f0a85cd82cd3455b45d9866279000504298 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 13:07:21 +0300 Subject: [PATCH 306/753] fix: fix sentry #6092213276 DataCloneError: Cannot decode detached ArrayBuffer --- src/basicSounds.ts | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/basicSounds.ts b/src/basicSounds.ts index 6c2b5f4f..40428c6b 100644 --- a/src/basicSounds.ts +++ b/src/basicSounds.ts @@ -17,17 +17,30 @@ const convertedSounds = [] as string[] export async function loadSound (path: string, contents = path) { if (loadingSounds.includes(path)) return true loadingSounds.push(path) - const res = await window.fetch(contents) - if (!res.ok) { - const error = `Failed to load sound ${path}` - if (isCypress()) throw new Error(error) - else console.warn(error) - return - } - const data = await res.arrayBuffer() - sounds[path] = data - loadingSounds.splice(loadingSounds.indexOf(path), 1) + try { + audioContext ??= new window.AudioContext() + + const res = await window.fetch(contents) + if (!res.ok) { + const error = `Failed to load sound ${path}` + if (isCypress()) throw new Error(error) + else console.warn(error) + return + } + const arrayBuffer = await res.arrayBuffer() + + // Decode the audio data immediately + const audioBuffer = await audioContext.decodeAudioData(arrayBuffer) + sounds[path] = audioBuffer + convertedSounds.push(path) // Mark as converted immediately + + loadingSounds.splice(loadingSounds.indexOf(path), 1) + } catch (err) { + console.warn(`Failed to load sound ${path}:`, err) + loadingSounds.splice(loadingSounds.indexOf(path), 1) + if (isCypress()) throw err + } } export const loadOrPlaySound = async (url, soundVolume = 1, loadTimeout = 500) => { @@ -53,13 +66,6 @@ export async function playSound (url, soundVolume = 1) { return } - for (const [soundName, sound] of Object.entries(sounds)) { - if (convertedSounds.includes(soundName)) continue - // eslint-disable-next-line no-await-in-loop - sounds[soundName] = await audioContext.decodeAudioData(sound) - convertedSounds.push(soundName) - } - const soundBuffer = sounds[url] if (!soundBuffer) { console.warn(`Sound ${url} not loaded yet`) From 0db49e78799021d90223f989abec11a99ff87f86 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Wed, 5 Mar 2025 15:11:42 +0300 Subject: [PATCH 307/753] feat: Full support for rendering blocks in inventory GUI powered by deeplsate (#292) --- package.json | 2 +- pnpm-lock.yaml | 22 +- renderer/viewer/lib/guiRenderer.ts | 275 +++++++++++++++++++++ renderer/viewer/lib/worldrendererCommon.ts | 13 + src/inventoryWindows.ts | 34 ++- src/react/EnterFullscreenButton.tsx | 9 +- 6 files changed, 336 insertions(+), 19 deletions(-) create mode 100644 renderer/viewer/lib/guiRenderer.ts diff --git a/package.json b/package.json index 0e0aa6a8..1a195884 100644 --- a/package.json +++ b/package.json @@ -148,8 +148,8 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", + "mc-assets": "^0.2.42", "mineflayer-mouse": "^0.0.5", - "mc-assets": "^0.2.37", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", "mineflayer-pathfinder": "^2.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dbbac60e..d5c2446f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -350,8 +350,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 mc-assets: - specifier: ^0.2.37 - version: 0.2.37 + specifier: ^0.2.42 + version: 0.2.42 minecraft-inventory-gui: specifier: github:zardoy/minecraft-inventory-gui#next version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/75e940a4cd50d89e0ba03db3733d5d704917a3c8(@types/react@18.2.20)(react@18.2.0) @@ -3589,9 +3589,6 @@ packages: resolution: {integrity: sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==} engines: {node: '>=8'} - apl-image-packer@1.1.0: - resolution: {integrity: sha512-Pb1Q76cp8xpY8X4OqVrnk5v1/tB5kOtCzwgOnx8IxMNeekFh/eNUiUKeX5fvGNViZiLmuKAAQc5ICuBDspZ4cA==} - app-root-dir@1.0.2: resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} @@ -6571,8 +6568,11 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mc-assets@0.2.37: - resolution: {integrity: sha512-49tk3shwxsDoV0PrrbORZEKg613vUQPULgusWjXNl8JEma5y41LEo57D6q4aHliXsV3Gb9ThrkFf6hIb0WlY1Q==} + maxrects-packer@2.7.3: + resolution: {integrity: sha512-bG6qXujJ1QgttZVIH4WDanhoJtvbud/xP/XPyf6A69C9RdA61BM4TomFALCq2nrTa+tARRIBB4LuIFsnUQU2wA==} + + mc-assets@0.2.42: + resolution: {integrity: sha512-j2D1RNYtB5Z9gFu9MVjyDBbiALI0mWZ3xW/A3PPefVAHm3HJ2T1vH+1XBov1spBGPl7u+Zo7mRXza3X0egbeOg==} engines: {node: '>=18.0.0'} mcraft-fun-mineflayer@0.1.8: @@ -13609,8 +13609,6 @@ snapshots: apache-md5@1.1.8: {} - apl-image-packer@1.1.0: {} - app-root-dir@1.0.2: {} aproba@2.0.0: @@ -17367,9 +17365,11 @@ snapshots: math-intrinsics@1.1.0: {} - mc-assets@0.2.37: + maxrects-packer@2.7.3: {} + + mc-assets@0.2.42: dependencies: - apl-image-packer: 1.1.0 + maxrects-packer: 2.7.3 zod: 3.24.1 mcraft-fun-mineflayer@0.1.8(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): diff --git a/renderer/viewer/lib/guiRenderer.ts b/renderer/viewer/lib/guiRenderer.ts new file mode 100644 index 00000000..b197040e --- /dev/null +++ b/renderer/viewer/lib/guiRenderer.ts @@ -0,0 +1,275 @@ +// Import placeholders - replace with actual imports for your environment +import { ItemRenderer, Identifier, ItemStack, NbtString, Structure, StructureRenderer, ItemRendererResources, BlockDefinition, BlockModel, TextureAtlas, Resources, ItemModel } from 'deepslate' +import { mat4, vec3 } from 'gl-matrix' +import { AssetsParser } from 'mc-assets/dist/assetsParser' +import { getLoadedImage } from 'mc-assets/dist/utils' +import { BlockModel as BlockModelMcAssets, AtlasParser } from 'mc-assets' +import { getLoadedBlockstatesStore, getLoadedModelsStore } from 'mc-assets/dist/stores' +import { makeTextureAtlas } from 'mc-assets/dist/atlasCreator' +import { proxy, ref } from 'valtio' +import { getItemDefinition } from 'mc-assets/dist/itemDefinitions' + +export const activeGuiAtlas = proxy({ + atlas: null as null | { json, image }, +}) + +export const getNonFullBlocksModels = () => { + const version = viewer.world.texturesVersion ?? 'latest' + const itemsDefinitions = viewer.world.itemsDefinitionsStore.data.latest + const blockModelsResolved = {} as Record + const itemsModelsResolved = {} as Record + const fullBlocksWithNonStandardDisplay = [] as string[] + const handledItemsWithDefinitions = new Set() + const assetsParser = new AssetsParser(version, getLoadedBlockstatesStore(viewer.world.blockstatesModels), getLoadedModelsStore(viewer.world.blockstatesModels)) + + const standardGuiDisplay = { + 'rotation': [ + 30, + 225, + 0 + ], + 'translation': [ + 0, + 0, + 0 + ], + 'scale': [ + 0.625, + 0.625, + 0.625 + ] + } + + const arrEqual = (a: number[], b: number[]) => a.length === b.length && a.every((x, i) => x === b[i]) + const addModelIfNotFullblock = (name: string, model: BlockModelMcAssets) => { + if (blockModelsResolved[name]) return + if (!model?.elements?.length) return + const isFullBlock = model.elements.length === 1 && arrEqual(model.elements[0].from, [0, 0, 0]) && arrEqual(model.elements[0].to, [16, 16, 16]) + if (isFullBlock) return + model['display'] ??= {} + model['display']['gui'] ??= standardGuiDisplay + blockModelsResolved[name] = model + } + + for (const [name, definition] of Object.entries(itemsDefinitions)) { + const item = getItemDefinition(viewer.world.itemsDefinitionsStore, { + version, + name, + properties: { + 'minecraft:display_context': 'gui', + }, + }) + if (item) { + const { resolvedModel } = assetsParser.getResolvedModelsByModel((item.special ? name : item.model).replace('minecraft:', '')) ?? {} + if (resolvedModel) { + handledItemsWithDefinitions.add(name) + } + if (resolvedModel?.elements) { + + let hasStandardDisplay = true + if (resolvedModel['display']?.gui) { + hasStandardDisplay = + arrEqual(resolvedModel['display'].gui.rotation, standardGuiDisplay.rotation) + && arrEqual(resolvedModel['display'].gui.translation, standardGuiDisplay.translation) + && arrEqual(resolvedModel['display'].gui.scale, standardGuiDisplay.scale) + } + + addModelIfNotFullblock(name, resolvedModel) + + if (!blockModelsResolved[name] && !hasStandardDisplay) { + fullBlocksWithNonStandardDisplay.push(name) + } + const notSideLight = resolvedModel['gui_light'] && resolvedModel['gui_light'] !== 'side' + if (!hasStandardDisplay || notSideLight) { + blockModelsResolved[name] = resolvedModel + } + } + if (!blockModelsResolved[name] && item.tints && resolvedModel) { + resolvedModel['tints'] = item.tints + if (resolvedModel.elements) { + blockModelsResolved[name] = resolvedModel + } else { + itemsModelsResolved[name] = resolvedModel + } + } + } + } + + for (const [name, blockstate] of Object.entries(viewer.world.blockstatesModels.blockstates.latest)) { + if (handledItemsWithDefinitions.has(name)) { + continue + } + const resolvedModel = assetsParser.getResolvedModelFirst({ name: name.replace('minecraft:', ''), properties: {} }, true) + if (resolvedModel) { + addModelIfNotFullblock(name, resolvedModel[0]) + } + } + + return { + blockModelsResolved, + itemsModelsResolved + } +} + +// customEvents.on('gameLoaded', () => { +// const res = getNonFullBlocksModels() +// }) + +const RENDER_SIZE = 64 + +const generateItemsGui = async (models: Record, isItems = false) => { + const img = await getLoadedImage(isItems ? viewer.world.itemsAtlasParser!.latestImage : viewer.world.blocksAtlasParser!.latestImage) + const canvasTemp = document.createElement('canvas') + canvasTemp.width = img.width + canvasTemp.height = img.height + canvasTemp.style.imageRendering = 'pixelated' + const ctx = canvasTemp.getContext('2d')! + ctx.imageSmoothingEnabled = false + ctx.drawImage(img, 0, 0) + + const atlasParser = isItems ? viewer.world.itemsAtlasParser! : viewer.world.blocksAtlasParser! + const textureAtlas = new TextureAtlas( + ctx.getImageData(0, 0, img.width, img.height), + Object.fromEntries(Object.entries(atlasParser.atlas.latest.textures).map(([key, value]) => { + return [key, [ + value.u, + value.v, + (value.u + (value.su ?? atlasParser.atlas.latest.suSv)), + (value.v + (value.sv ?? atlasParser.atlas.latest.suSv)), + ]] as [string, [number, number, number, number]] + })) + ) + + const PREVIEW_ID = Identifier.parse('preview:preview') + const PREVIEW_DEFINITION = new BlockDefinition({ '': { model: PREVIEW_ID.toString() } }, undefined) + + let modelData: any + let currentModelName: string | undefined + const resources: ItemRendererResources = { + getBlockModel (id) { + if (id.equals(PREVIEW_ID)) { + return BlockModel.fromJson(modelData ?? {}) + } + return null + }, + getTextureUV (texture) { + return textureAtlas.getTextureUV(texture.toString().slice(1).split('/').slice(1).join('/') as any) + }, + getTextureAtlas () { + return textureAtlas.getTextureAtlas() + }, + getItemComponents (id) { + return new Map() + }, + getItemModel (id) { + // const isSpecial = currentModelName === 'shield' || currentModelName === 'conduit' || currentModelName === 'trident' + const isSpecial = false + if (id.equals(PREVIEW_ID)) { + return ItemModel.fromJson({ + type: isSpecial ? 'minecraft:special' : 'minecraft:model', + model: isSpecial ? { + type: currentModelName, + } : PREVIEW_ID.toString(), + base: PREVIEW_ID.toString(), + tints: modelData?.tints, + }) + } + return null + }, + } + + const canvas = document.createElement('canvas') + canvas.width = RENDER_SIZE + canvas.height = RENDER_SIZE + const gl = canvas.getContext('webgl2', { preserveDrawingBuffer: true }) + if (!gl) { + throw new Error('Cannot get WebGL2 context') + } + + function resetGLContext (gl) { + gl.clearColor(0, 0, 0, 0) + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) + } + + // const includeOnly = ['powered_repeater', 'wooden_door'] + const includeOnly = [] as string[] + + const images: Record = {} + const item = new ItemStack(PREVIEW_ID, 1, new Map(Object.entries({ + 'minecraft:item_model': new NbtString(PREVIEW_ID.toString()), + }))) + const renderer = new ItemRenderer(gl, item, resources, { display_context: 'gui' }) + const missingTextures = new Set() + for (const [modelName, model] of Object.entries(models)) { + if (includeOnly.length && !includeOnly.includes(modelName)) continue + + const patchMissingTextures = () => { + for (const element of model.elements ?? []) { + for (const [faceName, face] of Object.entries(element.faces)) { + if (face.texture.startsWith('#')) { + missingTextures.add(`${modelName} ${faceName}: ${face.texture}`) + face.texture = 'block/unknown' + } + } + } + } + patchMissingTextures() + // TODO eggs + + modelData = model + currentModelName = modelName + resetGLContext(gl) + if (!modelData) continue + renderer.setItem(item, { display_context: 'gui' }) + renderer.drawItem() + const url = canvas.toDataURL() + // eslint-disable-next-line no-await-in-loop + const img = await getLoadedImage(url) + images[modelName] = img + } + + if (missingTextures.size) { + console.warn(`[guiRenderer] Missing textures in ${[...missingTextures].join(', ')}`) + } + + return images +} + +const generateAtlas = async (images: Record) => { + const atlas = makeTextureAtlas({ + input: Object.keys(images), + tileSize: RENDER_SIZE, + getLoadedImage (name) { + return { + image: images[name], + } + }, + }) + + // const atlasParser = new AtlasParser({ latest: atlas.json }, atlas.canvas.toDataURL()) + // const a = document.createElement('a') + // a.href = await atlasParser.createDebugImage(true) + // a.download = 'blocks_atlas.png' + // a.click() + + activeGuiAtlas.atlas = { + json: atlas.json, + image: ref(await getLoadedImage(atlas.canvas.toDataURL())), + } + + return atlas +} + +export const generateGuiAtlas = async () => { + const { blockModelsResolved, itemsModelsResolved } = getNonFullBlocksModels() + + // Generate blocks atlas + console.time('generate blocks gui atlas') + const blockImages = await generateItemsGui(blockModelsResolved, false) + console.timeEnd('generate blocks gui atlas') + console.time('generate items gui atlas') + const itemImages = await generateItemsGui(itemsModelsResolved, true) + console.timeEnd('generate items gui atlas') + await generateAtlas({ ...blockImages, ...itemImages }) + // await generateAtlas(blockImages) +} diff --git a/renderer/viewer/lib/worldrendererCommon.ts b/renderer/viewer/lib/worldrendererCommon.ts index a54f9fe8..261b18e6 100644 --- a/renderer/viewer/lib/worldrendererCommon.ts +++ b/renderer/viewer/lib/worldrendererCommon.ts @@ -24,6 +24,7 @@ import { chunkPos } from './simpleUtils' import { HandItemBlock } from './holdingBlock' import { updateStatText } from './ui/newStats' import { WorldRendererThree } from './worldrendererThree' +import { generateGuiAtlas } from './guiRenderer' function mod (x, n) { return ((x % n) + n) % n @@ -354,6 +355,10 @@ export abstract class WorldRendererCommon } } + async generateGuiTextures () { + await generateGuiAtlas() + } + async updateAssetsData (resourcePackUpdate = false, prioritizeBlockTextures?: string[]) { const blocksAssetsParser = new AtlasParser(this.sourceData.blocksAtlases, blocksAtlasLatest, blocksAtlasLegacy) const itemsAssetsParser = new AtlasParser(this.sourceData.itemsAtlases, itemsAtlasLatest, itemsAtlasLegacy) @@ -379,6 +384,7 @@ export abstract class WorldRendererCommon return texture }, this.customTextures?.items?.tileSize, undefined, customItemTextures) console.timeEnd('createItemsAtlas') + this.blocksAtlasParser = new AtlasParser({ latest: blocksAtlas }, blocksCanvas.toDataURL()) this.itemsAtlasParser = new AtlasParser({ latest: itemsAtlas }, itemsCanvas.toDataURL()) @@ -418,13 +424,20 @@ export abstract class WorldRendererCommon config: this.mesherConfig, }) } + if (!this.itemsAtlasParser) return const itemsTexture = await new THREE.TextureLoader().loadAsync(this.itemsAtlasParser.latestImage) itemsTexture.magFilter = THREE.NearestFilter itemsTexture.minFilter = THREE.NearestFilter itemsTexture.flipY = false viewer.entities.itemsTexture = itemsTexture + if (!this.itemsAtlasParser) return this.renderUpdateEmitter.emit('textureDownloaded') + + console.time('generateGuiTextures') + await this.generateGuiTextures() + console.timeEnd('generateGuiTextures') + if (!this.itemsAtlasParser) return this.renderUpdateEmitter.emit('itemsTextureDownloaded') console.log('textures loaded') } diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index d473f5be..4e4616cd 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -10,6 +10,7 @@ import { versionToNumber } from 'renderer/viewer/prepare/utils' import { getRenamedData } from 'flying-squid/dist/blockRenames' import PrismarineChatLoader from 'prismarine-chat' import { BlockModel } from 'mc-assets' +import { activeGuiAtlas } from 'renderer/viewer/lib/guiRenderer' import Generic95 from '../assets/generic_95.png' import { appReplacableResources } from './generated/resources' import { activeModalStack, hideCurrentModal, hideModal, miscUiState, showModal } from './globalState' @@ -155,7 +156,10 @@ const getImageSrc = (path): string | HTMLImageElement => { return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' } -const getImage = ({ path = undefined as string | undefined, texture = undefined as string | undefined, blockData = undefined as any }, onLoad = () => { }) => { +const getImage = ({ path = undefined as string | undefined, texture = undefined as string | undefined, blockData = undefined as any, image = undefined as HTMLImageElement | undefined }, onLoad = () => { }) => { + if (image) { + return image + } if (!path && !texture) throw new Error('Either pass path or texture') const loadPath = (blockData ? 'blocks' : path ?? texture)! if (loadedImagesCache.has(loadPath)) { @@ -184,7 +188,8 @@ export const renderSlot = (model: ResolvedItemModelRender, debugIsQuickbar = fal blockData?: Record & { resolvedModel: BlockModel }, scale?: number, slice?: number[], - modelName?: string + modelName?: string, + image?: HTMLImageElement } | undefined => { let itemModelName = model.modelName const originalItemName = itemModelName @@ -196,6 +201,23 @@ export const renderSlot = (model: ResolvedItemModelRender, debugIsQuickbar = fal let itemTexture + + if (!fullBlockModelSupport) { + const atlas = activeGuiAtlas.atlas?.json + // todo atlas holds all rendered blocks, not all possibly rendered item/block models, need to request this on demand instead (this is how vanilla works) + const item = atlas?.textures[itemModelName.replace('minecraft:', '').replace('block/', '').replace('blocks/', '').replace('item/', '').replace('items/', '')] + if (item) { + const x = item.u * atlas.width + const y = item.v * atlas.height + return { + texture: 'gui', + image: activeGuiAtlas.atlas!.image, + slice: [x, y, atlas.tileSize, atlas.tileSize], + scale: 0.25, + } + } + } + try { assertDefined(viewer.world.itemsRenderer) itemTexture = @@ -205,6 +227,8 @@ export const renderSlot = (model: ResolvedItemModelRender, debugIsQuickbar = fal inGameError(`Failed to render item ${itemModelName} (original: ${originalItemName}) on ${bot.version} (resourcepack: ${options.enabledResourcepack}): ${err.stack}`) itemTexture = viewer.world.itemsRenderer!.getItemTexture('block/errored')! } + + if ('type' in itemTexture) { // is item return { @@ -230,13 +254,13 @@ const getItemName = (slot: Item | RenderItem | null) => { return text.join('') } -const mapSlots = (slots: Array) => { +const mapSlots = (slots: Array, isJei = false) => { return slots.map((slot, i) => { // todo stateid if (!slot) return try { - const debugIsQuickbar = i === bot.inventory.hotbarStart + bot.quickBarSlot + const debugIsQuickbar = !isJei && i === bot.inventory.hotbarStart + bot.quickBarSlot const modelName = getItemModelName(slot, { 'minecraft:display_context': 'gui', }) const slotCustomProps = renderSlot({ modelName }, debugIsQuickbar) const itemCustomName = getItemName(slot) @@ -305,7 +329,7 @@ const upJei = (search: string) => { return new PrismarineItem(x.id, 1) }).filter(a => a !== null) lastWindow.pwindow.win.jeiSlotsPage = 0 - lastWindow.pwindow.win.jeiSlots = mapSlots(matchedSlots) + lastWindow.pwindow.win.jeiSlots = mapSlots(matchedSlots, true) } export const openItemsCanvas = (type, _bot = bot as typeof bot | null) => { diff --git a/src/react/EnterFullscreenButton.tsx b/src/react/EnterFullscreenButton.tsx index 3901b9ae..0bc41632 100644 --- a/src/react/EnterFullscreenButton.tsx +++ b/src/react/EnterFullscreenButton.tsx @@ -4,6 +4,7 @@ import { activeModalStack, miscUiState } from '../globalState' import Button from './Button' import { useUsingTouch } from './utilsApp' import { pixelartIcons } from './PixelartIcon' +import { showNotification } from './NotificationProvider' const hideOnModals = new Set(['chat']) @@ -33,8 +34,12 @@ export default () => { left: inMainMenu ? 35 : 5, width: 22, }} - onClick={() => { - void document.documentElement.requestFullscreen() + onClick={async () => { + try { + await document.documentElement.requestFullscreen() + } catch (err) { + showNotification(`${err.message ?? err}`, undefined, true) + } }} /> } From b9df1bcf9ec4b099eede64fde721327c082a95b1 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 15:12:05 +0300 Subject: [PATCH 308/753] fix enabling lighting falsey when load for chunks is enabled --- src/watchOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watchOptions.ts b/src/watchOptions.ts index 724fb2ba..3e607f49 100644 --- a/src/watchOptions.ts +++ b/src/watchOptions.ts @@ -75,7 +75,7 @@ export const watchOptionsAfterViewerInit = () => { viewer.world.mesherConfig.enableLighting = !bot.supportFeature('blockStateId') || options.newVersionsLighting; (viewer.world as WorldRendererThree).rerenderAllChunks() }) - customEvents.on('gameLoaded', () => { + customEvents.on('mineflayerBotCreated', () => { viewer.world.mesherConfig.enableLighting = !bot.supportFeature('blockStateId') || options.newVersionsLighting }) From 6fb18d44382b729163fa5c7ca2fb96313ea6455b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 15:26:59 +0300 Subject: [PATCH 309/753] fixes & workarounds rendering items in inventory (some were broken since last commit) --- src/inventoryWindows.ts | 2 +- src/resourcesManager.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 4e4616cd..6a5ab0d6 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -205,7 +205,7 @@ export const renderSlot = (model: ResolvedItemModelRender, debugIsQuickbar = fal if (!fullBlockModelSupport) { const atlas = activeGuiAtlas.atlas?.json // todo atlas holds all rendered blocks, not all possibly rendered item/block models, need to request this on demand instead (this is how vanilla works) - const item = atlas?.textures[itemModelName.replace('minecraft:', '').replace('block/', '').replace('blocks/', '').replace('item/', '').replace('items/', '')] + const item = atlas?.textures[itemModelName.replace('minecraft:', '').replace('block/', '').replace('blocks/', '').replace('item/', '').replace('items/', '').replace('_inventory', '').replace('_bottom', '')] if (item) { const x = item.u * atlas.width const y = item.v * atlas.height diff --git a/src/resourcesManager.ts b/src/resourcesManager.ts index fd92e58b..43c3882e 100644 --- a/src/resourcesManager.ts +++ b/src/resourcesManager.ts @@ -14,10 +14,11 @@ export const getItemModelName = (item: GeneralInputItem, specificProps: ItemSpec const itemSelector = playerState.getItemSelector({ ...specificProps }) - const model = getItemDefinition(viewer.world.itemsDefinitionsStore, { + const modelFromDef = getItemDefinition(viewer.world.itemsDefinitionsStore, { name: itemModelName, version: viewer.world.texturesVersion!, properties: itemSelector - })?.model ?? itemModelName + })?.model + const model = (modelFromDef === 'minecraft:special' ? undefined : modelFromDef) ?? itemModelName return model } From a846eb4500912578742518878c57903d22a28cf4 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 20:45:34 +0300 Subject: [PATCH 310/753] hotfix: fix world interaction crashes --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 1a195884..1bc36f9c 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "http-server": "^14.1.1", "https-browserify": "^1.0.0", "mc-assets": "^0.2.42", - "mineflayer-mouse": "^0.0.5", + "mineflayer-mouse": "^0.0.7", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", "mineflayer-pathfinder": "^2.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d5c2446f..491ef376 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -359,8 +359,8 @@ importers: specifier: github:zardoy/mineflayer version: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13) mineflayer-mouse: - specifier: ^0.0.5 - version: 0.0.5(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + specifier: ^0.0.7 + version: 0.0.7(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -6801,8 +6801,8 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824} version: 1.2.0 - mineflayer-mouse@0.0.5: - resolution: {integrity: sha512-0r/AOGTq+wZH9vrBcW93jH2dGRSlwlO6xc1Z67VJUFlZZ8oBefAOpiZq7LIGc7ROVbpcKEKjROdNv/iCFmzXYA==} + mineflayer-mouse@0.0.7: + resolution: {integrity: sha512-/cSDsc2ZPlvakc3BX+/K9VD64HAIa+LGiz34RpQvUy7hwx3nXdZjJHDjzEdn86BBzRF5pZOxIoXm8hlZKCYeeQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} mineflayer-pathfinder@2.4.4: @@ -17742,7 +17742,7 @@ snapshots: - encoding - supports-color - mineflayer-mouse@0.0.5(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + mineflayer-mouse@0.0.7(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): dependencies: change-case: 5.4.4 debug: 4.4.0(supports-color@8.1.1) From c6b8efe4e8b4885ecaad7403b41bcbc33675e3a4 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 22:22:35 +0300 Subject: [PATCH 311/753] hotfix: should fix edge case when canvas was out of viewport bounds on ios --- src/react/DebugEdges.tsx | 10 +++++++++- src/styles.css | 14 ++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/react/DebugEdges.tsx b/src/react/DebugEdges.tsx index a65b9f92..150e1f16 100644 --- a/src/react/DebugEdges.tsx +++ b/src/react/DebugEdges.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import { useIsHashActive } from './simpleHooks' export default () => { - const MODES_COUNT = 4 + const MODES_COUNT = 5 const [mode, setMode] = useState(0) const isHashActive = useIsHashActive('#edges') @@ -41,6 +41,14 @@ export default () => { styles.height = '100dvh' text = 'top 0 fixed 100dvh' } + if (mode === 4) { + styles.position = 'fixed' + styles.top = 0 + styles.left = 0 + styles.right = 0 + styles.height = '100dvh' + text = 'top 0 bottom 0 fixed 100dvh' + } return
Date: Wed, 5 Mar 2025 22:49:36 +0300 Subject: [PATCH 312/753] feat: Add interaction hint for touch-based entity targeting --- src/react/GameInteractionOverlay.tsx | 8 +++-- src/react/InteractionHint.module.css | 19 ++++++++++ src/react/InteractionHint.module.css.d.ts | 10 ++++++ src/react/InteractionHint.tsx | 44 +++++++++++++++++++++++ src/reactUi.tsx | 2 ++ 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/react/InteractionHint.module.css create mode 100644 src/react/InteractionHint.module.css.d.ts create mode 100644 src/react/InteractionHint.tsx diff --git a/src/react/GameInteractionOverlay.tsx b/src/react/GameInteractionOverlay.tsx index cb7a39f8..7c406e8d 100644 --- a/src/react/GameInteractionOverlay.tsx +++ b/src/react/GameInteractionOverlay.tsx @@ -150,9 +150,13 @@ function GameInteractionOverlayInner ({ document.dispatchEvent(new MouseEvent('mouseup', { button: 0 })) virtualClickActive = false } else if (!capturedPointer.active.activateCameraMove && (Date.now() - capturedPointer.active.time < touchStartBreakingBlockMs)) { - document.dispatchEvent(new MouseEvent('mousedown', { button: 2 })) + // single click action + const MOUSE_BUTTON_RIGHT = 2 + const MOUSE_BUTTON_LEFT = 0 + const gonnaAttack = !!bot.mouse.getCursorState().entity + document.dispatchEvent(new MouseEvent('mousedown', { button: gonnaAttack ? MOUSE_BUTTON_LEFT : MOUSE_BUTTON_RIGHT })) bot.mouse.update() - document.dispatchEvent(new MouseEvent('mouseup', { button: 2 })) + document.dispatchEvent(new MouseEvent('mouseup', { button: gonnaAttack ? MOUSE_BUTTON_LEFT : MOUSE_BUTTON_RIGHT })) } if (screenTouches > 0) { diff --git a/src/react/InteractionHint.module.css b/src/react/InteractionHint.module.css new file mode 100644 index 00000000..026b49c8 --- /dev/null +++ b/src/react/InteractionHint.module.css @@ -0,0 +1,19 @@ +.hint_container { + position: fixed; + top: 20%; + left: 0; + right: 0; + margin: 0 auto; + width: fit-content; + display: flex; + align-items: center; + gap: 8px; + pointer-events: none; + z-index: 1000; + text-shadow: 1px 1px 8px rgba(0, 0, 0, 1); +} + +.hint_text { + color: white; + font-size: 10px; +} diff --git a/src/react/InteractionHint.module.css.d.ts b/src/react/InteractionHint.module.css.d.ts new file mode 100644 index 00000000..45bdf30b --- /dev/null +++ b/src/react/InteractionHint.module.css.d.ts @@ -0,0 +1,10 @@ +// This file is automatically generated. +// Please do not change this file! +interface CssExports { + hintContainer: string; + hintText: string; + hint_container: string; + hint_text: string; +} +declare const cssExports: CssExports; +export default cssExports; diff --git a/src/react/InteractionHint.tsx b/src/react/InteractionHint.tsx new file mode 100644 index 00000000..9121249e --- /dev/null +++ b/src/react/InteractionHint.tsx @@ -0,0 +1,44 @@ +import { useEffect, useState } from 'react' +import { useSnapshot } from 'valtio' +import { options } from '../optionsStorage' +import PixelartIcon, { pixelartIcons } from './PixelartIcon' +import styles from './InteractionHint.module.css' +import { useUsingTouch } from './utilsApp' + +export default () => { + const usingTouch = useUsingTouch() + const { touchInteractionType } = useSnapshot(options) + const [hintText, setHintText] = useState(null) + + useEffect(() => { + const update = () => { + const cursorState = bot.mouse.getCursorState() + if (cursorState.entity) { + const entityName = cursorState.entity.displayName ?? cursorState.entity.name + setHintText(`Attack ${entityName}`) + } else { + setHintText(null) + } + } + + // Initial update + update() + + // Subscribe to physics ticks + bot.on('physicsTick', update) + + return () => { + bot.removeListener('physicsTick', update) + } + }, []) + + if (!usingTouch || touchInteractionType !== 'classic') return null + if (!hintText) return null + + return ( +
+ + {hintText} +
+ ) +} diff --git a/src/reactUi.tsx b/src/reactUi.tsx index ac2dbe74..f07c01ce 100644 --- a/src/reactUi.tsx +++ b/src/reactUi.tsx @@ -52,6 +52,7 @@ import MineflayerPluginConsole from './react/MineflayerPluginConsole' import { UIProvider } from './react/UIProvider' import { useAppScale } from './scaleInterface' import PacketsReplayProvider from './react/PacketsReplayProvider' +import InteractionHint from './react/InteractionHint' const RobustPortal = ({ children, to }) => { return createPortal({children}, to) @@ -146,6 +147,7 @@ const InGameUi = () => { + {showUI && }
{!disabledUiParts.includes('xp-bar') && } {!disabledUiParts.includes('hud-bars') && } From e7b012c08d270d813e365adb55fe3d99251c96f0 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 22:58:11 +0300 Subject: [PATCH 313/753] feat: Display players list on long chat button hold --- src/react/InteractionHint.tsx | 4 ++- src/react/MobileTopButtons.tsx | 33 +++++++++++++++++++------ src/react/PlayerListOverlayProvider.tsx | 4 +++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/react/InteractionHint.tsx b/src/react/InteractionHint.tsx index 9121249e..5c60cdc8 100644 --- a/src/react/InteractionHint.tsx +++ b/src/react/InteractionHint.tsx @@ -1,12 +1,14 @@ import { useEffect, useState } from 'react' import { useSnapshot } from 'valtio' import { options } from '../optionsStorage' +import { activeModalStack } from '../globalState' import PixelartIcon, { pixelartIcons } from './PixelartIcon' import styles from './InteractionHint.module.css' import { useUsingTouch } from './utilsApp' export default () => { const usingTouch = useUsingTouch() + const modalStack = useSnapshot(activeModalStack) const { touchInteractionType } = useSnapshot(options) const [hintText, setHintText] = useState(null) @@ -32,7 +34,7 @@ export default () => { } }, []) - if (!usingTouch || touchInteractionType !== 'classic') return null + if (!usingTouch || touchInteractionType !== 'classic' || modalStack.length > 0) return null if (!hintText) return null return ( diff --git a/src/react/MobileTopButtons.tsx b/src/react/MobileTopButtons.tsx index f686d8af..4d18f817 100644 --- a/src/react/MobileTopButtons.tsx +++ b/src/react/MobileTopButtons.tsx @@ -33,6 +33,28 @@ export default () => { } const longPressEvent = useLongPress(onLongPress, () => {}, defaultOptions) + + const onChatLongPress = () => { + document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' })) + } + + const onChatClick = () => { + if (activeModalStack.at(-1)?.reactType === 'chat') { + hideCurrentModal() + } else { + showModal({ reactType: 'chat' }) + } + } + + const chatLongPressEvent = useLongPress( + onChatLongPress, + onChatClick, + { + shouldPreventDefault: true, + delay: 300, + } + ) + // ios note: just don't use
From 09cd2c3f644c0cd52d53778aaa167a3c050e06ad Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 14 Mar 2025 01:50:54 +0300 Subject: [PATCH 342/753] fix(guiRenderer): dont break textures with custom namespaces rendering --- renderer/viewer/lib/guiRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderer/viewer/lib/guiRenderer.ts b/renderer/viewer/lib/guiRenderer.ts index f4af3d20..d2987ce6 100644 --- a/renderer/viewer/lib/guiRenderer.ts +++ b/renderer/viewer/lib/guiRenderer.ts @@ -155,7 +155,7 @@ const generateItemsGui = async (models: Record, isIt return null }, getTextureUV (texture) { - return textureAtlas.getTextureUV(texture.toString().slice(1).split('/').slice(1).join('/') as any) + return textureAtlas.getTextureUV(texture.toString().replace('minecraft:', '').replace('block/', '').replace('item/', '').replace('blocks/', '').replace('items/', '') as any) }, getTextureAtlas () { return textureAtlas.getTextureAtlas() From 518d6ad8661079eb84d948917a1fbb9a80a14130 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 14 Mar 2025 02:13:04 +0300 Subject: [PATCH 343/753] fix always display reconnect and better last packets display (time) --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ src/index.ts | 4 ++-- src/mineflayer/plugins/packetsRecording.ts | 2 ++ src/packetsReplay/replayPackets.ts | 11 +++++++++++ src/react/ServersListProvider.tsx | 2 +- 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index a1fcff61..f6ae254e 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "mojangson": "^2.0.4", "net-browserify": "github:zardoy/prismarinejs-net-browserify", "node-gzip": "^1.1.2", - "mcraft-fun-mineflayer": "^0.1.8", + "mcraft-fun-mineflayer": "^0.1.10", "peerjs": "^1.5.0", "pixelarticons": "^1.8.1", "pretty-bytes": "^6.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02a0a671..2b09f583 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,8 +135,8 @@ importers: specifier: ^4.17.21 version: 4.17.21 mcraft-fun-mineflayer: - specifier: ^0.1.8 - version: 0.1.8(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) + specifier: ^0.1.10 + version: 0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) minecraft-data: specifier: 3.83.1 version: 3.83.1 @@ -6690,9 +6690,9 @@ packages: resolution: {integrity: sha512-j2D1RNYtB5Z9gFu9MVjyDBbiALI0mWZ3xW/A3PPefVAHm3HJ2T1vH+1XBov1spBGPl7u+Zo7mRXza3X0egbeOg==} engines: {node: '>=18.0.0'} - mcraft-fun-mineflayer@0.1.8: - resolution: {integrity: sha512-jyJTihNHfeToBPwVs3QMKBlVcaCABJ25YN2eoIBQEVTRVFzaXh13XRpElphLzTMj1Q5XFYqufHtMoR4tsb08qQ==} - version: 0.1.8 + mcraft-fun-mineflayer@0.1.10: + resolution: {integrity: sha512-KHzPts82I39nTDZlGwqJo1JXLwaIUHphBbmGWv7oYztUrq3iPiJDEIFgst0ROO/apjtHjzbCM9eb19qWw1JM3Q==} + version: 0.1.10 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: '@roamhq/wrtc': '*' @@ -17579,7 +17579,7 @@ snapshots: maxrects-packer: 2.7.3 zod: 3.24.1 - mcraft-fun-mineflayer@0.1.8(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): + mcraft-fun-mineflayer@0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): dependencies: '@zardoy/flying-squid': 0.0.49(encoding@0.1.13) exit-hook: 2.2.1 diff --git a/src/index.ts b/src/index.ts index 67187c1c..83f9f289 100644 --- a/src/index.ts +++ b/src/index.ts @@ -362,8 +362,8 @@ export async function connect (connectOptions: ConnectOptions) { miscUiState.hasErrors = true if (miscUiState.gameLoaded) return - appStatusState.showReconnect = true setLoadingScreenStatus(`Error encountered. ${err}`, true) + appStatusState.showReconnect = true onPossibleErrorDisconnect() destroyAll() } @@ -712,8 +712,8 @@ export async function connect (connectOptions: ConnectOptions) { bot.on('kicked', (kickReason) => { console.log('You were kicked!', kickReason) const { formatted: kickReasonFormatted, plain: kickReasonString } = parseFormattedMessagePacket(kickReason) - appStatusState.showReconnect = true setLoadingScreenStatus(`The Minecraft server kicked you. Kick reason: ${kickReasonString}`, true, undefined, undefined, kickReasonFormatted) + appStatusState.showReconnect = true destroyAll() }) diff --git a/src/mineflayer/plugins/packetsRecording.ts b/src/mineflayer/plugins/packetsRecording.ts index f1ff18cf..a6b761e7 100644 --- a/src/mineflayer/plugins/packetsRecording.ts +++ b/src/mineflayer/plugins/packetsRecording.ts @@ -93,6 +93,8 @@ declare module 'mineflayer' { export const getLastAutoCapturedPackets = () => circularBuffer?.size export const downloadAutoCapturedPackets = () => { const logger = new PacketsLogger({ minecraftVersion: lastConnectVersion }) + logger.relativeTime = false + logger.formattedTime = true for (const packet of circularBuffer?.getLastElements() ?? []) { logger.log(packet.isFromServer, { name: packet.name, state: packet.state, time: packet.timestamp }, packet.params) } diff --git a/src/packetsReplay/replayPackets.ts b/src/packetsReplay/replayPackets.ts index 9e777b38..13891899 100644 --- a/src/packetsReplay/replayPackets.ts +++ b/src/packetsReplay/replayPackets.ts @@ -188,6 +188,10 @@ const mainPacketsReplayer = async (client: ServerClient, packets: ParsedReplayPa } if (packet.isFromServer) { + if (packet.params === null) { + console.warn('packet.params is null', packet) + continue + } playServerPacket(packet.name, packet.params) await new Promise(resolve => { setTimeout(resolve, packet.diff * packetsReplayState.speed + ADDITIONAL_DELAY * (packetsReplayState.customButtons.packetsSenderDelay.state ? 1 : 0)) @@ -216,6 +220,7 @@ const mainPacketsReplayer = async (client: ServerClient, packets: ParsedReplayPa setTimeout(resolve, 1000) })] : []) ]) + clientsPacketsWaiter.stopWaiting() clientPackets = [] } } @@ -236,6 +241,7 @@ interface PacketsWaiterOptions { interface PacketsWaiter { addPacket(name: string, params: any): void waitForPackets(packets: string[]): Promise + stopWaiting(): void } const createPacketsWaiter = (options: PacketsWaiterOptions = {}): PacketsWaiter => { @@ -296,6 +302,11 @@ const createPacketsWaiter = (options: PacketsWaiterOptions = {}): PacketsWaiter isWaiting = false packetHandler = null } + }, + stopWaiting () { + isWaiting = false + packetHandler = null + queuedPackets.length = 0 } } } diff --git a/src/react/ServersListProvider.tsx b/src/react/ServersListProvider.tsx index f0543e21..bf440c69 100644 --- a/src/react/ServersListProvider.tsx +++ b/src/react/ServersListProvider.tsx @@ -407,7 +407,7 @@ const Inner = ({ hidden, customServersList }: { hidden?: boolean, customServersL worldNameRightGrayed: additional?.textNameRightGrayed ?? '', iconSrc: additional?.icon, offline: additional?.offline, - group: 'Custom Servers' + group: 'Your Servers' } })} initialProxies={{ From a67b9d7aa215bb8de6fd5c23b2cfd58e0ac14fea Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 14 Mar 2025 19:11:14 +0300 Subject: [PATCH 344/753] active back all vanilla mechanics like hotbar wheel when replay window is minimized --- src/react/ReplayPanel.tsx | 8 +++++--- src/react/state/packetsReplayState.ts | 1 + src/utils.ts | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/react/ReplayPanel.tsx b/src/react/ReplayPanel.tsx index fd4082af..3b709882 100644 --- a/src/react/ReplayPanel.tsx +++ b/src/react/ReplayPanel.tsx @@ -1,9 +1,11 @@ import { useState, useEffect } from 'react' +import { useSnapshot } from 'valtio' import { filterPackets } from './packetsFilter' import { DARK_COLORS } from './components/replay/constants' import FilterInput from './components/replay/FilterInput' import PacketList from './components/replay/PacketList' import ProgressBar from './components/replay/ProgressBar' +import { packetsReplayState } from './state/packetsReplayState' interface Props { replayName: string @@ -41,7 +43,7 @@ export default function ReplayPanel ({ style }: Props) { const [filter, setFilter] = useState(defaultFilter) - const [isMinimized, setIsMinimized] = useState(false) + const { isMinimized } = useSnapshot(packetsReplayState) const { filtered: filteredPackets, hiddenCount } = filterPackets(packets.slice(-500), filter) useEffect(() => { @@ -50,7 +52,7 @@ export default function ReplayPanel ({ const handlePlayPauseClick = () => { if (isMinimized) { - setIsMinimized(false) + packetsReplayState.isMinimized = false } else { onPlayPause?.(!isPlaying) } @@ -113,7 +115,7 @@ export default function ReplayPanel ({
{replayName || 'Unnamed Replay'}
- - GitHub - - {linksButton} +
diff --git a/src/react/MainMenuRenderApp.tsx b/src/react/MainMenuRenderApp.tsx index cdcfc096..c112cb0e 100644 --- a/src/react/MainMenuRenderApp.tsx +++ b/src/react/MainMenuRenderApp.tsx @@ -8,7 +8,6 @@ import { setLoadingScreenStatus } from '../appStatus' import { openFilePicker, copyFilesAsync, mkdirRecursive, openWorldDirectory, removeFileRecursiveAsync } from '../browserfs' import MainMenu from './MainMenu' -import { DiscordButton } from './DiscordButton' const isMainMenu = () => { return activeModalStack.length === 0 && !miscUiState.gameLoaded @@ -145,7 +144,6 @@ export default () => { }} githubAction={() => openGithub()} optionsAction={() => openOptionsMenu('main')} - linksButton={} bottomRightLinks={process.env.MAIN_MENU_LINKS} openFileAction={e => { if (!!window.showDirectoryPicker && !e.shiftKey) { diff --git a/src/react/PauseLinkButtons.tsx b/src/react/PauseLinkButtons.tsx new file mode 100644 index 00000000..18331f2d --- /dev/null +++ b/src/react/PauseLinkButtons.tsx @@ -0,0 +1,50 @@ +import { useSnapshot } from 'valtio' +import { openURL } from 'renderer/viewer/lib/simpleUtils' +import { ErrorBoundary } from '@zardoy/react-util' +import { miscUiState } from '../globalState' +import { openGithub } from '../utils' +import Button from './Button' +import { DiscordButton } from './DiscordButton' +import styles from './PauseScreen.module.css' + +function PauseLinkButtonsInner () { + const { appConfig } = useSnapshot(miscUiState) + const pauseLinksConfig = appConfig?.pauseLinks + + if (!pauseLinksConfig) return null + + const renderButton = (button: Record, style: React.CSSProperties, key: number) => { + if (button.type === 'discord') { + return + } + if (button.type === 'github') { + return + } + if (button.type === 'url' && button.text) { + return + } + return null + } + + return ( + <> + {pauseLinksConfig.map((row, i) => { + const style = { width: (204 / row.length - (row.length > 1 ? 4 : 0)) + 'px' } + return ( +
+ {row.map((button, k) => renderButton(button, style, k))} +
+ ) + })} + + ) +} + +export default () => { + return { + console.error(error) + return null + }}> + + +} diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index 35d873ea..55ffe47f 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -34,6 +34,7 @@ import { DiscordButton } from './DiscordButton' import { showNotification } from './NotificationProvider' import { appStatusState, reconnectReload } from './AppStatusProvider' import NetworkStatus from './NetworkStatus' +import PauseLinkButtons from './PauseLinkButtons' const waitForPotentialRender = async () => { return new Promise(resolve => { @@ -227,26 +228,6 @@ export default () => { if (!isModalActive) return null - const pauseLinks: React.ReactNode[] = [] - const pauseLinksConfig = miscUiState.appConfig?.pauseLinks - if (pauseLinksConfig) { - for (const [i, row] of pauseLinksConfig.entries()) { - const rowButtons: React.ReactNode[] = [] - for (const [k, button] of row.entries()) { - const key = `${i}-${k}` - const style = { width: (204 / row.length - (row.length > 1 ? 4 : 0)) + 'px' } - if (button.type === 'discord') { - rowButtons.push() - } else if (button.type === 'github') { - rowButtons.push() - } else if (button.type === 'url' && button.text) { - rowButtons.push() - } - } - pauseLinks.push(
{rowButtons}
) - } - } - return
- {pauseLinks} + {singleplayer ? (
From 72028d925d4f01be0094ceb2badbe4a839006046 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 14 Mar 2025 23:25:13 +0300 Subject: [PATCH 347/753] feat: revamp right click experience by reworking block placing prediction and extending activatble items list --- package.json | 2 +- pnpm-lock.yaml | 35 ++++++++++++++++++++++------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index f6ae254e..6485c25e 100644 --- a/package.json +++ b/package.json @@ -151,7 +151,7 @@ "http-server": "^14.1.1", "https-browserify": "^1.0.0", "mc-assets": "^0.2.42", - "mineflayer-mouse": "^0.0.9", + "mineflayer-mouse": "^0.1.0", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", "mineflayer-pathfinder": "^2.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b09f583..dc4b1555 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -136,7 +136,7 @@ importers: version: 4.17.21 mcraft-fun-mineflayer: specifier: ^0.1.10 - version: 0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) + version: 0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)) minecraft-data: specifier: 3.83.1 version: 3.83.1 @@ -360,10 +360,10 @@ importers: version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/75e940a4cd50d89e0ba03db3733d5d704917a3c8(@types/react@18.2.20)(react@18.2.0) mineflayer: specifier: github:zardoy/mineflayer - version: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13) + version: https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13) mineflayer-mouse: - specifier: ^0.0.9 - version: 0.0.9(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + specifier: ^0.1.0 + version: 0.1.0(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -4129,6 +4129,10 @@ packages: resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} engines: {node: '>= 0.4'} + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -6916,8 +6920,8 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824} version: 1.2.0 - mineflayer-mouse@0.0.9: - resolution: {integrity: sha512-oViJrou2tziPuox/ZFJWZJMCnaF5+KPEsrbBgKmXVr3eK35iPohdhYwoKgqgBY8uXS/bNaFnkCR0K7ZDqyBF8g==} + mineflayer-mouse@0.1.0: + resolution: {integrity: sha512-NFfHASMo3iZOECoYOHVqGBFROVapzDJRlgANMiWnynO/oPUZkOJF6oRrcE33FCVHGlwMCT2S6N7TcqCYqF21Uw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} mineflayer-pathfinder@2.4.4: @@ -6927,8 +6931,8 @@ packages: resolution: {integrity: sha512-q7cmpZFaSI6sodcMJxc2GkV8IO84HbsUP+xNipGKfGg+FMISKabzdJ838Axb60qRtZrp6ny7LluQE7lesHvvxQ==} engines: {node: '>=18'} - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49: - resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49} + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d: + resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d} version: 4.25.0 engines: {node: '>=18'} @@ -14365,6 +14369,11 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + callsites@3.1.0: {} camel-case@4.1.2: @@ -15424,7 +15433,7 @@ snapshots: es-iterator-helpers@1.2.1: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 @@ -17579,12 +17588,12 @@ snapshots: maxrects-packer: 2.7.3 zod: 3.24.1 - mcraft-fun-mineflayer@0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): + mcraft-fun-mineflayer@0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)): dependencies: '@zardoy/flying-squid': 0.0.49(encoding@0.1.13) exit-hook: 2.2.1 minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/5ec3dd4b367fcc039fbcb3edd214fe3cf8178a6d(patch_hash=dkeyukcqlupmk563gwxsmjr3yu)(encoding@0.1.13) - mineflayer: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13) + mineflayer: https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13) prismarine-item: 1.16.0 ws: 8.18.0 transitivePeerDependencies: @@ -17949,7 +17958,7 @@ snapshots: - encoding - supports-color - mineflayer-mouse@0.0.9(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + mineflayer-mouse@0.1.0(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): dependencies: change-case: 5.4.4 debug: 4.4.0(supports-color@8.1.1) @@ -18010,7 +18019,7 @@ snapshots: - encoding - supports-color - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13): + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13): dependencies: minecraft-data: 3.83.1 minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/5ec3dd4b367fcc039fbcb3edd214fe3cf8178a6d(patch_hash=dkeyukcqlupmk563gwxsmjr3yu)(encoding@0.1.13) From 3e056946ec07c45e30182a7d012604564f5ab188 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 15 Mar 2025 02:20:47 +0300 Subject: [PATCH 348/753] add world download button --- src/react/PauseScreen.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index 55ffe47f..f051f9e1 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -35,6 +35,7 @@ import { showNotification } from './NotificationProvider' import { appStatusState, reconnectReload } from './AppStatusProvider' import NetworkStatus from './NetworkStatus' import PauseLinkButtons from './PauseLinkButtons' +import { pixelartIcons } from './PixelartIcon' const waitForPotentialRender = async () => { return new Promise(resolve => { @@ -161,7 +162,7 @@ export default () => { const { singleplayer, wanOpened, wanOpening } = useSnapshot(miscUiState) const { noConnection } = useSnapshot(gameAdditionalState) const { active: packetsReplaceActive, hasRecordedPackets: packetsReplaceHasRecordedPackets } = useSnapshot(packetsRecordingState) - const { displayRecordButton } = useSnapshot(options) + const { displayRecordButton: displayPacketsButtons } = useSnapshot(options) const handlePointerLockChange = () => { if (!pointerLock.hasPointerLock && activeModalStack.length === 0) { @@ -234,7 +235,7 @@ export default () => { icon="pixelarticons:folder" onClick={async () => openWorldActions()} /> - {displayRecordButton && ( + {displayPacketsButtons && ( <>
From da35cfb8a23aa3978ad53bdca0e6bcf45e697a03 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 15 Mar 2025 16:46:51 +0300 Subject: [PATCH 349/753] up mouse --- package.json | 4 ++-- pnpm-lock.yaml | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 6485c25e..9dd43eaf 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "mojangson": "^2.0.4", "net-browserify": "github:zardoy/prismarinejs-net-browserify", "node-gzip": "^1.1.2", - "mcraft-fun-mineflayer": "^0.1.10", + "mcraft-fun-mineflayer": "^0.1.12", "peerjs": "^1.5.0", "pixelarticons": "^1.8.1", "pretty-bytes": "^6.1.1", @@ -151,7 +151,7 @@ "http-server": "^14.1.1", "https-browserify": "^1.0.0", "mc-assets": "^0.2.42", - "mineflayer-mouse": "^0.1.0", + "mineflayer-mouse": "^0.1.1", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", "mineflayer-pathfinder": "^2.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc4b1555..d8d28545 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,8 +135,8 @@ importers: specifier: ^4.17.21 version: 4.17.21 mcraft-fun-mineflayer: - specifier: ^0.1.10 - version: 0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)) + specifier: ^0.1.12 + version: 0.1.12(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)) minecraft-data: specifier: 3.83.1 version: 3.83.1 @@ -362,8 +362,8 @@ importers: specifier: github:zardoy/mineflayer version: https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13) mineflayer-mouse: - specifier: ^0.1.0 - version: 0.1.0(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + specifier: ^0.1.1 + version: 0.1.1(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -6694,9 +6694,9 @@ packages: resolution: {integrity: sha512-j2D1RNYtB5Z9gFu9MVjyDBbiALI0mWZ3xW/A3PPefVAHm3HJ2T1vH+1XBov1spBGPl7u+Zo7mRXza3X0egbeOg==} engines: {node: '>=18.0.0'} - mcraft-fun-mineflayer@0.1.10: - resolution: {integrity: sha512-KHzPts82I39nTDZlGwqJo1JXLwaIUHphBbmGWv7oYztUrq3iPiJDEIFgst0ROO/apjtHjzbCM9eb19qWw1JM3Q==} - version: 0.1.10 + mcraft-fun-mineflayer@0.1.12: + resolution: {integrity: sha512-BhfkagVJX+QmD/dt3qNQS5f7g3/7NI//OfSW4VnRolCnZtrLU8ekr59bLRcNmUWsvtTjkg+wbMeXwclHshSWOA==} + version: 0.1.12 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: '@roamhq/wrtc': '*' @@ -6920,8 +6920,8 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824} version: 1.2.0 - mineflayer-mouse@0.1.0: - resolution: {integrity: sha512-NFfHASMo3iZOECoYOHVqGBFROVapzDJRlgANMiWnynO/oPUZkOJF6oRrcE33FCVHGlwMCT2S6N7TcqCYqF21Uw==} + mineflayer-mouse@0.1.1: + resolution: {integrity: sha512-7jKN+6pIGtQVfYxEIm4tA9CYwTS8Mgn/qJ2wyhrAoIEW8smCHUu0kj5Sdo0TwTCdlOQClKt8aEBZ13E7MGqOhg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} mineflayer-pathfinder@2.4.4: @@ -17588,7 +17588,7 @@ snapshots: maxrects-packer: 2.7.3 zod: 3.24.1 - mcraft-fun-mineflayer@0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)): + mcraft-fun-mineflayer@0.1.12(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)): dependencies: '@zardoy/flying-squid': 0.0.49(encoding@0.1.13) exit-hook: 2.2.1 @@ -17958,7 +17958,7 @@ snapshots: - encoding - supports-color - mineflayer-mouse@0.1.0(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + mineflayer-mouse@0.1.1(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): dependencies: change-case: 5.4.4 debug: 4.4.0(supports-color@8.1.1) From 36bf18b02f616b900852016a93ab22f79061f39b Mon Sep 17 00:00:00 2001 From: Vitaly Date: Mon, 17 Mar 2025 16:05:04 +0300 Subject: [PATCH 350/753] feat: refactor all app storage managment (#310) --- src/appConfig.ts | 3 + src/browserfs.ts | 20 +--- src/controls.ts | 5 +- src/optionsGuiScheme.tsx | 112 ++++++++++++++++++- src/optionsStorage.ts | 33 ++++-- src/react/AddServerOrConnect.tsx | 18 +-- src/react/Chat.css | 5 +- src/react/Input.tsx | 18 ++- src/react/MainMenuRenderApp.tsx | 18 --- src/react/SelectOption.tsx | 131 +++++++++++++++++++--- src/react/ServersList.tsx | 79 +++++++------ src/react/ServersListProvider.tsx | 177 +++++++++--------------------- src/react/appStorageProvider.ts | 135 +++++++++++++++++++++++ src/react/serversStorage.ts | 62 +++-------- src/react/storageProvider.ts | 13 --- 15 files changed, 525 insertions(+), 304 deletions(-) create mode 100644 src/react/appStorageProvider.ts delete mode 100644 src/react/storageProvider.ts diff --git a/src/appConfig.ts b/src/appConfig.ts index 3d6d8f93..b8f83ad1 100644 --- a/src/appConfig.ts +++ b/src/appConfig.ts @@ -1,6 +1,7 @@ import { disabledSettings, options, qsOptions } from './optionsStorage' import { miscUiState } from './globalState' import { setLoadingScreenStatus } from './appStatus' +import { setStorageDataOnAppConfigLoad } from './react/appStorageProvider' export type AppConfig = { // defaultHost?: string @@ -42,6 +43,8 @@ export const loadAppConfig = (appConfig: AppConfig) => { } } } + + setStorageDataOnAppConfigLoad() } export const isBundledConfigUsed = !!process.env.INLINED_APP_CONFIG diff --git a/src/browserfs.ts b/src/browserfs.ts index 0f4579b8..9fb0771f 100644 --- a/src/browserfs.ts +++ b/src/browserfs.ts @@ -15,6 +15,7 @@ import { getFixedFilesize } from './downloadAndOpenFile' import { packetsReplayState } from './react/state/packetsReplayState' import { createFullScreenProgressReporter } from './core/progressReporter' import { showNotification } from './react/NotificationProvider' +import { resetAppStorage } from './react/appStorageProvider' const { GoogleDriveFileSystem } = require('google-drive-browserfs/src/backends/GoogleDrive') browserfs.install(window) @@ -620,24 +621,13 @@ export const openWorldZip = async (...args: Parameters } } -export const resetLocalStorageWorld = () => { - for (const key of Object.keys(localStorage)) { - if (/^[\da-fA-F]{8}(?:\b-[\da-fA-F]{4}){3}\b-[\da-fA-F]{12}$/g.test(key) || key === '/') { - localStorage.removeItem(key) - } - } -} - -export const resetLocalStorageWithoutWorld = () => { - for (const key of Object.keys(localStorage)) { - if (!/^[\da-fA-F]{8}(?:\b-[\da-fA-F]{4}){3}\b-[\da-fA-F]{12}$/g.test(key) && key !== '/') { - localStorage.removeItem(key) - } - } +export const resetLocalStorage = () => { resetOptions() + resetAppStorage() } -window.resetLocalStorageWorld = resetLocalStorageWorld +window.resetLocalStorage = resetLocalStorage + export const openFilePicker = (specificCase?: 'resourcepack') => { // create and show input picker let picker: HTMLInputElement = document.body.querySelector('input#file-zip-picker')! diff --git a/src/controls.ts b/src/controls.ts index a3a54ffb..69b94636 100644 --- a/src/controls.ts +++ b/src/controls.ts @@ -25,11 +25,12 @@ import { showNotification } from './react/NotificationProvider' import { lastConnectOptions } from './react/AppStatusProvider' import { onCameraMove, onControInit } from './cameraRotationControls' import { createNotificationProgressReporter } from './core/progressReporter' +import { appStorage } from './react/appStorageProvider' -export const customKeymaps = proxy(JSON.parse(localStorage.keymap || '{}')) as UserOverridesConfig +export const customKeymaps = proxy(appStorage.keybindings) subscribe(customKeymaps, () => { - localStorage.keymap = JSON.stringify(customKeymaps) + appStorage.keybindings = customKeymaps }) const controlOptions = { diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index 7032e644..5851dd8d 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -3,19 +3,21 @@ import { useSnapshot } from 'valtio' import { openURL } from 'renderer/viewer/lib/simpleUtils' import { noCase } from 'change-case' import { gameAdditionalState, miscUiState, openOptionsMenu, showModal } from './globalState' -import { AppOptions, options } from './optionsStorage' +import { AppOptions, getChangedSettings, options, resetOptions } from './optionsStorage' import Button from './react/Button' import { OptionMeta, OptionSlider } from './react/OptionsItems' import Slider from './react/Slider' import { getScreenRefreshRate } from './utils' import { setLoadingScreenStatus } from './appStatus' -import { openFilePicker, resetLocalStorageWithoutWorld } from './browserfs' +import { openFilePicker, resetLocalStorage } from './browserfs' import { completeResourcepackPackInstall, getResourcePackNames, resourcePackState, uninstallResourcePack } from './resourcePack' import { downloadPacketsReplay, packetsRecordingState } from './packetsReplay/packetsReplayLegacy' -import { showOptionsModal } from './react/SelectOption' +import { showInputsModal, showOptionsModal } from './react/SelectOption' import supportedVersions from './supportedVersions.mjs' import { getVersionAutoSelect } from './connect' import { createNotificationProgressReporter } from './core/progressReporter' +import { customKeymaps } from './controls' +import { appStorage } from './react/appStorageProvider' export const guiOptionsScheme: { [t in OptionsGroupType]: Array<{ [K in keyof AppOptions]?: Partial> } & { custom? }> @@ -450,9 +452,19 @@ export const guiOptionsScheme: { return + >Reset settings + }, + }, + { + custom () { + return }, }, { @@ -460,6 +472,11 @@ export const guiOptionsScheme: { return Developer }, }, + { + custom () { + return + } + }, + { + custom () { + return + } + }, + { + custom () { + return + } + }, + { + custom () { + return + } + } + ], } -export type OptionsGroupType = 'main' | 'render' | 'interface' | 'controls' | 'sound' | 'advanced' | 'VR' +export type OptionsGroupType = 'main' | 'render' | 'interface' | 'controls' | 'sound' | 'advanced' | 'VR' | 'export-import' const Category = ({ children }) =>
>) => { export type AppOptions = typeof defaultOptions -// when opening html file locally in browser, localStorage is shared between all ever opened html files, so we try to avoid conflicts -const localStorageKey = process.env?.SINGLE_FILE_BUILD ? 'minecraftWebClientOptions' : 'options' +const isDeepEqual = (a: any, b: any): boolean => { + if (a === b) return true + if (typeof a !== typeof b) return false + if (typeof a !== 'object') return false + if (a === null || b === null) return a === b + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) return false + return a.every((item, index) => isDeepEqual(item, b[index])) + } + const keysA = Object.keys(a) + const keysB = Object.keys(b) + if (keysA.length !== keysB.length) return false + return keysA.every(key => isDeepEqual(a[key], b[key])) +} + +export const getChangedSettings = () => { + return Object.fromEntries( + Object.entries(options).filter(([key, value]) => !isDeepEqual(defaultOptions[key], value)) + ) +} + export const options: AppOptions = proxy({ ...defaultOptions, ...initialAppConfig.defaultSettings, - ...migrateOptions(JSON.parse(localStorage[localStorageKey] || '{}')), + ...migrateOptions(appStorage.options), ...qsOptions }) @@ -181,14 +198,14 @@ export const resetOptions = () => { Object.defineProperty(window, 'debugChangedOptions', { get () { - return Object.fromEntries(Object.entries(options).filter(([key, v]) => defaultOptions[key] !== v)) + return getChangedSettings() }, }) subscribe(options, () => { // Don't save disabled settings to localStorage const saveOptions = omitObj(options, [...disabledSettings.value] as any) - localStorage[localStorageKey] = JSON.stringify(saveOptions) + appStorage.options = saveOptions }) type WatchValue = >(proxy: T, callback: (p: T, isChanged: boolean) => void) => () => void diff --git a/src/react/AddServerOrConnect.tsx b/src/react/AddServerOrConnect.tsx index 1186fd9a..c25db793 100644 --- a/src/react/AddServerOrConnect.tsx +++ b/src/react/AddServerOrConnect.tsx @@ -3,7 +3,7 @@ import { appQueryParams } from '../appParams' import { fetchServerStatus, isServerValid } from '../api/mcStatusApi' import { parseServerAddress } from '../parseServerAddress' import Screen from './Screen' -import Input from './Input' +import Input, { INPUT_LABEL_WIDTH, InputWithLabel } from './Input' import Button from './Button' import SelectGameVersion from './SelectGameVersion' import { usePassesScaledDimensions } from './UIProvider' @@ -32,8 +32,6 @@ interface Props { allowAutoConnect?: boolean } -const ELEMENTS_WIDTH = 190 - export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQs, onQsConnect, placeholders, accounts, versions, allowAutoConnect }: Props) => { const isSmallHeight = !usePassesScaledDimensions(null, 350) const qsParamName = parseQs ? appQueryParams.name : undefined @@ -256,20 +254,8 @@ export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQ const ButtonWrapper = ({ ...props }: React.ComponentProps) => { props.style ??= {} - props.style.width = ELEMENTS_WIDTH + props.style.width = INPUT_LABEL_WIDTH return
} + +export default Input + +export const INPUT_LABEL_WIDTH = 190 + +export const InputWithLabel = ({ label, span, ...props }: React.ComponentProps & { label, span? }) => { + return
+ + +
+} diff --git a/src/react/MainMenuRenderApp.tsx b/src/react/MainMenuRenderApp.tsx index c112cb0e..f678e7f7 100644 --- a/src/react/MainMenuRenderApp.tsx +++ b/src/react/MainMenuRenderApp.tsx @@ -122,24 +122,6 @@ export default () => { singleplayerAvailable={singleplayerAvailable} connectToServerAction={() => showModal({ reactType: 'serversList' })} singleplayerAction={async () => { - const oldFormatSave = fs.existsSync('./world/level.dat') - if (oldFormatSave) { - setLoadingScreenStatus('Migrating old save, don\'t close the page') - try { - await mkdirRecursive('/data/worlds/local') - await copyFilesAsync('/world/', '/data/worlds/local') - try { - await removeFileRecursiveAsync('/world/') - } catch (err) { - console.error(err) - } - } catch (err) { - console.warn(err) - alert('Failed to migrate world from localStorage') - } finally { - setLoadingScreenStatus(undefined) - } - } showModal({ reactType: 'singleplayer' }) }} githubAction={() => openGithub()} diff --git a/src/react/SelectOption.tsx b/src/react/SelectOption.tsx index 9db20c86..4df471c0 100644 --- a/src/react/SelectOption.tsx +++ b/src/react/SelectOption.tsx @@ -1,10 +1,14 @@ import { proxy, useSnapshot } from 'valtio' +import { useEffect, useRef } from 'react' +import { noCase } from 'change-case' +import { titleCase } from 'title-case' import { hideCurrentModal, showModal } from '../globalState' import { parseFormattedMessagePacket } from '../botUtils' import Screen from './Screen' import { useIsModalActive } from './utilsApp' import Button from './Button' import MessageFormattedString from './MessageFormattedString' +import Input, { InputWithLabel } from './Input' const state = proxy({ title: '', @@ -12,6 +16,8 @@ const state = proxy({ showCancel: true, minecraftJsonMessage: null as null | Record, behavior: 'resolve-close' as 'resolve-close' | 'close-resolve', + inputs: {} as Record, + inputsConfirmButton: '' }) let resolve @@ -35,17 +41,63 @@ export const showOptionsModal = async ( title, options, showCancel: cancel, - minecraftJsonMessage: minecraftJsonMessageParsed + minecraftJsonMessage: minecraftJsonMessageParsed, + inputs: {}, + inputsConfirmButton: '' + }) + }) +} + +type InputOption = { + type: 'text' | 'checkbox' + defaultValue?: string | boolean + label?: string +} +export const showInputsModal = async >( + title: string, + inputs: T, + { cancel = true, minecraftJsonMessage }: { cancel?: boolean, minecraftJsonMessage? } = {} +): Promise<{ + [K in keyof T]: T[K] extends { type: 'text' } + ? string + : T[K] extends { type: 'checkbox' } + ? boolean + : never +}> => { + showModal({ reactType: 'general-select' }) + let minecraftJsonMessageParsed + if (minecraftJsonMessage) { + const parseResult = parseFormattedMessagePacket(minecraftJsonMessage) + minecraftJsonMessageParsed = parseResult.formatted + if (parseResult.plain) { + title += ` (${parseResult.plain})` + } + } + return new Promise((_resolve) => { + resolve = _resolve + Object.assign(state, { + title, + inputs, + showCancel: cancel, + minecraftJsonMessage: minecraftJsonMessageParsed, + options: [], + inputsConfirmButton: 'Confirm' }) }) } export default () => { - const { title, options, showCancel, minecraftJsonMessage } = useSnapshot(state) + const { title, options, showCancel, minecraftJsonMessage, inputs, inputsConfirmButton } = useSnapshot(state) const isModalActive = useIsModalActive('general-select') + const inputValues = useRef({}) + + useEffect(() => { + inputValues.current = Object.fromEntries(Object.entries(inputs).map(([key, input]) => [key, input.defaultValue ?? (input.type === 'checkbox' ? false : '')])) + }, [inputs]) + if (!isModalActive) return - const resolveClose = (value: string | undefined) => { + const resolveClose = (value: any) => { if (state.behavior === 'resolve-close') { resolve(value) hideCurrentModal() @@ -59,17 +111,66 @@ export default () => { {minecraftJsonMessage &&
} - {options.map(option => )} - {showCancel && } +
+ {options.length > 0 &&
+ {options.map(option => )} +
} +
+ {Object.entries(inputs).map(([key, input]) => { + const label = input.label ?? titleCase(noCase(key)) + return
+ {input.type === 'text' && ( + { + inputValues.current[key] = e.target.value + }} + /> + )} + {input.type === 'checkbox' && ( + + )} +
+ })} +
+ {inputs && inputsConfirmButton && ( + + )} + {showCancel && ( + + )} +
} diff --git a/src/react/ServersList.tsx b/src/react/ServersList.tsx index 46541af6..7b34f016 100644 --- a/src/react/ServersList.tsx +++ b/src/react/ServersList.tsx @@ -1,64 +1,61 @@ -import React from 'react' +import React, { useMemo } from 'react' +import { useSnapshot } from 'valtio' +import { miscUiState } from '../globalState' +import { appQueryParams } from '../appParams' import Singleplayer from './Singleplayer' import Input from './Input' import Button from './Button' import PixelartIcon, { pixelartIcons } from './PixelartIcon' - import Select from './Select' import { BaseServerInfo } from './AddServerOrConnect' import { useIsSmallWidth } from './simpleHooks' +import { appStorage, SavedProxiesData, ServerHistoryEntry } from './appStorageProvider' + +const getInitialProxies = () => { + const proxies = [] as string[] + if (miscUiState.appConfig?.defaultProxy) { + proxies.push(miscUiState.appConfig.defaultProxy) + } + return proxies +} + +export const getCurrentProxy = (): string | undefined => { + return appQueryParams.proxy ?? appStorage.proxiesData?.selected ?? getInitialProxies()[0] +} + +export const getCurrentUsername = () => { + return appQueryParams.username ?? appStorage.username +} interface Props extends React.ComponentProps { joinServer: (info: BaseServerInfo | string, additional: { shouldSave?: boolean index?: number }) => void - initialProxies: SavedProxiesLocalStorage - updateProxies: (proxies: SavedProxiesLocalStorage) => void - username: string - setUsername: (username: string) => void onProfileClick?: () => void setQuickConnectIp?: (ip: string) => void - serverHistory?: Array<{ - ip: string - versionOverride?: string - numConnects: number - }> -} - -export interface SavedProxiesLocalStorage { - proxies: readonly string[] - selected: string -} - -type ProxyStatusResult = { - time: number - ping: number - status: 'success' | 'error' | 'unknown' } export default ({ - initialProxies, - updateProxies: updateProxiesProp, joinServer, - username, - setUsername, onProfileClick, setQuickConnectIp, - serverHistory, ...props }: Props) => { - const [proxies, setProxies] = React.useState(initialProxies) - - const updateProxies = (newData: SavedProxiesLocalStorage) => { - setProxies(newData) - updateProxiesProp(newData) - } - + const snap = useSnapshot(appStorage) + const username = useMemo(() => getCurrentUsername(), [appQueryParams.username, appStorage.username]) const [serverIp, setServerIp] = React.useState('') const [save, setSave] = React.useState(true) const [activeHighlight, setActiveHighlight] = React.useState(undefined as 'quick-connect' | 'server-list' | undefined) + const updateProxies = (newData: SavedProxiesData) => { + appStorage.proxiesData = newData + } + + const setUsername = (username: string) => { + appStorage.username = username + } + const getActiveHighlightStyles = (type: typeof activeHighlight) => { const styles: React.CSSProperties = { transition: 'filter 0.2s', @@ -71,6 +68,8 @@ export default ({ const isSmallWidth = useIsSmallWidth() + const initialProxies = getInitialProxies() + const proxiesData = snap.proxiesData ?? { proxies: initialProxies, selected: initialProxies[0] } return setActiveHighlight('quick-connect')} onMouseLeave={() => setActiveHighlight(undefined)} > - {/* todo history */} - {serverHistory?.map((server) => ( -