From fc7df81b0fb640d01ededf66f872653a54b31cd3 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 13 Aug 2024 01:07:21 +0300 Subject: [PATCH 001/865] up mc-assets to fix chest --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 50364040..23f0d1cf 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", - "mc-assets": "^0.2.9", + "mc-assets": "^0.2.10", "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 2bf9a7f7..8a64a3c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -338,8 +338,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 mc-assets: - specifier: ^0.2.9 - version: 0.2.9 + specifier: ^0.2.10 + version: 0.2.10 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) @@ -6233,8 +6233,8 @@ packages: peerDependencies: react: ^18.2.0 - mc-assets@0.2.9: - resolution: {integrity: sha512-vtJ0UYRb8fIte15LEvfsrpBYPRH0WTwPHTdBj0AwN4/xoPmKdD2y7l/g5VP7JnpLvXO7Ke+uqRZLkDGBJIRNMQ==} + mc-assets@0.2.10: + resolution: {integrity: sha512-o/PL0mv5vMcszM/pFjp174psG7nve/GhwH8ft5QDYB5smju6HalWkqB2gG4W7nxr4y3fNuB3FJbGcOI6ZQFJ5A==} engines: {node: '>=18.0.0'} md5-file@4.0.0: @@ -16471,7 +16471,7 @@ snapshots: dependencies: react: 18.2.0 - mc-assets@0.2.9: {} + mc-assets@0.2.10: {} md5-file@4.0.0: {} From f7615ae581b6510411cd07326d3e4544d838bfa4 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 13 Aug 2024 01:18:23 +0300 Subject: [PATCH 002/865] experimental: faster server start & build --- rsbuild.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 4b78b144..350f2b3a 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -130,7 +130,7 @@ export default defineConfig({ } if (!dev) { build.onBeforeBuild(async () => { - await prep() + prep() }) build.onAfterBuild(async () => { const { count, size, warnings } = await generateSW({ @@ -144,7 +144,7 @@ export default defineConfig({ }) }) } - build.onBeforeStartDevServer(prep) + build.onBeforeStartDevServer(() => prep()) }, }, ], From dd285544c35db5944da2ba4ea2be92ea7615a92e Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 13 Aug 2024 02:03:16 +0300 Subject: [PATCH 003/865] enable mouse raw input and chat select by default for new users! --- README.MD | 4 ++-- src/optionsStorage.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.MD b/README.MD index 69b6386c..dcfe3fd4 100644 --- a/README.MD +++ b/README.MD @@ -25,10 +25,10 @@ All components that are in [Storybook](https://mcraft.fun/storybook) are publish ### Recommended Settings -- Controls -> **Raw Input** -> **On** - This will make the controls more precise - Controls -> **Touch Controls Type** -> **Joystick** - Controls -> **Auto Full Screen** -> **On** - To avoid ctrl+w issue -- Interface -> **Chat Select** -> **On** - To select chat messages +- Controls -> **Raw Input** -> **On** - This will make the controls more precise (UPD: already enabled by default) +- Interface -> **Chat Select** -> **On** - To select chat messages (UPD: already enabled by default) ### World Loading diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index 3eadcb2c..990dd31e 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -10,7 +10,7 @@ const defaultOptions = { multiplayerRenderDistance: 3, closeConfirmation: true, autoFullScreen: false, - mouseRawInput: false, + mouseRawInput: true, autoExitFullscreen: false, localUsername: 'wanderer', mouseSensX: 50, @@ -69,7 +69,7 @@ const defaultOptions = { renderEntities: true, smoothLighting: true, newVersionsLighting: false, - chatSelect: false, + chatSelect: true, autoJump: 'auto' as 'auto' | 'always' | 'never', autoParkour: false, From 39ccf846c2271bacac4fdf908bbebe4f83c249f5 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 13 Aug 2024 02:06:04 +0300 Subject: [PATCH 004/865] fix more grass block item render and remove misleading sounds warning --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- src/index.ts | 3 +-- src/soundSystem.ts | 13 ------------- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 23f0d1cf..6d31f0d9 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", - "mc-assets": "^0.2.10", + "mc-assets": "^0.2.11", "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 8a64a3c1..9cee15da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -338,8 +338,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 mc-assets: - specifier: ^0.2.10 - version: 0.2.10 + specifier: ^0.2.11 + version: 0.2.11 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) @@ -6233,8 +6233,8 @@ packages: peerDependencies: react: ^18.2.0 - mc-assets@0.2.10: - resolution: {integrity: sha512-o/PL0mv5vMcszM/pFjp174psG7nve/GhwH8ft5QDYB5smju6HalWkqB2gG4W7nxr4y3fNuB3FJbGcOI6ZQFJ5A==} + mc-assets@0.2.11: + resolution: {integrity: sha512-j56kUmmVtWU5U+xa6diWLfi2ANftIdC9eGoueL86Ixf02vQuB3WFUZS6si0emJkcUWGtyqrr9ubztvT8cPUp2Q==} engines: {node: '>=18.0.0'} md5-file@4.0.0: @@ -16471,7 +16471,7 @@ snapshots: dependencies: react: 18.2.0 - mc-assets@0.2.10: {} + mc-assets@0.2.11: {} md5-file@4.0.0: {} diff --git a/src/index.ts b/src/index.ts index 5941c268..4189570f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -83,7 +83,7 @@ import { fsState } from './loadSave' import { watchFov } from './rendererUtils' import { loadInMemorySave } from './react/SingleplayerProvider' -import { downloadSoundsIfNeeded, earlyCheck as earlySoundsMapCheck } from './soundSystem' +import { downloadSoundsIfNeeded } from './soundSystem' import { ua } from './react/utils' import { handleMovementStickDelta, joystickPointer } from './react/TouchAreasControls' import { possiblyHandleStateVariable } from './googledrive' @@ -552,7 +552,6 @@ async function connect (connectOptions: ConnectOptions) { // "mapDownloader-saveInternal": false, // do not save into memory, todo must be implemeneted as we do really care of ram }) as unknown as typeof __type_bot window.bot = bot - earlySoundsMapCheck() customEvents.emit('mineflayerBotCreated') if (singleplayer || p2pMultiplayer) { // in case of p2pMultiplayer there is still flying-squid on the host side diff --git a/src/soundSystem.ts b/src/soundSystem.ts index 0187a9b0..d0caf01f 100644 --- a/src/soundSystem.ts +++ b/src/soundSystem.ts @@ -235,19 +235,6 @@ subscribeKey(miscUiState, 'gameLoaded', async () => { // } // } -export const earlyCheck = () => { - const { allSoundsMap } = globalObject - if (!allSoundsMap) return - - // todo also use major versioned hardcoded sounds - const soundsMap = allSoundsMap[bot.version] - - if (!soundsMap) { - console.warn('No sounds map for version', bot.version, 'supported versions are', Object.keys(allSoundsMap).join(', ')) - showNotification('Warning', 'No sounds map for version ' + bot.version) - } -} - const getVersionedSound = (version: string, item: string, itemsMapSortedEntries: Array<[string, string[]]>) => { const verNumber = versionToNumber(version) for (const [itemsVer, items] of itemsMapSortedEntries) { From be78985edf90c29c64e9646e9b679bfb7ac76060 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 14 Aug 2024 14:46:40 +0300 Subject: [PATCH 005/865] hotfix: better render items for legacy versions (still some blocks are not rendered) --- src/inventoryWindows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 2490281f..6ef8ebf7 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -39,7 +39,7 @@ export const onGameLoad = (onLoad) => { const checkIfLoaded = () => { if (!viewer.world.itemsAtlasParser) return - itemsRenderer = new ItemsRenderer('latest', viewer.world.blockstatesModels, viewer.world.itemsAtlasParser, viewer.world.blocksAtlasParser) + itemsRenderer = new ItemsRenderer(bot.version, viewer.world.blockstatesModels, viewer.world.itemsAtlasParser, viewer.world.blocksAtlasParser) globalThis.itemsRenderer = itemsRenderer if (allImagesLoadedState.value) return onLoad?.() From 71289e3ef4e187cc88bcc4e99194866656bd1b40 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Thu, 15 Aug 2024 03:12:32 +0300 Subject: [PATCH 006/865] Lint JSX (#175) --- .eslintrc.json | 41 ++++++++ CONTRIBUTING.md | 94 ++++++++++++++++++ src/GlobalSearchInput.tsx | 3 +- src/index.ts | 1 - src/optionsGuiScheme.tsx | 116 ++++++++++++++--------- src/react/AddServerOrConnect.tsx | 9 +- src/react/AppStatus.tsx | 5 +- src/react/ArmorBar.tsx | 3 +- src/react/BedTime.tsx | 3 +- src/react/Book.tsx | 11 ++- src/react/BossBarOverlay.tsx | 7 +- src/react/BreathBar.tsx | 2 +- src/react/ButtonWithTooltip.tsx | 29 +++--- src/react/Chat.stories.tsx | 23 +++-- src/react/Chat.tsx | 11 ++- src/react/ConceptCommandsGui.stories.tsx | 5 +- src/react/CreateWorld.tsx | 39 ++++---- src/react/Crosshair.tsx | 12 ++- src/react/DeathScreen.tsx | 16 ++-- src/react/DebugOverlay.tsx | 8 +- src/react/DiscordButton.tsx | 3 +- src/react/FoodBar.tsx | 3 +- src/react/GoogleButton.tsx | 18 ++-- src/react/HealthBar.tsx | 3 +- src/react/HeldMapUi.tsx | 15 +-- src/react/HotbarRenderApp.tsx | 22 +++-- src/react/IndicatorEffects.tsx | 10 +- src/react/Input.tsx | 5 +- src/react/KeybindingsCustom.tsx | 34 ++++--- src/react/KeybindingsScreen.tsx | 23 +++-- src/react/MainMenu.tsx | 13 ++- src/react/MobileTopButtons.tsx | 48 ++++++---- src/react/ModuleSignsViewer.tsx | 2 +- src/react/NoModalFoundProvider.tsx | 9 +- src/react/Notification.tsx | 43 +++++---- src/react/PauseScreen.tsx | 6 +- src/react/PixelartIcon.tsx | 8 +- src/react/Screen.tsx | 2 +- src/react/SelectOption.tsx | 22 +++-- src/react/ServersList.stories.tsx | 3 +- src/react/ServersList.tsx | 81 +++++++++------- src/react/SignEditor.stories.tsx | 8 +- src/react/SignEditor.tsx | 37 ++++---- src/react/SignInMessage.stories.tsx | 3 +- src/react/SignInMessage.tsx | 28 ++++-- src/react/Singleplayer.tsx | 47 +++++---- src/react/Slider.tsx | 4 +- src/react/SoundMuffler.tsx | 16 ++-- src/react/Tabs.tsx | 41 ++++---- src/react/TouchAreasControls.tsx | 14 ++- src/react/XPBar.tsx | 2 +- src/reactUi.tsx | 19 ++-- 52 files changed, 664 insertions(+), 366 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 67f6d8b0..f454100a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -103,6 +103,47 @@ // "@stylistic/newline-per-chained-call": "error", // not sure if needed "@stylistic/new-parens": "error", "@stylistic/no-confusing-arrow": "error", + "@stylistic/wrap-iife": "error", + "@stylistic/space-before-blocks": "error", + "@stylistic/type-generic-spacing": "error", + "@stylistic/template-tag-spacing": "error", + "@stylistic/template-curly-spacing": "error", + "@stylistic/type-annotation-spacing": "error", + "@stylistic/jsx-child-element-spacing": "error", + // buggy + // "@stylistic/jsx-closing-bracket-location": "error", + // "@stylistic/jsx-closing-tag-location": "error", + "@stylistic/jsx-curly-brace-presence": "error", + "@stylistic/jsx-curly-newline": "error", + "@stylistic/jsx-curly-spacing": "error", + "@stylistic/jsx-equals-spacing": "error", + "@stylistic/jsx-first-prop-new-line": "error", + "@stylistic/jsx-function-call-newline": "error", + "@stylistic/jsx-max-props-per-line": [ + "error", + { + "maximum": 7 + } + ], + "@stylistic/jsx-pascal-case": "error", + "@stylistic/jsx-props-no-multi-spaces": "error", + "@stylistic/jsx-self-closing-comp": "error", + // "@stylistic/jsx-sort-props": [ + // "error", + // { + // "callbacksLast": false, + // "shorthandFirst": true, + // "shorthandLast": false, + // "multiline": "ignore", + // "ignoreCase": true, + // "noSortAlphabetically": true, + // "reservedFirst": [ + // "key", + // "className" + // ], + // "locale": "auto" + // } + // ], // perf "import/no-deprecated": "off", // --- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e1e4dfc7..4a737a6d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,6 +5,8 @@ After forking the repository, run the following commands to get started: 0. Ensure you have [Node.js](https://nodejs.org) and `pnpm` installed. To install pnpm run `npm i -g pnpm@9.0.4`. 1. Install dependencies: `pnpm i` 2. Start the project in development mode: `pnpm start` +3. Read the [Tasks Categories](#tasks-categories) and [Workflow](#workflow) sections below +4. Let us know if you are working on something and be sure to open a PR if you got any changes. Happy coding! ## Project Structure @@ -57,6 +59,20 @@ How different modules are used: - `mineflayer` - provider `bot` variable and as mineflayer states it is a wrapper for the `node-minecraft-protocol` module and is used to connect and interact with real Java Minecraft servers. However not all events & properties are exposed and sometimes you have to use `bot._client.on('packet_name', data => ...)` to handle packets that are not handled via mineflayer API. Also you can use almost any mineflayer plugin. +## Running Main App + Playground + +To start the main web app and playground, run `pnpm run-all`. Note is doesn't start storybook and tests. + +## Cypress Tests (E2E) + +Cypress tests are located in `cypress` folder. To run them, run `pnpm test-mc-server` and then `pnpm test:cypress` when the `pnpm prod-start` is running (or change the port to 3000 to test with the dev server). Usually you don't need to run these until you get issues on the CI. + +## Unit Tests + +There are not many unit tests for now (which we are trying to improve). +Location of unit tests: `**/*.test.ts` files in `src` folder and `prismarine-viewer` folder. +Start them with `pnpm test-unit`. + ## Making protocol-related changes You can get a description of packets for the latest protocol version from and for previous protocol versions from (look for *Page* links that have *Protocol* in URL). @@ -75,6 +91,84 @@ Also there are [src/generatedClientPackets.ts](src/generatedClientPackets.ts) an - Use `start-prod` script to start the project in production mode after running the `build` script to build the project. - If CI is failing on the next branch for some reason, feel free to use the latest commit for release branch. We will update the base branch asap. Please, always make sure to allow maintainers do changes when opening PRs. +## Tasks Categories + +(most important for now are on top). + +## 1. Client-side Logic (most important right now) + +Everything related to the client side packets. Investigate issues when something goes wrong with some server. It's much easier to work on these types of tasks when you have experience in Java with Minecraft, a deep understanding of the original client, and know how to debug it (which is not hard actually). Right now the client is easily detectable by anti-cheat plugins, and the main goal is to fix it (mostly because of wrong physics implementation). + +Priority tasks: + +- Rewrite or fix the physics logic (Botcraft or Grim can be used as a reference as well) +- Implement basic minecart / boat / horse riding +- Fix auto jump module (false triggers, performance issues) +- Investigate connection issues to some servers +- Setup a platform for automatic cron testing against the latest version of the anti-cheat plugins +- ... + +Goals: + +- Make more servers playable. Right now on hypixel-like servers (servers with minigames), only tnt run (and probably ) is fully playable. + +Notes: + +- You can see the incoming/outgoing packets in the console (F12 in Chrome) by enabling `options.debugLogNotFrequentPackets = true`. However, if you need a FULL log of all packets, you can start recording the packets by going into `Settings` > `Advanced` > `Enable Packets Replay` and then you can download the file and use it to replay the packets. +- You can use mcraft-e2e studio to send the same packets over and over again (which is useful for testing) or use the packets replayer (which is useful for debugging). + +## 2. Three.js Renderer + +Example tasks: + +- Improve / fix entity rendering +- Better update entities on specific packets +- Investigate performance issues under different conditions (instructions provided) +- Work on the playground code + +Goals: + +- Fix a lot of entity rendering issues (including position updates) +- Implement switching camera mode (first person, third person, etc) +- Animated blocks +- Armor rendering +- ... + +Note: + +- It's useful to know how to use helpers & additional cameras (e.g. setScissor) + +## 3. Server-side Logic + +Flying squid fork (space-squid). +Example tasks: + +- Add missing commands (e.g. /scoreboard) +- Basic physics (player fall damage, falling blocks & entities) +- Basic entities AI (spawning, attacking) +- Pvp +- Emit more packets on some specific events (e.g. when a player uses an item) +- Make more maps playable (e.g. fix when something is not implemented in both server and client and blocking map interaction) +- ... + +Long Term Goals: + +- Make most adventure maps playable +- Make a way to complete the game from the scratch (crafting, different dimensions, terrain generation, etc) +- Make bedwars playable! +Most of the tasks are straightforward to implement, just be sure to use a debugger ;). If you feel you are stuck, ask for help on Discord. Absolutely any tests / refactor suggestions are welcome! + +## 4. Frontend + +New React components, improve UI (including mobile support). + +## Workflow + +1. Locate the problem on the public test server & make an easily reproducible environment (you can also use local packets replay server or your custom server setup). Dm me for details on public test server / replay server +2. Debug the code, find an issue in the code, isolate the problem +3. Develop, try to fix and test. Finally we should find a way to fix it. It's ideal to have an automatic test but it's not necessary for now +3. Repeat step 1 to make sure the task is done and the problem is fixed (or the feature is implemented) + ### Would be useful to have - cleanup folder & modules structure, cleanup playground code diff --git a/src/GlobalSearchInput.tsx b/src/GlobalSearchInput.tsx index c29c64a4..d9266950 100644 --- a/src/GlobalSearchInput.tsx +++ b/src/GlobalSearchInput.tsx @@ -13,7 +13,8 @@ function InnerSearch () { margin: 'auto', zIndex: 11, width: 'min-content', - }}> + }} + > - { - options.frameLimit = newVal > frameLimitMax! ? false : newVal - }} /> - + >Keybindings + }, mouseSensX: {}, mouseSensY: { @@ -325,9 +339,12 @@ export const guiOptionsScheme: { advanced: [ { custom () { - return + return }, }, { @@ -338,17 +355,24 @@ export const guiOptionsScheme: { { custom () { const { active } = useSnapshot(packetsReplaceSessionState) - return + return }, }, { custom () { const { active } = useSnapshot(packetsReplaceSessionState) - return + return }, } ], diff --git a/src/react/AddServerOrConnect.tsx b/src/react/AddServerOrConnect.tsx index e973fafd..648cda98 100644 --- a/src/react/AddServerOrConnect.tsx +++ b/src/react/AddServerOrConnect.tsx @@ -83,7 +83,8 @@ export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQ display: 'grid', gap: 3, gridTemplateColumns: smallWidth ? '1fr' : '1fr 1fr' - }}> + }} + > {!lockConnect && <>
setServerName(value)} placeholder='Defaults to IP' /> @@ -98,7 +99,8 @@ export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQ
diff --git a/src/react/AppStatus.tsx b/src/react/AppStatus.tsx index 603a1508..ca83c29a 100644 --- a/src/react/AppStatus.tsx +++ b/src/react/AppStatus.tsx @@ -34,7 +34,8 @@ export default ({ status, isError, hideDots = false, lastStatus = '', backAction + }} + > {status} {isError || hideDots ? '' : loadingDots} @@ -48,7 +49,7 @@ export default ({ status, isError, hideDots = false, lastStatus = '', backAction <> {backAction && + diff --git a/src/react/Chat.tsx b/src/react/Chat.tsx index 60467171..bd9a1714 100644 --- a/src/react/Chat.tsx +++ b/src/react/Chat.tsx @@ -212,9 +212,11 @@ export default ({ return ( <> -
+
{opacity &&
{messages.map((m) => ( @@ -246,7 +248,8 @@ export default ({ onClose?.() } } - }}> + }} + > {isIos && +const defaultIcon = const Button2 = ({ title, icon }) => { //@ts-expect-error @@ -23,7 +23,8 @@ const Comp = () => { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10 - }}> + }} + > } /> } /> diff --git a/src/react/CreateWorld.tsx b/src/react/CreateWorld.tsx index 87b36777..08e316a1 100644 --- a/src/react/CreateWorld.tsx +++ b/src/react/CreateWorld.tsx @@ -29,10 +29,12 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer }, []) return - { - e.preventDefault() - createClick() - }}> + { + e.preventDefault() + createClick() + }} + > - { + creatingWorldState.version = value + }} + > {versions.map(({ version, label }) => { return })} @@ -56,14 +60,17 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer + }} + >World Type: {type} + {/* */}
@@ -72,7 +79,9 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer
+ }} + >Cancel +
Note: store important saves in folders on the drive!
@@ -85,9 +94,7 @@ export const WorldCustomize = ({ backClick }) => { return
-
- -
+
diff --git a/src/react/Crosshair.tsx b/src/react/Crosshair.tsx index 9079fb66..542545f7 100644 --- a/src/react/Crosshair.tsx +++ b/src/react/Crosshair.tsx @@ -60,11 +60,13 @@ export default () => { return
- {displayIndicator &&
} + '--crosshair-indicator-size': `${indicatorSize}px`, + borderLeft: `solid ${indicatorSize * indicatorProgress}px white`, + backgroundColor: alternativeIndicator ? 'dodgerblue' : undefined, + }} + />} } diff --git a/src/react/DeathScreen.tsx b/src/react/DeathScreen.tsx index 290e6285..ea4dc726 100644 --- a/src/react/DeathScreen.tsx +++ b/src/react/DeathScreen.tsx @@ -18,12 +18,16 @@ export default ({ dieReasonMessage, respawnCallback, disconnectCallback }: Props
-
diff --git a/src/react/DebugOverlay.tsx b/src/react/DebugOverlay.tsx index 1753d767..9315d8a4 100644 --- a/src/react/DebugOverlay.tsx +++ b/src/react/DebugOverlay.tsx @@ -130,7 +130,7 @@ export default () => {

Prismarine Web Client ({bot.version})

E: {entitiesCount}

{dimension}

-
+

XYZ: {pos.x.toFixed(3)} / {pos.y.toFixed(3)} / {pos.z.toFixed(3)}

Chunk: {Math.floor(pos.x % 16)} ~ {Math.floor(pos.z % 16)} in {Math.floor(pos.x / 16)} ~ {Math.floor(pos.z / 16)}

Packets: {packetsString}

@@ -140,13 +140,13 @@ export default () => {

Biome: minecraft:{loadedData.biomesArray[biomeId]?.name ?? 'unknown biome'}

Day: {day}

-
+
{Object.entries(customEntries.current).map(([name, value]) =>

{name}: {value}

)}

Renderer: {rendererDevice} powered by three.js r{THREE.REVISION}

-
+
{cursorBlock ? (<>

{cursorBlock.name}

{ @@ -154,7 +154,7 @@ export default () => { return

{name}: { typeof value === 'boolean' ? ( - {value} + {String(value)} ) : value }

diff --git a/src/react/DiscordButton.tsx b/src/react/DiscordButton.tsx index a43d62ff..0e3a22b5 100644 --- a/src/react/DiscordButton.tsx +++ b/src/react/DiscordButton.tsx @@ -67,7 +67,8 @@ export const DropdownButton = ({ text, links }: { text: string, links: DropdownB key={el.text} style={{ width: '98px', fontSize: '7px' }} onClick={el.clickHandler} - >{el.text} + >{el.text} + })}
} diff --git a/src/react/FoodBar.tsx b/src/react/FoodBar.tsx index 012e23ea..5a131c07 100644 --- a/src/react/FoodBar.tsx +++ b/src/react/FoodBar.tsx @@ -69,7 +69,8 @@ export default ({ Array.from({ length: 10 }, () => 0) .map((num, index) =>
) + className='food' + />) }
diff --git a/src/react/GoogleButton.tsx b/src/react/GoogleButton.tsx index 4effecb6..dabbbf21 100644 --- a/src/react/GoogleButton.tsx +++ b/src/react/GoogleButton.tsx @@ -1,18 +1,20 @@ import './GoogleButton.css' export default ({ onClick }) => { - return
diff --git a/src/react/KeybindingsScreen.tsx b/src/react/KeybindingsScreen.tsx index c4485878..79d9e186 100644 --- a/src/react/KeybindingsScreen.tsx +++ b/src/react/KeybindingsScreen.tsx @@ -178,16 +178,19 @@ export default ({ setUserConfig, handleClick, bindsMap: bindsMap.current - }}> + }} + > {awaitingInputType && } -
+ >Back + {Object.entries(commands).map(([group, actions], index) => { if (group === 'custom') return null @@ -198,7 +201,8 @@ export default ({ color: 'rgba(255, 255, 255, 0.7)', fontSize: '6px', textAlign: 'center' - }}> + }} + > Note: Left, right and middle click keybindings are hardcoded and cannot be changed currently.
) : null} @@ -221,7 +225,7 @@ export default ({ group={group} action={action} index={index} - inputType={'keyboard'} + inputType="keyboard" keys={keys} gamepad={gamepad} />)} @@ -244,7 +248,7 @@ export default ({ group={group} action={action} index={0} - inputType={'gamepad'} + inputType="gamepad" keys={keys} gamepad={gamepad} /> @@ -306,7 +310,7 @@ export const ButtonWithMatchesAlert = ({ //@ts-format-ignore-region
- This bind is already in use. + This bind is already in use.
) //@ts-format-ignore-endregion - : null - } + : null}
} diff --git a/src/react/MainMenu.tsx b/src/react/MainMenu.tsx index e84fe0cb..b9679224 100644 --- a/src/react/MainMenu.tsx +++ b/src/react/MainMenu.tsx @@ -40,7 +40,7 @@ export default ({
-
+
Prismarine is a beautiful block
@@ -108,10 +108,13 @@ export default ({ Prismarine Web Client {versionStatus} - Privacy Policy + Privacy Policy + A Minecraft client in the browser!
diff --git a/src/react/MobileTopButtons.tsx b/src/react/MobileTopButtons.tsx index c686bb0b..ffeb7adf 100644 --- a/src/react/MobileTopButtons.tsx +++ b/src/react/MobileTopButtons.tsx @@ -35,24 +35,34 @@ export default () => { // ios note: just don't use + } diff --git a/src/react/Notification.tsx b/src/react/Notification.tsx index a9b7a95a..4d73da7e 100644 --- a/src/react/Notification.tsx +++ b/src/react/Notification.tsx @@ -32,29 +32,32 @@ export default ({ type = 'message', message, subMessage = '', open, icon = '', a {state => { const addStyles = { ...basicStyle, ...stateStyles[state] } - return
+ return
+ }} + >
{message}
@@ -62,7 +65,9 @@ export default ({ type = 'message', message, subMessage = '', open, icon = '', a fontSize: '7px', whiteSpace: 'nowrap', color: 'lightgray', - }}>{subMessage}
+ }} + >{subMessage} +
}} diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index adb91ac3..560412de 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -107,7 +107,7 @@ export default () => { if (!isModalActive) return null return )} - {showCancel && } + {options.map(option => )} + {showCancel && } } diff --git a/src/react/ServersList.stories.tsx b/src/react/ServersList.stories.tsx index 486c4e7d..0a0ef151 100644 --- a/src/react/ServersList.stories.tsx +++ b/src/react/ServersList.stories.tsx @@ -18,7 +18,8 @@ const meta: Meta = { accounts={['testting']} onConfirm={(info) => { console.log('add server', info) - }} /> : + }} + /> : { - e.preventDefault() - let ip = serverIp - let version - let msAuth = false - const parts = ip.split(':') - if (parts.at(-1) === 'ms') { - msAuth = true - parts.pop() - } - if (parts.length > 1 && parts.at(-1)!.includes('.')) { - version = parts.at(-1)! - ip = parts.slice(0, -1).join(':') - } - joinServer({ - ip, - versionOverride: version, - authenticatedAccountOverride: msAuth ? true : undefined, // todo popup selector - }, { - shouldSave: save, - }) - }} + return { + e.preventDefault() + let ip = serverIp + let version + let msAuth = false + const parts = ip.split(':') + if (parts.at(-1) === 'ms') { + msAuth = true + parts.pop() + } + if (parts.length > 1 && parts.at(-1)!.includes('.')) { + version = parts.at(-1)! + ip = parts.slice(0, -1).join(':') + } + joinServer({ + ip, + versionOverride: version, + authenticatedAccountOverride: msAuth ? true : undefined, // todo popup selector + }, { + shouldSave: save, + }) + }} >
{/* todo history */} setServerIp(value)} /> + /> Save +
} searchRowChildrenOverride={
+ }} + >
Proxy:
@@ -104,11 +109,13 @@ export default ({ initialProxies, updateProxies: updateProxiesProp, joinServer, status='unknown' ip='' /> - {autocomplete.groupedOptions &&
    + }} + > {autocomplete.groupedOptions.map((proxy, index) => { const { itemRef, ...optionProps } = autocomplete.getOptionProps({ option: proxy, index }) return @@ -144,9 +151,11 @@ const ProxyRender = ({ status, ip, inputRef, value, setValue, ...props }: { success: 'cellular-signal-3', } - return
    + return
    + }} + >
    + }} + > {ip.replace(/^https?:\/\//, '')}
    diff --git a/src/react/SignEditor.stories.tsx b/src/react/SignEditor.stories.tsx index e931bdca..e7bcdd40 100644 --- a/src/react/SignEditor.stories.tsx +++ b/src/react/SignEditor.stories.tsx @@ -5,9 +5,11 @@ import SignEditor from './SignEditor' const meta: Meta = { component: SignEditor, render (args) { - return { - console.log('handleClick', result) - }} /> + return { + console.log('handleClick', result) + }} + /> } } diff --git a/src/react/SignEditor.tsx b/src/react/SignEditor.tsx index e3af3139..1d3be96e 100644 --- a/src/react/SignEditor.tsx +++ b/src/react/SignEditor.tsx @@ -48,11 +48,12 @@ export default ({ handleInput, isWysiwyg, handleClick }: Props) => { const nextElem = elements[focusedElemIndex + dir] nextElem?.focus() } - }}> + }} + >
    {isWysiwyg ? ( -

    +

    ) : [1, 2, 3, 4].map((value, index) => { return { maxLength={15} // overriden by handleInput onChange={(e) => { handleInput(e.currentTarget) - }} /> - }) - } -

    } diff --git a/src/react/SignInMessage.stories.tsx b/src/react/SignInMessage.stories.tsx index 16528700..a86a47e8 100644 --- a/src/react/SignInMessage.stories.tsx +++ b/src/react/SignInMessage.stories.tsx @@ -4,8 +4,7 @@ import SignInMessage from './SignInMessage' const meta: Meta<{ open }> = { component: SignInMessage as any, render ({ open }) { - return + return }, } diff --git a/src/react/SignInMessage.tsx b/src/react/SignInMessage.tsx index c33cdf41..de4359c1 100644 --- a/src/react/SignInMessage.tsx +++ b/src/react/SignInMessage.tsx @@ -36,7 +36,8 @@ export default ({ height: 213, color: 'black', // borderRadius: 8, - }}> + }} + >
    {code}
    + }} + >{code} +
+ }} + > Waiting... {timeLeft}
+ }} + > To join a Minecraft server {connectingServer} using your Microsoft account, you need to visit{' '} Direct Link + >Direct Link + {' '} or {' '} {loginLink} + >{loginLink} + {' '} and enter the code above.
@@ -85,7 +92,8 @@ export default ({ fontSize: 12, marginTop: 5, color: 'gray' - }}> + }} + > Join only vanilla servers! This client is detectable and may result in a ban by anti-cheat plugins.
} {setSaveToken && } @@ -104,6 +113,7 @@ export default ({ marginTop: -5, }} onClick={onCancel} - >Cancel + >Cancel + } diff --git a/src/react/Singleplayer.tsx b/src/react/Singleplayer.tsx index a7851835..427bc621 100644 --- a/src/react/Singleplayer.tsx +++ b/src/react/Singleplayer.tsx @@ -45,12 +45,14 @@ const World = ({ name, isFocused, title, lastPlayed, size, detail = '', onFocus, return filesize(size) }, [size]) - return
onFocus?.(name)} onKeyDown={(e) => { - if (e.code === 'Enter' || e.code === 'Space') { - e.preventDefault() - onInteraction?.(e.code === 'Enter' ? 'enter' : 'space') - } - }} onDoubleClick={() => onInteraction?.('enter')}> + return
onFocus?.(name)} onKeyDown={(e) => { + if (e.code === 'Enter' || e.code === 'Space') { + e.preventDefault() + onInteraction?.(e.code === 'Enter' ? 'enter' : 'space') + } + }} onDoubleClick={() => onInteraction?.('enter')} + > world preview
@@ -134,18 +136,22 @@ export default ({ setSearch(value)} />
}
- { - setActiveProvider?.(tab as any) - }} fullSize /> + { + setActiveProvider?.(tab as any) + }} fullSize + />
+ }} + > { providerActions &&
+ }} + > Actions: {Object.entries(providerActions).map(([label, action]) => ( typeof action === 'function' ? : {action} ))} @@ -154,15 +160,19 @@ export default ({ { worldData ? worldData.filter(data => data.title.toLowerCase().includes(search.toLowerCase())).map(({ name, size, detail, ...rest }) => ( - { - if (interaction === 'enter') onWorldAction('load', name) - else if (interaction === 'space') firstButton.current?.focus() - }} detail={detail} /> + { + if (interaction === 'enter') onWorldAction('load', name) + else if (interaction === 'space') firstButton.current?.focus() + }} + detail={detail} + /> )) :
{error || 'Loading (check #dev console if loading too long)...'}
+ }}>{error || 'Loading (check #dev console if loading too long)...'} +
} { warning &&
+ }} + > {warning} {warningAction && {warningActionLabel}}
} @@ -186,7 +197,7 @@ export default ({ {serversLayout ? : - } + }
diff --git a/src/react/Slider.tsx b/src/react/Slider.tsx index 34cc2717..e177578c 100644 --- a/src/react/Slider.tsx +++ b/src/react/Slider.tsx @@ -73,8 +73,8 @@ const Slider: React.FC = ({ fireValueUpdate(true) }} /> -
-
+
+
diff --git a/src/react/SoundMuffler.tsx b/src/react/SoundMuffler.tsx index ecbae14d..d8571353 100644 --- a/src/react/SoundMuffler.tsx +++ b/src/react/SoundMuffler.tsx @@ -17,13 +17,15 @@ const SoundRow = ({ sound, children }) => { {sound} {children}
- +
} diff --git a/src/react/Tabs.tsx b/src/react/Tabs.tsx index 4e750a8a..86265083 100644 --- a/src/react/Tabs.tsx +++ b/src/react/Tabs.tsx @@ -16,24 +16,30 @@ export default ({ tabs, activeTab, labels, onTabChange, fullSize, style, disable width: fullSize ? '100%' : undefined, display: fullSize ? 'flex' : undefined, ...style, - }}> + }} + > {tabs.map(tab => { const active = tab === activeTab - return
- + return
+ {active &&
} + }} + />}
})}
diff --git a/src/react/TouchAreasControls.tsx b/src/react/TouchAreasControls.tsx index 0476be48..58ea51aa 100644 --- a/src/react/TouchAreasControls.tsx +++ b/src/react/TouchAreasControls.tsx @@ -201,7 +201,8 @@ export default ({ touchActive, setupActive, buttonsPositions, closeButtonsSetup left: `${pointer.x / window.innerWidth * 100}%`, top: `${pointer.y / window.innerHeight * 100}%` } : {} - }}> + }} + >
+ }} + > + }} + >Cancel + + }} + >Apply +
}
} diff --git a/src/react/XPBar.tsx b/src/react/XPBar.tsx index 5de4c61a..0727879f 100644 --- a/src/react/XPBar.tsx +++ b/src/react/XPBar.tsx @@ -8,7 +8,7 @@ export default ({ progress, level, gamemode }: { progress: number; level: number className={styles['xp-bar-bg']} style={{ display: gamemode === 'creative' || gamemode === 'spectator' ? 'none' : 'block' }} > -
+
0 ? 'block' : 'none' }}>{level}
diff --git a/src/reactUi.tsx b/src/reactUi.tsx index 655a6029..b40c47a1 100644 --- a/src/reactUi.tsx +++ b/src/reactUi.tsx @@ -160,7 +160,7 @@ const App = () => {
-
+
@@ -186,18 +186,23 @@ const App = () => {
-
+
} const PerComponentErrorBoundary = ({ children }) => { - return children.map((child, i) => { - const componentNameClean = (child.type.name || child.type.displayName || 'Unknown').replaceAll(/__|_COMPONENT/g, '') - showNotification(`UI component ${componentNameClean} crashed!`, 'Please report this. Use console for more.', true, undefined) - return null - }}>{child}) + return children.map((child, i) => { + const componentNameClean = (child.type.name || child.type.displayName || 'Unknown').replaceAll(/__|_COMPONENT/g, '') + showNotification(`UI component ${componentNameClean} crashed!`, 'Please report this. Use console for more.', true, undefined) + return null + }} + > + {child} + ) } renderToDom(, { From d903c47d3f95195372343389e55a6ef7760dd87e Mon Sep 17 00:00:00 2001 From: Valery-a <83373303+Valery-a@users.noreply.github.com> Date: Fri, 16 Aug 2024 02:55:10 +0300 Subject: [PATCH 007/865] fix: do not interact with blocks behind entities (#177) --- src/worldInteractions.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts index ffb5fa4a..7a9aa508 100644 --- a/src/worldInteractions.ts +++ b/src/worldInteractions.ts @@ -221,7 +221,12 @@ class WorldInteraction { // todo this shouldnt be done in the render loop, migrate the code to dom events to avoid delays on lags update () { const inSpectator = bot.game.gameMode === 'spectator' - const cursorBlock = inSpectator && !options.showCursorBlockInSpectator ? null : bot.blockAtCursor(5) + const entity = getEntityCursor() + let cursorBlock = inSpectator && !options.showCursorBlockInSpectator ? null : bot.blockAtCursor(5) + if (entity) { + cursorBlock = null + } + let cursorBlockDiggable = cursorBlock if (cursorBlock && !bot.canDigBlock(cursorBlock) && bot.game.gameMode !== 'creative') cursorBlockDiggable = null From f5da7f22610855183f8ab8361b61c9736fc59892 Mon Sep 17 00:00:00 2001 From: gguio <109200692+gguio@users.noreply.github.com> Date: Fri, 16 Aug 2024 13:24:13 +0400 Subject: [PATCH 008/865] feat: New select component (#169) --- package.json | 1 + pnpm-lock.yaml | 380 ++++++++++++++++++++---------- src/react/AddServerOrConnect.tsx | 24 +- src/react/CreateWorld.tsx | 19 +- src/react/Input.tsx | 21 +- src/react/Select.css | 5 + src/react/Select.stories.tsx | 24 ++ src/react/Select.tsx | 109 +++++++++ src/react/SelectGameVersion.tsx | 51 ++++ src/react/ServersList.tsx | 98 +------- src/react/ServersListProvider.tsx | 4 +- src/react/Singleplayer.tsx | 2 +- src/react/select.module.css | 20 ++ src/react/select.module.css.d.ts | 8 + 14 files changed, 527 insertions(+), 239 deletions(-) create mode 100644 src/react/Select.css create mode 100644 src/react/Select.stories.tsx create mode 100644 src/react/Select.tsx create mode 100644 src/react/SelectGameVersion.tsx create mode 100644 src/react/select.module.css create mode 100644 src/react/select.module.css.d.ts diff --git a/package.json b/package.json index 6d31f0d9..7b27333e 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "qrcode.react": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-select": "^5.8.0", "react-transition-group": "^4.4.5", "remark": "^15.0.1", "sanitize-filename": "^1.6.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9cee15da..486eebe6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -136,7 +136,7 @@ importers: version: 3.65.0 minecraft-protocol: specifier: github:PrismarineJS/node-minecraft-protocol#master - version: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/495eed56ab230b2615596590064671356d86a2dc(patch_hash=7otpchsbv7hxsuis4rrrwdtbve)(encoding@0.1.13) + version: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/7057ad979b416192ada235f2f4e3b5eb26af5fa1(patch_hash=7otpchsbv7hxsuis4rrrwdtbve)(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) @@ -185,6 +185,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-select: + specifier: ^5.8.0 + version: 5.8.0(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-transition-group: specifier: ^4.4.5 version: 4.4.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -345,7 +348,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/7f65e46a048f1bc2b57775d84b32400dce707321(encoding@0.1.13) + version: https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502(encoding@0.1.13) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -423,7 +426,7 @@ importers: version: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 prismarine-chunk: specifier: github:zardoy/prismarine-chunk - version: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/9662306deea57d8d0ba0a2a3f3f7adb95f0131e3(minecraft-data@3.65.0) + version: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.65.0) prismarine-schematic: specifier: ^1.2.0 version: 1.2.3 @@ -1243,9 +1246,15 @@ packages: '@emotion/babel-plugin@11.11.0': resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} + '@emotion/babel-plugin@11.12.0': + resolution: {integrity: sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==} + '@emotion/cache@11.11.0': resolution: {integrity: sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==} + '@emotion/cache@11.13.1': + resolution: {integrity: sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==} + '@emotion/css@11.5.0': resolution: {integrity: sha512-mqjz/3aqR9rp40M+pvwdKYWxlQK4Nj3cnNjo3Tx6SM14dSsEn7q/4W2/I7PlgG+mb27iITHugXuBIHH/QwUBVQ==} peerDependencies: @@ -1257,29 +1266,64 @@ packages: '@emotion/hash@0.9.1': resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==} + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + '@emotion/memoize@0.8.1': resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.13.0': + resolution: {integrity: sha512-WkL+bw1REC2VNV1goQyfxjx1GYJkcc23CRQkXX+vZNLINyfI7o+uUn/rTGPt/xJ3bJHd5GcljgnxHf4wRw5VWQ==} + peerDependencies: + '@types/react': '*' + react: ^18.2.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@emotion/serialize@1.1.2': resolution: {integrity: sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==} + '@emotion/serialize@1.3.0': + resolution: {integrity: sha512-jACuBa9SlYajnpIVXB+XOXnfJHyckDfe6fOpORIM6yhBDlqGuExvDdZYHDQGoDf3bZXGv7tNr+LpLjJqiEQ6EA==} + '@emotion/sheet@1.2.2': resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==} + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + '@emotion/unitless@0.8.1': resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} + '@emotion/unitless@0.9.0': + resolution: {integrity: sha512-TP6GgNZtmtFaFcsOgExdnfxLLpRDla4Q66tnenA9CktvVSdNKDvMVuUah4QvWPIpNjrWsGg3qeGo9a43QooGZQ==} + '@emotion/use-insertion-effect-with-fallbacks@1.0.1': resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==} peerDependencies: react: ^18.2.0 + '@emotion/use-insertion-effect-with-fallbacks@1.1.0': + resolution: {integrity: sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==} + peerDependencies: + react: ^18.2.0 + '@emotion/utils@1.2.1': resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==} + '@emotion/utils@1.4.0': + resolution: {integrity: sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==} + '@emotion/weak-memoize@0.3.1': resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==} + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + '@esbuild/aix-ppc64@0.19.11': resolution: {integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==} engines: {node: '>=12'} @@ -1746,6 +1790,7 @@ packages: '@humanwhocodes/config-array@0.11.11': resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -1753,6 +1798,7 @@ packages: '@humanwhocodes/object-schema@1.2.1': resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + deprecated: Use @eslint/object-schema instead '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} @@ -3505,10 +3551,12 @@ packages: are-we-there-yet@2.0.0: resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} engines: {node: '>=10'} + deprecated: This package is no longer supported. are-we-there-yet@3.0.1: resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -5194,10 +5242,12 @@ packages: gauge@3.0.2: resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} engines: {node: '>=10'} + deprecated: This package is no longer supported. gauge@4.0.4: resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} @@ -5299,10 +5349,12 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} @@ -5439,6 +5491,9 @@ packages: resolution: {integrity: sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==} hasBin: true + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} @@ -5569,6 +5624,7 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -6160,6 +6216,7 @@ packages: loupe@2.3.6: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + deprecated: Please upgrade to 2.3.7 which fixes GHSA-4q6p-r6v2-jvc5 lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -6277,6 +6334,9 @@ packages: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + memoizerific@1.11.3: resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} @@ -6426,11 +6486,6 @@ 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/495eed56ab230b2615596590064671356d86a2dc: - resolution: {tarball: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/495eed56ab230b2615596590064671356d86a2dc} - version: 1.47.0 - engines: {node: '>=14'} - minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/7057ad979b416192ada235f2f4e3b5eb26af5fa1: resolution: {tarball: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/7057ad979b416192ada235f2f4e3b5eb26af5fa1} version: 1.47.0 @@ -6454,8 +6509,8 @@ packages: resolution: {integrity: sha512-QMMNPx4IyZE7ydAzjvGLQLCnQNUOfkk1qVZKxTTS9q3qPTAewz4GhsVUBtbQ8LSbHthe5RcQ1Sgxs4wlIma/Qw==} engines: {node: '>=18'} - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/7f65e46a048f1bc2b57775d84b32400dce707321: - resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/7f65e46a048f1bc2b57775d84b32400dce707321} + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502: + resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502} version: 4.20.1 engines: {node: '>=18'} @@ -6713,10 +6768,12 @@ packages: npmlog@5.0.1: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. npmlog@6.0.2: resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -6973,6 +7030,7 @@ packages: phin@2.9.3: resolution: {integrity: sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} @@ -7116,11 +7174,6 @@ packages: prismarine-chat@1.10.1: resolution: {integrity: sha512-XukYcuueuhDxzEXG7r8BZyt6jOObrPPB4JESCgb+/XenB9nExoSHF8eTQWWj8faKPLqm1dRQaYwFJlNBlJZJUw==} - prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/9662306deea57d8d0ba0a2a3f3f7adb95f0131e3: - resolution: {tarball: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/9662306deea57d8d0ba0a2a3f3f7adb95f0131e3} - 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 @@ -7473,6 +7526,12 @@ packages: '@types/react': optional: true + react-select@5.8.0: + resolution: {integrity: sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==} + peerDependencies: + react: ^18.2.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-style-singleton@2.2.1: resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} engines: {node: '>=10'} @@ -7687,14 +7746,17 @@ packages: rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@5.0.1: @@ -8667,6 +8729,15 @@ packages: peerDependencies: react: ^18.2.0 + use-isomorphic-layout-effect@1.1.2: + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^18.2.0 + peerDependenciesMeta: + '@types/react': + optional: true + use-resize-observer@9.1.0: resolution: {integrity: sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==} peerDependencies: @@ -8981,6 +9052,7 @@ packages: workbox-google-analytics@7.0.0: resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained workbox-navigation-preload@7.0.0: resolution: {integrity: sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==} @@ -9311,7 +9383,7 @@ snapshots: '@babel/helper-member-expression-to-functions@7.22.5': dependencies: - '@babel/types': 7.22.11 + '@babel/types': 7.23.0 '@babel/helper-member-expression-to-functions@7.23.0': dependencies: @@ -9329,7 +9401,7 @@ snapshots: dependencies: '@babel/core': 7.22.11 '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-module-imports': 7.22.5 + '@babel/helper-module-imports': 7.22.15 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 '@babel/helper-validator-identifier': 7.22.5 @@ -9389,7 +9461,7 @@ snapshots: dependencies: '@babel/helper-function-name': 7.22.5 '@babel/template': 7.22.5 - '@babel/types': 7.22.11 + '@babel/types': 7.23.0 '@babel/helpers@7.22.11': dependencies: @@ -10113,7 +10185,7 @@ snapshots: '@emotion/babel-plugin@11.11.0': dependencies: '@babel/helper-module-imports': 7.22.5 - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@emotion/hash': 0.9.1 '@emotion/memoize': 0.8.1 '@emotion/serialize': 1.1.2 @@ -10124,6 +10196,20 @@ snapshots: source-map: 0.5.7 stylis: 4.2.0 + '@emotion/babel-plugin@11.12.0': + dependencies: + '@babel/helper-module-imports': 7.22.15 + '@babel/runtime': 7.24.5 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.0 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + '@emotion/cache@11.11.0': dependencies: '@emotion/memoize': 0.8.1 @@ -10132,6 +10218,14 @@ snapshots: '@emotion/weak-memoize': 0.3.1 stylis: 4.2.0 + '@emotion/cache@11.13.1': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.0 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + '@emotion/css@11.5.0(@babel/core@7.22.11)': dependencies: '@emotion/babel-plugin': 11.11.0 @@ -10144,8 +10238,26 @@ snapshots: '@emotion/hash@0.9.1': {} + '@emotion/hash@0.9.2': {} + '@emotion/memoize@0.8.1': {} + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.13.0(@types/react@18.2.20)(react@18.2.0)': + dependencies: + '@babel/runtime': 7.24.5 + '@emotion/babel-plugin': 11.12.0 + '@emotion/cache': 11.13.1 + '@emotion/serialize': 1.3.0 + '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.2.0) + '@emotion/utils': 1.4.0 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.20 + '@emotion/serialize@1.1.2': dependencies: '@emotion/hash': 0.9.1 @@ -10154,18 +10266,38 @@ snapshots: '@emotion/utils': 1.2.1 csstype: 3.1.2 + '@emotion/serialize@1.3.0': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.9.0 + '@emotion/utils': 1.4.0 + csstype: 3.1.2 + '@emotion/sheet@1.2.2': {} + '@emotion/sheet@1.4.0': {} + '@emotion/unitless@0.8.1': {} + '@emotion/unitless@0.9.0': {} + '@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0)': dependencies: react: 18.2.0 + '@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.2.0)': + dependencies: + react: 18.2.0 + '@emotion/utils@1.2.1': {} + '@emotion/utils@1.4.0': {} + '@emotion/weak-memoize@0.3.1': {} + '@emotion/weak-memoize@0.4.0': {} + '@esbuild/aix-ppc64@0.19.11': optional: true @@ -10500,7 +10632,7 @@ snapshots: '@jimp/bmp@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 bmp-js: 0.1.0 @@ -10509,7 +10641,7 @@ snapshots: '@jimp/core@0.10.3': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/utils': 0.10.3 any-base: 1.1.0 buffer: 6.0.3 @@ -10525,14 +10657,14 @@ snapshots: '@jimp/custom@0.10.3': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/core': 0.10.3 core-js: 3.37.1 optional: true '@jimp/gif@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10541,7 +10673,7 @@ snapshots: '@jimp/jpeg@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10550,7 +10682,7 @@ snapshots: '@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10558,7 +10690,7 @@ snapshots: '@jimp/plugin-blur@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10566,7 +10698,7 @@ snapshots: '@jimp/plugin-circle@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10574,7 +10706,7 @@ snapshots: '@jimp/plugin-color@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10583,7 +10715,7 @@ snapshots: '@jimp/plugin-contain@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3) '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3) @@ -10594,7 +10726,7 @@ snapshots: '@jimp/plugin-cover@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3) '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3) @@ -10605,7 +10737,7 @@ snapshots: '@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10613,7 +10745,7 @@ snapshots: '@jimp/plugin-displace@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10621,7 +10753,7 @@ snapshots: '@jimp/plugin-dither@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10629,7 +10761,7 @@ snapshots: '@jimp/plugin-fisheye@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10637,7 +10769,7 @@ snapshots: '@jimp/plugin-flip@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-rotate@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)))': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/plugin-rotate': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)) '@jimp/utils': 0.10.3 @@ -10646,7 +10778,7 @@ snapshots: '@jimp/plugin-gaussian@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10654,7 +10786,7 @@ snapshots: '@jimp/plugin-invert@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10662,7 +10794,7 @@ snapshots: '@jimp/plugin-mask@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10670,7 +10802,7 @@ snapshots: '@jimp/plugin-normalize@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10678,7 +10810,7 @@ snapshots: '@jimp/plugin-print@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3) '@jimp/utils': 0.10.3 @@ -10688,7 +10820,7 @@ snapshots: '@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10696,7 +10828,7 @@ snapshots: '@jimp/plugin-rotate@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3) '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3) @@ -10707,7 +10839,7 @@ snapshots: '@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3) '@jimp/utils': 0.10.3 @@ -10716,7 +10848,7 @@ snapshots: '@jimp/plugin-shadow@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blur@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/plugin-blur': 0.10.3(@jimp/custom@0.10.3) '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3) @@ -10726,7 +10858,7 @@ snapshots: '@jimp/plugin-threshold@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-color@0.10.3(@jimp/custom@0.10.3))(@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3))': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/plugin-color': 0.10.3(@jimp/custom@0.10.3) '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3) @@ -10736,7 +10868,7 @@ snapshots: '@jimp/plugins@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3) '@jimp/plugin-blur': 0.10.3(@jimp/custom@0.10.3) @@ -10765,7 +10897,7 @@ snapshots: '@jimp/png@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/utils': 0.10.3 core-js: 3.37.1 @@ -10774,7 +10906,7 @@ snapshots: '@jimp/tiff@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 core-js: 3.37.1 utif: 2.0.1 @@ -10782,7 +10914,7 @@ snapshots: '@jimp/types@0.10.3(@jimp/custom@0.10.3)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/bmp': 0.10.3(@jimp/custom@0.10.3) '@jimp/custom': 0.10.3 '@jimp/gif': 0.10.3(@jimp/custom@0.10.3) @@ -10795,7 +10927,7 @@ snapshots: '@jimp/utils@0.10.3': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 core-js: 3.37.1 regenerator-runtime: 0.13.11 optional: true @@ -10966,15 +11098,15 @@ snapshots: '@radix-ui/number@1.0.1': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/primitive@1.0.1': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -10984,7 +11116,7 @@ snapshots: '@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -10997,28 +11129,28 @@ snapshots: '@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 react: 18.2.0 optionalDependencies: '@types/react': 18.2.20 '@radix-ui/react-context@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 react: 18.2.0 optionalDependencies: '@types/react': 18.2.20 '@radix-ui/react-direction@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 react: 18.2.0 optionalDependencies: '@types/react': 18.2.20 '@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -11032,14 +11164,14 @@ snapshots: '@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 react: 18.2.0 optionalDependencies: '@types/react': 18.2.20 '@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.20)(react@18.2.0) @@ -11051,7 +11183,7 @@ snapshots: '@radix-ui/react-id@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.20)(react@18.2.0) react: 18.2.0 optionalDependencies: @@ -11059,7 +11191,7 @@ snapshots: '@radix-ui/react-popper@1.1.2(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@floating-ui/react-dom': 2.0.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) @@ -11078,7 +11210,7 @@ snapshots: '@radix-ui/react-portal@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -11088,7 +11220,7 @@ snapshots: '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-slot': 1.0.2(@types/react@18.2.20)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -11098,7 +11230,7 @@ snapshots: '@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) @@ -11116,7 +11248,7 @@ snapshots: '@radix-ui/react-select@1.2.2(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/number': 1.0.1 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -11146,7 +11278,7 @@ snapshots: '@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -11156,7 +11288,7 @@ snapshots: '@radix-ui/react-slot@1.0.2(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) react: 18.2.0 optionalDependencies: @@ -11164,7 +11296,7 @@ snapshots: '@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-direction': 1.0.1(@types/react@18.2.20)(react@18.2.0) @@ -11180,7 +11312,7 @@ snapshots: '@radix-ui/react-toggle@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.20)(react@18.2.0) @@ -11192,7 +11324,7 @@ snapshots: '@radix-ui/react-toolbar@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-direction': 1.0.1(@types/react@18.2.20)(react@18.2.0) @@ -11208,14 +11340,14 @@ snapshots: '@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 react: 18.2.0 optionalDependencies: '@types/react': 18.2.20 '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.20)(react@18.2.0) react: 18.2.0 optionalDependencies: @@ -11223,7 +11355,7 @@ snapshots: '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.20)(react@18.2.0) react: 18.2.0 optionalDependencies: @@ -11231,21 +11363,21 @@ snapshots: '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 react: 18.2.0 optionalDependencies: '@types/react': 18.2.20 '@radix-ui/react-use-previous@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 react: 18.2.0 optionalDependencies: '@types/react': 18.2.20 '@radix-ui/react-use-rect@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/rect': 1.0.1 react: 18.2.0 optionalDependencies: @@ -11253,7 +11385,7 @@ snapshots: '@radix-ui/react-use-size@1.0.1(@types/react@18.2.20)(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.20)(react@18.2.0) react: 18.2.0 optionalDependencies: @@ -11261,7 +11393,7 @@ snapshots: '@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -11271,7 +11403,7 @@ snapshots: '@radix-ui/rect@1.0.1': dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@react-oauth/google@0.12.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: @@ -13196,7 +13328,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 cosmiconfig: 7.1.0 resolve: 1.22.4 @@ -14207,7 +14339,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 csstype: 3.1.2 dom-serializer@2.0.0: @@ -15598,6 +15730,10 @@ snapshots: nopt: 1.0.10 optional: true + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + hosted-git-info@2.8.9: {} hosted-git-info@4.1.0: @@ -16084,7 +16220,7 @@ snapshots: jimp@0.10.3: dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 '@jimp/custom': 0.10.3 '@jimp/plugins': 0.10.3(@jimp/custom@0.10.3) '@jimp/types': 0.10.3(@jimp/custom@0.10.3) @@ -16534,6 +16670,8 @@ snapshots: dependencies: fs-monkey: 1.0.6 + memoize-one@6.0.0: {} + memoizerific@1.11.3: dependencies: map-or-similar: 1.5.0 @@ -16746,31 +16884,6 @@ snapshots: - '@types/react' - react - minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/495eed56ab230b2615596590064671356d86a2dc(patch_hash=7otpchsbv7hxsuis4rrrwdtbve)(encoding@0.1.13): - dependencies: - '@types/readable-stream': 4.0.12 - aes-js: 3.1.2 - buffer-equal: 1.0.1 - debug: 4.3.4(supports-color@8.1.1) - endian-toggle: 0.0.0 - lodash.get: 4.4.2 - lodash.merge: 4.6.2 - minecraft-data: 3.65.0 - minecraft-folder-path: 1.2.0 - node-fetch: 2.7.0(encoding@0.1.13) - node-rsa: 0.4.2 - prismarine-auth: 2.4.2(encoding@0.1.13) - prismarine-chat: 1.10.1 - prismarine-nbt: 2.5.0 - prismarine-realms: 1.3.2(encoding@0.1.13) - protodef: 1.15.0 - readable-stream: 4.5.2 - uuid-1345: 1.0.2 - yggdrasil: 1.7.0(encoding@0.1.13) - transitivePeerDependencies: - - encoding - - supports-color - minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/7057ad979b416192ada235f2f4e3b5eb26af5fa1(patch_hash=7otpchsbv7hxsuis4rrrwdtbve)(encoding@0.1.13): dependencies: '@types/readable-stream': 4.0.12 @@ -16840,11 +16953,11 @@ snapshots: mineflayer@4.20.1(encoding@0.1.13): dependencies: minecraft-data: 3.65.0 - minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/495eed56ab230b2615596590064671356d86a2dc(patch_hash=7otpchsbv7hxsuis4rrrwdtbve)(encoding@0.1.13) + minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/7057ad979b416192ada235f2f4e3b5eb26af5fa1(patch_hash=7otpchsbv7hxsuis4rrrwdtbve)(encoding@0.1.13) prismarine-biome: 1.3.0(minecraft-data@3.65.0)(prismarine-registry@1.7.0) prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 prismarine-chat: 1.10.1 - prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/9662306deea57d8d0ba0a2a3f3f7adb95f0131e3(minecraft-data@3.65.0) + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.65.0) prismarine-entity: 2.3.1 prismarine-item: 1.14.0 prismarine-nbt: 2.5.0 @@ -16860,14 +16973,14 @@ snapshots: - encoding - supports-color - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/7f65e46a048f1bc2b57775d84b32400dce707321(encoding@0.1.13): + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502(encoding@0.1.13): dependencies: minecraft-data: 3.65.0 - minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/495eed56ab230b2615596590064671356d86a2dc(patch_hash=7otpchsbv7hxsuis4rrrwdtbve)(encoding@0.1.13) + minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/7057ad979b416192ada235f2f4e3b5eb26af5fa1(patch_hash=7otpchsbv7hxsuis4rrrwdtbve)(encoding@0.1.13) prismarine-biome: 1.3.0(minecraft-data@3.65.0)(prismarine-registry@1.7.0) prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 prismarine-chat: 1.10.1 - prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/9662306deea57d8d0ba0a2a3f3f7adb95f0131e3(minecraft-data@3.65.0) + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.65.0) prismarine-entity: 2.3.1 prismarine-item: 1.14.0 prismarine-nbt: 2.5.0 @@ -17611,19 +17724,6 @@ snapshots: prismarine-nbt: 2.5.0 prismarine-registry: 1.7.0 - prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/9662306deea57d8d0ba0a2a3f3f7adb95f0131e3(minecraft-data@3.65.0): - dependencies: - prismarine-biome: 1.3.0(minecraft-data@3.65.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 - 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.65.0): dependencies: prismarine-biome: 1.3.0(minecraft-data@3.65.0)(prismarine-registry@1.7.0) @@ -17666,7 +17766,7 @@ snapshots: prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/0228b5252f48a0d6ad7f36d7189851c427fbe8c4(minecraft-data@3.65.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/9662306deea57d8d0ba0a2a3f3f7adb95f0131e3(minecraft-data@3.65.0) + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.65.0) prismarine-nbt: 2.5.0 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/187a87f6d71cba12881a7bbaa510ed9085bf6da7 uint4: 0.1.2 @@ -18072,6 +18172,22 @@ snapshots: optionalDependencies: '@types/react': 18.2.20 + react-select@5.8.0(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + '@babel/runtime': 7.24.5 + '@emotion/cache': 11.11.0 + '@emotion/react': 11.13.0(@types/react@18.2.20)(react@18.2.0) + '@floating-ui/dom': 1.5.3 + '@types/react-transition-group': 4.4.7 + memoize-one: 6.0.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-transition-group: 4.4.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.20)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + react-style-singleton@2.2.1(@types/react@18.2.20)(react@18.2.0): dependencies: get-nonce: 1.0.1 @@ -18241,7 +18357,7 @@ snapshots: regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 regexp-tree@0.1.27: {} @@ -18402,7 +18518,7 @@ snapshots: rtl-css-js@1.16.1: dependencies: - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.24.5 run-parallel@1.2.0: dependencies: @@ -19536,6 +19652,12 @@ snapshots: dequal: 1.0.0 react: 18.2.0 + use-isomorphic-layout-effect@1.1.2(@types/react@18.2.20)(react@18.2.0): + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.20 + use-resize-observer@9.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@juggle/resize-observer': 3.3.1 diff --git a/src/react/AddServerOrConnect.tsx b/src/react/AddServerOrConnect.tsx index 648cda98..a933a1d7 100644 --- a/src/react/AddServerOrConnect.tsx +++ b/src/react/AddServerOrConnect.tsx @@ -1,7 +1,8 @@ -import React, { useEffect } from 'react' +import React from 'react' import Screen from './Screen' import Input from './Input' import Button from './Button' +import SelectGameVersion from './SelectGameVersion' import { useIsSmallWidth } from './simpleHooks' export interface BaseServerInfo { @@ -24,11 +25,12 @@ interface Props { defaults?: Pick accounts?: string[] authenticatedAccounts?: number + versions?: string[] } const ELEMENTS_WIDTH = 190 -export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQs, onQsConnect, defaults, accounts, authenticatedAccounts }: Props) => { +export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQs, onQsConnect, defaults, accounts, versions, authenticatedAccounts }: Props) => { const qsParams = parseQs ? new URLSearchParams(window.location.search) : undefined const qsParamName = qsParams?.get('name') const qsParamIp = qsParams?.get('ip') @@ -93,7 +95,23 @@ export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQ setServerIp(value)} /> setServerPort(value)} placeholder='25565' />
Overrides:
- setVersionOverride(value)} placeholder='Optional, but recommended to specify' /> +
+ + { return { value: v, label: v } }) ?? []} + onChange={(value) => { + setVersionOverride(value) + }} + // inputProps={{ + // placeholder: 'Optional, but recommended to specify', + // disabled: lockConnect && qsParamVersion !== null + // }} + /> +
+ setProxyOverride(value)} placeholder={defaults?.proxyOverride} /> setUsernameOverride(value)} placeholder={defaults?.usernameOverride} />
diff --git a/src/react/Select.tsx b/src/react/Select.tsx index 7fe8aaea..8d79fe54 100644 --- a/src/react/Select.tsx +++ b/src/react/Select.tsx @@ -14,14 +14,11 @@ interface Props { initialOptions: OptionStorage[] updateOptions: (options: string) => void getCssOnInput?: (input: string) => CSSProperties | undefined - processOption?: (option: string) => string onValueChange?: (newVal: string) => void defaultValue?: { value: string, label: string } - iconInput?: string placeholder?: string - iconOption?: string containerStyle?: CSSProperties - inputProps?: React.ComponentProps + disabled?: boolean } export default ({ @@ -31,7 +28,8 @@ export default ({ onValueChange, defaultValue, containerStyle, - placeholder + placeholder, + disabled }: Props) => { const [inputValue, setInputValue] = useState(defaultValue?.label ?? '') const [currValue, setCurrValue] = useState(defaultValue?.label ?? '') @@ -48,6 +46,7 @@ export default ({ formatCreateLabel={(value) => { return 'Use "' + value + '"' }} + isDisabled={disabled} placeholder={placeholder ?? ''} onChange={(e, action) => { console.log('value:', e?.value) diff --git a/src/react/SelectGameVersion.tsx b/src/react/SelectGameVersion.tsx index ec8c9726..8188b827 100644 --- a/src/react/SelectGameVersion.tsx +++ b/src/react/SelectGameVersion.tsx @@ -1,6 +1,5 @@ import React, { CSSProperties } from 'react' import Select from './Select' -import Input from './Input' type Version = { value: string, label: string } @@ -9,11 +8,9 @@ export default ( { versions: Version[], selected?: Version, - inputProps?: React.ComponentProps, onChange?: (newValue: string) => void, updateOptions?: (newSel: string) => void, - containerStyle?: CSSProperties - } + } & Pick, 'containerStyle' | 'placeholder' | 'disabled'> ) => { return Date: Sun, 25 Aug 2024 23:43:10 +0300 Subject: [PATCH 024/865] fix: make entities movements smoother --- prismarine-viewer/viewer/lib/entities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prismarine-viewer/viewer/lib/entities.js b/prismarine-viewer/viewer/lib/entities.js index 8d8630ac..57a45a44 100644 --- a/prismarine-viewer/viewer/lib/entities.js +++ b/prismarine-viewer/viewer/lib/entities.js @@ -15,7 +15,7 @@ import { WalkingGeneralSwing } from './entity/animations' import externalTexturesJson from './entity/externalTextures.json' import { disposeObject } from './threeJsUtils' -export const TWEEN_DURATION = 50 // todo should be 100 +export const TWEEN_DURATION = 120 /** * @param {string} username From de3907aefedbb9516283bd1b0dcdee795e17b709 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 25 Aug 2024 23:53:07 +0300 Subject: [PATCH 025/865] add more context to item render error --- src/inventoryWindows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 6ef8ebf7..247fad67 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -166,7 +166,7 @@ const renderSlot = (slot: RenderSlot, skipBlock = false): { itemTexture = itemsRenderer.getItemTexture(itemName) ?? itemsRenderer.getItemTexture('item/missing_texture')! } catch (err) { itemTexture = itemsRenderer.getItemTexture('block/errored')! - inGameError(err) + inGameError(`Failed to render item ${itemName} on ${bot.version} (resourcepack: ${options.enabledResourcepack}): ${err.message}`) } if ('type' in itemTexture) { // is item From 34a6f1d9c35d28786d57519069fdc73b14ec2704 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 26 Aug 2024 00:41:23 +0300 Subject: [PATCH 026/865] fix: fix critical bug which was resulting in incorrect modals (and whole app) state in some extremely rare cases --- src/globalState.ts | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/globalState.ts b/src/globalState.ts index 3e6c06e8..d4366399 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -35,24 +35,11 @@ subscribe(activeModalStack, () => { } }) -export const customDisplayManageKeyword = 'custom' - -const defaultModalActions = { - show (modal: Modal) { - if (modal.elem) modal.elem.style.display = 'block' - }, - hide (modal: Modal) { - if (modal.elem) modal.elem.style.display = 'none' - } -} - /** * @returns true if operation was successful */ const showModalInner = (modal: Modal) => { const cancel = modal.elem?.show?.() - if (cancel && cancel !== customDisplayManageKeyword) return false - if (cancel !== 'custom') defaultModalActions.show(modal) return true } @@ -60,7 +47,6 @@ export const showModal = (elem: /* (HTMLElement & Record) | */{ re const resolved = elem const curModal = activeModalStack.at(-1) if (/* elem === curModal?.elem || */(elem.reactType && elem.reactType === curModal?.reactType) || !showModalInner(resolved)) return - if (curModal) defaultModalActions.hide(curModal) activeModalStack.push(resolved) } @@ -71,21 +57,21 @@ export const showModal = (elem: /* (HTMLElement & Record) | */{ re export const hideModal = (modal = activeModalStack.at(-1), data: any = undefined, options: { force?: boolean; restorePrevious?: boolean } = {}) => { const { force = false, restorePrevious = true } = options if (!modal) return - let cancel - if (modal.elem) { - cancel = modal.elem.hide?.(data) - } else if (modal.reactType) { - cancel = notHideableModalsWithoutForce.has(modal.reactType) ? !force : undefined - } - if (force && cancel !== customDisplayManageKeyword) { + let cancel = notHideableModalsWithoutForce.has(modal.reactType) ? !force : undefined + if (force) { cancel = undefined } - if (!cancel || cancel === customDisplayManageKeyword) { - if (cancel !== customDisplayManageKeyword) defaultModalActions.hide(modal) - activeModalStack.pop() + if (!cancel) { + let lastModal = activeModalStack.at(-1) + for (let i = activeModalStack.length - 1; i >= 0; i--) { + if (activeModalStack[i].reactType === modal.reactType) { + activeModalStack.splice(i, 1) + break + } + } const newModal = activeModalStack.at(-1) - if (newModal && restorePrevious) { + if (newModal && lastModal !== newModal && restorePrevious) { // would be great to ignore cancel I guess? showModalInner(newModal) } From ffc9a0c458c33c54f94e9b2bbac47ca56a1d0cb9 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 26 Aug 2024 03:11:23 +0300 Subject: [PATCH 027/865] fix: update "save remote world to your device" function to support new HTTP backend --- src/browserfs.ts | 47 +++++++++++++++++++++++++++++++++++-- src/globalState.ts | 2 +- src/loadSave.ts | 1 + src/react/PauseScreen.tsx | 49 ++++++++++++++++++++++++++++++++++----- 4 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/browserfs.ts b/src/browserfs.ts index 0c4c7664..82d24058 100644 --- a/src/browserfs.ts +++ b/src/browserfs.ts @@ -233,6 +233,7 @@ export const mountGoogleDriveFolder = async (readonly: boolean, rootId: string) fsState.isReadonly = readonly fsState.syncFs = false fsState.inMemorySave = false + fsState.remoteBackend = true return true } @@ -313,6 +314,7 @@ export const openWorldDirectory = async (dragndropHandle?: FileSystemDirectoryHa fsState.isReadonly = !writeAccess fsState.syncFs = false fsState.inMemorySave = false + fsState.remoteBackend = false await loadSave() } @@ -352,7 +354,33 @@ export const possiblyCleanHandle = (callback = () => { }) => { } } -export const copyFilesAsyncWithProgress = async (pathSrc: string, pathDest: string, throwRootNotExist = true) => { +const readdirSafe = async (path: string) => { + try { + return await fs.promises.readdir(path) + } catch (err) { + return null + } +} + +export const collectFilesToCopy = async (basePath: string, safe = false): Promise => { + const result: string[] = [] + const countFiles = async (relPath: string) => { + const resolvedPath = join(basePath, relPath) + const files = relPath === '.' && !safe ? await fs.promises.readdir(resolvedPath) : await readdirSafe(resolvedPath) + if (!files) return null + await Promise.all(files.map(async file => { + const res = await countFiles(join(relPath, file)) + if (res === null) { + // is file + result.push(join(relPath, file)) + } + })) + } + await countFiles('.') + return result +} + +export const copyFilesAsyncWithProgress = async (pathSrc: string, pathDest: string, throwRootNotExist = true, addMsg = '') => { const stat = await existsViaStats(pathSrc) if (!stat) { if (throwRootNotExist) throw new Error(`Cannot copy. Source directory ${pathSrc} does not exist`) @@ -387,7 +415,7 @@ export const copyFilesAsyncWithProgress = async (pathSrc: string, pathDest: stri let copied = 0 await copyFilesAsync(pathSrc, pathDest, (name) => { copied++ - setLoadingScreenStatus(`Copying files (${copied}/${filesCount}): ${name}`) + setLoadingScreenStatus(`Copying files${addMsg} (${copied}/${filesCount}): ${name}`) }) } finally { setLoadingScreenStatus(undefined) @@ -402,6 +430,19 @@ export const existsViaStats = async (path: string) => { } } +export const fileExistsAsyncOptimized = async (path: string) => { + try { + await fs.promises.readdir(path) + } catch (err) { + if (err.code === 'ENOTDIR') return true + // eslint-disable-next-line sonarjs/prefer-single-boolean-return + if (err.code === 'ENOENT') return false + // throw err + return false + } + return true +} + export const copyFilesAsync = async (pathSrc: string, pathDest: string, fileCopied?: (name) => void) => { // query: can't use fs.copy! use fs.promises.writeFile and readFile const files = await fs.promises.readdir(pathSrc) @@ -467,6 +508,7 @@ export const openWorldFromHttpDir = async (fileDescriptorUrl: string/* | undefi fsState.isReadonly = true fsState.syncFs = false fsState.inMemorySave = false + fsState.remoteBackend = true await loadSave() } @@ -497,6 +539,7 @@ const openWorldZipInner = async (file: File | ArrayBuffer, name = file['name']) fsState.isReadonly = true fsState.syncFs = true fsState.inMemorySave = false + fsState.remoteBackend = false if (fs.existsSync('/world/level.dat')) { await loadSave() diff --git a/src/globalState.ts b/src/globalState.ts index d4366399..b0a447f2 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -63,7 +63,7 @@ export const hideModal = (modal = activeModalStack.at(-1), data: any = undefined } if (!cancel) { - let lastModal = activeModalStack.at(-1) + const lastModal = activeModalStack.at(-1) for (let i = activeModalStack.length - 1; i >= 0; i--) { if (activeModalStack[i].reactType === modal.reactType) { activeModalStack.splice(i, 1) diff --git a/src/loadSave.ts b/src/loadSave.ts index 48054094..6c7da6bb 100644 --- a/src/loadSave.ts +++ b/src/loadSave.ts @@ -21,6 +21,7 @@ export const fsState = proxy({ saveLoaded: false, openReadOperations: 0, openWriteOperations: 0, + remoteBackend: false }) const PROPOSE_BACKUP = true diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index 560412de..96b00648 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -1,4 +1,5 @@ import { join } from 'path' +import fs from 'fs' import { useEffect } from 'react' import { useSnapshot } from 'valtio' import { usedServerPathsV1 } from 'flying-squid/dist/lib/modules/world' @@ -14,7 +15,7 @@ import { fsState } from '../loadSave' import { disconnect } from '../flyingSquidUtils' import { pointerLock, setLoadingScreenStatus } from '../utils' import { closeWan, openToWanAndCopyJoinLink, getJoinLink } from '../localServerMultiplayer' -import { copyFilesAsyncWithProgress, mkdirRecursive, uniqueFileNameFromWorldName } from '../browserfs' +import { collectFilesToCopy, fileExistsAsyncOptimized, mkdirRecursive, uniqueFileNameFromWorldName } from '../browserfs' import { useIsModalActive } from './utilsApp' import { showOptionsModal } from './SelectOption' import Button from './Button' @@ -29,12 +30,44 @@ export const saveToBrowserMemory = async () => { const { worldFolder } = localServer.options const saveRootPath = await uniqueFileNameFromWorldName(worldFolder.split('/').pop(), `/data/worlds`) await mkdirRecursive(saveRootPath) - for (const copyPath of [...usedServerPathsV1, 'icon.png']) { - const srcPath = join(worldFolder, copyPath) - const savePath = join(saveRootPath, copyPath) - // eslint-disable-next-line no-await-in-loop - await copyFilesAsyncWithProgress(srcPath, savePath, false) + const allRootPaths = [...usedServerPathsV1] + const allFilesToCopy = [] as string[] + for (const dirBase of allRootPaths) { + if (dirBase.includes('.') && await fileExistsAsyncOptimized(join(worldFolder, dirBase))) { + allFilesToCopy.push(dirBase) + continue + } + let res = await collectFilesToCopy(join(worldFolder, dirBase), true) + if (dirBase === 'region') { + res = res.filter(x => x.endsWith('.mca')) + } + allFilesToCopy.push(...res.map(x => join(dirBase, x))) } + const pathsSplit = allFilesToCopy.reduce((acc, cur, i) => { + if (i % 15 === 0) { + acc.push([]) + } + acc.at(-1)!.push(cur) + return acc + // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter + }, [] as string[][]) + let copied = 0 + const upProgress = () => { + copied++ + const action = fsState.remoteBackend ? 'Downloading & copying' : 'Copying' + setLoadingScreenStatus(`${action} files (${copied}/${allFilesToCopy.length})`) + } + for (const copyPaths of pathsSplit) { + // eslint-disable-next-line no-await-in-loop + await Promise.all(copyPaths.map(async (copyPath) => { + const srcPath = join(worldFolder, copyPath) + const savePath = join(saveRootPath, copyPath) + await mkdirRecursive(savePath) + await fs.promises.writeFile(savePath, await fs.promises.readFile(srcPath)) + upProgress() + })) + } + return saveRootPath } catch (err) { void showOptionsModal(`Error while saving the world: ${err.message}`, []) @@ -101,6 +134,10 @@ export default () => { const action = await showOptionsModal('World actions...', ['Save to browser memory']) if (action === 'Save to browser memory') { await saveToBrowserMemory() + // fsState.inMemorySave = true + // fsState.syncFs = false + // fsState.isReadonly = false + // fsState.remoteBackend = false } } From 69cfb89a8dab29018424cdc2fa421a5cea4978e1 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 26 Aug 2024 03:15:02 +0300 Subject: [PATCH 028/865] display notification on save --- src/react/PauseScreen.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index 96b00648..9f25810d 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -22,6 +22,7 @@ import Button from './Button' import Screen from './Screen' import styles from './PauseScreen.module.css' import { DiscordButton } from './DiscordButton' +import { showNotification } from './NotificationProvider' export const saveToBrowserMemory = async () => { setLoadingScreenStatus('Saving world') @@ -33,10 +34,12 @@ export const saveToBrowserMemory = async () => { const allRootPaths = [...usedServerPathsV1] const allFilesToCopy = [] as string[] for (const dirBase of allRootPaths) { + // eslint-disable-next-line no-await-in-loop if (dirBase.includes('.') && await fileExistsAsyncOptimized(join(worldFolder, dirBase))) { allFilesToCopy.push(dirBase) continue } + // eslint-disable-next-line no-await-in-loop let res = await collectFilesToCopy(join(worldFolder, dirBase), true) if (dirBase === 'region') { res = res.filter(x => x.endsWith('.mca')) @@ -133,7 +136,10 @@ export default () => { } const action = await showOptionsModal('World actions...', ['Save to browser memory']) if (action === 'Save to browser memory') { - await saveToBrowserMemory() + const path = await saveToBrowserMemory() + if (!path) return + const saveName = path.split('/').at(-1) + showNotification(`World saved to ${saveName}`, 'Load it to keep your progress!') // fsState.inMemorySave = true // fsState.syncFs = false // fsState.isReadonly = false From 83ea44b099ae8a879242f76246253c15c18b4a02 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 27 Aug 2024 01:07:04 +0300 Subject: [PATCH 029/865] feat: sort worlds in singleplayer menu by last saved world (last level.dat edited) --- src/react/SingleplayerProvider.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/react/SingleplayerProvider.tsx b/src/react/SingleplayerProvider.tsx index 91aa23c0..16cf8f65 100644 --- a/src/react/SingleplayerProvider.tsx +++ b/src/react/SingleplayerProvider.tsx @@ -62,6 +62,7 @@ export const readWorlds = (abortController: AbortController) => { const newMappedWorlds = (await Promise.allSettled(worlds.map(async (folder) => { const { levelDat } = (await readLevelDat(`${worldsPath}/${folder}`))! + const levelDatStat = await fs.promises.stat(`${worldsPath}/${folder}/level.dat`) let size = 0 if (providersEnableFeatures[provider].calculateSize) { // todo use whole dir size @@ -88,7 +89,8 @@ export const readWorlds = (abortController: AbortController) => { detail: `${levelDat.Version?.Name ?? 'unknown version'}, ${folder}`, iconSrc: iconBase64 ? `data:image/png;base64,${iconBase64}` : undefined, size, - } satisfies WorldProps + lastModified: levelDatStat.mtimeMs, + } satisfies WorldProps & { lastModified?: number } }))).filter((x, i) => { if (x.status === 'rejected') { console.warn(x.reason) @@ -96,7 +98,12 @@ export const readWorlds = (abortController: AbortController) => { return false } return true - }).map(x => (x as Extract).value) + }).map(x => (x as Extract).value).sort((a, b) => { + const getScore = (x: typeof a) => { + return x.lastModified ?? 0 + } + return getScore(b) - getScore(a) + }) if (abortController.signal.aborted) return worldsProxy.value = newMappedWorlds } catch (err) { From 00e7d4a0650cb8a4c4dca05a538210b095160273 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 27 Aug 2024 01:12:10 +0300 Subject: [PATCH 030/865] fix(regression): most of the items were not renderer in old versions (before 1.13) --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- src/inventoryWindows.ts | 5 ++++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index f8eb87eb..695aff89 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", - "mc-assets": "^0.2.11", + "mc-assets": "^0.2.12", "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 4cab8b08..5dffb3da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -341,8 +341,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 mc-assets: - specifier: ^0.2.11 - version: 0.2.11 + specifier: ^0.2.12 + version: 0.2.12 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) @@ -6290,8 +6290,8 @@ packages: peerDependencies: react: ^18.2.0 - mc-assets@0.2.11: - resolution: {integrity: sha512-j56kUmmVtWU5U+xa6diWLfi2ANftIdC9eGoueL86Ixf02vQuB3WFUZS6si0emJkcUWGtyqrr9ubztvT8cPUp2Q==} + mc-assets@0.2.12: + resolution: {integrity: sha512-ZbiodI0vgcwGT0M3AGc+0N2h7JsnrfjzhlA5AzpSQfkGbNp3wp/VeFmI4/lGm0JPJi9+LgXGDUuspRQzQwhobg==} engines: {node: '>=18.0.0'} md5-file@4.0.0: @@ -16607,7 +16607,7 @@ snapshots: dependencies: react: 18.2.0 - mc-assets@0.2.11: {} + mc-assets@0.2.12: {} md5-file@4.0.0: {} diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 247fad67..64e589f9 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -9,6 +9,8 @@ import nbt from 'prismarine-nbt' import { splitEvery, equals } from 'rambda' import PItem, { Item } from 'prismarine-item' import { ItemsRenderer } from 'mc-assets/dist/itemsRenderer' +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' +import { getRenamedData } from 'flying-squid/dist/blockRenames' import Generic95 from '../assets/generic_95.png' import { appReplacableResources } from './generated/resources' import { activeModalStack, hideCurrentModal, hideModal, miscUiState, showModal } from './globalState' @@ -158,11 +160,12 @@ const renderSlot = (slot: RenderSlot, skipBlock = false): { scale?: number, slice?: number[] } | undefined => { - const itemName = slot.name + let itemName = slot.name const isItem = loadedData.itemsByName[itemName] let itemTexture try { + if (versionToNumber(bot.version) < versionToNumber('1.13')) itemName = getRenamedData(isItem ? 'items' : 'blocks', itemName, bot.version, '1.13.1') as string itemTexture = itemsRenderer.getItemTexture(itemName) ?? itemsRenderer.getItemTexture('item/missing_texture')! } catch (err) { itemTexture = itemsRenderer.getItemTexture('block/errored')! From aa9400e88505fea11574187f57eb931bb34c4d06 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 29 Aug 2024 16:28:26 +0300 Subject: [PATCH 031/865] always enable cors so able to connect from prod domains --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index dd1ccaa1..4e541486 100644 --- a/server.js +++ b/server.js @@ -17,7 +17,7 @@ const app = express() const isProd = process.argv.includes('--prod') app.use(compression()) -// app.use(cors()) +app.use(cors()) app.use(netApi({ allowOrigin: '*' })) if (!isProd) { app.use('/sounds', express.static(path.join(__dirname, './generated/sounds/'))) From f32f30ca5a05e8a3fc9e487305c92dde4d56b3f7 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 29 Aug 2024 16:28:45 +0300 Subject: [PATCH 032/865] also watch mesher when running the main start script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 695aff89..e20c548a 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "dev-rsbuild": "rsbuild dev", "dev-proxy": "node server.js", - "start": "run-p dev-rsbuild dev-proxy", + "start": "run-p dev-rsbuild dev-proxy watch-mesher", "start-watch-script": "nodemon -w rsbuild.config.ts --watch", "build": "rsbuild build", "build-analyze": "BUNDLE_ANALYZE=true rsbuild build", From 506dfa14cf8fbb80f0ea6cebe0406aef9f275eb1 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 31 Aug 2024 01:00:30 +0300 Subject: [PATCH 033/865] wip testing --- prismarine-viewer/examples/playground.ts | 21 +++++++++++++-------- prismarine-viewer/viewer/lib/viewer.ts | 6 ++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/prismarine-viewer/examples/playground.ts b/prismarine-viewer/examples/playground.ts index 28dcceef..5d3c11f9 100644 --- a/prismarine-viewer/examples/playground.ts +++ b/prismarine-viewer/examples/playground.ts @@ -279,10 +279,13 @@ async function main () { const entityUpdateShared = () => { viewer.entities.clear() + params.entity = 'allay' if (!params.entity) return - worldView.emit('entity', { - id: 'id', name: params.entity, pos: targetPos.offset(0.5, 1, 0.5), width: 1, height: 1, username: localStorage.testUsername, yaw: Math.PI, pitch: 0 - }) + for (let i = 0; i < 75; i++) { + worldView.emit('entity', { + id: `id${i}`, name: 'player', pos: targetPos.offset(i + 0.5, 1, 0.5), width: 1, height: 1, username: localStorage.testUsername, yaw: Math.PI, pitch: 0 + }) + } const enableSkeletonDebug = (obj) => { const { children, isSkeletonHelper } = obj if (!Array.isArray(children)) return @@ -294,9 +297,11 @@ async function main () { if (typeof child === 'object') enableSkeletonDebug(child) } } - enableSkeletonDebug(viewer.entities.entities['id']) + // enableSkeletonDebug(viewer.entities.entities['id']) setTimeout(() => { + console.time('render') viewer.render() + console.timeEnd('render') }, TWEEN_DURATION) } @@ -358,10 +363,10 @@ async function main () { continuousRender = params.entity === 'player' entityUpdateShared() if (!params.entity) return - if (params.entity === 'player') { - viewer.entities.updatePlayerSkin('id', viewer.entities.entities.id.username, true, true) - viewer.entities.playAnimation('id', 'running') - } + // if (params.entity === 'player') { + // viewer.entities.updatePlayerSkin('id', viewer.entities.entities.id.username, true, true) + // viewer.entities.playAnimation('id', 'running') + // } // let prev = false // setInterval(() => { // viewer.entities.playAnimation('id', prev ? 'running' : 'idle') diff --git a/prismarine-viewer/viewer/lib/viewer.ts b/prismarine-viewer/viewer/lib/viewer.ts index d52f79b7..e8b102c1 100644 --- a/prismarine-viewer/viewer/lib/viewer.ts +++ b/prismarine-viewer/viewer/lib/viewer.ts @@ -77,6 +77,12 @@ export class Viewer { // this.primitives.clear() } + testPlayerLag () { + for (let i = 0; i < 10; i++) { + this.entities.update() + } + } + setVersion (userVersion: string, texturesVersion = userVersion) { console.log('[viewer] Using version:', userVersion, 'textures:', texturesVersion) void this.world.setVersion(userVersion, texturesVersion).then(async () => { From b472849c4745d21fe55d80750d430813171b33a3 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 31 Aug 2024 17:47:17 +0300 Subject: [PATCH 034/865] fix: packets replay crash on message send on latest versions --- src/packetsReplayBase.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/packetsReplayBase.ts b/src/packetsReplayBase.ts index 375da3cb..fda41ae2 100644 --- a/src/packetsReplayBase.ts +++ b/src/packetsReplayBase.ts @@ -20,7 +20,11 @@ export class PacketsLogger { } const diff = `+${Date.now() - this.lastPacketTime}` - const str = `${isFromServer ? 'S' : 'C'} ${packet.state}:${packet.name} ${diff} ${JSON.stringify(data)}` + // serialize bigint + const str = `${isFromServer ? 'S' : 'C'} ${packet.state}:${packet.name} ${diff} ${JSON.stringify(data, (key, value) => { + if (typeof value === 'bigint') return value.toString() + return value + })}` this.logStr(str) this.lastPacketTime = Date.now() } From 72a54989adc1f166f4cddf197ee9ed6506df23bd Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 31 Aug 2024 17:47:46 +0300 Subject: [PATCH 035/865] fix(important): chunk unload was never implemented --- prismarine-viewer/viewer/lib/worldDataEmitter.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prismarine-viewer/viewer/lib/worldDataEmitter.ts b/prismarine-viewer/viewer/lib/worldDataEmitter.ts index ea956b81..381526d9 100644 --- a/prismarine-viewer/viewer/lib/worldDataEmitter.ts +++ b/prismarine-viewer/viewer/lib/worldDataEmitter.ts @@ -72,6 +72,9 @@ export class WorldDataEmitter extends EventEmitter { chunkColumnLoad: (pos: Vec3) => { this.loadChunk(pos) }, + chunkColumnUnload: (pos: Vec3) => { + this.unloadChunk(pos) + }, blockUpdate: (oldBlock: any, newBlock: any) => { const stateId = newBlock.stateId ?? ((newBlock.type << 4) | newBlock.metadata) this.emitter.emit('blockUpdate', { pos: oldBlock.position, stateId }) From 7748e8c384fdeefc359ed3d3247b5cfcf9683290 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 31 Aug 2024 17:48:13 +0300 Subject: [PATCH 036/865] fix: cleanup entities in all cases on world switch --- pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5dffb3da..1810903f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -348,7 +348,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/326c5a1681b09ee2bb783f20a562c6055788b502(encoding@0.1.13) + version: https://codeload.github.com/zardoy/mineflayer/tar.gz/c6755fc3758eeb3bbbb98479efe03b7935486247(encoding@0.1.13) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -6509,8 +6509,8 @@ packages: resolution: {integrity: sha512-QMMNPx4IyZE7ydAzjvGLQLCnQNUOfkk1qVZKxTTS9q3qPTAewz4GhsVUBtbQ8LSbHthe5RcQ1Sgxs4wlIma/Qw==} engines: {node: '>=18'} - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502: - resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502} + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/c6755fc3758eeb3bbbb98479efe03b7935486247: + resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/c6755fc3758eeb3bbbb98479efe03b7935486247} version: 4.20.1 engines: {node: '>=18'} From 17a3166f7d12163276095d69eb4695eabb7aeacb Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 31 Aug 2024 17:56:35 +0300 Subject: [PATCH 037/865] fix lockfile --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1810903f..16eba168 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16973,7 +16973,7 @@ snapshots: - encoding - supports-color - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502(encoding@0.1.13): + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/c6755fc3758eeb3bbbb98479efe03b7935486247(encoding@0.1.13): dependencies: minecraft-data: 3.65.0 minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/7057ad979b416192ada235f2f4e3b5eb26af5fa1(patch_hash=7otpchsbv7hxsuis4rrrwdtbve)(encoding@0.1.13) From 447f5eabc84e88a70ab97a81d6a427c5260c6183 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 31 Aug 2024 18:51:31 +0300 Subject: [PATCH 038/865] skip building worker in dev by default because of new scripts change --- rsbuild.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 422af603..fd05db8a 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -124,7 +124,7 @@ export default defineConfig({ if (fs.existsSync('./prismarine-viewer/public/mesher.js') && dev) { // copy mesher fs.copyFileSync('./prismarine-viewer/public/mesher.js', './dist/mesher.js') - } else { + } else if (!dev) { await execAsync('pnpm run build-mesher') } fs.writeFileSync('./dist/version.txt', buildingVersion, 'utf-8') From 0dc261258ad805f5bdeaeddd456bfd8ded17d6f3 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 31 Aug 2024 19:05:20 +0300 Subject: [PATCH 039/865] fix: fix bug ?singleplayer=1&version=1.20.4 didn't work in safari because of different setTimeout timing --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e20c548a..5337cebf 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "esbuild-plugin-polyfill-node": "^0.3.0", "express": "^4.18.2", "filesize": "^10.0.12", - "flying-squid": "npm:@zardoy/flying-squid@^0.0.35", + "flying-squid": "npm:@zardoy/flying-squid@^0.0.36", "fs-extra": "^11.1.1", "google-drive-browserfs": "github:zardoy/browserfs#google-drive", "jszip": "^3.10.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16eba168..6697f33b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,8 +117,8 @@ importers: specifier: ^10.0.12 version: 10.0.12 flying-squid: - specifier: npm:@zardoy/flying-squid@^0.0.35 - version: '@zardoy/flying-squid@0.0.35(encoding@0.1.13)' + specifier: npm:@zardoy/flying-squid@^0.0.36 + version: '@zardoy/flying-squid@0.0.36(encoding@0.1.13)' fs-extra: specifier: ^11.1.1 version: 11.1.1 @@ -3386,8 +3386,8 @@ packages: resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==} engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'} - '@zardoy/flying-squid@0.0.35': - resolution: {integrity: sha512-6cZdDi7yaqxh6KbOPhDueipcr9DBgJ3mJY+/QwAjaSzhP//5n1BLjyVGlx2Ncs/6Vns2grTOmeuDhJjMbVgjQg==} + '@zardoy/flying-squid@0.0.36': + resolution: {integrity: sha512-d4clMPDpw723SDF5P2mMVNfbthUFLX6OT+vTCECAMshX8/M7CyMq/q9BfBQoeJcBL0H9nplhwtFbnx3Edb2fzA==} engines: {node: '>=8'} hasBin: true @@ -12923,7 +12923,7 @@ snapshots: '@types/emscripten': 1.39.8 tslib: 1.14.1 - '@zardoy/flying-squid@0.0.35(encoding@0.1.13)': + '@zardoy/flying-squid@0.0.36(encoding@0.1.13)': dependencies: '@tootallnate/once': 2.0.0 change-case: 4.1.2 From eb0bc02647070f80cab7a7671fd747071d6b406f Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 31 Aug 2024 19:50:33 +0300 Subject: [PATCH 040/865] feat: All versions now are available offline! (#174) --- cypress/e2e/index.spec.ts | 2 +- experiments/decode.html | 1 + experiments/decode.ts | 26 + pnpm-lock.yaml | 1011 ++++++++++++++++- prismarine-viewer/esbuild.mjs | 18 +- prismarine-viewer/examples/playground.ts | 29 +- prismarine-viewer/package.json | 3 + .../viewer/lib/worldrendererCommon.ts | 10 +- rsbuild.config.ts | 8 +- scripts/build.js | 1 - scripts/genShims.ts | 17 - scripts/makeOptimizedMcData.mjs | 230 ++++ scripts/prepareData.mjs | 72 -- scripts/testOptimizedMcdata.ts | 99 ++ src/getCollisionInteractionShapes.ts | 17 + src/getCollisionShapes.ts | 15 - src/index.ts | 7 +- src/optimizeJson.ts | 264 +++++ src/react/CreateWorld.tsx | 4 +- src/shims/minecraftData.ts | 92 ++ 20 files changed, 1781 insertions(+), 145 deletions(-) create mode 100644 experiments/decode.html create mode 100644 experiments/decode.ts create mode 100644 scripts/makeOptimizedMcData.mjs delete mode 100644 scripts/prepareData.mjs create mode 100644 scripts/testOptimizedMcdata.ts create mode 100644 src/getCollisionInteractionShapes.ts create mode 100644 src/optimizeJson.ts create mode 100644 src/shims/minecraftData.ts diff --git a/cypress/e2e/index.spec.ts b/cypress/e2e/index.spec.ts index b0d7118b..cb1b6880 100644 --- a/cypress/e2e/index.spec.ts +++ b/cypress/e2e/index.spec.ts @@ -14,7 +14,7 @@ const compareRenderedFlatWorld = () => { } const testWorldLoad = () => { - return cy.document().then({ timeout: 25_000 }, doc => { + return cy.document().then({ timeout: 35_000 }, doc => { return new Cypress.Promise(resolve => { doc.addEventListener('cypress-world-ready', resolve) }) diff --git a/experiments/decode.html b/experiments/decode.html new file mode 100644 index 00000000..fd55e622 --- /dev/null +++ b/experiments/decode.html @@ -0,0 +1 @@ + diff --git a/experiments/decode.ts b/experiments/decode.ts new file mode 100644 index 00000000..6d0f876d --- /dev/null +++ b/experiments/decode.ts @@ -0,0 +1,26 @@ +// Include the pako library +import pako from 'pako'; +import compressedJsRaw from './compressed.js?raw' + +function decompressFromBase64(input) { + // Decode the Base64 string + const binaryString = atob(input); + const len = binaryString.length; + const bytes = new Uint8Array(len); + + // Convert the binary string to a byte array + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + // Decompress the byte array + const decompressedData = pako.inflate(bytes, { to: 'string' }); + + return decompressedData; +} + +// Use the function +console.time('decompress'); +const decompressedData = decompressFromBase64(compressedJsRaw); +console.timeEnd('decompress') +console.log(decompressedData) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6697f33b..8075be60 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -461,6 +461,10 @@ importers: node-canvas-webgl: specifier: ^0.3.0 version: 0.3.0(encoding@0.1.13) + devDependencies: + live-server: + specifier: ^1.2.2 + version: 1.2.2 prismarine-viewer/viewer/sign-renderer: dependencies: @@ -3535,10 +3539,21 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + anymatch@2.0.0: + resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + apache-crypt@1.2.6: + resolution: {integrity: sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==} + engines: {node: '>=8'} + + apache-md5@1.1.8: + resolution: {integrity: sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==} + engines: {node: '>=8'} + app-root-dir@1.0.2: resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} @@ -3568,6 +3583,18 @@ packages: resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} engines: {node: '>=10'} + arr-diff@4.0.0: + resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} + engines: {node: '>=0.10.0'} + + arr-flatten@1.1.0: + resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} + engines: {node: '>=0.10.0'} + + arr-union@3.1.0: + resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} + engines: {node: '>=0.10.0'} + array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -3590,6 +3617,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + array-unique@0.3.2: + resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} + engines: {node: '>=0.10.0'} + array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} @@ -3645,6 +3676,10 @@ packages: assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + assign-symbols@1.0.0: + resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} + engines: {node: '>=0.10.0'} + ast-types@0.14.2: resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} engines: {node: '>=4'} @@ -3661,6 +3696,9 @@ packages: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} + async-each@1.0.6: + resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==} + async-limiter@1.0.1: resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} @@ -3677,6 +3715,11 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} + atob@2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true + available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -3745,13 +3788,23 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} + base@0.11.2: + resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} + engines: {node: '>=0.10.0'} + basic-auth@2.0.1: resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} engines: {node: '>= 0.8'} + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + bcryptjs@2.4.3: + resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==} + better-opn@3.0.2: resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} engines: {node: '>=12.0.0'} @@ -3760,6 +3813,10 @@ packages: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} + binary-extensions@1.13.1: + resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} + engines: {node: '>=0.10.0'} + binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -3812,6 +3869,10 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + braces@2.3.2: + resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} + engines: {node: '>=0.10.0'} + braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -3916,6 +3977,10 @@ packages: resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + cache-base@1.0.1: + resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} + engines: {node: '>=0.10.0'} + cachedir@2.4.0: resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} engines: {node: '>=6'} @@ -3997,6 +4062,10 @@ packages: resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} engines: {node: '>= 0.8.0'} + chokidar@2.1.8: + resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} + deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies + chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -4019,6 +4088,10 @@ packages: cipher-base@1.0.4: resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + class-utils@0.3.6: + resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} + engines: {node: '>=0.10.0'} + classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -4069,6 +4142,10 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + collection-visit@1.0.0: + resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} + engines: {node: '>=0.10.0'} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -4152,6 +4229,10 @@ packages: confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + console-browserify@1.2.0: resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} @@ -4193,6 +4274,10 @@ packages: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} + copy-descriptor@0.1.1: + resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} + engines: {node: '>=0.10.0'} + copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} @@ -4366,6 +4451,10 @@ packages: decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + decompress-response@4.2.1: resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} engines: {node: '>=8'} @@ -4412,6 +4501,18 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + define-property@0.2.5: + resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} + engines: {node: '>=0.10.0'} + + define-property@1.0.0: + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} + engines: {node: '>=0.10.0'} + + define-property@2.0.2: + resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==} + engines: {node: '>=0.10.0'} + defu@6.1.2: resolution: {integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==} @@ -4426,6 +4527,10 @@ packages: delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -4961,6 +5066,9 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -4997,6 +5105,10 @@ packages: resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} engines: {node: '>=6'} + expand-brackets@2.1.4: + resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} + engines: {node: '>=0.10.0'} + expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} @@ -5014,9 +5126,21 @@ packages: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} engines: {node: '>= 0.10.0'} + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extend-shallow@3.0.2: + resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} + engines: {node: '>=0.10.0'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extglob@2.0.4: + resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} + engines: {node: '>=0.10.0'} + extract-zip@1.7.0: resolution: {integrity: sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==} hasBin: true @@ -5055,6 +5179,10 @@ packages: fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -5092,10 +5220,18 @@ packages: resolution: {integrity: sha512-6RS9gDchbn+qWmtV2uSjo5vmKizgfCQeb5jKmqx8HyzA3MoLqqyQxN+QcjkGBJt7FjJ9qFce67Auyya5rRRbpw==} engines: {node: '>= 10.4.0'} + fill-range@4.0.0: + resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} + engines: {node: '>=0.10.0'} + fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} + finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + finalhandler@1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} engines: {node: '>= 0.8'} @@ -5158,6 +5294,10 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-in@1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + foreground-child@2.0.0: resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} engines: {node: '>=8.0.0'} @@ -5188,10 +5328,17 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} + fragment-cache@0.2.1: + resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} + engines: {node: '>=0.10.0'} + fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -5221,6 +5368,12 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@1.2.13: + resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} + engines: {node: '>= 4.0'} + os: [darwin] + deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2 + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -5305,6 +5458,10 @@ packages: get-tsconfig@4.7.2: resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + get-value@2.0.6: + resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} + engines: {node: '>=0.10.0'} + getos@3.2.1: resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==} @@ -5325,6 +5482,9 @@ packages: resolution: {integrity: sha512-yBbfpChOtFvg5D+KtMaBFvj6yt3vUnheNAH+UrQH2TfDB8kr0tERdL0Tjhe0W7xJ6jR6ftQBluTZR9jXUnKe8g==} engines: {node: '>=14.0.0'} + glob-parent@3.1.0: + resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -5458,6 +5618,22 @@ packages: has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + has-value@0.3.1: + resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} + engines: {node: '>=0.10.0'} + + has-value@1.0.0: + resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} + engines: {node: '>=0.10.0'} + + has-values@0.1.4: + resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} + engines: {node: '>=0.10.0'} + + has-values@1.0.0: + resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} + engines: {node: '>=0.10.0'} + has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -5515,16 +5691,27 @@ packages: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} + http-auth@3.1.3: + resolution: {integrity: sha512-Jbx0+ejo2IOx+cRUYAGS1z6RGc6JfYUNkysZM4u4Sfk1uLlGv814F7/PIjQQAuThLdAWxb74JMGd5J8zex1VQg==} + engines: {node: '>=4.6.1'} + http-browserify@1.7.0: resolution: {integrity: sha512-Irf/LJXmE3cBzU1eaR4+NEX6bmVLqt1wkmDiA7kBwH7zmb0D8kBAXsDmQ88hhj/qv9iEZKlyGx/hrMcFi8sOHw==} http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + http-errors@1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + http-parser-js@0.5.8: + resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} + http-proxy-agent@5.0.0: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} engines: {node: '>= 6'} @@ -5626,6 +5813,9 @@ packages: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -5661,6 +5851,10 @@ packages: resolution: {integrity: sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==} engines: {node: '>=8'} + is-accessor-descriptor@1.0.1: + resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==} + engines: {node: '>= 0.10'} + is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -5685,6 +5879,10 @@ packages: is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-binary-path@1.0.1: + resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==} + engines: {node: '>=0.10.0'} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -5693,6 +5891,9 @@ packages: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + is-builtin-module@3.2.1: resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} engines: {node: '>=6'} @@ -5711,6 +5912,10 @@ packages: is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-data-descriptor@1.0.1: + resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} + engines: {node: '>= 0.4'} + is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} engines: {node: '>= 0.4'} @@ -5722,11 +5927,27 @@ packages: is-deflate@1.0.0: resolution: {integrity: sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==} + is-descriptor@0.1.7: + resolution: {integrity: sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==} + engines: {node: '>= 0.4'} + + is-descriptor@1.0.3: + resolution: {integrity: sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==} + engines: {node: '>= 0.4'} + is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} hasBin: true + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extendable@1.0.1: + resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} + engines: {node: '>=0.10.0'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -5745,6 +5966,10 @@ packages: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} + is-glob@3.1.0: + resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} + engines: {node: '>=0.10.0'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -5787,6 +6012,10 @@ packages: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} + is-number@3.0.0: + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} + engines: {node: '>=0.10.0'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -5876,6 +6105,14 @@ packages: resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} engines: {node: '>= 0.4'} + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-wsl@1.1.0: + resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==} + engines: {node: '>=4'} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -5895,6 +6132,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isobject@2.1.0: + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} + engines: {node: '>=0.10.0'} + isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} @@ -6076,6 +6317,14 @@ packages: keyv@4.5.3: resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + kind-of@3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + + kind-of@4.0.0: + resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} + engines: {node: '>=0.10.0'} + kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -6124,6 +6373,11 @@ packages: enquirer: optional: true + live-server@1.2.2: + resolution: {integrity: sha512-t28HXLjITRGoMSrCOv4eZ88viHaBVIjKjdI5PO92Vxlu+twbk6aE0t7dVIaz6ZWkjPilYFV6OSdMYl9ybN2B4w==} + engines: {node: '>=0.10.0'} + hasBin: true + load-bmfont@1.4.1: resolution: {integrity: sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==} @@ -6269,6 +6523,10 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -6280,6 +6538,13 @@ packages: map-or-similar@1.5.0: resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} + map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + + map-visit@1.0.0: + resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} + engines: {node: '>=0.10.0'} + markdown-it@14.0.0: resolution: {integrity: sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==} hasBin: true @@ -6431,6 +6696,10 @@ packages: micromark@4.0.0: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + micromatch@3.1.10: + resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==} + engines: {node: '>=0.10.0'} + micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -6578,6 +6847,10 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + mixin-deep@1.3.2: + resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} + engines: {node: '>=0.10.0'} + mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} @@ -6608,6 +6881,10 @@ packages: moo@0.5.2: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + morgan@1.10.0: + resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==} + engines: {node: '>= 0.8.0'} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -6643,6 +6920,10 @@ packages: 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'} + napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} @@ -6753,6 +7034,10 @@ packages: resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} engines: {node: '>=10'} + normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -6782,6 +7067,10 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-copy@0.1.0: + resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} + engines: {node: '>=0.10.0'} + object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} @@ -6796,6 +7085,10 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + object-visit@1.0.1: + resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} + engines: {node: '>=0.10.0'} + object.assign@4.1.4: resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} engines: {node: '>= 0.4'} @@ -6816,6 +7109,10 @@ packages: resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} engines: {node: '>= 0.4'} + object.pick@1.3.0: + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} + engines: {node: '>=0.10.0'} + object.values@1.1.7: resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} @@ -6827,6 +7124,10 @@ packages: omggif@1.0.10: resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -6850,6 +7151,11 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true + opn@6.0.0: + resolution: {integrity: sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==} + engines: {node: '>=8'} + deprecated: The package has been renamed to `open` + optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -6948,12 +7254,19 @@ packages: pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + pascalcase@0.1.1: + resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} + engines: {node: '>=0.10.0'} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} path-case@3.0.4: resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} + path-dirname@1.0.2: + resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==} + path-exists-cli@2.0.0: resolution: {integrity: sha512-qGr0A87KYCznmvabblxyxnzA/MtPZ28wH+4SCMP4tjTFAbzqwvs5xpUZExAYzq5OgHe5vIswzdH5iosCb8YF/Q==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7007,6 +7320,9 @@ packages: pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pbkdf2@3.1.2: resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} engines: {node: '>=0.12'} @@ -7108,6 +7424,10 @@ packages: resolution: {integrity: sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==} engines: {node: '>= 0.12.0'} + posix-character-classes@0.1.1: + resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} + engines: {node: '>=0.10.0'} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -7322,6 +7642,10 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-middleware@0.15.0: + resolution: {integrity: sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==} + engines: {node: '>=0.8.0'} + psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} @@ -7604,6 +7928,10 @@ packages: resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdirp@2.2.1: + resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} + engines: {node: '>=0.10'} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -7646,6 +7974,10 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + regex-not@1.0.2: + resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} + engines: {node: '>=0.10.0'} + regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true @@ -7689,6 +8021,17 @@ packages: remark@15.0.1: resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + + repeat-element@1.1.4: + resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} + engines: {node: '>=0.10.0'} + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + request-progress@3.0.0: resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} @@ -7717,6 +8060,10 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve-url@0.2.1: + resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} + deprecated: https://github.com/lydell/resolve-url#deprecated + resolve@1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true @@ -7816,6 +8163,9 @@ packages: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safe-regex@1.1.0: + resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -7876,6 +8226,10 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + serve-index@1.9.1: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} @@ -7899,9 +8253,16 @@ packages: resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} engines: {node: '>=6.9'} + set-value@2.0.1: + resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} + engines: {node: '>=0.10.0'} + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -8001,6 +8362,18 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + snapdragon-node@2.1.1: + resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==} + engines: {node: '>=0.10.0'} + + snapdragon-util@3.0.1: + resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==} + engines: {node: '>=0.10.0'} + + snapdragon@0.8.2: + resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} + engines: {node: '>=0.10.0'} + socket.io-adapter@1.1.2: resolution: {integrity: sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==} @@ -8048,9 +8421,17 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-resolve@0.5.3: + resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated + source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map-url@0.4.1: + resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} + deprecated: See https://github.com/lydell/source-map-url#deprecated + source-map@0.5.6: resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} engines: {node: '>=0.10.0'} @@ -8086,6 +8467,13 @@ packages: spdx-license-ids@3.0.13: resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} + split-string@3.1.0: + resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} + engines: {node: '>=0.10.0'} + + split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -8113,12 +8501,20 @@ packages: stacktrace-js@2.0.2: resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + static-extend@0.1.2: + resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} + engines: {node: '>=0.10.0'} + stats-gl@1.0.5: resolution: {integrity: sha512-XimMxvwnf1Qf5KwebhcoA34kcX+fWEkIl0QjNkCbu4IpoyDMMsOajExn7FIq5w569k45+LhmsuRlGSrsvmGdNw==} stats.js@0.17.0: resolution: {integrity: sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==} + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -8136,6 +8532,9 @@ packages: stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + stream-http@3.2.0: resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} @@ -8410,10 +8809,22 @@ packages: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} + to-object-path@0.3.0: + resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} + engines: {node: '>=0.10.0'} + + to-regex-range@2.1.1: + resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} + engines: {node: '>=0.10.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + to-regex@3.0.2: + resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} + engines: {node: '>=0.10.0'} + tocbot@4.21.2: resolution: {integrity: sha512-R5Muhi/TUu4i4snWVrMgNoXyJm2f8sJfdgIkQvqb+cuIXQEIMAiWGWgCgYXHqX4+XiS/Bnm7IYZ9Zy6NVe6lhw==} @@ -8620,6 +9031,10 @@ packages: unified@11.0.4: resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + union-value@1.0.1: + resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} + engines: {node: '>=0.10.0'} + union@0.5.0: resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} engines: {node: '>= 0.8.0'} @@ -8669,6 +9084,9 @@ packages: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} + unix-crypt-td-js@1.1.4: + resolution: {integrity: sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -8676,6 +9094,10 @@ packages: unplugin@1.5.0: resolution: {integrity: sha512-9ZdRwbh/4gcm1JTOkp9lAkIDrtOyOxgHmY7cjuwI8L/2RTikMcVG25GsZwNAgRuap3iDw2jeq7eoqtAsz5rW3A==} + unset-value@1.0.0: + resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} + engines: {node: '>=0.10.0'} + untildify@4.0.0: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} engines: {node: '>=8'} @@ -8705,6 +9127,10 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + urix@0.1.0: + resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} + deprecated: Please see https://github.com/lydell/urix#deprecated + url-join@4.0.1: resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} @@ -8769,6 +9195,10 @@ packages: typescript: optional: true + use@3.1.1: + resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} + engines: {node: '>=0.10.0'} + utf8-byte-length@1.0.4: resolution: {integrity: sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==} @@ -8788,6 +9218,11 @@ packages: uuid-1345@1.0.2: resolution: {integrity: sha512-bA5zYZui+3nwAc0s3VdGQGBfbVsJLVX7Np7ch2aqcEWFi5lsAEcmO3+lx3djM1npgpZI8KY2FITZ2uYTnYUYyw==} + uuid@3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -8979,6 +9414,14 @@ packages: resolution: {integrity: sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==} engines: {node: '>=6.0.0', npm: '>=3.10.0'} + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} @@ -13096,11 +13539,24 @@ snapshots: any-promise@1.3.0: {} + anymatch@2.0.0: + dependencies: + micromatch: 3.1.10 + normalize-path: 2.1.1 + transitivePeerDependencies: + - supports-color + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 + apache-crypt@1.2.6: + dependencies: + unix-crypt-td-js: 1.1.4 + + apache-md5@1.1.8: {} + app-root-dir@1.0.2: {} aproba@2.0.0: @@ -13131,6 +13587,12 @@ snapshots: dependencies: tslib: 2.6.2 + arr-diff@4.0.0: {} + + arr-flatten@1.1.0: {} + + arr-union@3.1.0: {} + array-buffer-byte-length@1.0.0: dependencies: call-bind: 1.0.2 @@ -13162,6 +13624,8 @@ snapshots: array-union@2.1.0: {} + array-unique@0.3.2: {} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.7 @@ -13259,6 +13723,8 @@ snapshots: assertion-error@1.1.0: {} + assign-symbols@1.0.0: {} + ast-types@0.14.2: dependencies: tslib: 2.6.2 @@ -13274,6 +13740,8 @@ snapshots: astral-regex@2.0.0: optional: true + async-each@1.0.6: {} + async-limiter@1.0.1: {} async@2.6.4: @@ -13286,6 +13754,8 @@ snapshots: at-least-node@1.0.0: {} + atob@2.1.2: {} + available-typed-arrays@1.0.5: {} available-typed-arrays@1.0.7: @@ -13370,21 +13840,37 @@ snapshots: base64id@2.0.0: {} + base@0.11.2: + dependencies: + cache-base: 1.0.1 + class-utils: 0.3.6 + component-emitter: 1.3.0 + define-property: 1.0.0 + isobject: 3.0.1 + mixin-deep: 1.3.2 + pascalcase: 0.1.1 + basic-auth@2.0.1: dependencies: safe-buffer: 5.1.2 + batch@0.6.1: {} + bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 optional: true + bcryptjs@2.4.3: {} + better-opn@3.0.2: dependencies: open: 8.4.2 big-integer@1.6.51: {} + binary-extensions@1.13.1: {} + binary-extensions@2.2.0: {} bindings@1.5.0: @@ -13466,6 +13952,21 @@ snapshots: dependencies: balanced-match: 1.0.2 + braces@2.3.2: + dependencies: + arr-flatten: 1.1.0 + array-unique: 0.3.2 + extend-shallow: 2.0.1 + fill-range: 4.0.0 + isobject: 3.0.1 + repeat-element: 1.1.4 + snapdragon: 0.8.2 + snapdragon-node: 2.1.1 + split-string: 3.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + braces@3.0.2: dependencies: fill-range: 7.0.1 @@ -13612,6 +14113,18 @@ snapshots: - bluebird optional: true + cache-base@1.0.1: + dependencies: + collection-visit: 1.0.0 + component-emitter: 1.3.0 + get-value: 2.0.6 + has-value: 1.0.0 + isobject: 3.0.1 + set-value: 2.0.1 + to-object-path: 0.3.0 + union-value: 1.0.1 + unset-value: 1.0.0 + cachedir@2.4.0: optional: true @@ -13732,6 +14245,24 @@ snapshots: check-more-types@2.24.0: optional: true + chokidar@2.1.8: + dependencies: + anymatch: 2.0.0 + async-each: 1.0.6 + braces: 2.3.2 + glob-parent: 3.1.0 + inherits: 2.0.4 + is-binary-path: 1.0.1 + is-glob: 4.0.3 + normalize-path: 3.0.0 + path-is-absolute: 1.0.1 + readdirp: 2.2.1 + upath: 1.2.0 + optionalDependencies: + fsevents: 1.2.13 + transitivePeerDependencies: + - supports-color + chokidar@3.5.3: dependencies: anymatch: 3.1.3 @@ -13757,6 +14288,13 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 + class-utils@0.3.6: + dependencies: + arr-union: 3.1.0 + define-property: 0.2.5 + isobject: 3.0.1 + static-extend: 0.1.2 + classnames@2.5.1: {} clean-regexp@1.0.0: @@ -13807,6 +14345,11 @@ snapshots: clsx@2.1.1: {} + collection-visit@1.0.0: + dependencies: + map-visit: 1.0.0 + object-visit: 1.0.1 + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -13857,8 +14400,7 @@ snapshots: component-emitter@1.2.1: optional: true - component-emitter@1.3.0: - optional: true + component-emitter@1.3.0: {} component-inherit@0.0.3: optional: true @@ -13890,6 +14432,15 @@ snapshots: confusing-browser-globals@1.0.11: {} + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + console-browserify@1.2.0: {} console-control-strings@1.1.0: @@ -13930,6 +14481,8 @@ snapshots: cookie@0.5.0: {} + copy-descriptor@0.1.1: {} + copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 @@ -14183,6 +14736,8 @@ snapshots: dependencies: character-entities: 2.0.2 + decode-uri-component@0.2.2: {} + decompress-response@4.2.1: dependencies: mimic-response: 2.1.0 @@ -14231,6 +14786,19 @@ snapshots: has-property-descriptors: 1.0.0 object-keys: 1.1.1 + define-property@0.2.5: + dependencies: + is-descriptor: 0.1.7 + + define-property@1.0.0: + dependencies: + is-descriptor: 1.0.3 + + define-property@2.0.2: + dependencies: + is-descriptor: 1.0.3 + isobject: 3.0.1 + defu@6.1.2: {} del@6.1.1: @@ -14249,6 +14817,8 @@ snapshots: delegates@1.0.0: optional: true + depd@1.1.2: {} + depd@2.0.0: {} dequal@1.0.0: {} @@ -15068,6 +15638,16 @@ snapshots: etag@1.8.1: {} + event-stream@3.3.4: + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.1.0 + pause-stream: 0.0.11 + split: 0.3.3 + stream-combiner: 0.0.4 + through: 2.3.8 + event-target-shim@5.0.1: {} eventemitter2@6.4.7: @@ -15117,6 +15697,18 @@ snapshots: exit-hook@2.2.1: {} + expand-brackets@2.1.4: + dependencies: + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + posix-character-classes: 0.1.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + expand-template@2.0.3: {} exponential-backoff@3.1.1: @@ -15166,8 +15758,30 @@ snapshots: transitivePeerDependencies: - supports-color + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extend-shallow@3.0.2: + dependencies: + assign-symbols: 1.0.0 + is-extendable: 1.0.1 + extend@3.0.2: {} + extglob@2.0.4: + dependencies: + array-unique: 0.3.2 + define-property: 1.0.0 + expand-brackets: 2.1.4 + extend-shallow: 2.0.1 + fragment-cache: 0.2.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + extract-zip@1.7.0: dependencies: concat-stream: 1.6.2 @@ -15214,6 +15828,10 @@ snapshots: dependencies: reusify: 1.0.4 + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + fb-watchman@2.0.2: dependencies: bser: 2.1.1 @@ -15252,10 +15870,29 @@ snapshots: filesize@10.0.12: {} + fill-range@4.0.0: + dependencies: + extend-shallow: 2.0.1 + is-number: 3.0.0 + repeat-string: 1.6.1 + to-regex-range: 2.1.1 + fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 + finalhandler@1.1.2: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + finalhandler@1.2.0: dependencies: debug: 2.6.9 @@ -15320,6 +15957,8 @@ snapshots: dependencies: is-callable: 1.2.7 + for-in@1.0.2: {} + foreground-child@2.0.0: dependencies: cross-spawn: 7.0.3 @@ -15365,8 +16004,14 @@ snapshots: forwarded@0.2.0: {} + fragment-cache@0.2.1: + dependencies: + map-cache: 0.2.2 + fresh@0.5.2: {} + from@0.1.7: {} + fs-constants@1.0.0: {} fs-extra@10.1.0: @@ -15403,6 +16048,12 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@1.2.13: + dependencies: + bindings: 1.5.0 + nan: 2.18.0 + optional: true + fsevents@2.3.3: optional: true @@ -15496,6 +16147,8 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-value@2.0.6: {} + getos@3.2.1: dependencies: async: 3.2.5 @@ -15536,6 +16189,11 @@ snapshots: - supports-color optional: true + glob-parent@3.1.0: + dependencies: + is-glob: 3.1.0 + path-dirname: 1.0.2 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -15688,6 +16346,25 @@ snapshots: has-unicode@2.0.1: optional: true + has-value@0.3.1: + dependencies: + get-value: 2.0.6 + has-values: 0.1.4 + isobject: 2.1.0 + + has-value@1.0.0: + dependencies: + get-value: 2.0.6 + has-values: 1.0.0 + isobject: 3.0.1 + + has-values@0.1.4: {} + + has-values@1.0.0: + dependencies: + is-number: 3.0.0 + kind-of: 4.0.0 + has@1.0.3: dependencies: function-bind: 1.1.1 @@ -15750,6 +16427,13 @@ snapshots: html-tags@3.3.1: {} + http-auth@3.1.3: + dependencies: + apache-crypt: 1.2.6 + apache-md5: 1.1.8 + bcryptjs: 2.4.3 + uuid: 3.4.0 + http-browserify@1.7.0: dependencies: Base64: 0.2.1 @@ -15757,6 +16441,13 @@ snapshots: http-cache-semantics@4.1.1: {} + http-errors@1.6.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -15765,6 +16456,8 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-parser-js@0.5.8: {} + http-proxy-agent@5.0.0: dependencies: '@tootallnate/once': 2.0.0 @@ -15887,6 +16580,8 @@ snapshots: once: 1.4.0 wrappy: 1.0.2 + inherits@2.0.3: {} + inherits@2.0.4: {} ini@1.3.8: {} @@ -15921,6 +16616,10 @@ snapshots: is-absolute-url@3.0.3: {} + is-accessor-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + is-arguments@1.1.1: dependencies: call-bind: 1.0.2 @@ -15949,6 +16648,10 @@ snapshots: dependencies: has-bigints: 1.0.2 + is-binary-path@1.0.1: + dependencies: + binary-extensions: 1.13.1 + is-binary-path@2.1.0: dependencies: binary-extensions: 2.2.0 @@ -15958,6 +16661,8 @@ snapshots: call-bind: 1.0.2 has-tostringtag: 1.0.0 + is-buffer@1.1.6: {} + is-builtin-module@3.2.1: dependencies: builtin-modules: 3.3.0 @@ -15977,6 +16682,10 @@ snapshots: dependencies: hasown: 2.0.2 + is-data-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 @@ -15987,8 +16696,24 @@ snapshots: is-deflate@1.0.0: {} + is-descriptor@0.1.7: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + + is-descriptor@1.0.3: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + is-docker@2.2.1: {} + is-extendable@0.1.1: {} + + is-extendable@1.0.1: + dependencies: + is-plain-object: 2.0.4 + is-extglob@2.1.1: {} is-finalizationregistry@1.0.2: @@ -16004,6 +16729,10 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-glob@3.1.0: + dependencies: + is-extglob: 2.1.1 + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -16038,6 +16767,10 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-number@3.0.0: + dependencies: + kind-of: 3.2.2 + is-number@7.0.0: {} is-obj@1.0.1: {} @@ -16107,6 +16840,10 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 + is-windows@1.0.2: {} + + is-wsl@1.1.0: {} + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 @@ -16123,6 +16860,10 @@ snapshots: isexe@2.0.0: {} + isobject@2.1.0: + dependencies: + isarray: 1.0.0 + isobject@3.0.1: {} isstream@0.1.2: @@ -16378,6 +17119,14 @@ snapshots: dependencies: json-buffer: 3.0.1 + kind-of@3.2.2: + dependencies: + is-buffer: 1.1.6 + + kind-of@4.0.0: + dependencies: + is-buffer: 1.1.6 + kind-of@6.0.3: {} kleur@3.0.3: {} @@ -16426,6 +17175,24 @@ snapshots: enquirer: 2.4.1 optional: true + live-server@1.2.2: + dependencies: + chokidar: 2.1.8 + colors: 1.4.0 + connect: 3.7.0 + cors: 2.8.5 + event-stream: 3.3.4 + faye-websocket: 0.11.4 + http-auth: 3.1.3 + morgan: 1.10.0 + object-assign: 4.1.1 + opn: 6.0.0 + proxy-middleware: 0.15.0 + send: 0.18.0 + serve-index: 1.9.1 + transitivePeerDependencies: + - supports-color + load-bmfont@1.4.1: dependencies: buffer-equal: 0.0.1 @@ -16588,12 +17355,20 @@ snapshots: dependencies: tmpl: 1.0.5 + map-cache@0.2.2: {} + map-obj@1.0.1: {} map-obj@4.3.0: {} map-or-similar@1.5.0: {} + map-stream@0.1.0: {} + + map-visit@1.0.0: + dependencies: + object-visit: 1.0.1 + markdown-it@14.0.0: dependencies: argparse: 2.0.1 @@ -16839,6 +17614,24 @@ snapshots: transitivePeerDependencies: - supports-color + micromatch@3.1.10: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + braces: 2.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + extglob: 2.0.4 + fragment-cache: 0.2.1 + kind-of: 6.0.3 + nanomatch: 1.2.13 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + micromatch@4.0.5: dependencies: braces: 3.0.2 @@ -17066,6 +17859,11 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + mixin-deep@1.3.2: + dependencies: + for-in: 1.0.2 + is-extendable: 1.0.1 + mkdirp-classic@0.5.3: {} mkdirp@0.3.0: @@ -17092,6 +17890,16 @@ snapshots: moo@0.5.2: {} + morgan@1.10.0: + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 2.0.0 + on-finished: 2.3.0 + on-headers: 1.0.2 + transitivePeerDependencies: + - supports-color + mri@1.2.0: {} ms@2.0.0: {} @@ -17126,6 +17934,22 @@ snapshots: nanoid@3.3.7: {} + nanomatch@1.2.13: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + fragment-cache: 0.2.1 + is-windows: 1.0.2 + kind-of: 6.0.3 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + napi-build-utils@1.0.2: {} natural-compare-lite@1.4.0: {} @@ -17263,6 +18087,10 @@ snapshots: semver: 7.6.0 validate-npm-package-license: 3.0.4 + normalize-path@2.1.1: + dependencies: + remove-trailing-separator: 1.1.0 + normalize-path@3.0.0: {} npm-run-all@4.1.5: @@ -17303,6 +18131,12 @@ snapshots: object-assign@4.1.1: {} + object-copy@0.1.0: + dependencies: + copy-descriptor: 0.1.1 + define-property: 0.2.5 + kind-of: 3.2.2 + object-inspect@1.12.3: {} object-inspect@1.13.1: {} @@ -17314,6 +18148,10 @@ snapshots: object-keys@1.1.1: {} + object-visit@1.0.1: + dependencies: + isobject: 3.0.1 + object.assign@4.1.4: dependencies: call-bind: 1.0.2 @@ -17347,6 +18185,10 @@ snapshots: es-abstract: 1.23.3 es-object-atoms: 1.0.0 + object.pick@1.3.0: + dependencies: + isobject: 3.0.1 + object.values@1.1.7: dependencies: call-bind: 1.0.2 @@ -17362,6 +18204,10 @@ snapshots: omggif@1.0.10: optional: true + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -17384,6 +18230,10 @@ snapshots: opener@1.5.2: {} + opn@6.0.0: + dependencies: + is-wsl: 1.1.0 + optionator@0.9.3: dependencies: '@aashutoshrathi/word-wrap': 1.2.6 @@ -17503,6 +18353,8 @@ snapshots: no-case: 3.0.4 tslib: 2.6.2 + pascalcase@0.1.1: {} + path-browserify@1.0.1: {} path-case@3.0.4: @@ -17510,6 +18362,8 @@ snapshots: dot-case: 3.0.4 tslib: 2.6.2 + path-dirname@1.0.2: {} + path-exists-cli@2.0.0: dependencies: meow: 10.1.5 @@ -17546,6 +18400,10 @@ snapshots: pathval@1.1.1: {} + pause-stream@0.0.11: + dependencies: + through: 2.3.8 + pbkdf2@3.1.2: dependencies: create-hash: 1.2.0 @@ -17641,6 +18499,8 @@ snapshots: transitivePeerDependencies: - supports-color + posix-character-classes@0.1.1: {} + possible-typed-array-names@1.0.0: {} postcss@8.4.31: @@ -17947,6 +18807,8 @@ snapshots: proxy-from-env@1.1.0: {} + proxy-middleware@0.15.0: {} + psl@1.9.0: optional: true @@ -18304,6 +19166,14 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 + readdirp@2.2.1: + dependencies: + graceful-fs: 4.2.11 + micromatch: 3.1.10 + readable-stream: 2.3.8 + transitivePeerDependencies: + - supports-color + readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -18359,6 +19229,11 @@ snapshots: dependencies: '@babel/runtime': 7.24.5 + regex-not@1.0.2: + dependencies: + extend-shallow: 3.0.2 + safe-regex: 1.1.0 + regexp-tree@0.1.27: {} regexp.prototype.flags@1.5.1: @@ -18431,6 +19306,12 @@ snapshots: transitivePeerDependencies: - supports-color + remove-trailing-separator@1.1.0: {} + + repeat-element@1.1.4: {} + + repeat-string@1.6.1: {} + request-progress@3.0.0: dependencies: throttleit: 1.0.0 @@ -18450,6 +19331,8 @@ snapshots: resolve-pkg-maps@1.0.0: {} + resolve-url@0.2.1: {} + resolve@1.22.4: dependencies: is-core-module: 2.13.0 @@ -18558,6 +19441,10 @@ snapshots: es-errors: 1.3.0 is-regex: 1.1.4 + safe-regex@1.1.0: + dependencies: + ret: 0.1.15 + safer-buffer@2.1.2: {} sanitize-filename@1.6.3: @@ -18629,6 +19516,18 @@ snapshots: dependencies: randombytes: 2.1.0 + serve-index@1.9.1: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.6.3 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + serve-static@1.15.0: dependencies: encodeurl: 1.0.2 @@ -18665,8 +19564,17 @@ snapshots: set-harmonic-interval@1.0.1: {} + set-value@2.0.1: + dependencies: + extend-shallow: 2.0.1 + is-extendable: 0.1.1 + is-plain-object: 2.0.4 + split-string: 3.1.0 + setimmediate@1.0.5: {} + setprototypeof@1.1.0: {} + setprototypeof@1.2.0: {} sha.js@2.4.11: @@ -18786,6 +19694,29 @@ snapshots: dot-case: 3.0.4 tslib: 2.6.2 + snapdragon-node@2.1.1: + dependencies: + define-property: 1.0.0 + isobject: 3.0.1 + snapdragon-util: 3.0.1 + + snapdragon-util@3.0.1: + dependencies: + kind-of: 3.2.2 + + snapdragon@0.8.2: + dependencies: + base: 0.11.2 + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + map-cache: 0.2.2 + source-map: 0.5.7 + source-map-resolve: 0.5.3 + use: 3.1.1 + transitivePeerDependencies: + - supports-color + socket.io-adapter@1.1.2: optional: true @@ -18898,11 +19829,21 @@ snapshots: source-map-js@1.2.0: {} + source-map-resolve@0.5.3: + dependencies: + atob: 2.1.2 + decode-uri-component: 0.2.2 + resolve-url: 0.2.1 + source-map-url: 0.4.1 + urix: 0.1.0 + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 + source-map-url@0.4.1: {} + source-map@0.5.6: {} source-map@0.5.7: {} @@ -18931,6 +19872,14 @@ snapshots: spdx-license-ids@3.0.13: {} + split-string@3.1.0: + dependencies: + extend-shallow: 3.0.2 + + split@0.3.3: + dependencies: + through: 2.3.8 + sprintf-js@1.0.3: {} sshpk@1.17.0: @@ -18970,10 +19919,17 @@ snapshots: stack-generator: 2.0.10 stacktrace-gps: 3.1.2 + static-extend@0.1.2: + dependencies: + define-property: 0.2.5 + object-copy: 0.1.0 + stats-gl@1.0.5: {} stats.js@0.17.0: {} + statuses@1.5.0: {} + statuses@2.0.1: {} std-env@3.4.3: {} @@ -18994,6 +19950,10 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + stream-combiner@0.0.4: + dependencies: + duplexer: 0.1.2 + stream-http@3.2.0: dependencies: builtin-status-codes: 3.0.0 @@ -19279,8 +20239,7 @@ snapshots: readable-stream: 2.3.8 xtend: 4.0.2 - through@2.3.8: - optional: true + through@2.3.8: {} timers-browserify@2.0.12: dependencies: @@ -19316,10 +20275,26 @@ snapshots: to-fast-properties@2.0.0: {} + to-object-path@0.3.0: + dependencies: + kind-of: 3.2.2 + + to-regex-range@2.1.1: + dependencies: + is-number: 3.0.0 + repeat-string: 1.6.1 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + to-regex@3.0.2: + dependencies: + define-property: 2.0.2 + extend-shallow: 3.0.2 + regex-not: 1.0.2 + safe-regex: 1.1.0 + tocbot@4.21.2: {} toggle-selection@1.0.6: {} @@ -19532,6 +20507,13 @@ snapshots: trough: 2.2.0 vfile: 6.0.1 + union-value@1.0.1: + dependencies: + arr-union: 3.1.0 + get-value: 2.0.6 + is-extendable: 0.1.1 + set-value: 2.0.1 + union@0.5.0: dependencies: qs: 6.11.2 @@ -19590,6 +20572,8 @@ snapshots: universalify@2.0.0: {} + unix-crypt-td-js@1.1.4: {} + unpipe@1.0.0: {} unplugin@1.5.0: @@ -19599,6 +20583,11 @@ snapshots: webpack-sources: 3.2.3 webpack-virtual-modules: 0.5.0 + unset-value@1.0.0: + dependencies: + has-value: 0.3.1 + isobject: 3.0.1 + untildify@4.0.0: {} upath@1.2.0: {} @@ -19627,6 +20616,8 @@ snapshots: dependencies: punycode: 2.3.0 + urix@0.1.0: {} + url-join@4.0.1: {} url-parse@1.5.10: @@ -19684,6 +20675,8 @@ snapshots: optionalDependencies: typescript: 5.5.4 + use@3.1.1: {} + utf8-byte-length@1.0.4: {} utif@2.0.1: @@ -19707,6 +20700,8 @@ snapshots: dependencies: macaddress: 0.5.3 + uuid@3.4.0: {} + uuid@8.3.2: {} uuid@9.0.1: {} @@ -19914,6 +20909,14 @@ snapshots: dependencies: sdp: 3.2.0 + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.8 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + whatwg-encoding@2.0.0: dependencies: iconv-lite: 0.6.3 diff --git a/prismarine-viewer/esbuild.mjs b/prismarine-viewer/esbuild.mjs index 90741ee1..e410c8b6 100644 --- a/prismarine-viewer/esbuild.mjs +++ b/prismarine-viewer/esbuild.mjs @@ -6,20 +6,19 @@ import * as esbuild from 'esbuild' import { polyfillNode } from 'esbuild-plugin-polyfill-node' import path, { dirname, join } from 'path' import { fileURLToPath } from 'url' +import childProcess from 'child_process' +import supportedVersions from '../src/supportedVersions.mjs' const dev = process.argv.includes('-w') const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url))) -const mcDataPath = join(__dirname, '../dist/mc-data') +const mcDataPath = join(__dirname, '../generated/minecraft-data-optimized.json') if (!fs.existsSync(mcDataPath)) { - // shouldn't it be in the viewer instead? - await import('../scripts/prepareData.mjs') + childProcess.execSync('tsx ../scripts/makeOptimizedMcData.mjs', { stdio: 'inherit', cwd: __dirname }) } fs.copyFileSync(join(__dirname, 'playground.html'), join(__dirname, 'public/index.html')) -fsExtra.copySync(mcDataPath, join(__dirname, 'public/mc-data')) -const availableVersions = fs.readdirSync(mcDataPath).map(ver => ver.replace('.js', '')) /** @type {import('esbuild').BuildOptions} */ const buildOptions = { @@ -37,7 +36,7 @@ const buildOptions = { ], keepNames: true, banner: { - js: `globalThis.global = globalThis;globalThis.includedVersions = ${JSON.stringify(availableVersions)};`, + js: `globalThis.global = globalThis;globalThis.includedVersions = ${JSON.stringify(supportedVersions)};`, }, alias: { events: 'events', @@ -63,13 +62,14 @@ const buildOptions = { }, () => { const defaultVersionsObj = {} return { - contents: `window.mcData ??= ${JSON.stringify(defaultVersionsObj)};module.exports = { pc: window.mcData }`, - loader: 'js', + contents: fs.readFileSync(join(__dirname, '../src/shims/minecraftData.ts'), 'utf8'), + loader: 'ts', + resolveDir: join(__dirname, '../src/shims'), } }) build.onEnd((e) => { if (e.errors.length) return - // fs.writeFileSync(join(__dirname, 'dist/metafile.json'), JSON.stringify(e.metafile), 'utf8') + fs.writeFileSync(join(__dirname, './public/metafile.json'), JSON.stringify(e.metafile), 'utf8') }) } }, diff --git a/prismarine-viewer/examples/playground.ts b/prismarine-viewer/examples/playground.ts index 28dcceef..1726fe86 100644 --- a/prismarine-viewer/examples/playground.ts +++ b/prismarine-viewer/examples/playground.ts @@ -15,6 +15,7 @@ import { loadScript } from '../viewer/lib/utils' import { TWEEN_DURATION } from '../viewer/lib/entities' import { EntityMesh } from '../viewer/lib/entity/EntityMesh' import { WorldDataEmitter, Viewer } from '../viewer' +import '../../src/getCollisionShapes' import { toMajorVersion } from '../../src/utils' window.THREE = THREE @@ -65,21 +66,21 @@ async function main () { let continuousRender = false const { version } = params + await window._LOAD_MC_DATA() // temporary solution until web worker is here, cache data for faster reloads - const globalMcData = window['mcData'] - if (!globalMcData['version']) { - const major = toMajorVersion(version) - const sessionKey = `mcData-${major}` - if (sessionStorage[sessionKey]) { - Object.assign(globalMcData, JSON.parse(sessionStorage[sessionKey])) - } else { - if (sessionStorage.length > 1) sessionStorage.clear() - await loadScript(`./mc-data/${major}.js`) - try { - sessionStorage[sessionKey] = JSON.stringify(Object.fromEntries(Object.entries(globalMcData).filter(([ver]) => ver.startsWith(major)))) - } catch { } - } - } + // const globalMcData = window['mcData'] + // if (!globalMcData['version']) { + // const major = toMajorVersion(version) + // const sessionKey = `mcData-${major}` + // if (sessionStorage[sessionKey]) { + // Object.assign(globalMcData, JSON.parse(sessionStorage[sessionKey])) + // } else { + // if (sessionStorage.length > 1) sessionStorage.clear() + // try { + // sessionStorage[sessionKey] = JSON.stringify(Object.fromEntries(Object.entries(globalMcData).filter(([ver]) => ver.startsWith(major)))) + // } catch { } + // } + // } const mcData: IndexedData = require('minecraft-data')(version) window['loadedData'] = mcData diff --git a/prismarine-viewer/package.json b/prismarine-viewer/package.json index d0ea3826..02b0a304 100644 --- a/prismarine-viewer/package.json +++ b/prismarine-viewer/package.json @@ -38,5 +38,8 @@ "optionalDependencies": { "canvas": "^2.11.2", "node-canvas-webgl": "^0.3.0" + }, + "devDependencies": { + "live-server": "^1.2.2" } } diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index 204ad65f..58e5a6f5 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -2,7 +2,7 @@ import { EventEmitter } from 'events' import { Vec3 } from 'vec3' import * as THREE from 'three' -import mcDataRaw from 'minecraft-data/data.js' // handled correctly in esbuild plugin +import mcDataRaw from 'minecraft-data/data.js' // note: using alias import blocksAtlases from 'mc-assets/dist/blocksAtlases.json' import blocksAtlasLatest from 'mc-assets/dist/blocksAtlasLatest.png' import blocksAtlasLegacy from 'mc-assets/dist/blocksAtlasLegacy.png' @@ -223,8 +223,12 @@ export abstract class WorldRendererCommon sendMesherMcData () { const allMcData = mcDataRaw.pc[this.version] ?? mcDataRaw.pc[toMajorVersion(this.version)] - const mcData = Object.fromEntries(Object.entries(allMcData).filter(([key]) => dynamicMcDataFiles.includes(key))) - mcData.version = JSON.parse(JSON.stringify(mcData.version)) + const mcData = { + version: JSON.parse(JSON.stringify(allMcData.version)) + } + for (const key of dynamicMcDataFiles) { + mcData[key] = allMcData[key] + } for (const worker of this.workers) { worker.postMessage({ type: 'mcData', mcData, config: this.mesherConfig }) diff --git a/rsbuild.config.ts b/rsbuild.config.ts index fd05db8a..588cc35d 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -101,9 +101,10 @@ export default defineConfig({ const prep = async () => { console.time('total-prep') fs.mkdirSync('./generated', { recursive: true }) - if (!fs.existsSync('./generated/minecraft-data-data.js')) { - childProcess.execSync('tsx ./scripts/genShims.ts', { stdio: 'inherit' }) + if (!fs.existsSync('./generated/minecraft-data-optimized.json') || require('./generated/minecraft-data-optimized.json').versionKey !== require('minecraft-data/package.json').version) { + childProcess.execSync('tsx ./scripts/makeOptimizedMcData.mjs', { stdio: 'inherit' }) } + childProcess.execSync('tsx ./scripts/genShims.ts', { stdio: 'inherit' }) if (!fs.existsSync('./generated/latestBlockCollisionsShapes.json')) { childProcess.execSync('tsx ./scripts/optimizeBlockCollisions.ts', { stdio: 'inherit' }) } @@ -117,7 +118,6 @@ export default defineConfig({ configJson.defaultProxy = ':8080' } fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8') - childProcess.execSync('node ./scripts/prepareData.mjs', { stdio: 'inherit' }) // childProcess.execSync('./scripts/prepareSounds.mjs', { stdio: 'inherit' }) // childProcess.execSync('tsx ./scripts/genMcDataTypes.ts', { stdio: 'inherit' }) // childProcess.execSync('tsx ./scripts/genPixelartTypes.ts', { stdio: 'inherit' }) @@ -164,7 +164,7 @@ export default defineConfig({ // throw new Error(`${resource.request} was requested by ${resource.contextInfo.issuer}`) } if (absolute.endsWith('/minecraft-data/data.js')) { - resource.request = path.join(__dirname, './generated/minecraft-data-data.js') + resource.request = path.join(__dirname, './src/shims/minecraftData.ts') } })) addRules([ diff --git a/scripts/build.js b/scripts/build.js index 7d3dc3fc..9b8a85a5 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -45,7 +45,6 @@ exports.getSwAdditionalEntries = () => { // need to be careful with this const filesToCachePatterns = [ 'index.html', - `mc-data/${defaultLocalServerOptions.versionMajor}.js`, 'background/**', // todo-low copy from assets '*.mp3', diff --git a/scripts/genShims.ts b/scripts/genShims.ts index e688e525..0ee8d91e 100644 --- a/scripts/genShims.ts +++ b/scripts/genShims.ts @@ -1,24 +1,7 @@ import fs from 'fs' -import MinecraftData from 'minecraft-data' -import MCProtocol from 'minecraft-protocol' import { appReplacableResources } from '../src/resourcesSource' -const { supportedVersions, defaultVersion } = MCProtocol - -// gen generated/minecraft-data-data.js - -const data = MinecraftData(defaultVersion) -const defaultVersionObj = { - [defaultVersion]: { - version: data.version, - protocol: data.protocol, - } -} - -const mcDataContents = `window.mcData ??= ${JSON.stringify(defaultVersionObj)};module.exports = { pc: window.mcData }` - fs.mkdirSync('./generated', { recursive: true }) -fs.writeFileSync('./generated/minecraft-data-data.js', mcDataContents, 'utf8') // app resources diff --git a/scripts/makeOptimizedMcData.mjs b/scripts/makeOptimizedMcData.mjs new file mode 100644 index 00000000..d0adcae2 --- /dev/null +++ b/scripts/makeOptimizedMcData.mjs @@ -0,0 +1,230 @@ +//@ts-check +import { build } from 'esbuild' +import { existsSync } from 'node:fs' +import Module from "node:module" +import { dirname } from 'node:path' +import supportedVersions from '../src/supportedVersions.mjs' +import { gzipSizeFromFileSync } from 'gzip-size' +import fs from 'fs' +import {default as _JsonOptimizer} from '../src/optimizeJson' +import { gzipSync } from 'zlib'; +import MinecraftData from 'minecraft-data' +import MCProtocol from 'minecraft-protocol' + +/** @type {typeof _JsonOptimizer} */ +//@ts-ignore +const JsonOptimizer = _JsonOptimizer.default + +// console.log(a.diff_main(JSON.stringify({ a: 1 }), JSON.stringify({ a: 1, b: 2 }))) + +const require = Module.createRequire(import.meta.url) + +const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') + +function toMajor (version) { + const [a, b] = (version + '').split('.') + return `${a}.${b}` +} + +const versions = {} +const dataTypes = new Set() + +for (const [version, dataSet] of Object.entries(dataPaths.pc)) { + if (!supportedVersions.includes(version)) continue + for (const type of Object.keys(dataSet)) { + dataTypes.add(type) + } + versions[version] = dataSet +} + +const versionToNumber = (ver) => { + const [x, y = '0', z = '0'] = ver.split('.') + return +`${x.padStart(2, '0')}${y.padStart(2, '0')}${z.padStart(2, '0')}` +} + +// if not included here (even as {}) will not be bundled & accessible! +const compressedOutput = false +// const dataTypeBundling = { +// protocol: { +// // ignoreRemoved: true, +// // ignoreChanges: true +// } +// } +const dataTypeBundling = { + language: { + ignoreRemoved: true, + ignoreChanges: true + }, + blocks: { + arrKey: 'name', + // ignoreRemoved: true, + // genChanges (source, diff) { + // const diffs = {} + // const newItems = {} + // for (const [key, val] of Object.entries(diff)) { + // const src = source[key] + // if (!src) { + // newItems[key] = val + // continue + // } + // const { minStateId, defaultState, maxStateId } = val + // if (defaultState === undefined || minStateId === src.minStateId || maxStateId === src.maxStateId || defaultState === src.defaultState) continue + // diffs[key] = [minStateId, defaultState, maxStateId] + // } + // return { + // stateChanges: diffs + // } + // }, + // ignoreChanges: true + }, + items: { + arrKey: 'name' + }, + attributes: { + arrKey: 'name' + }, + particles: { + arrKey: 'name' + }, + effects: { + arrKey: 'name' + }, + enchantments: { + arrKey: 'name' + }, + instruments: { + arrKey: 'name' + }, + foods: { + arrKey: 'name' + }, + entities: { + arrKey: 'id+type' + }, + materials: {}, + windows: { + arrKey: 'name' + }, + version: { + raw: true + }, + tints: {}, + biomes: { + arrKey: 'name' + }, + entityLoot: { + arrKey: 'entity' + }, + blockLoot: { + arrKey: 'block' + }, + recipes: {}, // todo we can do better + blockCollisionShapes: {}, + loginPacket: {}, + protocol: { + raw: true + }, + sounds: { + arrKey: 'name' + } +} + +const notBundling = [...dataTypes.keys()].filter(x => !Object.keys(dataTypeBundling).includes(x)) +console.log("Not bundling minecraft-data data:", notBundling) + +let previousData = {} +// /** @type {Record} */ +const diffSources = {} +const versionsArr = Object.entries(versions) +const sizePerDataType = {} +const rawDataVersions = {} +// const versionsArr = Object.entries(versions).slice(-1) +for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) { + for (const [dataType, dataPath] of Object.entries(dataSet)) { + const config = dataTypeBundling[dataType] + if (!config) continue + if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { + // contents += ` get ${dataType} () { return window.globalGetCollisionShapes?.("${version}") },\n` + continue + } + const loc = `minecraft-data/data/${dataPath}/` + const dataPathAbsolute = require.resolve(`minecraft-data/${loc}${dataType}`) + // const data = fs.readFileSync(dataPathAbsolute, 'utf8') + const dataRaw = require(dataPathAbsolute) + let injectCode = '' + let rawData = dataRaw + if (config.raw) { + rawDataVersions[dataType] ??= {} + rawDataVersions[dataType][version] = rawData + rawData = dataRaw + } else { + if (!diffSources[dataType]) { + diffSources[dataType] = new JsonOptimizer(config.arrKey, config.ignoreChanges, config.ignoreRemoved) + } + try { + diffSources[dataType].recordDiff(version, dataRaw) + injectCode = `restoreDiff(sources, ${JSON.stringify(dataType)}, ${JSON.stringify(version)})` + } catch (err) { + const error = new Error(`Failed to diff ${dataType} for ${version}: ${err.message}`) + error.stack = err.stack + throw error + } + } + sizePerDataType[dataType] ??= 0 + sizePerDataType[dataType] += Buffer.byteLength(JSON.stringify(injectCode || rawData), 'utf8') + if (config.genChanges && previousData[dataType]) { + const changes = config.genChanges(previousData[dataType], dataRaw) + // Object.assign(data, changes) + } + previousData[dataType] = dataRaw + } +} +const sources = Object.fromEntries(Object.entries(diffSources).map(x => { + const data = x[1].export() + // const data = {} + sizePerDataType[x[0]] += Buffer.byteLength(JSON.stringify(data), 'utf8') + return [x[0], data] +})) +Object.assign(sources, rawDataVersions) +sources.versionKey = require('minecraft-data/package.json').version + +const totalSize = Object.values(sizePerDataType).reduce((acc, val) => acc + val, 0) +console.log('total size (mb)', totalSize / 1024 / 1024) +console.log( + 'size per data type (mb, %)', + Object.fromEntries(Object.entries(sizePerDataType).map(([dataType, size]) => { + return [dataType, [size / 1024 / 1024, Math.round(size / totalSize * 100)]]; + }).sort((a, b) => { + //@ts-ignore + return b[1][1] - a[1][1]; + })) +) + +function compressToBase64(input) { + const buffer = gzipSync(input); + return buffer.toString('base64'); +} + +const filePath = './generated/minecraft-data-optimized.json' +fs.writeFileSync(filePath, JSON.stringify(sources), 'utf8') +if (compressedOutput) { + const minizedCompressed = compressToBase64(fs.readFileSync(filePath)) + console.log('size of compressed', Buffer.byteLength(minizedCompressed, 'utf8') / 1000 / 1000) + const compressedFilePath = './experiments/compressed.js' + fs.writeFileSync(compressedFilePath, minizedCompressed, 'utf8') +} + +console.log('size', fs.lstatSync(filePath).size / 1000 / 1000, gzipSizeFromFileSync(filePath) / 1000 / 1000) + +// always bundled + +const { defaultVersion } = MCProtocol +const data = MinecraftData(defaultVersion) +const initialMcData = { + [defaultVersion]: { + version: data.version, + protocol: data.protocol, + } +} + +fs.writeFileSync('./generated/minecraft-initial-data.json', JSON.stringify(initialMcData), 'utf8') diff --git a/scripts/prepareData.mjs b/scripts/prepareData.mjs deleted file mode 100644 index ab92499e..00000000 --- a/scripts/prepareData.mjs +++ /dev/null @@ -1,72 +0,0 @@ -//@ts-check -import { build } from 'esbuild' -import { existsSync } from 'node:fs' -import Module from "node:module" -import { dirname } from 'node:path' -import supportedVersions from '../src/supportedVersions.mjs' - -if (existsSync('dist/mc-data') && !process.argv.includes('-f')) { - console.log('using cached prepared data') - process.exit(0) -} - -const require = Module.createRequire(import.meta.url) - -const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') - -function toMajor (version) { - const [a, b] = (version + '').split('.') - return `${a}.${b}` -} - -const grouped = {} - -for (const [version, data] of Object.entries(dataPaths.pc)) { - if (!supportedVersions.includes(version)) continue - const major = toMajor(version) - grouped[major] ??= {} - grouped[major][version] = data -} - -const versionToNumber = (ver) => { - const [x, y = '0', z = '0'] = ver.split('.') - return +`${x.padStart(2, '0')}${y.padStart(2, '0')}${z.padStart(2, '0')}` -} - -console.log('preparing data') -console.time('data prepared') -let builds = [] -for (const [major, versions] of Object.entries(grouped)) { - // if (major !== '1.19') continue - let contents = 'Object.assign(window.mcData, {\n' - for (const [version, dataSet] of Object.entries(versions)) { - contents += ` '${version}': {\n` - for (const [dataType, dataPath] of Object.entries(dataSet)) { - if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { - contents += ` get ${dataType} () { return window.globalGetCollisionShapes?.("${version}") },\n` - continue - } - const loc = `minecraft-data/data/${dataPath}/` - contents += ` get ${dataType} () { return require("./${loc}${dataType}.json") },\n` - } - contents += ' },\n' - } - contents += '})' - - const promise = build({ - bundle: true, - outfile: `dist/mc-data/${major}.js`, - stdin: { - contents, - - resolveDir: dirname(require.resolve('minecraft-data')), - sourcefile: `mcData${major}.js`, - loader: 'js', - }, - metafile: true, - }) - // require('fs').writeFileSync('dist/mc-data/metafile.json', JSON.stringify(promise.metafile), 'utf8') - builds.push(promise) -} -await Promise.all(builds) -console.timeEnd('data prepared') diff --git a/scripts/testOptimizedMcdata.ts b/scripts/testOptimizedMcdata.ts new file mode 100644 index 00000000..17b5f7ed --- /dev/null +++ b/scripts/testOptimizedMcdata.ts @@ -0,0 +1,99 @@ +import assert from 'assert' +import JsonOptimizer from '../src/optimizeJson'; +import fs from 'fs' +import minecraftData from 'minecraft-data' + +const json = JSON.parse(fs.readFileSync('./generated/minecraft-data-optimized.json', 'utf8')) + +const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') + +const validateData = (ver, type) => { + const target = JsonOptimizer.restoreData(json[type], ver) + const arrKey = json[type].arrKey + const originalPath = dataPaths.pc[ver][type] + const original = require(`minecraft-data/minecraft-data/data/${originalPath}/${type}.json`) + if (arrKey) { + const originalKeys = original.map(a => JsonOptimizer.getByArrKey(a, arrKey)) as string[] + for (const [i, item] of originalKeys.entries()) { + if (originalKeys.indexOf(item) !== i) { + console.warn(`${type} ${ver} Incorrect source, duplicated arrKey (${arrKey}) ${item}. Ignoring!`) // todo should span instead + const index = originalKeys.indexOf(item); + original.splice(index, 1) + originalKeys.splice(index, 1) + } + } + // if (target.length !== originalKeys.length) { + // throw new Error(`wrong arr length: ${target.length} !== ${original.length}`) + // } + checkKeys(originalKeys, target.map(a => JsonOptimizer.getByArrKey(a, arrKey))) + for (const item of target as any[]) { + const keys = Object.entries(item).map(a => a[0]) + const origItem = original.find(a => JsonOptimizer.getByArrKey(a, arrKey) === JsonOptimizer.getByArrKey(item, arrKey)); + const keysSource = Object.entries(origItem).map(a => a[0]) + checkKeys(keysSource, keys, true, 'prop keys', true) + checkObj(origItem, item) + } + } else { + const keysOriginal = Object.keys(original) + const keysTarget = Object.keys(target) + checkKeys(keysOriginal, keysTarget) + for (const key of keysTarget) { + checkObj(original[key], target[key]) + } + } +} + +const checkObj = (source, diffing) => { + checkKeys(Object.keys(source), Object.keys(diffing)) + for (const [key, val] of Object.entries(source)) { + if (JSON.stringify(val) !== JSON.stringify(diffing[key])) { + throw new Error(`different value of ${key}: ${val} ${diffing[key]}`) + } + } +} + +const checkKeys = (source, diffing, isUniq = true, msg = '', redunantOk = false) => { + if (isUniq) { + for (const [i, item] of diffing.entries()) { + if (diffing.indexOf(item) !== i) { + throw new Error(`Duplicate: ${item}: ${i} ${diffing.indexOf(item)} ${msg}`) + } + } + } + for (const key of source) { + if (!diffing.includes(key)) { + throw new Error(`Diffing does not include "${key}" (${msg})`) + } + } + if (!redunantOk) { + for (const key of diffing) { + if (!source.includes(key)) { + throw new Error(`Source does not include "${key}" (${msg})`) + } + } + } +} + +// const data = minecraftData('1.20.4') +const oldId = JsonOptimizer.restoreData(json['blocks'], '1.20').find(x => x.name === 'brown_stained_glass').id; +const newId = JsonOptimizer.restoreData(json['blocks'], '1.20.4').find(x => x.name === 'brown_stained_glass').id; +assert(oldId !== newId) +// test all types + all versions + +for (const type of Object.keys(json)) { + if (!json[type].__IS_OPTIMIZED__) continue + if (type === 'language') continue // we have loose data for language for size reasons + console.log('validating', type) + const source = json[type] + let checkedVer = 0 + for (const ver of Object.keys(source.diffs)) { + try { + validateData(ver, type) + } catch (err) { + err.message = `Failed to validate ${type} for ${ver}: ${err.message}` + throw err; + } + checkedVer++ + } + console.log('Checked versions:', checkedVer) +} diff --git a/src/getCollisionInteractionShapes.ts b/src/getCollisionInteractionShapes.ts new file mode 100644 index 00000000..9dead22b --- /dev/null +++ b/src/getCollisionInteractionShapes.ts @@ -0,0 +1,17 @@ +import { getRenamedData } from 'flying-squid/dist/blockRenames' +import outputInteractionShapesJson from './interactionShapesGenerated.json' +import './getCollisionShapes' + +export default () => { + customEvents.on('gameLoaded', () => { + // todo also remap block states (e.g. redstone)! + const renamedBlocksInteraction = getRenamedData('blocks', Object.keys(outputInteractionShapesJson), '1.20.2', bot.version) + const interactionShapes = { + ...outputInteractionShapesJson, + ...Object.fromEntries(Object.entries(outputInteractionShapesJson).map(([block, shape], i) => [renamedBlocksInteraction[i], shape])) + } + interactionShapes[''] = interactionShapes['air'] + // todo make earlier + window.interactionShapes = interactionShapes + }) +} diff --git a/src/getCollisionShapes.ts b/src/getCollisionShapes.ts index 4ee0e802..383adc0e 100644 --- a/src/getCollisionShapes.ts +++ b/src/getCollisionShapes.ts @@ -1,6 +1,5 @@ import { getRenamedData } from 'flying-squid/dist/blockRenames' import collisionShapesInit from '../generated/latestBlockCollisionsShapes.json' -import outputInteractionShapesJson from './interactionShapesGenerated.json' // defining globally to be used in loaded data, not sure of better workaround window.globalGetCollisionShapes = (version) => { @@ -13,17 +12,3 @@ window.globalGetCollisionShapes = (version) => { } return collisionShapes } - -export default () => { - customEvents.on('gameLoaded', () => { - // todo also remap block states (e.g. redstone)! - const renamedBlocksInteraction = getRenamedData('blocks', Object.keys(outputInteractionShapesJson), '1.20.2', bot.version) - const interactionShapes = { - ...outputInteractionShapesJson, - ...Object.fromEntries(Object.entries(outputInteractionShapesJson).map(([block, shape], i) => [renamedBlocksInteraction[i], shape])) - } - interactionShapes[''] = interactionShapes['air'] - // todo make earlier - window.interactionShapes = interactionShapes - }) -} diff --git a/src/index.ts b/src/index.ts index c9040e35..39a110a3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import './globals' import './devtools' import './entities' import './globalDomListeners' -import initCollisionShapes from './getCollisionShapes' +import initCollisionShapes from './getCollisionInteractionShapes' import { onGameLoad } from './inventoryWindows' import { supportedVersions } from 'minecraft-protocol' import protocolMicrosoftAuth from 'minecraft-protocol/src/client/microsoftAuth' @@ -380,6 +380,7 @@ async function connect (connectOptions: ConnectOptions) { try { const serverOptions = defaultsDeep({}, connectOptions.serverOverrides ?? {}, options.localServerOptions, defaultServerOptions) Object.assign(serverOptions, connectOptions.serverOverridesFlat ?? {}) + window._LOAD_MC_DATA() // start loading data (if not loaded yet) const downloadMcData = async (version: string) => { if (connectOptions.authenticatedAccount && versionToNumber(version) < versionToNumber('1.19.4')) { // todo support it (just need to fix .export crash) @@ -392,13 +393,13 @@ async function connect (connectOptions: ConnectOptions) { // ignore cache hit versionsByMinecraftVersion.pc[lastVersion]!['dataVersion']!++ } + setLoadingScreenStatus(`Loading data for ${version}`) if (!document.fonts.check('1em mojangles')) { // todo instead re-render signs on load await document.fonts.load('1em mojangles').catch(() => { }) } - setLoadingScreenStatus(`Downloading data for ${version}`) + await window._MC_DATA_RESOLVER.promise // ensure data is loaded await downloadSoundsIfNeeded() - await loadScript(`./mc-data/${toMajorVersion(version)}.js`) miscUiState.loadedDataVersion = version try { await resourcepackReload(version) diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts new file mode 100644 index 00000000..442a72a1 --- /dev/null +++ b/src/optimizeJson.ts @@ -0,0 +1,264 @@ +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' + +type IdMap = Record + +type DiffData = { + removed: number[], + changed: any[], + removedProps: Array<[number, number[]]>, + added +} + +type SourceData = { + keys: IdMap, + properties: IdMap + source: Record + diffs: Record + arrKey? + __IS_OPTIMIZED__: true +} + +export default class JsonOptimizer { + keys = {} as IdMap + idToKey = {} as Record + properties = {} as IdMap + source = {} + previousKeys = [] as number[] + previousValues = {} as Record + diffs = {} as Record + + constructor (public arrKey?: string, public ignoreChanges = false, public ignoreRemoved = false) { } + + export () { + const { keys, properties, source, arrKey, diffs } = this + return { + keys, + properties, + source, + arrKey, + diffs, + '__IS_OPTIMIZED__': true + } satisfies SourceData + } + + diffObj (diffing): DiffData { + const removed = [] as number[] + const changed = [] as any[] + const removedProps = [] as any[] + const { arrKey, ignoreChanges, ignoreRemoved } = this + const added = [] as number[] + + if (!diffing || typeof diffing !== 'object') throw new Error('diffing data is not object') + if (Array.isArray(diffing) && !arrKey) throw new Error('arrKey is required for arrays') + const diffingObj = Array.isArray(diffing) ? Object.fromEntries(diffing.map(x => { + const key = JsonOptimizer.getByArrKey(x, arrKey!) + return [key, x] + })) : diffing + + const possiblyNewKeys = Object.keys(diffingObj) + this.keys ??= {} + this.properties ??= {} + let lastRootKeyId = Object.values(this.keys).length + let lastItemKeyId = Object.values(this.properties).length + for (const key of possiblyNewKeys) { + this.keys[key] ??= lastRootKeyId++ + this.idToKey[this.keys[key]] = key + } + const DEBUG = false + + const addDiff = (key, newVal, prevVal) => { + const valueMapped = [] as any[] + const isItemObj = typeof newVal === 'object' && newVal + const keyId = this.keys[key] + if (isItemObj) { + const removedPropsLocal = [] as any[] + for (const [prop, val] of Object.entries(newVal)) { + // mc-data: why push only changed props? eg for blocks only stateId are different between all versions so we skip a lot of duplicated data like block props + if (!isEqualStructured(newVal[prop], prevVal[prop])) { + let keyMapped = this.properties[prop] + if (keyMapped === undefined) { + this.properties[prop] = lastItemKeyId++ + keyMapped = this.properties[prop] + } + valueMapped.push(DEBUG ? prop : keyMapped, newVal[prop]) + } + } + // also add undefined for removed props + for (const prop of Object.keys(prevVal)) { + if (prop in newVal) continue + let keyMapped = this.properties[prop] + if (keyMapped === undefined) { + this.properties[prop] = lastItemKeyId++ + keyMapped = this.properties[prop] + } + removedPropsLocal.push(DEBUG ? prop : keyMapped) + } + removedProps.push([keyId, removedPropsLocal]) + } + changed.push(DEBUG ? key : keyId, isItemObj ? valueMapped : newVal) + } + for (const [id, sourceVal] of Object.entries(this.source)) { + const key = this.idToKey[id] + const diffVal = diffingObj[key] + if (!ignoreChanges && diffVal !== undefined) { + this.previousValues[id] ??= this.source[id] + const prevVal = this.previousValues[id] + if (!isEqualStructured(prevVal, diffVal)) { + addDiff(key, diffVal, prevVal) + } + this.previousValues[id] = diffVal + } + } + for (const [key, val] of Object.entries(diffingObj)) { + const id = this.keys[key] + if (!this.source[id]) { + this.source[id] = val + } + added.push(id) + } + + for (const previousKey of this.previousKeys) { + const key = this.idToKey[previousKey] + if (diffingObj[key] === undefined && !ignoreRemoved) { + removed.push(previousKey) + } + } + + for (const toRemove of removed) { + this.previousKeys.splice(this.previousKeys.indexOf(toRemove), 1) + } + + for (const previousKey of this.previousKeys) { + const index = added.indexOf(previousKey) + if (index === -1) continue + added.splice(index, 1) + } + + this.previousKeys = [...this.previousKeys, ...added] + + return { + removed, + changed, + added, + removedProps + } + } + + recordDiff (key: string, diffObj: string) { + const diff = this.diffObj(diffObj) + this.diffs[key] = diff + } + + static isOptimizedChangeDiff (changePossiblyArrDiff) { + if (!Array.isArray(changePossiblyArrDiff)) return false + if (changePossiblyArrDiff.length % 2 !== 0) return false + for (let i = 0; i < changePossiblyArrDiff.length; i += 2) { + if (typeof changePossiblyArrDiff[i] !== 'number') return false + } + return true + } + + static restoreData ({ keys, properties, source, arrKey, diffs }: SourceData, targetKey: string) { + // if (!diffs[targetKey]) throw new Error(`The requested data to restore with key ${targetKey} does not exist`) + source = structuredClone(source) + const keysById = Object.fromEntries(Object.entries(keys).map(x => [x[1], x[0]])) + const propertiesById = Object.fromEntries(Object.entries(properties).map(x => [x[1], x[0]])) + const dataByKeys = {} as Record + for (const [versionKey, { added, changed, removed, removedProps }] of Object.entries(diffs)) { + for (const toAdd of added) { + dataByKeys[toAdd] = source[toAdd] + } + for (const toRemove of removed) { + delete dataByKeys[toRemove] + } + for (let i = 0; i < changed.length; i += 2) { + const key = changed[i] + const change = changed[i + 1] + const isOptimizedChange = JsonOptimizer.isOptimizedChangeDiff(change) + if (isOptimizedChange) { + // apply optimized diff + for (let k = 0; k < change.length; k += 2) { + const propId = change[k] + const newVal = change[k + 1] + const prop = propertiesById[propId] + // const prop = propId + if (prop === undefined) throw new Error(`Property id change is undefined: ${propId}`) + dataByKeys[key][prop] = newVal + } + } else { + dataByKeys[key] = change + } + } + for (const [key, removePropsId] of removedProps) { + for (const removePropId of removePropsId) { + const removeProp = propertiesById[removePropId] + delete dataByKeys[key][removeProp] + } + } + if (versionToNumber(versionKey) <= versionToNumber(targetKey)) { + break + } + } + if (arrKey) { + return Object.values(dataByKeys) + } else { + return Object.fromEntries(Object.entries(dataByKeys).map(([key, val]) => [keysById[key], val])) + } + } + + static getByArrKey (item: any, arrKey: string) { + return arrKey.split('+').map(x => item[x]).join('+') + } + + static resolveDefaults (arr) { + if (!Array.isArray(arr)) throw new Error('not an array') + const propsValueCount = {} as { + [key: string]: { + [val: string]: number + } + } + for (const obj of arr) { + if (typeof obj !== 'object' || !obj) continue + for (const [key, val] of Object.entries(obj)) { + const valJson = JSON.stringify(val) + propsValueCount[key] ??= {} + propsValueCount[key][valJson] ??= 0 + propsValueCount[key][valJson] += 1 + } + } + const defaults = Object.fromEntries(Object.entries(propsValueCount).map(([prop, values]) => { + const defaultValue = Object.entries(values).sort(([, count1], [, count2]) => count2 - count1)[0][0] + return [prop, defaultValue] + })) + + const newData = [] as any[] + const noData = {} + for (const [i, obj] of arr.entries()) { + if (typeof obj !== 'object' || !obj) { + newData.push(obj) + continue + } + for (const key of Object.keys(defaults)) { + const val = obj[key] + if (!val) { + noData[key] ??= [] + noData[key].push(key) + continue + } + if (defaults[key] === JSON.stringify(val)) { + delete obj[key] + } + } + newData.push(obj) + } + + return { + data: newData, + defaults + } + } +} + +const isEqualStructured = (val1, val2) => { + return JSON.stringify(val1) === JSON.stringify(val2) +} diff --git a/src/react/CreateWorld.tsx b/src/react/CreateWorld.tsx index 0ff34a39..da710be1 100644 --- a/src/react/CreateWorld.tsx +++ b/src/react/CreateWorld.tsx @@ -45,8 +45,8 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer placeholder='World name' /> { return { value: obj.version, label: obj.version === defaultVersion ? obj.version + ' (available offline)' : obj.version } })} - selected={{ value: defaultVersion, label: defaultVersion + ' (available offline)' }} + versions={versions.map((obj) => { return { value: obj.version, label: obj.version } })} + selected={{ value: defaultVersion, label: defaultVersion }} onChange={(value) => { creatingWorldState.version = value ?? defaultVersion }} diff --git a/src/shims/minecraftData.ts b/src/shims/minecraftData.ts new file mode 100644 index 00000000..6edb5f48 --- /dev/null +++ b/src/shims/minecraftData.ts @@ -0,0 +1,92 @@ +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' +import JsonOptimizer from '../optimizeJson' +import minecraftInitialDataJson from '../../generated/minecraft-initial-data.json' +import { toMajorVersion } from '../utils' + +const customResolver = () => { + const resolver = Promise.withResolvers() + let resolvedData + return { + ...resolver, + get resolvedData () { + return resolvedData + }, + resolve (data) { + resolver.resolve(data) + resolvedData = data + } + } +} + +const optimizedDataResolver = customResolver() +window._MC_DATA_RESOLVER = optimizedDataResolver +window._LOAD_MC_DATA = async () => { + if (optimizedDataResolver.resolvedData) return + optimizedDataResolver.resolve(await import('../../generated/minecraft-data-optimized.json')) +} + +// 30 seconds +const cacheTtl = 30 * 1000 +const cache = new Map() +const cacheTime = new Map() +const possiblyGetFromCache = (version: string) => { + if (minecraftInitialDataJson[version] && !optimizedDataResolver.resolvedData) { + return minecraftInitialDataJson[version] + } + if (cache.has(version)) { + return cache.get(version) + } + const inner = () => { + if (!optimizedDataResolver.resolvedData) { + throw new Error(`Data for ${version} is not ready yet`) + } + const dataTypes = Object.keys(optimizedDataResolver.resolvedData) + const allRestored = {} + for (const dataType of dataTypes) { + if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { + const shapes = window.globalGetCollisionShapes?.(version) + if (shapes) { + allRestored[dataType] = shapes + continue + } + } + + const data = optimizedDataResolver.resolvedData[dataType] + if (data.__IS_OPTIMIZED__) { + allRestored[dataType] = JsonOptimizer.restoreData(data, version) + } else { + allRestored[dataType] = data[version] ?? data[toMajorVersion(version)] + } + } + return allRestored + } + const data = inner() + cache.set(version, data) + cacheTime.set(version, Date.now()) + return data +} +window.allLoadedMcData = new Proxy({}, { + get (t, version: string) { + // special properties like $typeof + if (version.includes('$')) return + // todo enumerate all props + return new Proxy({}, { + get (target, prop) { + return possiblyGetFromCache(version)[prop] + }, + }) + } +}) + +setInterval(() => { + const now = Date.now() + for (const [version, time] of cacheTime) { + if (now - time > cacheTtl) { + cache.delete(version) + cacheTime.delete(version) + } + } +}, 1000) + +export const pc = window.allLoadedMcData +export default { pc } From bbd01d9682b8b39bfb54a75cb739832b6e14ec5e Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 1 Sep 2024 02:37:22 +0300 Subject: [PATCH 041/865] fix: when left click was pressed down the swing arm packet sending was not limited --- src/worldInteractions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts index 7a9aa508..bc54efcd 100644 --- a/src/worldInteractions.ts +++ b/src/worldInteractions.ts @@ -38,6 +38,7 @@ class WorldInteraction { currentDigTime prevOnGround lastBlockPlaced: number + lastSwing = 0 buttons = [false, false, false] lastButtons = [false, false, false] breakStartTime: number | undefined = 0 @@ -350,8 +351,9 @@ class WorldInteraction { }) customEvents.emit('digStart') this.lastDigged = Date.now() - } else { + } else if (performance.now() - this.lastSwing > 200) { bot.swingArm('right') + this.lastSwing = performance.now() } } this.prevOnGround = onGround From ee966395c6ff78cdb5fe957a3c6ae0751432fb2f Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sun, 1 Sep 2024 03:32:53 +0300 Subject: [PATCH 042/865] feat: Display holding block (experimental setting) (#190) --- experiments/three.html | 1 + experiments/three.ts | 101 +++++++++ prismarine-viewer/viewer/lib/entities.js | 85 +++---- prismarine-viewer/viewer/lib/holdingBlock.ts | 207 ++++++++++++++++++ .../viewer/lib/mesher/standaloneRenderer.ts | 16 ++ prismarine-viewer/viewer/lib/viewer.ts | 30 ++- .../viewer/lib/worldDataEmitter.ts | 35 ++- .../viewer/lib/worldrendererCommon.ts | 6 + .../viewer/lib/worldrendererThree.ts | 35 ++- src/inventoryWindows.ts | 9 +- src/optionsGuiScheme.tsx | 1 + src/optionsStorage.ts | 1 + src/topRightStats.ts | 11 + src/watchOptions.ts | 2 +- src/worldInteractions.ts | 6 + 15 files changed, 489 insertions(+), 57 deletions(-) create mode 100644 experiments/three.html create mode 100644 experiments/three.ts create mode 100644 prismarine-viewer/viewer/lib/holdingBlock.ts diff --git a/experiments/three.html b/experiments/three.html new file mode 100644 index 00000000..8765081b --- /dev/null +++ b/experiments/three.html @@ -0,0 +1 @@ + diff --git a/experiments/three.ts b/experiments/three.ts new file mode 100644 index 00000000..7a629a13 --- /dev/null +++ b/experiments/three.ts @@ -0,0 +1,101 @@ +import * as THREE from 'three' +import * as tweenJs from '@tweenjs/tween.js' +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' +import * as THREE from 'three'; +import Jimp from 'jimp'; + +const scene = new THREE.Scene() +const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000) +camera.position.set(0, 0, 5) +const renderer = new THREE.WebGLRenderer() +renderer.setSize(window.innerWidth, window.innerHeight) +document.body.appendChild(renderer.domElement) + +const controls = new OrbitControls(camera, renderer.domElement) + +const geometry = new THREE.BoxGeometry(1, 1, 1) +const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }) +const cube = new THREE.Mesh(geometry, material) +cube.position.set(0.5, 0.5, 0.5); +const group = new THREE.Group() +group.add(cube) +group.position.set(-0.5, -0.5, -0.5); +const outerGroup = new THREE.Group() +outerGroup.add(group) +outerGroup.scale.set(0.2, 0.2, 0.2) +outerGroup.position.set(1, 1, 0) +scene.add(outerGroup) + +// const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0x00_00_ff, transparent: true, opacity: 0.5 })) +// mesh.position.set(0.5, 1, 0.5) +// const group = new THREE.Group() +// group.add(mesh) +// group.position.set(-0.5, -1, -0.5) +// const outerGroup = new THREE.Group() +// outerGroup.add(group) +// // outerGroup.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z) +// scene.add(outerGroup) + + new tweenJs.Tween(group.rotation).to({ z: THREE.MathUtils.degToRad(90) }, 1000).yoyo(true).repeat(Infinity).start() + +const tweenGroup = new tweenJs.Group() +function animate () { + tweenGroup.update() + requestAnimationFrame(animate) +// cube.rotation.x += 0.01 +// cube.rotation.y += 0.01 + renderer.render(scene, camera) +} +animate() + +// let animation + +window.animate = () => { + // new Tween.Tween(group.position).to({ y: group.position.y - 1}, 1000 * 0.35/2).yoyo(true).repeat(1).start() + new tweenJs.Tween(group.rotation, tweenGroup).to({ z: THREE.MathUtils.degToRad(90) }, 1000 * 0.35 / 2).yoyo(true).repeat(Infinity).start().onRepeat(() => { + console.log('done') + }) +} + +window.stop = () => { + tweenGroup.removeAll() +} + + +function createGeometryFromImage() { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABEElEQVQ4jWNkIAPw2Zv9J0cfXPOSvx/+L/n74T+HqsJ/JlI1T9u3i6H91B7ybdY+vgZuO1majV+fppFmPnuz/+ihy2dv9t/49Wm8mlECkV1FHh5FfPZm/1XXTGX4cechA4eKPMNVq1CGH7cfMBJ0rlxX+X8OVYX/xq9P/5frKifoZ0Z0AwS8HRkYGBgYvt+8xyDXUUbQZgwJPnuz/+wq8gw/7zxk+PXsFUFno0h6mon+l5fgZFhwnYmBTUqMgYGBgaAhLMiaHQyFGOZvf8Lw49FXRgYGhv8MDAwwg/7jMoQFFury/C8Y5m9/wnADohnZVryJhoWBARJ9Cw69gtmMAgiFAcuvZ68Yfj17hU8NXgAATdKfkzbQhBEAAAAASUVORK5CYII=' + console.log('img.complete', img.complete) + img.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const context = canvas.getContext('2d'); + context.drawImage(img, 0, 0, img.width, img.height); + const imgData = context.getImageData(0, 0, img.width, img.height); + + const shape = new THREE.Shape(); + for (let y = 0; y < img.height; y++) { + for (let x = 0; x < img.width; x++) { + const index = (y * img.width + x) * 4; + const alpha = imgData.data[index + 3]; + if (alpha !== 0) { + shape.lineTo(x, y); + } + } + } + + const geometry = new THREE.ShapeGeometry(shape); + resolve(geometry); + }; + img.onerror = reject; + }); +} + +// Usage: +const shapeGeomtry = createGeometryFromImage().then(geometry => { + const material = new THREE.MeshBasicMaterial({ color: 0xffffff }); + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); +}) diff --git a/prismarine-viewer/viewer/lib/entities.js b/prismarine-viewer/viewer/lib/entities.js index 57a45a44..01ce7d6d 100644 --- a/prismarine-viewer/viewer/lib/entities.js +++ b/prismarine-viewer/viewer/lib/entities.js @@ -282,6 +282,46 @@ export class Entities extends EventEmitter { } } + getItemMesh(item) { + const textureUv = this.getItemUv?.(item.itemId ?? item.blockId) + if (textureUv) { + // todo use geometry buffer uv instead! + const { u, v, size, su, sv, texture } = textureUv + const itemsTexture = texture.clone() + itemsTexture.flipY = true + itemsTexture.offset.set(u, 1 - v - (sv ?? size)) + itemsTexture.repeat.set(su ?? size, sv ?? size) + itemsTexture.needsUpdate = true + itemsTexture.magFilter = THREE.NearestFilter + itemsTexture.minFilter = THREE.NearestFilter + const itemsTextureFlipped = itemsTexture.clone() + itemsTextureFlipped.repeat.x *= -1 + itemsTextureFlipped.needsUpdate = true + itemsTextureFlipped.offset.set(u + (su ?? size), 1 - v - (sv ?? size)) + const material = new THREE.MeshStandardMaterial({ + map: itemsTexture, + transparent: true, + alphaTest: 0.1, + }) + const materialFlipped = new THREE.MeshStandardMaterial({ + map: itemsTextureFlipped, + transparent: true, + alphaTest: 0.1, + }) + const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 0), [ + // top left and right bottom are black box materials others are transparent + new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), + new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), + material, materialFlipped, + ]) + return { + mesh, + itemsTexture, + itemsTextureFlipped, + } + } + } + update(/** @type {import('prismarine-entity').Entity & {delete?, pos}} */entity, overrides) { let isPlayerModel = entity.name === 'player' if (entity.name === 'zombie' || entity.name === 'zombie_villager' || entity.name === 'husk') { @@ -296,52 +336,23 @@ export class Entities extends EventEmitter { //@ts-expect-error const item = entity.metadata?.find(m => typeof m === 'object' && m?.itemCount) if (item) { - const textureUv = this.getItemUv?.(item.itemId ?? item.blockId) - if (textureUv) { - // todo use geometry buffer uv instead! - const { u, v, size, su, sv, texture } = textureUv - const itemsTexture = texture.clone() - itemsTexture.flipY = true - itemsTexture.offset.set(u, 1 - v - (sv ?? size)) - itemsTexture.repeat.set(su ?? size, sv ?? size) - itemsTexture.needsUpdate = true - itemsTexture.magFilter = THREE.NearestFilter - itemsTexture.minFilter = THREE.NearestFilter - const itemsTextureFlipped = itemsTexture.clone() - itemsTextureFlipped.repeat.x *= -1 - itemsTextureFlipped.needsUpdate = true - itemsTextureFlipped.offset.set(u + (su ?? size), 1 - v - (sv ?? size)) - const material = new THREE.MeshStandardMaterial({ - map: itemsTexture, - transparent: true, - alphaTest: 0.1, - }) - const materialFlipped = new THREE.MeshStandardMaterial({ - map: itemsTextureFlipped, - transparent: true, - alphaTest: 0.1, - }) - mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 0), [ - // top left and right bottom are black box materials others are transparent - new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), - new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), - material, materialFlipped, - ]) - mesh.scale.set(0.5, 0.5, 0.5) - mesh.position.set(0, 0.2, 0) + const object = this.getItemMesh(item) + if (object) { + object.scale.set(0.5, 0.5, 0.5) + object.position.set(0, 0.2, 0) // set faces // mesh.position.set(targetPos.x + 0.5 + 2, targetPos.y + 0.5, targetPos.z + 0.5) // viewer.scene.add(mesh) const clock = new THREE.Clock() - mesh.onBeforeRender = () => { + object.onBeforeRender = () => { const delta = clock.getDelta() - mesh.rotation.y += delta + object.rotation.y += delta } //@ts-expect-error group.additionalCleanup = () => { // important: avoid texture memory leak and gpu slowdown - itemsTexture.dispose() - itemsTextureFlipped.dispose() + object.itemsTexture.dispose() + object.itemsTextureFlipped.dispose() } } } diff --git a/prismarine-viewer/viewer/lib/holdingBlock.ts b/prismarine-viewer/viewer/lib/holdingBlock.ts new file mode 100644 index 00000000..f5a0ca79 --- /dev/null +++ b/prismarine-viewer/viewer/lib/holdingBlock.ts @@ -0,0 +1,207 @@ +import * as THREE from 'three' +import * as tweenJs from '@tweenjs/tween.js' +import worldBlockProvider from 'mc-assets/dist/worldBlockProvider' +import { getThreeBlockModelGroup, renderBlockThree, setBlockPosition } from './mesher/standaloneRenderer' + +export type HandItemBlock = { + name + properties +} + +export default class HoldingBlock { + holdingBlock: THREE.Object3D | undefined = undefined + swingAnimation: tweenJs.Group | undefined = undefined + blockSwapAnimation: { + tween: tweenJs.Group + hidden: boolean + } | undefined = undefined + cameraGroup = new THREE.Mesh() + objectOuterGroup = new THREE.Group() + objectInnerGroup = new THREE.Group() + camera: THREE.Group | THREE.PerspectiveCamera + stopUpdate = false + lastHeldItem: HandItemBlock | undefined + toBeRenderedItem: HandItemBlock | undefined + isSwinging = false + nextIterStopCallbacks: Array<() => void> | undefined + + constructor (public scene: THREE.Scene) { + this.initCameraGroup() + } + + initCameraGroup () { + this.cameraGroup = new THREE.Mesh() + this.scene.add(this.cameraGroup) + } + + startSwing () { + this.nextIterStopCallbacks = undefined // forget about cancelling + if (this.isSwinging) return + this.swingAnimation = new tweenJs.Group() + this.isSwinging = true + const cube = this.cameraGroup.children[0] + if (cube) { + // const DURATION = 1000 * 0.35 / 2 + const DURATION = 1000 * 0.35 / 3 + // const DURATION = 1000 + const initialPos = { + x: this.objectInnerGroup.position.x, + y: this.objectInnerGroup.position.y, + z: this.objectInnerGroup.position.z + } + const initialRot = { + x: this.objectInnerGroup.rotation.x, + y: this.objectInnerGroup.rotation.y, + z: this.objectInnerGroup.rotation.z + } + const mainAnim = new tweenJs.Tween(this.objectInnerGroup.position, this.swingAnimation).to({ y: this.objectInnerGroup.position.y - this.objectInnerGroup.scale.y / 2 }, DURATION).yoyo(true).repeat(Infinity).start() + let i = 0 + mainAnim.onRepeat(() => { + i++ + if (this.nextIterStopCallbacks && i % 2 === 0) { + for (const callback of this.nextIterStopCallbacks) { + callback() + } + this.nextIterStopCallbacks = undefined + this.isSwinging = false + this.swingAnimation!.removeAll() + this.swingAnimation = undefined + // todo refactor to be more generic for animations + this.objectInnerGroup.position.set(initialPos.x, initialPos.y, initialPos.z) + // this.objectInnerGroup.rotation.set(initialRot.x, initialRot.y, initialRot.z) + Object.assign(this.objectInnerGroup.rotation, initialRot) + } + }) + + new tweenJs.Tween(this.objectInnerGroup.rotation, this.swingAnimation).to({ z: THREE.MathUtils.degToRad(90) }, DURATION).yoyo(true).repeat(Infinity).start() + new tweenJs.Tween(this.objectInnerGroup.rotation, this.swingAnimation).to({ x: -THREE.MathUtils.degToRad(90) }, DURATION).yoyo(true).repeat(Infinity).start() + } + } + + async stopSwing () { + if (!this.isSwinging) return + // might never resolve! + /* return */void new Promise((resolve) => { + this.nextIterStopCallbacks ??= [] + this.nextIterStopCallbacks.push(() => { + resolve() + }) + }) + } + + update (camera: typeof this.camera) { + this.camera = camera + this.swingAnimation?.update() + this.blockSwapAnimation?.tween.update() + this.updateCameraGroup() + } + + // worldTest () { + // const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshPhongMaterial({ color: 0x00_00_ff, transparent: true, opacity: 0.5 })) + // mesh.position.set(0.5, 0.5, 0.5) + // const group = new THREE.Group() + // group.add(mesh) + // group.position.set(-0.5, -0.5, -0.5) + // const outerGroup = new THREE.Group() + // outerGroup.add(group) + // outerGroup.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z) + // this.scene.add(outerGroup) + + // new tweenJs.Tween(group.rotation).to({ z: THREE.MathUtils.degToRad(90) }, 1000).yoyo(true).repeat(Infinity).start() + // } + + async playBlockSwapAnimation () { + // if (this.blockSwapAnimation) return + this.blockSwapAnimation ??= { + tween: new tweenJs.Group(), + hidden: false + } + const DURATION = 1000 * 0.35 / 2 + const tween = new tweenJs.Tween(this.objectInnerGroup.position, this.blockSwapAnimation.tween).to({ + y: this.objectInnerGroup.position.y + (this.objectInnerGroup.scale.y * 1.5 * (this.blockSwapAnimation.hidden ? 1 : -1)) + }, DURATION).start() + return new Promise((resolve) => { + tween.onComplete(() => { + if (this.blockSwapAnimation!.hidden) { + this.blockSwapAnimation = undefined + } else { + this.blockSwapAnimation!.hidden = !this.blockSwapAnimation!.hidden + } + resolve() + }) + }) + } + + isDifferentItem (block: HandItemBlock | undefined) { + return this.lastHeldItem && (this.lastHeldItem.name !== block?.name || JSON.stringify(this.lastHeldItem.properties) !== JSON.stringify(block?.properties ?? '{}')) + } + + updateCameraGroup () { + if (this.stopUpdate) return + const { camera } = this + this.cameraGroup.position.copy(camera.position) + this.cameraGroup.rotation.copy(camera.rotation) + + const viewerSize = viewer.renderer.getSize(new THREE.Vector2()) + // const x = window.x ?? 0.25 * viewerSize.width / viewerSize.height + // const x = 0 * viewerSize.width / viewerSize.height + const x = 0.2 * viewerSize.width / viewerSize.height + this.objectOuterGroup.position.set(x, -0.3, -0.45) + } + + async initHandObject (material: THREE.Material, blockstatesModels: any, blocksAtlases: any, block?: HandItemBlock) { + let animatingCurrent = false + if (!this.swingAnimation && !this.blockSwapAnimation && this.isDifferentItem(block)) { + animatingCurrent = true + await this.playBlockSwapAnimation() + this.holdingBlock?.removeFromParent() + this.holdingBlock = undefined + } + this.lastHeldItem = block + if (!block) { + this.holdingBlock?.removeFromParent() + this.holdingBlock = undefined + this.swingAnimation = undefined + this.blockSwapAnimation = undefined + return + } + const blockProvider = worldBlockProvider(blockstatesModels, blocksAtlases, 'latest') + const models = blockProvider.getAllResolvedModels0_1(block, true) + const blockInner = getThreeBlockModelGroup(material, models, undefined, 'plains', loadedData) + // const { mesh: itemMesh } = viewer.entities.getItemMesh({ + // itemId: 541, + // })! + // itemMesh.position.set(0.5, 0.5, 0.5) + // const blockInner = itemMesh + blockInner.name = 'holdingBlock' + const blockOuterGroup = new THREE.Group() + blockOuterGroup.add(blockInner) + this.holdingBlock = blockInner + this.objectInnerGroup = new THREE.Group() + this.objectInnerGroup.add(blockOuterGroup) + this.objectInnerGroup.position.set(-0.5, -0.5, -0.5) + // todo cleanup + if (animatingCurrent) { + this.objectInnerGroup.position.y -= this.objectInnerGroup.scale.y * 1.5 + } + Object.assign(blockOuterGroup.position, { x: 0.5, y: 0.5, z: 0.5 }) + + this.objectOuterGroup = new THREE.Group() + this.objectOuterGroup.add(this.objectInnerGroup) + + this.cameraGroup.add(this.objectOuterGroup) + const rotation = -45 + -90 + // const rotation = -45 // should be for item + this.holdingBlock.rotation.set(0, THREE.MathUtils.degToRad(rotation), 0, 'ZYX') + + // const scale = window.scale ?? 0.2 + const scale = 0.2 + this.objectOuterGroup.scale.set(scale, scale, scale) + // this.objectOuterGroup.position.set(x, window.y ?? -0.41, window.z ?? -0.45) + // this.objectOuterGroup.position.set(x, 0, -0.45) + + if (animatingCurrent) { + await this.playBlockSwapAnimation() + } + } +} diff --git a/prismarine-viewer/viewer/lib/mesher/standaloneRenderer.ts b/prismarine-viewer/viewer/lib/mesher/standaloneRenderer.ts index 2dc2f599..43369cc2 100644 --- a/prismarine-viewer/viewer/lib/mesher/standaloneRenderer.ts +++ b/prismarine-viewer/viewer/lib/mesher/standaloneRenderer.ts @@ -272,3 +272,19 @@ export const renderBlockThree = (...args: Parameters) => { + const geometry = renderBlockThree(...args) + const mesh = new THREE.Mesh(geometry, material) + mesh.position.set(-0.5, -0.5, -0.5) + const group = new THREE.Group() + group.add(mesh) + group.rotation.set(0, -THREE.MathUtils.degToRad(90), 0, 'ZYX') + globalThis.mesh = group + return group + // return new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshPhongMaterial({ color: 0x00_00_ff, transparent: true, opacity: 0.5 })) +} + +export const setBlockPosition = (object: THREE.Object3D, position: { x: number, y: number, z: number }) => { + object.position.set(position.x + 0.5, position.y + 0.5, position.z + 0.5) +} diff --git a/prismarine-viewer/viewer/lib/viewer.ts b/prismarine-viewer/viewer/lib/viewer.ts index 7893bccc..7cd759e4 100644 --- a/prismarine-viewer/viewer/lib/viewer.ts +++ b/prismarine-viewer/viewer/lib/viewer.ts @@ -7,7 +7,7 @@ import { Entities } from './entities' import { Primitives } from './primitives' import { WorldRendererThree } from './worldrendererThree' import { WorldRendererCommon, WorldRendererConfig, defaultWorldRendererConfig } from './worldrendererCommon' -import { renderBlockThree } from './mesher/standaloneRenderer' +import { getThreeBlockModelGroup, renderBlockThree, setBlockPosition } from './mesher/standaloneRenderer' export class Viewer { scene: THREE.Scene @@ -101,18 +101,32 @@ export class Viewer { } demoModel () { + //@ts-expect-error + const pos = cursorBlockRel(0, 1, 0).position const blockProvider = worldBlockProvider(this.world.blockstatesModels, this.world.blocksAtlases, 'latest') const models = blockProvider.getAllResolvedModels0_1({ - name: 'item_frame', + name: 'furnace', properties: { - map: false + // map: false } - }) - const geometry = renderBlockThree(models, undefined, 'plains', loadedData) + }, true) const { material } = this.world - // block material - const mesh = new THREE.Mesh(geometry, material) - mesh.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z) + const mesh = getThreeBlockModelGroup(material, models, undefined, 'plains', loadedData) + // mesh.rotation.y = THREE.MathUtils.degToRad(90) + setBlockPosition(mesh, pos) + const helper = new THREE.BoxHelper(mesh, 0xff_ff_00) + mesh.add(helper) + this.scene.add(mesh) + } + + demoItem () { + //@ts-expect-error + const pos = cursorBlockRel(0, 1, 0).position + const { mesh } = this.entities.getItemMesh({ + itemId: 541, + })! + mesh.position.set(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5) + // mesh.scale.set(0.5, 0.5, 0.5) const helper = new THREE.BoxHelper(mesh, 0xff_ff_00) mesh.add(helper) this.scene.add(mesh) diff --git a/prismarine-viewer/viewer/lib/worldDataEmitter.ts b/prismarine-viewer/viewer/lib/worldDataEmitter.ts index 381526d9..d832d3db 100644 --- a/prismarine-viewer/viewer/lib/worldDataEmitter.ts +++ b/prismarine-viewer/viewer/lib/worldDataEmitter.ts @@ -5,6 +5,7 @@ import { EventEmitter } from 'events' import { generateSpiralMatrix, ViewRect } from 'flying-squid/dist/utils' import { Vec3 } from 'vec3' import { BotEvents } from 'mineflayer' +import { getItemFromBlock } from '../../../src/botUtils' import { chunkPos } from './simpleUtils' export type ChunkPosKey = string @@ -20,6 +21,14 @@ export class WorldDataEmitter extends EventEmitter { private eventListeners: Record = {} private readonly emitter: WorldDataEmitter keepChunksDistance = 0 + _handDisplay = false + get handDisplay () { + return this._handDisplay + } + set handDisplay (newVal) { + this._handDisplay = newVal + this.eventListeners.heldItemChanged?.() + } constructor (public world: typeof __type_bot['world'], public viewDistance: number, position: Vec3 = new Vec3(0, 0, 0)) { super() @@ -55,7 +64,7 @@ export class WorldDataEmitter extends EventEmitter { }) } - this.eventListeners[bot.username] = { + this.eventListeners = { // 'move': botPosition, entitySpawn (e: any) { emitEntity(e) @@ -70,7 +79,7 @@ export class WorldDataEmitter extends EventEmitter { this.emitter.emit('entity', { id: e.id, delete: true }) }, chunkColumnLoad: (pos: Vec3) => { - this.loadChunk(pos) + void this.loadChunk(pos) }, chunkColumnUnload: (pos: Vec3) => { this.unloadChunk(pos) @@ -82,7 +91,24 @@ export class WorldDataEmitter extends EventEmitter { time: () => { this.emitter.emit('time', bot.time.timeOfDay) }, + heldItemChanged: () => { + if (!this.handDisplay) { + viewer.world.onHandItemSwitch(undefined) + return + } + const newItem = bot.heldItem + if (!newItem) { + viewer.world.onHandItemSwitch(undefined) + return + } + const block = loadedData.blocksByName[newItem.name] + // todo clean types + const blockProperties = block ? new window.PrismarineBlock(block.id, 'void', newItem.metadata).getProperties() : {} + viewer.world.onHandItemSwitch({ name: newItem.name, properties: blockProperties }) + }, } satisfies Partial + this.eventListeners.heldItemChanged() + bot._client.on('update_light', ({ chunkX, chunkZ }) => { const chunkPos = new Vec3(chunkX * 16, 0, chunkZ * 16) @@ -105,7 +131,7 @@ export class WorldDataEmitter extends EventEmitter { this.emitter.emit('listening') } - for (const [evt, listener] of Object.entries(this.eventListeners[bot.username])) { + for (const [evt, listener] of Object.entries(this.eventListeners)) { bot.on(evt as any, listener) } @@ -116,10 +142,9 @@ export class WorldDataEmitter extends EventEmitter { } removeListenersFromBot (bot: import('mineflayer').Bot) { - for (const [evt, listener] of Object.entries(this.eventListeners[bot.username])) { + for (const [evt, listener] of Object.entries(this.eventListeners)) { bot.removeListener(evt as any, listener) } - delete this.eventListeners[bot.username] } async init (pos: Vec3) { diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index 58e5a6f5..482907d4 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -16,6 +16,7 @@ import { toMajorVersion } from '../../../src/utils' import { buildCleanupDecorator } from './cleanupDecorator' import { defaultMesherConfig } from './mesher/shared' import { chunkPos } from './simpleUtils' +import { HandItemBlock } from './holdingBlock' function mod (x, n) { return ((x % n) + n) % n @@ -37,6 +38,7 @@ type CustomTexturesData = { export abstract class WorldRendererCommon { worldConfig = { minY: 0, worldHeight: 256 } + // todo need to cleanup material = new THREE.MeshLambertMaterial({ vertexColors: true, transparent: true, alphaTest: 0.1 }) @worldCleanup() @@ -59,6 +61,7 @@ export abstract class WorldRendererCommon textureDownloaded (): void }> customTexturesDataUrl = undefined as string | undefined + @worldCleanup() currentTextureImage = undefined as any workers: any[] = [] viewerPosition?: Vec3 @@ -157,6 +160,9 @@ export abstract class WorldRendererCommon } } + onHandItemSwitch (item: HandItemBlock | undefined): void { } + changeHandSwingingState (isAnimationPlaying: boolean): void { } + abstract handleWorkerMessage (data: WorkerReceive): void abstract updateCamera (pos: Vec3 | null, yaw: number, pitch: number): void diff --git a/prismarine-viewer/viewer/lib/worldrendererThree.ts b/prismarine-viewer/viewer/lib/worldrendererThree.ts index 6c14e243..b1644ebd 100644 --- a/prismarine-viewer/viewer/lib/worldrendererThree.ts +++ b/prismarine-viewer/viewer/lib/worldrendererThree.ts @@ -9,7 +9,7 @@ import { renderSign } from '../sign-renderer' import { chunkPos, sectionPos } from './simpleUtils' import { WorldRendererCommon, WorldRendererConfig } from './worldrendererCommon' import { disposeObject } from './threeJsUtils' -import { renderBlockThree } from './mesher/standaloneRenderer' +import HoldingBlock, { HandItemBlock } from './holdingBlock' export class WorldRendererThree extends WorldRendererCommon { outputFormat = 'threeJs' as const @@ -19,7 +19,7 @@ export class WorldRendererThree extends WorldRendererCommon { signsCache = new Map() starField: StarField cameraSectionPos: Vec3 = new Vec3(0, 0, 0) - cameraGroup = new THREE.Group() + holdingBlock: HoldingBlock get tilesRendered () { return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).tilesCount, 0) @@ -28,8 +28,34 @@ export class WorldRendererThree extends WorldRendererCommon { constructor (public scene: THREE.Scene, public renderer: THREE.WebGLRenderer, public config: WorldRendererConfig) { super(config) this.starField = new StarField(scene) - // this.initCameraGroup() - // this.initHandObject() + this.holdingBlock = new HoldingBlock(this.scene) + this.onHandItemSwitch({ + name: 'furnace', + properties: {} + }) + + this.renderUpdateEmitter.on('textureDownloaded', () => { + if (this.holdingBlock.toBeRenderedItem) { + this.onHandItemSwitch(this.holdingBlock.toBeRenderedItem) + this.holdingBlock.toBeRenderedItem = undefined + } + }) + } + + onHandItemSwitch (item: HandItemBlock | undefined) { + if (!this.currentTextureImage) { + this.holdingBlock.toBeRenderedItem = item + return + } + void this.holdingBlock.initHandObject(this.material, this.blockstatesModels, this.blocksAtlases, item) + } + + changeHandSwingingState (isAnimationPlaying: boolean) { + if (isAnimationPlaying) { + this.holdingBlock.startSwing() + } else { + void this.holdingBlock.stopSwing() + } } timeUpdated (newTime: number): void { @@ -173,6 +199,7 @@ export class WorldRendererThree extends WorldRendererCommon { render () { tweenJs.update() + this.holdingBlock.update(this.camera) // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style const cam = this.camera instanceof THREE.Group ? this.camera.children.find(child => child instanceof THREE.PerspectiveCamera) as THREE.PerspectiveCamera : this.camera this.renderer.render(this.scene, cam) diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 64e589f9..e5964646 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -87,8 +87,13 @@ export const onGameLoad = (onLoad) => { return } const craftingSlots = bot.inventory.slots.slice(1, 5) - const resultingItem = getResultingRecipe(craftingSlots, 2) - void bot.creative.setInventorySlot(craftingResultSlot, resultingItem ?? null) + try { + const resultingItem = getResultingRecipe(craftingSlots, 2) + void bot.creative.setInventorySlot(craftingResultSlot, resultingItem ?? null) + } catch (err) { + console.error(err) + // todo resolve the error! and why would we ever get here on every update? + } }) as any) bot.on('windowClose', () => { diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index 644ec66c..2082acdb 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -88,6 +88,7 @@ export const guiOptionsScheme: { unit: '', tooltip: 'Additional distance to keep the chunks loading before unloading them by marking them as too far', }, + handDisplay: {}, }, ], main: [ diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index cac8c9f8..b7bf4abe 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -47,6 +47,7 @@ const defaultOptions = { enabledResourcepack: null as string | null, useVersionsTextures: 'latest', serverResourcePacks: 'prompt' as 'prompt' | 'always' | 'never', + handDisplay: false, // antiAliasing: false, diff --git a/src/topRightStats.ts b/src/topRightStats.ts index 71303e81..f0462ae6 100644 --- a/src/topRightStats.ts +++ b/src/topRightStats.ts @@ -75,3 +75,14 @@ export const statsEnd = () => { stats2.end() statsGl.end() } + +window.statsPerSec = {} +let statsPerSec = {} +window.addStatPerSec = (name) => { + statsPerSec[name] ??= 0 + statsPerSec[name]++ +} +setInterval(() => { + window.statsPerSec = statsPerSec + statsPerSec = {} +}, 1000) diff --git a/src/watchOptions.ts b/src/watchOptions.ts index 9deb46a5..d26d2b90 100644 --- a/src/watchOptions.ts +++ b/src/watchOptions.ts @@ -66,11 +66,11 @@ export const watchOptionsAfterViewerInit = () => { let viewWatched = false export const watchOptionsAfterWorldViewInit = () => { - worldView!.keepChunksDistance = options.keepChunksDistance if (viewWatched) return viewWatched = true watchValue(options, o => { if (!worldView) return worldView.keepChunksDistance = o.keepChunksDistance + worldView.handDisplay = o.handDisplay }) } diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts index bc54efcd..cef65b96 100644 --- a/src/worldInteractions.ts +++ b/src/worldInteractions.ts @@ -294,6 +294,8 @@ class WorldInteraction { bot.lookAt = oldLookAt }).catch(console.warn) } + viewer.world.changeHandSwingingState(true) + viewer.world.changeHandSwingingState(false) } else if (!stop) { const offhand = activate ? false : activatableItems(bot.inventory.slots[45]?.name ?? '') bot.activateItem(offhand) // todo offhand @@ -351,11 +353,15 @@ class WorldInteraction { }) customEvents.emit('digStart') this.lastDigged = Date.now() + viewer.world.changeHandSwingingState(true) } 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) + } this.prevOnGround = onGround // Show cursor From 66d26ad2e6a6673e97e6bd08b67d97de052984ba Mon Sep 17 00:00:00 2001 From: Valery-a <83373303+Valery-a@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:53:48 +0300 Subject: [PATCH 043/865] feat: add visuals for entities damaging (#186) --- docs-assets/handled-packets.md | 2 +- prismarine-viewer/viewer/lib/entities.js | 18 ++++++++++++++++++ src/entities.ts | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/docs-assets/handled-packets.md b/docs-assets/handled-packets.md index 0671987c..497ec5ec 100644 --- a/docs-assets/handled-packets.md +++ b/docs-assets/handled-packets.md @@ -32,8 +32,8 @@ ❌ world_border_warning_reach ❌ simulation_distance ❌ chunk_biomes -❌ damage_event ❌ hurt_animation +✅ damage_event ✅ spawn_entity ✅ spawn_entity_experience_orb ✅ named_entity_spawn diff --git a/prismarine-viewer/viewer/lib/entities.js b/prismarine-viewer/viewer/lib/entities.js index 01ce7d6d..59d1164a 100644 --- a/prismarine-viewer/viewer/lib/entities.js +++ b/prismarine-viewer/viewer/lib/entities.js @@ -496,4 +496,22 @@ export class Entities extends EventEmitter { new TWEEN.Tween(e.rotation).to({ y: e.rotation.y + dy }, TWEEN_DURATION).start() } } + + handleDamageEvent(entityId, damageAmount) { + const entityMesh = this.entities[entityId]?.children.find(c => c.name === 'mesh') + if (entityMesh) { + entityMesh.traverse((child) => { + if (child instanceof THREE.Mesh) { + const clonedMaterial = child.material.clone() + clonedMaterial.dispose() + child.material = child.material.clone() + const originalColor = child.material.color.clone() + child.material.color.set(0xff_00_00) + new TWEEN.Tween(child.material.color) + .to(originalColor, 500) + .start() + } + }) + } + } } diff --git a/src/entities.ts b/src/entities.ts index b238d4ea..b5daa220 100644 --- a/src/entities.ts +++ b/src/entities.ts @@ -1,4 +1,5 @@ import { Entity } from 'prismarine-entity' +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' import tracker from '@nxg-org/mineflayer-tracker' import { loader as autoJumpPlugin } from '@nxg-org/mineflayer-auto-jump' import { subscribeKey } from 'valtio/utils' @@ -88,6 +89,21 @@ customEvents.on('gameLoaded', () => { } }) + bot._client.on('damage_event', (data) => { + const { entityId, sourceTypeId: damage } = data + if (viewer.entities.entities[entityId]) { + viewer.entities.handleDamageEvent(entityId, damage) + } + }) + + bot._client.on('entity_status', (data) => { + if (versionToNumber(bot.version) >= versionToNumber('1.19.4')) return + const { entityId, entityStatus } = data + if (entityStatus === 2 && viewer.entities.entities[entityId]) { + viewer.entities.handleDamageEvent(entityId, entityStatus) + } + }) + const loadedSkinEntityIds = new Set() const playerRenderSkin = (e: Entity) => { From 574dbafc282ddac71355e68fed8c1e29f5589c06 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 2 Sep 2024 23:46:22 +0300 Subject: [PATCH 044/865] fix(renderer,important): fix all known rendering issues with starfield by @sa2urami --- prismarine-viewer/viewer/lib/worldrendererThree.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/prismarine-viewer/viewer/lib/worldrendererThree.ts b/prismarine-viewer/viewer/lib/worldrendererThree.ts index b1644ebd..c89bc4cb 100644 --- a/prismarine-viewer/viewer/lib/worldrendererThree.ts +++ b/prismarine-viewer/viewer/lib/worldrendererThree.ts @@ -413,6 +413,7 @@ class StarField { this.points?.position.copy?.(camera.position) material.uniforms.time.value = clock.getElapsedTime() * speed } + this.points.renderOrder = -1 } remove () { @@ -439,7 +440,7 @@ class StarfieldMaterial extends THREE.ShaderMaterial { void main() { vColor = color; vec4 mvPosition = modelViewMatrix * vec4(position, 0.5); - gl_PointSize = size * (30.0 / -mvPosition.z) * (3.0 + sin(time + 100.0)); + gl_PointSize = 0.7 * size * (30.0 / -mvPosition.z) * (3.0 + sin(time + 100.0)); gl_Position = projectionMatrix * mvPosition; }`, fragmentShader: /* glsl */ ` @@ -448,11 +449,7 @@ class StarfieldMaterial extends THREE.ShaderMaterial { varying vec3 vColor; void main() { float opacity = 1.0; - if (fade == 1.0) { - float d = distance(gl_PointCoord, vec2(0.5, 0.5)); - opacity = 1.0 / (1.0 + exp(16.0 * (d - 0.25))); - } - gl_FragColor = vec4(vColor, opacity); + gl_FragColor = vec4(vColor, 1.0); #include #include <${version >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}> From 00dd6060914a4347d6d59b1e1fb9da8c3dc6b6b6 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 2 Sep 2024 23:50:46 +0300 Subject: [PATCH 045/865] [skip ci] cleanup starfield code --- prismarine-viewer/viewer/lib/worldrendererThree.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prismarine-viewer/viewer/lib/worldrendererThree.ts b/prismarine-viewer/viewer/lib/worldrendererThree.ts index c89bc4cb..deec264e 100644 --- a/prismarine-viewer/viewer/lib/worldrendererThree.ts +++ b/prismarine-viewer/viewer/lib/worldrendererThree.ts @@ -62,9 +62,9 @@ export class WorldRendererThree extends WorldRendererCommon { const nightTime = 13_500 const morningStart = 23_000 const displayStars = newTime > nightTime && newTime < morningStart - if (displayStars && !this.starField.points) { + if (displayStars) { this.starField.addToScene() - } else if (!displayStars && this.starField.points) { + } else { this.starField.remove() } } From 0d3a3affd75c72de49a02132adf8ef5bd13210b7 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 01:00:54 +0300 Subject: [PATCH 046/865] fix recently introduced bug with crafting in singleplayer --- scripts/makeOptimizedMcData.mjs | 4 ++- scripts/testOptimizedMcdata.ts | 35 ++++++++++++++----- ...tation_console_controller_gamepad_icon.svg | 1 - src/inventoryWindows.ts | 6 ++-- src/optimizeJson.ts | 7 +++- src/{ => react}/GlobalSearchInput.tsx | 5 ++- src/react/Input.tsx | 12 +++++-- src/reactUi.tsx | 2 +- 8 files changed, 52 insertions(+), 20 deletions(-) delete mode 100644 src/cross_playstation_console_controller_gamepad_icon.svg rename src/{ => react}/GlobalSearchInput.tsx (87%) diff --git a/scripts/makeOptimizedMcData.mjs b/scripts/makeOptimizedMcData.mjs index d0adcae2..9794aeed 100644 --- a/scripts/makeOptimizedMcData.mjs +++ b/scripts/makeOptimizedMcData.mjs @@ -118,7 +118,9 @@ const dataTypeBundling = { blockLoot: { arrKey: 'block' }, - recipes: {}, // todo we can do better + recipes: { + raw: true + }, // todo we can do better blockCollisionShapes: {}, loginPacket: {}, protocol: { diff --git a/scripts/testOptimizedMcdata.ts b/scripts/testOptimizedMcdata.ts index 17b5f7ed..d6c74384 100644 --- a/scripts/testOptimizedMcdata.ts +++ b/scripts/testOptimizedMcdata.ts @@ -8,7 +8,7 @@ const json = JSON.parse(fs.readFileSync('./generated/minecraft-data-optimized.js const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') const validateData = (ver, type) => { - const target = JsonOptimizer.restoreData(json[type], ver) + const target = JsonOptimizer.restoreData(structuredClone(json[type]), ver) const arrKey = json[type].arrKey const originalPath = dataPaths.pc[ver][type] const original = require(`minecraft-data/minecraft-data/data/${originalPath}/${type}.json`) @@ -43,16 +43,33 @@ const validateData = (ver, type) => { } } -const checkObj = (source, diffing) => { - checkKeys(Object.keys(source), Object.keys(diffing)) - for (const [key, val] of Object.entries(source)) { - if (JSON.stringify(val) !== JSON.stringify(diffing[key])) { - throw new Error(`different value of ${key}: ${val} ${diffing[key]}`) - } +const sortObj = (obj) => { + const sorted = {} + for (const key of Object.keys(obj).sort()) { + sorted[key] = obj[key] } + return sorted } -const checkKeys = (source, diffing, isUniq = true, msg = '', redunantOk = false) => { +const checkObj = (source, diffing) => { + if (!Array.isArray(source)) { + source = sortObj(source) + } + if (!Array.isArray(diffing)) { + diffing = sortObj(diffing) + } + if (JSON.stringify(source) !== JSON.stringify(diffing)) { + throw new Error(`different value: ${JSON.stringify(source)} ${JSON.stringify(diffing)}`) + } + // checkKeys(Object.keys(source), Object.keys(diffing)) + // for (const [key, val] of Object.entries(source)) { + // if (JSON.stringify(val) !== JSON.stringify(diffing[key])) { + // throw new Error(`different value of ${key}: ${val} ${diffing[key]}`) + // } + // } +} + +const checkKeys = (source, diffing, isUniq = true, msg = '', redundantIsOk = false) => { if (isUniq) { for (const [i, item] of diffing.entries()) { if (diffing.indexOf(item) !== i) { @@ -65,7 +82,7 @@ const checkKeys = (source, diffing, isUniq = true, msg = '', redunantOk = false) throw new Error(`Diffing does not include "${key}" (${msg})`) } } - if (!redunantOk) { + if (!redundantIsOk) { for (const key of diffing) { if (!source.includes(key)) { throw new Error(`Source does not include "${key}" (${msg})`) diff --git a/src/cross_playstation_console_controller_gamepad_icon.svg b/src/cross_playstation_console_controller_gamepad_icon.svg deleted file mode 100644 index d7d176e2..00000000 --- a/src/cross_playstation_console_controller_gamepad_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index e5964646..b23c88fa 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -69,11 +69,12 @@ export const onGameLoad = (onLoad) => { } }) + // workaround: singleplayer player inventory crafting bot.inventory.on('updateSlot', ((_oldSlot, oldItem, newItem) => { - const oldSlot = _oldSlot as number + const currentSlot = _oldSlot as number if (!miscUiState.singleplayer) return const { craftingResultSlot } = bot.inventory - if (oldSlot === craftingResultSlot && oldItem && !newItem) { + if (currentSlot === craftingResultSlot && oldItem && !newItem) { for (let i = 1; i < 5; i++) { const count = bot.inventory.slots[i]?.count if (count && count > 1) { @@ -86,6 +87,7 @@ export const onGameLoad = (onLoad) => { } return } + if (currentSlot > 4) return const craftingSlots = bot.inventory.slots.slice(1, 5) try { const resultingItem = getResultingRecipe(craftingSlots, 2) diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts index 442a72a1..00547ffe 100644 --- a/src/optimizeJson.ts +++ b/src/optimizeJson.ts @@ -192,7 +192,12 @@ export default class JsonOptimizer { for (const [key, removePropsId] of removedProps) { for (const removePropId of removePropsId) { const removeProp = propertiesById[removePropId] - delete dataByKeys[key][removeProp] + // todo: this is not correct! + if (Array.isArray(dataByKeys[key])) { + dataByKeys[key].splice(removeProp, 1) + } else { + delete dataByKeys[key][removeProp] + } } } if (versionToNumber(versionKey) <= versionToNumber(targetKey)) { diff --git a/src/GlobalSearchInput.tsx b/src/react/GlobalSearchInput.tsx similarity index 87% rename from src/GlobalSearchInput.tsx rename to src/react/GlobalSearchInput.tsx index d9266950..6f2d56d1 100644 --- a/src/GlobalSearchInput.tsx +++ b/src/react/GlobalSearchInput.tsx @@ -1,6 +1,6 @@ import { useSnapshot } from 'valtio' -import { miscUiState } from './globalState' -import Input from './react/Input' +import { miscUiState } from '../globalState' +import Input from './Input' function InnerSearch () { const { currentTouch } = useSnapshot(miscUiState) @@ -19,7 +19,6 @@ function InnerSearch () { autoFocus={currentTouch === false} width={50} placeholder='Search...' - defaultValue="" onChange={({ target: { value } }) => { customEvents.emit('search', value) }} diff --git a/src/react/Input.tsx b/src/react/Input.tsx index c3d40491..41dbc7ba 100644 --- a/src/react/Input.tsx +++ b/src/react/Input.tsx @@ -27,8 +27,16 @@ export default ({ autoFocus, rootStyles, inputRef, validateInput, ...inputProps return
{ setValidationStyle(validateInput?.(e.target.value) ?? {}) setValue(e.target.value) diff --git a/src/reactUi.tsx b/src/reactUi.tsx index b40c47a1..029b2493 100644 --- a/src/reactUi.tsx +++ b/src/reactUi.tsx @@ -28,7 +28,7 @@ import SoundMuffler from './react/SoundMuffler' import TouchControls from './react/TouchControls' import widgets from './react/widgets' import { useIsWidgetActive } from './react/utilsApp' -import GlobalSearchInput from './GlobalSearchInput' +import GlobalSearchInput from './react/GlobalSearchInput' import TouchAreasControlsProvider from './react/TouchAreasControlsProvider' import NotificationProvider, { showNotification } from './react/NotificationProvider' import HotbarRenderApp from './react/HotbarRenderApp' From b2ac80602c8850b4f54ba13e653f56cda66b4190 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 01:10:11 +0300 Subject: [PATCH 047/865] feat(important): redirect to origin website from maps.mcraft.fun which makes testing maps so much easier on preview deploys and locally --- src/react/MainMenuRenderApp.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/react/MainMenuRenderApp.tsx b/src/react/MainMenuRenderApp.tsx index acd61803..37c7966b 100644 --- a/src/react/MainMenuRenderApp.tsx +++ b/src/react/MainMenuRenderApp.tsx @@ -70,6 +70,9 @@ export default () => { } }, []) + let mapsProviderUrl = appConfig?.mapsProvider + if (mapsProviderUrl && location.origin !== 'https://mcraft.fun') mapsProviderUrl = mapsProviderUrl + '?to=' + encodeURIComponent(location.href) + // todo clean, use custom csstransition return {(state) =>
@@ -107,7 +110,7 @@ export default () => { openFilePicker() } }} - mapsProvider={appConfig?.mapsProvider} + mapsProvider={mapsProviderUrl} versionStatus={versionStatus} versionTitle={versionTitle} onVersionClick={async () => { From 559f535207c5dbeab23dbeb82b965a84bf8a377b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 01:11:49 +0300 Subject: [PATCH 048/865] don't lie of resoure pack support --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index b21e8c8a..8ecd7e78 100644 --- a/README.MD +++ b/README.MD @@ -17,7 +17,7 @@ For building the project yourself / contributing, see [Development, Debugging & - Works offline - Play with friends over internet! (P2P is powered by Peer.js discovery servers) - First-class touch (mobile) & controller support -- FULL Resource pack support: Custom GUI, all textures & custom models! Server resource packs are also supported. +- Basic Resource pack support: Custom GUI, all textures. Server resource packs are not supported yet. - Builtin JEI with recipes & guides for every item (also replaces creative inventory) - even even more! From 698fb1d388a0c0dc2805a9ae6d2af7c4cdcc0183 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 01:13:12 +0300 Subject: [PATCH 049/865] fix tsc --- src/optimizeJson.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts index 00547ffe..491c8301 100644 --- a/src/optimizeJson.ts +++ b/src/optimizeJson.ts @@ -194,7 +194,7 @@ export default class JsonOptimizer { const removeProp = propertiesById[removePropId] // todo: this is not correct! if (Array.isArray(dataByKeys[key])) { - dataByKeys[key].splice(removeProp, 1) + dataByKeys[key].splice(removeProp as any, 1) // splice accepts strings as well } else { delete dataByKeys[key][removeProp] } From c2a34ea9f1874ef675e8bccebfc250f2ea4273fa Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 02:48:16 +0300 Subject: [PATCH 050/865] fix(preflat-worlds): improve mesher performance by 2x by syncing the code from webgpu branch fixes #191 --- prismarine-viewer/examples/shared.ts | 11 ++ prismarine-viewer/viewer/lib/mesher/mesher.ts | 12 +- prismarine-viewer/viewer/lib/mesher/models.ts | 158 +++++++++++------- prismarine-viewer/viewer/lib/mesher/shared.ts | 26 ++- prismarine-viewer/viewer/lib/mesher/world.ts | 2 + .../viewer/lib/worldrendererCommon.ts | 19 ++- 6 files changed, 164 insertions(+), 64 deletions(-) create mode 100644 prismarine-viewer/examples/shared.ts diff --git a/prismarine-viewer/examples/shared.ts b/prismarine-viewer/examples/shared.ts new file mode 100644 index 00000000..4ef9b417 --- /dev/null +++ b/prismarine-viewer/examples/shared.ts @@ -0,0 +1,11 @@ +export type BlockFaceType = { + side: number + textureIndex: number + textureName?: string + tint?: [number, number, number] + isTransparent?: boolean +} + +export type BlockType = { + faces: BlockFaceType[] +} diff --git a/prismarine-viewer/viewer/lib/mesher/mesher.ts b/prismarine-viewer/viewer/lib/mesher/mesher.ts index 118f79c7..4813cfc9 100644 --- a/prismarine-viewer/viewer/lib/mesher/mesher.ts +++ b/prismarine-viewer/viewer/lib/mesher/mesher.ts @@ -11,6 +11,7 @@ if (module.require) { global.performance = r('perf_hooks').performance } +let workerIndex = 0 let world: World let dirtySections = new Map() let allDataReady = false @@ -85,8 +86,9 @@ const handleMessage = data => { switch (data.type) { case 'mesherData': { - setMesherData(data.blockstatesModels, data.blocksAtlas) + setMesherData(data.blockstatesModels, data.blocksAtlas, data.config.outputFormat === 'webgpu') allDataReady = true + workerIndex = data.workerIndex break } @@ -148,18 +150,22 @@ setInterval(() => { for (const key of dirtySections.keys()) { const [x, y, z] = key.split(',').map(v => parseInt(v, 10)) const chunk = world.getColumn(x, z) + let processTime = 0 if (chunk?.getSection(new Vec3(x, y, z))) { + const start = performance.now() const geometry = getSectionGeometry(x, y, z, world) - const transferable = [geometry.positions.buffer, geometry.normals.buffer, geometry.colors.buffer, geometry.uvs.buffer] + const transferable = [geometry.positions?.buffer, geometry.normals?.buffer, geometry.colors?.buffer, geometry.uvs?.buffer].filter(Boolean) //@ts-expect-error postMessage({ type: 'geometry', key, geometry }, transferable) + processTime = performance.now() - start } else { // console.info('[mesher] Missing section', x, y, z) } const dirtyTimes = dirtySections.get(key) if (!dirtyTimes) throw new Error('dirtySections.get(key) is falsy') for (let i = 0; i < dirtyTimes; i++) { - postMessage({ type: 'sectionFinished', key }) + postMessage({ type: 'sectionFinished', key, workerIndex, processTime }) + processTime = 0 } dirtySections.delete(key) } diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index c4249ac4..54c879b8 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -1,8 +1,10 @@ import { Vec3 } from 'vec3' import worldBlockProvider, { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider' import legacyJson from '../../../../src/preflatMap.json' +import { BlockType } from '../../../examples/shared' import { World, BlockModelPartsResolved, WorldBlock as Block } from './world' import { BlockElement, buildRotationMatrix, elemFaces, matmul3, matmulmat3, vecadd3, vecsub3 } from './modelsGeometryCommon' +import { MesherGeometryOutput } from './shared' let blockProvider: WorldBlockProvider @@ -19,6 +21,19 @@ for (const key of Object.keys(tintsData)) { tints[key] = prepareTints(tintsData[key]) } +type TestTileData = { + block: string + faces: Array<{ + face: string + neighbor: string + light?: number + }> +} + +type Tiles = { + [blockPos: string]: BlockType & TestTileData +} + function prepareTints (tints) { const map = new Map() const defaultValue = tintToGl(tints.default) @@ -54,19 +69,25 @@ export function preflatBlockCalculation (block: Block, world: World, position: V ] // set needed props to true: east:'false',north:'false',south:'false',west:'false' const props = {} + let changed = false for (const [i, neighbor] of neighbors.entries()) { const isConnectedToSolid = isSolidConnection ? (neighbor && !neighbor.transparent) : false if (isConnectedToSolid || neighbor?.name === block.name) { props[['south', 'north', 'east', 'west'][i]] = 'true' + changed = true } } - return props + return changed ? props : undefined } // case 'gate_in_wall': {} case 'block_snowy': { const aboveIsSnow = world.getBlock(position.offset(0, 1, 0))?.name === 'snow' - return { - snowy: `${aboveIsSnow}` + if (aboveIsSnow) { + return { + snowy: `${aboveIsSnow}` + } + } else { + return } } case 'door': { @@ -139,7 +160,7 @@ function renderLiquid (world: World, cursor: Vec3, texture: any | undefined, typ if (!neighbor) continue if (neighbor.type === type) continue const isGlass = neighbor.name.includes('glass') - if ((isCube(neighbor) && !isUp) || neighbor.getProperties().waterlogged) continue + if ((isCube(neighbor) && !isUp) || neighbor.material === 'plant' || neighbor.getProperties().waterlogged) continue let tint = [1, 1, 1] if (water) { @@ -151,11 +172,12 @@ function renderLiquid (world: World, cursor: Vec3, texture: any | undefined, typ } if (needTiles) { - attr.tiles[`${cursor.x},${cursor.y},${cursor.z}`] ??= { + const tiles = attr.tiles as Tiles + tiles[`${cursor.x},${cursor.y},${cursor.z}`] ??= { block: 'water', faces: [], } - attr.tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({ + tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({ face, neighbor: `${neighborPos.x},${neighborPos.y},${neighborPos.z}`, // texture: eFace.texture.name, @@ -183,7 +205,7 @@ function renderLiquid (world: World, cursor: Vec3, texture: any | undefined, typ let needRecompute = false -function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: boolean, attr: Record, globalMatrix: any, globalShift: any, block: Block, biome: string) { +function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: boolean, attr: MesherGeometryOutput, globalMatrix: any, globalShift: any, block: Block, biome: string) { const position = cursor // const key = `${position.x},${position.y},${position.z}` // if (!globalThis.allowedBlocks.includes(key)) return @@ -192,7 +214,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: // eslint-disable-next-line guard-for-in for (const face in element.faces) { const eFace = element.faces[face] - const { corners, mask1, mask2 } = elemFaces[face] + const { corners, mask1, mask2, side } = elemFaces[face] const dir = matmul3(globalMatrix, elemFaces[face].dir) if (eFace.cullface) { @@ -214,7 +236,10 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: const maxz = element.to[2] const texture = eFace.texture as any - const { u, v, su, sv } = texture + const { u } = texture + const { v } = texture + const { su } = texture + const { sv } = texture const ndx = Math.floor(attr.positions.length / 3) @@ -246,7 +271,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: let localMatrix = null as any let localShift = null as any - if (element.rotation) { + if (element.rotation && !needTiles) { // todo do we support rescale? localMatrix = buildRotationMatrix( element.rotation.axis, @@ -272,21 +297,23 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: (pos[2] ? maxz : minz) ] - vertex = vecadd3(matmul3(localMatrix, vertex), localShift) - vertex = vecadd3(matmul3(globalMatrix, vertex), globalShift) - vertex = vertex.map(v => v / 16) + if (!needTiles) { + vertex = vecadd3(matmul3(localMatrix, vertex), localShift) + vertex = vecadd3(matmul3(globalMatrix, vertex), globalShift) + vertex = vertex.map(v => v / 16) - attr.positions.push( - vertex[0] + (cursor.x & 15) - 8, - vertex[1] + (cursor.y & 15) - 8, - vertex[2] + (cursor.z & 15) - 8 - ) + attr.positions.push( + vertex[0] + (cursor.x & 15) - 8, + vertex[1] + (cursor.y & 15) - 8, + vertex[2] + (cursor.z & 15) - 8 + ) - attr.normals.push(...dir) + attr.normals.push(...dir) - const baseu = (pos[3] - 0.5) * uvcs - (pos[4] - 0.5) * uvsn + 0.5 - const basev = (pos[3] - 0.5) * uvsn + (pos[4] - 0.5) * uvcs + 0.5 - attr.uvs.push(baseu * su + u, basev * sv + v) + const baseu = (pos[3] - 0.5) * uvcs - (pos[4] - 0.5) * uvsn + 0.5 + const basev = (pos[3] - 0.5) * uvsn + (pos[4] - 0.5) * uvcs + 0.5 + attr.uvs.push(baseu * su + u, basev * sv + v) + } let light = 1 if (doAO) { @@ -322,40 +349,49 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: aos.push(ao) } - attr.colors.push(baseLight * tint[0] * light, baseLight * tint[1] * light, baseLight * tint[2] * light) + if (!needTiles) { + attr.colors.push(baseLight * tint[0] * light, baseLight * tint[1] * light, baseLight * tint[2] * light) + } } + const lightWithColor = [baseLight * tint[0], baseLight * tint[1], baseLight * tint[2]] as [number, number, number] + if (needTiles) { - attr.tiles[`${cursor.x},${cursor.y},${cursor.z}`] ??= { + const tiles = attr.tiles as Tiles + tiles[`${cursor.x},${cursor.y},${cursor.z}`] ??= { block: block.name, faces: [], } - attr.tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({ - face, - neighbor: `${neighborPos.x},${neighborPos.y},${neighborPos.z}`, - light: baseLight - // texture: eFace.texture.name, - }) + const needsOnlyOneFace = false + const isTilesEmpty = tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.length < 1 + if (isTilesEmpty || !needsOnlyOneFace) { + tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({ + face, + side, + textureIndex: eFace.texture.tileIndex, + neighbor: `${neighborPos.x},${neighborPos.y},${neighborPos.z}`, + light: baseLight, + tint: lightWithColor, + //@ts-expect-error debug prop + texture: eFace.texture.debugName || block.name, + } satisfies BlockType['faces'][number] & TestTileData['faces'][number] as any) + } } - if (doAO && aos[0] + aos[3] >= aos[1] + aos[2]) { - attr.indices.push( - // eslint-disable-next-line @stylistic/function-call-argument-newline - ndx, ndx + 3, ndx + 2, - ndx, ndx + 1, ndx + 3 - ) - } else { - attr.indices.push( - // eslint-disable-next-line @stylistic/function-call-argument-newline - ndx, ndx + 1, ndx + 2, - ndx + 2, ndx + 1, ndx + 3 - ) + if (!needTiles) { + if (doAO && aos[0] + aos[3] >= aos[1] + aos[2]) { + attr.indices.push( + ndx, ndx + 3, ndx + 2, ndx, ndx + 1, ndx + 3 + ) + } else { + attr.indices.push( + ndx, ndx + 1, ndx + 2, ndx + 2, ndx + 1, ndx + 3 + ) + } } } } -const makeLooseObj = (obj: Record) => obj - const invisibleBlocks = new Set(['air', 'cave_air', 'void_air', 'barrier']) const isBlockWaterlogged = (block: Block) => block.getProperties().waterlogged === true || block.getProperties().waterlogged === 'true' @@ -365,7 +401,7 @@ let erroredBlockModel: BlockModelPartsResolved export function getSectionGeometry (sx, sy, sz, world: World) { let delayedRender = [] as Array<() => void> - const attr = makeLooseObj({ + const attr: MesherGeometryOutput = { sx: sx + 8, sy: sy + 8, sz: sz + 8, @@ -381,9 +417,10 @@ export function getSectionGeometry (sx, sy, sz, world: World) { tiles: {}, // todo this can be removed here signs: {}, + isFull: true, highestBlocks: {}, hadErrors: false - } as Record) + } const cursor = new Vec3(0, 0, 0) for (cursor.y = sy; cursor.y < sy + 16; cursor.y++) { @@ -419,19 +456,17 @@ export function getSectionGeometry (sx, sy, sz, world: World) { } const biome = block.biome.name - let preflatRecomputeVariant = !!(block as any)._originalProperties if (world.preflat) { const patchProperties = preflatBlockCalculation(block, world, cursor) if (patchProperties) { - //@ts-expect-error block._originalProperties ??= block._properties - //@ts-expect-error block._properties = { ...block._originalProperties, ...patchProperties } - preflatRecomputeVariant = true + if (block.models && JSON.stringify(block._originalProperties) !== JSON.stringify(block._properties)) { + // recompute models + block.models = undefined + } } else { - //@ts-expect-error block._properties = block._originalProperties ?? block._properties - //@ts-expect-error block._originalProperties = undefined } } @@ -449,7 +484,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { if (block.name !== 'water' && block.name !== 'lava' && !invisibleBlocks.has(block.name)) { // cache let { models } = block - if (block.models === undefined || preflatRecomputeVariant) { + if (block.models === undefined) { try { models = blockProvider.getAllResolvedModels0_1({ name: block.name, @@ -515,7 +550,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { delayedRender = [] let ndx = attr.positions.length / 3 - for (let i = 0; i < attr.t_positions.length / 12; i++) { + for (let i = 0; i < attr.t_positions!.length / 12; i++) { attr.indices.push( ndx, ndx + 1, ndx + 2, ndx + 2, ndx + 1, ndx + 3, // eslint-disable-next-line @stylistic/function-call-argument-newline @@ -525,10 +560,10 @@ export function getSectionGeometry (sx, sy, sz, world: World) { ndx += 4 } - attr.positions.push(...attr.t_positions) - attr.normals.push(...attr.t_normals) - attr.colors.push(...attr.t_colors) - attr.uvs.push(...attr.t_uvs) + attr.positions.push(...attr.t_positions!) + attr.normals.push(...attr.t_normals!) + attr.colors.push(...attr.t_colors!) + attr.uvs.push(...attr.t_uvs!) delete attr.t_positions delete attr.t_normals @@ -540,6 +575,13 @@ export function getSectionGeometry (sx, sy, sz, world: World) { attr.colors = new Float32Array(attr.colors) as any attr.uvs = new Float32Array(attr.uvs) as any + if (needTiles) { + delete attr.positions + delete attr.normals + delete attr.colors + delete attr.uvs + } + return attr } diff --git a/prismarine-viewer/viewer/lib/mesher/shared.ts b/prismarine-viewer/viewer/lib/mesher/shared.ts index 782a3141..f96c7d4b 100644 --- a/prismarine-viewer/viewer/lib/mesher/shared.ts +++ b/prismarine-viewer/viewer/lib/mesher/shared.ts @@ -1,11 +1,35 @@ +import { BlockType } from '../../../examples/shared' + export const defaultMesherConfig = { version: '', enableLighting: true, skyLight: 15, smoothLighting: true, - outputFormat: 'threeJs' as 'threeJs' | 'webgl', + outputFormat: 'threeJs' as 'threeJs' | 'webgpu', textureSize: 1024, // for testing debugModelVariant: undefined as undefined | number[] } export type MesherConfig = typeof defaultMesherConfig + +export type MesherGeometryOutput = { + sx: number, + sy: number, + sz: number, + // resulting: float32array + positions: any, + normals: any, + colors: any, + uvs: any, + t_positions?: number[], + t_normals?: number[], + t_colors?: number[], + t_uvs?: number[], + + indices: number[], + tiles: Record, + signs: Record, + isFull: boolean + highestBlocks: Record + hadErrors: boolean +} diff --git a/prismarine-viewer/viewer/lib/mesher/world.ts b/prismarine-viewer/viewer/lib/mesher/world.ts index db940f66..ae65118c 100644 --- a/prismarine-viewer/viewer/lib/mesher/world.ts +++ b/prismarine-viewer/viewer/lib/mesher/world.ts @@ -26,6 +26,8 @@ export type WorldBlock = Omit & { isCube: boolean /** cache */ models?: BlockModelPartsResolved | null + _originalProperties?: Record + _properties?: Record } diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index 482907d4..fe5a3ad2 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -91,8 +91,17 @@ export abstract class WorldRendererCommon items?: CustomTexturesData blocks?: CustomTexturesData } = {} + workersProcessAverageTime = 0 + workersProcessAverageTimeCount = 0 + maxWorkersProcessTime = 0 + edgeChunks = {} as Record + lastAddChunk = null as null | { + timeout: any + x: number + z: number + } - abstract outputFormat: 'threeJs' | 'webgl' + abstract outputFormat: 'threeJs' | 'webgpu' constructor (public config: WorldRendererConfig) { // this.initWorkers(1) // preload script on page load @@ -145,6 +154,11 @@ export abstract class WorldRendererCommon } this.renderUpdateEmitter.emit('update') + if (data.processTime) { + this.workersProcessAverageTimeCount++ + this.workersProcessAverageTime = ((this.workersProcessAverageTime * (this.workersProcessAverageTimeCount - 1)) + data.processTime) / this.workersProcessAverageTimeCount + this.maxWorkersProcessTime = Math.max(this.maxWorkersProcessTime, data.processTime) + } } } worker.onmessage = ({ data }) => { @@ -265,7 +279,7 @@ export abstract class WorldRendererCommon this.currentTextureImage = this.material.map.image this.mesherConfig.textureSize = this.material.map.image.width - for (const worker of this.workers) { + for (const [i, worker] of this.workers.entries()) { const { blockstatesModels } = this if (this.customBlockStates) { // TODO! remove from other versions as well @@ -282,6 +296,7 @@ export abstract class WorldRendererCommon } worker.postMessage({ type: 'mesherData', + workerIndex: i, blocksAtlas: { latest: blocksAtlas }, From 684261e5157a8891b8e2ec65283c314a517f7328 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 03:08:19 +0300 Subject: [PATCH 051/865] fix building, update test types --- prismarine-viewer/examples/shared.ts | 19 +++++++++++++------ prismarine-viewer/viewer/lib/mesher/models.ts | 15 ++++----------- .../viewer/lib/worldrendererCommon.ts | 7 ++++--- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/prismarine-viewer/examples/shared.ts b/prismarine-viewer/examples/shared.ts index 4ef9b417..d7a402dd 100644 --- a/prismarine-viewer/examples/shared.ts +++ b/prismarine-viewer/examples/shared.ts @@ -1,11 +1,18 @@ export type BlockFaceType = { - side: number - textureIndex: number - textureName?: string - tint?: [number, number, number] - isTransparent?: boolean + side: number + textureIndex: number + tint?: [number, number, number] + isTransparent?: boolean + + // for testing + face: string + neighbor: string + light?: number } export type BlockType = { - faces: BlockFaceType[] + faces: BlockFaceType[] + + // for testing + block: string } diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index 54c879b8..1f19fc71 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -21,17 +21,8 @@ for (const key of Object.keys(tintsData)) { tints[key] = prepareTints(tintsData[key]) } -type TestTileData = { - block: string - faces: Array<{ - face: string - neighbor: string - light?: number - }> -} - type Tiles = { - [blockPos: string]: BlockType & TestTileData + [blockPos: string]: BlockType } function prepareTints (tints) { @@ -180,6 +171,8 @@ function renderLiquid (world: World, cursor: Vec3, texture: any | undefined, typ tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({ face, neighbor: `${neighborPos.x},${neighborPos.y},${neighborPos.z}`, + side: 0, // todo + textureIndex: 0, // texture: eFace.texture.name, }) } @@ -374,7 +367,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: tint: lightWithColor, //@ts-expect-error debug prop texture: eFace.texture.debugName || block.name, - } satisfies BlockType['faces'][number] & TestTileData['faces'][number] as any) + } satisfies BlockType['faces'][number]) } } diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index fe5a3ad2..49de2ca0 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -14,7 +14,7 @@ import TypedEmitter from 'typed-emitter' import { dynamicMcDataFiles } from '../../buildMesherConfig.mjs' import { toMajorVersion } from '../../../src/utils' import { buildCleanupDecorator } from './cleanupDecorator' -import { defaultMesherConfig } from './mesher/shared' +import { MesherGeometryOutput, defaultMesherConfig } from './mesher/shared' import { chunkPos } from './simpleUtils' import { HandItemBlock } from './holdingBlock' @@ -123,8 +123,9 @@ export abstract class WorldRendererCommon if (!this.active) return this.handleWorkerMessage(data) if (data.type === 'geometry') { - for (const key in data.geometry.highestBlocks) { - const highest = data.geometry.highestBlocks[key] + const geometry = data.geometry as MesherGeometryOutput + for (const key in geometry.highestBlocks) { + const highest = geometry.highestBlocks[key] if (!this.highestBlocks[key] || this.highestBlocks[key].y < highest.y) { this.highestBlocks[key] = highest } From 9e7711e386ef3764f824b5e7d19e93ed79fdc5f4 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 03:15:17 +0300 Subject: [PATCH 052/865] add eaglercraft as alternative, fix types again --- README.MD | 1 + prismarine-viewer/viewer/lib/mesher/models.ts | 2 +- prismarine-viewer/viewer/lib/mesher/shared.ts | 2 +- prismarine-viewer/viewer/lib/mesher/test/mesherTester.ts | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.MD b/README.MD index 8ecd7e78..e875873b 100644 --- a/README.MD +++ b/README.MD @@ -188,3 +188,4 @@ General: ### Alternatives - [https://github.com/ClassiCube/ClassiCube](ClassiCube - Better C# Rewrite) [DEMO](https://www.classicube.net/server/play/?warned=true) +- [https://m.eaglercraft.com/](EaglerCraft) - Eaglercraft runnable on mobile (real Minecraft in the browser) diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index 1f19fc71..88505c06 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -410,7 +410,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { tiles: {}, // todo this can be removed here signs: {}, - isFull: true, + // isFull: true, highestBlocks: {}, hadErrors: false } diff --git a/prismarine-viewer/viewer/lib/mesher/shared.ts b/prismarine-viewer/viewer/lib/mesher/shared.ts index f96c7d4b..30b62c45 100644 --- a/prismarine-viewer/viewer/lib/mesher/shared.ts +++ b/prismarine-viewer/viewer/lib/mesher/shared.ts @@ -29,7 +29,7 @@ export type MesherGeometryOutput = { indices: number[], tiles: Record, signs: Record, - isFull: boolean + // isFull: boolean highestBlocks: Record hadErrors: boolean } diff --git a/prismarine-viewer/viewer/lib/mesher/test/mesherTester.ts b/prismarine-viewer/viewer/lib/mesher/test/mesherTester.ts index 986aa079..2885c2a7 100644 --- a/prismarine-viewer/viewer/lib/mesher/test/mesherTester.ts +++ b/prismarine-viewer/viewer/lib/mesher/test/mesherTester.ts @@ -41,7 +41,7 @@ export const setup = (version, initialBlocks: Array<[number[], string]>) => { reload() const getLights = () => { - return Object.fromEntries(getGeometry().faces.map(({ face, light }) => ([face, light * 15 - 2]))) + return Object.fromEntries(getGeometry().faces.map(({ face, light }) => ([face, (light ?? 0) * 15 - 2]))) } const setLight = (x: number, y: number, z: number, val = 0) => { From 306f894d8cc5c6314bb0495ddaaaa4a19be2cb4c Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 4 Sep 2024 03:18:56 +0300 Subject: [PATCH 053/865] fix(important,singleplayer): stop loading chunks in previous position when teleported to a new pos and always start loading chunks in new pos --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5337cebf..bd18bc03 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "esbuild-plugin-polyfill-node": "^0.3.0", "express": "^4.18.2", "filesize": "^10.0.12", - "flying-squid": "npm:@zardoy/flying-squid@^0.0.36", + "flying-squid": "npm:@zardoy/flying-squid@^0.0.38", "fs-extra": "^11.1.1", "google-drive-browserfs": "github:zardoy/browserfs#google-drive", "jszip": "^3.10.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8075be60..77810ab0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,8 +117,8 @@ importers: specifier: ^10.0.12 version: 10.0.12 flying-squid: - specifier: npm:@zardoy/flying-squid@^0.0.36 - version: '@zardoy/flying-squid@0.0.36(encoding@0.1.13)' + specifier: npm:@zardoy/flying-squid@^0.0.38 + version: '@zardoy/flying-squid@0.0.38(encoding@0.1.13)' fs-extra: specifier: ^11.1.1 version: 11.1.1 @@ -3390,8 +3390,8 @@ packages: resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==} engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'} - '@zardoy/flying-squid@0.0.36': - resolution: {integrity: sha512-d4clMPDpw723SDF5P2mMVNfbthUFLX6OT+vTCECAMshX8/M7CyMq/q9BfBQoeJcBL0H9nplhwtFbnx3Edb2fzA==} + '@zardoy/flying-squid@0.0.38': + resolution: {integrity: sha512-xz/ZuWmva3mlT1cigOudOMqa5iQF2sWsUUVeBNUoqfHscXoXl0TIOXnRScBeEGZjY2fD7meJ24nlcInewgNfZg==} engines: {node: '>=8'} hasBin: true @@ -13366,7 +13366,7 @@ snapshots: '@types/emscripten': 1.39.8 tslib: 1.14.1 - '@zardoy/flying-squid@0.0.36(encoding@0.1.13)': + '@zardoy/flying-squid@0.0.38(encoding@0.1.13)': dependencies: '@tootallnate/once': 2.0.0 change-case: 4.1.2 From 5aaa687392cef67e153270463461ce3a46704813 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Wed, 4 Sep 2024 05:03:17 +0300 Subject: [PATCH 054/865] feat: display progress of downloading chunks visually (#195) --- prismarine-viewer/viewer/lib/mesher/models.ts | 10 +- src/react/AppStatus.tsx | 13 ++- src/react/AppStatusProvider.tsx | 33 ++++-- src/react/DiveTransition.tsx | 12 +- src/react/LoadingChunks.css | 12 ++ src/react/LoadingChunks.stories.tsx | 43 +++++++ src/react/LoadingChunks.tsx | 60 ++++++++++ src/react/PauseScreen.tsx | 108 ++++++++++++++---- src/utils.ts | 9 +- 9 files changed, 263 insertions(+), 37 deletions(-) create mode 100644 src/react/LoadingChunks.css create mode 100644 src/react/LoadingChunks.stories.tsx create mode 100644 src/react/LoadingChunks.tsx diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index 88505c06..8b9115e7 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -282,6 +282,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: const aos: number[] = [] const neighborPos = position.plus(new Vec3(...dir)) + // 10% const baseLight = world.getLight(neighborPos, undefined, undefined, block.name) / 15 for (const pos of corners) { let vertex = [ @@ -290,7 +291,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: (pos[2] ? maxz : minz) ] - if (!needTiles) { + if (!needTiles) { // 10% vertex = vecadd3(matmul3(localMatrix, vertex), localShift) vertex = vecadd3(matmul3(globalMatrix, vertex), globalShift) vertex = vertex.map(v => v / 16) @@ -411,7 +412,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { // todo this can be removed here signs: {}, // isFull: true, - highestBlocks: {}, + highestBlocks: {}, // todo migrate to map for 2% boost perf hadErrors: false } @@ -449,7 +450,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { } const biome = block.biome.name - if (world.preflat) { + if (world.preflat) { // 10% perf const patchProperties = preflatBlockCalculation(block, world, cursor) if (patchProperties) { block._originalProperties ??= block._properties @@ -505,6 +506,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { const model = modelVars[useVariant] ?? modelVars[0] if (!model) continue + // #region 10% let globalMatrix = null as any let globalShift = null as any for (const axis of ['x', 'y', 'z'] as const) { @@ -518,6 +520,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { globalShift = [8, 8, 8] globalShift = vecsub3(globalShift, matmul3(globalMatrix, globalShift)) } + // #endregion for (const element of model.elements ?? []) { const ao = model.ao ?? true @@ -527,6 +530,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { renderElement(world, pos, element, ao, attr, globalMatrix, globalShift, block, biome) }) } else { + // 60% renderElement(world, cursor, element, ao, attr, globalMatrix, globalShift, block, biome) } } diff --git a/src/react/AppStatus.tsx b/src/react/AppStatus.tsx index ca83c29a..31f08641 100644 --- a/src/react/AppStatus.tsx +++ b/src/react/AppStatus.tsx @@ -2,8 +2,18 @@ import { useEffect, useState } from 'react' import styles from './appStatus.module.css' import Button from './Button' import Screen from './Screen' +import LoadingChunks from './LoadingChunks' -export default ({ status, isError, hideDots = false, lastStatus = '', backAction = undefined as undefined | (() => void), description = '', actionsSlot = null as React.ReactNode | null }) => { +export default ({ + status, + isError, + hideDots = false, + lastStatus = '', + backAction = undefined as undefined | (() => void), + description = '', + actionsSlot = null as React.ReactNode | null, + children +}) => { const [loadingDots, setLoadingDots] = useState('') useEffect(() => { @@ -52,6 +62,7 @@ export default ({ status, isError, hideDots = false, lastStatus = '', backAction +} + export const tryFindOptionConfig = (option: keyof AppOptions) => { for (const group of Object.values(guiOptionsScheme)) { for (const optionConfig of group) { diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index b7bf4abe..936d5bfc 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -82,6 +82,8 @@ const defaultOptions = { /** Wether to popup sign editor on server action */ autoSignEditor: true, wysiwygSignEditor: 'auto' as 'auto' | 'always' | 'never', + displayBossBars: false, // boss bar overlay was removed for some reason, enable safely + disabledUiParts: [] as string[], } function getDefaultTouchControlsPositions () { diff --git a/src/react/BossBarOverlayProvider.tsx b/src/react/BossBarOverlayProvider.tsx index 9bb7d948..5cac3c8a 100644 --- a/src/react/BossBarOverlayProvider.tsx +++ b/src/react/BossBarOverlayProvider.tsx @@ -1,5 +1,4 @@ import { useState, useEffect } from 'react' -import { BotEvents } from 'mineflayer' import BossBar, { BossBarType } from './BossBarOverlay' import './BossBarOverlay.css' @@ -8,9 +7,8 @@ export default () => { const [bossBars, setBossBars] = useState(new Map()) useEffect(() => { - // typescript error: no bossBarCreated in BotEvents. Why?? - bot.on('bossBarCreated' as keyof BotEvents, (bossBar) => { - setBossBars(prevBossBars => new Map(prevBossBars.set(bossBar.entityUUID, bossBar))) + bot.on('bossBarCreated', (bossBar) => { + setBossBars(prevBossBars => new Map(prevBossBars.set(bossBar.entityUUID, bossBar as any))) }) bot.on('bossBarUpdated', (bossBar) => { setBossBars(prevBossBars => new Map(prevBossBars.set(bossBar.entityUUID, bossBar as BossBarType))) diff --git a/src/react/DebugOverlay.tsx b/src/react/DebugOverlay.tsx index 9315d8a4..23f0d7db 100644 --- a/src/react/DebugOverlay.tsx +++ b/src/react/DebugOverlay.tsx @@ -14,10 +14,17 @@ export default () => { received: {} as { [key: string]: number }, sent: {} as { [key: string]: number } }) + window.packetsCountByNamePerSec = packetsCountByNamePerSec + const packetsCountByNamePer10Sec = useRef({ + received: {} as { [key: string]: number }, + sent: {} as { [key: string]: number } + }) + window.packetsCountByNamePer10Sec = packetsCountByNamePer10Sec const packetsCountByName = useRef({ received: {} as { [key: string]: number }, sent: {} as { [key: string]: number } }) + window.packetsCountByName = packetsCountByName const ignoredPackets = useRef(new Set([] as any[])) const [packetsString, setPacketsString] = useState('') const [showDebug, setShowDebug] = useState(false) @@ -62,9 +69,11 @@ export default () => { const managePackets = (type, name, data) => { packetsCountByName.current[type][name] ??= 0 packetsCountByName.current[type][name]++ + packetsCountByNamePerSec.current[type][name] ??= 0 + packetsCountByNamePerSec.current[type][name]++ + packetsCountByNamePer10Sec.current[type][name] ??= 0 + packetsCountByNamePer10Sec.current[type][name]++ if (options.debugLogNotFrequentPackets && !ignoredPackets.current.has(name) && !hardcodedListOfDebugPacketsToIgnore[type].includes(name)) { - packetsCountByNamePerSec.current[type][name] ??= 0 - packetsCountByNamePerSec.current[type][name]++ if (packetsCountByNamePerSec.current[type][name] > 5 || packetsCountByName.current[type][name] > 100) { // todo think of tracking the count within 10s console.info(`[packet ${name} was ${type} too frequent] Ignoring...`) ignoredPackets.current.add(name) @@ -76,12 +85,17 @@ export default () => { useEffect(() => { document.addEventListener('keydown', handleF3) + let update = 0 const packetsUpdateInterval = setInterval(() => { setPacketsString(`↓ ${received.current.count} (${(received.current.size / 1024).toFixed(2)} KB/s, ${getFixedFilesize(receivedTotal.current)}) ↑ ${sent.current.count}`) received.current = { ...defaultPacketsCount } sent.current = { ...defaultPacketsCount } packetsCountByNamePerSec.current.received = {} packetsCountByNamePerSec.current.sent = {} + if (update++ % 10 === 0) { + packetsCountByNamePer10Sec.current.received = {} + packetsCountByNamePer10Sec.current.sent = {} + } }, 1000) const freqUpdateInterval = setInterval(() => { diff --git a/src/react/IndicatorEffectsProvider.tsx b/src/react/IndicatorEffectsProvider.tsx index 00b78d8a..b82782e6 100644 --- a/src/react/IndicatorEffectsProvider.tsx +++ b/src/react/IndicatorEffectsProvider.tsx @@ -3,6 +3,7 @@ import { useEffect, useMemo } from 'react' import { inGameError } from '../utils' import { fsState } from '../loadSave' import { miscUiState } from '../globalState' +import { options } from '../optionsStorage' import IndicatorEffects, { EffectType, defaultIndicatorsState } from './IndicatorEffects' import { images } from './effectsImages' @@ -52,6 +53,7 @@ const getEffectIndex = (newEffect: EffectType) => { export default () => { const stateIndicators = useSnapshot(state.indicators) const { hasErrors } = useSnapshot(miscUiState) + const { disabledUiParts } = useSnapshot(options) const { isReadonly, openReadOperations, openWriteOperations } = useSnapshot(fsState) const allIndicators: typeof defaultIndicatorsState = { readonlyFiles: isReadonly, diff --git a/src/react/ScoreboardProvider.tsx b/src/react/ScoreboardProvider.tsx index bcae9907..cb67b941 100644 --- a/src/react/ScoreboardProvider.tsx +++ b/src/react/ScoreboardProvider.tsx @@ -10,6 +10,7 @@ export default function ScoreboardProvider () { useMemo(() => { // useMemo instead of useEffect to register them asap and not after the initial dom render const updateSidebarScoreboard = () => { + addStatPerSec('scoreboard') if (bot.scoreboard.sidebar) { setTitle(bot.scoreboard.sidebar.title) setItems([...bot.scoreboard.sidebar.items]) diff --git a/src/reactUi.tsx b/src/reactUi.tsx index 029b2493..599a18f7 100644 --- a/src/reactUi.tsx +++ b/src/reactUi.tsx @@ -42,6 +42,8 @@ import BedTime from './react/BedTime' import NoModalFoundProvider from './react/NoModalFoundProvider' import SignInMessageProvider from './react/SignInMessageProvider' import BookProvider from './react/BookProvider' +import { options } from './optionsStorage' +import BossBarOverlayProvider from './react/BossBarOverlayProvider' const RobustPortal = ({ children, to }) => { return createPortal({children}, to) @@ -99,6 +101,7 @@ const InGameComponent = ({ children }) => { const InGameUi = () => { const { gameLoaded, showUI: showUIRaw } = useSnapshot(miscUiState) + const { disabledUiParts, displayBossBars } = useSnapshot(options) const hasModals = useSnapshot(activeModalStack).length > 0 const showUI = showUIRaw || hasModals if (!gameLoaded || !bot) return @@ -107,26 +110,27 @@ const InGameUi = () => { {/* apply scaling */}
- - - - - + {!disabledUiParts.includes('death-screen') && } + {!disabledUiParts.includes('debug-overlay') && } + {!disabledUiParts.includes('mobile-top-buttons') && } + {!disabledUiParts.includes('players-list') && } + {!disabledUiParts.includes('chat') && } - - - - - + {!disabledUiParts.includes('title') && } + {!disabledUiParts.includes('scoreboard') && } + {!disabledUiParts.includes('effects-indicators') && } + {!disabledUiParts.includes('crosshair') && } + {!disabledUiParts.includes('books') && } + {!disabledUiParts.includes('bossbars') && displayBossBars && }
- - + {!disabledUiParts.includes('xp-bar') && } + {!disabledUiParts.includes('hud-bars') && }
- {showUI && } + {showUI && !disabledUiParts.includes('hotbar') && }
From 1c7fdc21a6b3c997a2cece632bf40184a02e57d7 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 7 Sep 2024 19:33:16 +0300 Subject: [PATCH 067/865] add a way to to disable neighbor chunk updates and all the UI (needed for perf testing) --- .../viewer/lib/worldrendererCommon.ts | 17 ++++++++++------- src/optionsGuiScheme.tsx | 7 ++++--- src/optionsStorage.ts | 1 + src/reactUi.tsx | 2 +- src/watchOptions.ts | 4 ++++ 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index 49de2ca0..92fc9681 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -100,6 +100,7 @@ export abstract class WorldRendererCommon x: number z: number } + neighborChunkUpdates = true abstract outputFormat: 'threeJs' | 'webgpu' @@ -321,7 +322,7 @@ export abstract class WorldRendererCommon for (let y = this.worldConfig.minY; y < this.worldConfig.worldHeight; y += 16) { const loc = new Vec3(x, y, z) this.setSectionDirty(loc) - if (!isLightUpdate || this.mesherConfig.smoothLighting) { + if (this.neighborChunkUpdates && (!isLightUpdate || this.mesherConfig.smoothLighting)) { this.setSectionDirty(loc.offset(-16, 0, 0)) this.setSectionDirty(loc.offset(16, 0, 0)) this.setSectionDirty(loc.offset(0, 0, -16)) @@ -357,12 +358,14 @@ export abstract class WorldRendererCommon worker.postMessage({ type: 'blockUpdate', pos, stateId }) } this.setSectionDirty(pos) - if ((pos.x & 15) === 0) this.setSectionDirty(pos.offset(-16, 0, 0)) - if ((pos.x & 15) === 15) this.setSectionDirty(pos.offset(16, 0, 0)) - if ((pos.y & 15) === 0) this.setSectionDirty(pos.offset(0, -16, 0)) - if ((pos.y & 15) === 15) this.setSectionDirty(pos.offset(0, 16, 0)) - if ((pos.z & 15) === 0) this.setSectionDirty(pos.offset(0, 0, -16)) - if ((pos.z & 15) === 15) this.setSectionDirty(pos.offset(0, 0, 16)) + if (this.neighborChunkUpdates) { + if ((pos.x & 15) === 0) this.setSectionDirty(pos.offset(-16, 0, 0)) + if ((pos.x & 15) === 15) this.setSectionDirty(pos.offset(16, 0, 0)) + if ((pos.y & 15) === 0) this.setSectionDirty(pos.offset(0, -16, 0)) + if ((pos.y & 15) === 15) this.setSectionDirty(pos.offset(0, 16, 0)) + if ((pos.z & 15) === 0) this.setSectionDirty(pos.offset(0, 0, -16)) + if ((pos.z & 15) === 15) this.setSectionDirty(pos.offset(0, 0, 16)) + } } queueAwaited = false diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index fc9e347e..015013d1 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -91,6 +91,7 @@ export const guiOptionsScheme: { tooltip: 'Additional distance to keep the chunks loading before unloading them by marking them as too far', }, handDisplay: {}, + neighborChunkUpdates: {}, }, ], main: [ @@ -429,15 +430,15 @@ const Category = ({ children }) =>
{ const { disabledUiParts } = useSnapshot(options) - const currentlyEnabled = disabledUiParts.includes(name) + const currentlyDisabled = disabledUiParts.includes(name) if (addUiText) label = `${label} UI` return + >{currentlyDisabled ? 'Enable' : 'Disable'} {label} } export const tryFindOptionConfig = (option: keyof AppOptions) => { diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index 936d5bfc..d5a9e5aa 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -84,6 +84,7 @@ const defaultOptions = { wysiwygSignEditor: 'auto' as 'auto' | 'always' | 'never', displayBossBars: false, // boss bar overlay was removed for some reason, enable safely disabledUiParts: [] as string[], + neighborChunkUpdates: true } function getDefaultTouchControlsPositions () { diff --git a/src/reactUi.tsx b/src/reactUi.tsx index 599a18f7..2fb63c19 100644 --- a/src/reactUi.tsx +++ b/src/reactUi.tsx @@ -104,7 +104,7 @@ const InGameUi = () => { const { disabledUiParts, displayBossBars } = useSnapshot(options) const hasModals = useSnapshot(activeModalStack).length > 0 const showUI = showUIRaw || hasModals - if (!gameLoaded || !bot) return + if (!gameLoaded || !bot || disabledUiParts.includes('*')) return return <> diff --git a/src/watchOptions.ts b/src/watchOptions.ts index d26d2b90..926c6be1 100644 --- a/src/watchOptions.ts +++ b/src/watchOptions.ts @@ -62,6 +62,10 @@ export const watchOptionsAfterViewerInit = () => { if (!(viewer.world instanceof WorldRendererThree)) return viewer.world.starField.enabled = o.starfieldRendering }) + + watchValue(options, o => { + viewer.world.neighborChunkUpdates = o.neighborChunkUpdates + }) } let viewWatched = false From a063a0d75b58ecca6d692581aaf47bbc7f051966 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 7 Sep 2024 19:42:50 +0300 Subject: [PATCH 068/865] fix: fix cobblestone_wall and player head (skull) rendering in preflat versions --- prismarine-viewer/viewer/lib/mesher/models.ts | 23 +++++++++++++++--- src/preflatMap.json | 24 +++++++++---------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index 8b9115e7..c7bce0c0 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -479,15 +479,32 @@ export function getSectionGeometry (sx, sy, sz, world: World) { // cache let { models } = block if (block.models === undefined) { + const props = block.getProperties() try { + // fixme + if (world.preflat) { + if (block.name === 'cobblestone_wall') { + props.up = 'true' + for (const key of ['north', 'south', 'east', 'west']) { + const val = props[key] + if (val === 'false' || val === 'true') { + props[key] = val === 'true' ? 'low' : 'none' + } + } + } + } + models = blockProvider.getAllResolvedModels0_1({ name: block.name, - properties: block.getProperties(), + properties: props, })! - if (!models.length) models = null + if (!models.length) { + console.debug('[mesher] block to render not found', block.name, props) + models = null + } } catch (err) { models ??= erroredBlockModel - console.error(`Critical assets error. Unable to get block model for ${block.name}[${JSON.stringify(block.getProperties())}]: ` + err.message, err.stack) + console.error(`Critical assets error. Unable to get block model for ${block.name}[${JSON.stringify(props)}]: ` + err.message, err.stack) attr.hadErrors = true } } diff --git a/src/preflatMap.json b/src/preflatMap.json index fdf2640f..81c2a20a 100644 --- a/src/preflatMap.json +++ b/src/preflatMap.json @@ -925,18 +925,18 @@ "143:11": "oak_button[face=wall,facing=south,powered=true]", "143:12": "oak_button[face=wall,facing=north,powered=true]", "143:13": "oak_button[face=floor,facing=north,powered=true]", - "144:0": "undefined[facing=down,nodrop=false]", - "144:1": "undefined[facing=up,nodrop=false]", - "144:2": "undefined[facing=north,nodrop=false]", - "144:3": "undefined[facing=south,nodrop=false]", - "144:4": "undefined[facing=west,nodrop=false]", - "144:5": "undefined[facing=east,nodrop=false]", - "144:8": "undefined[facing=down,nodrop=true]", - "144:9": "undefined[facing=up,nodrop=true]", - "144:10": "undefined[facing=north,nodrop=true]", - "144:11": "undefined[facing=south,nodrop=true]", - "144:12": "undefined[facing=west,nodrop=true]", - "144:13": "undefined[facing=east,nodrop=true]", + "144:0": "player_head[facing=down]", + "144:1": "player_head[facing=up]", + "144:2": "player_head[facing=north]", + "144:3": "player_head[facing=south]", + "144:4": "player_head[facing=west]", + "144:5": "player_head[facing=east]", + "144:8": "player_head[facing=down]", + "144:9": "player_head[facing=up]", + "144:10": "player_head[facing=north]", + "144:11": "player_head[facing=south]", + "144:12": "player_head[facing=west]", + "144:13": "player_head[facing=east]", "145:0": "anvil[facing=south]", "145:1": "anvil[facing=west]", "145:2": "anvil[facing=north]", From fad9fd6e3a6052e8fe8b291d187e103f5e0b205d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 7 Sep 2024 19:48:08 +0300 Subject: [PATCH 069/865] fix: provide a hack to just render blocks all the blocks even with unknown states for preflat versions --- prismarine-viewer/viewer/lib/mesher/models.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index c7bce0c0..e63a92ea 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -497,7 +497,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { models = blockProvider.getAllResolvedModels0_1({ name: block.name, properties: props, - })! + }, world.preflat)! // fixme! this is a hack (also need a setting for all versions) if (!models.length) { console.debug('[mesher] block to render not found', block.name, props) models = null From d743981fc24f8d6f451c940f2b896b9ddb9e2ae9 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 18:20:34 +0300 Subject: [PATCH 070/865] up workflow files: new commands --- .github/workflows/ci.yml | 2 +- .github/workflows/fix-lint.yml | 23 +++++++++++++++++++++++ .github/workflows/merge-next.yml | 24 ++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/fix-lint.yml create mode 100644 .github/workflows/merge-next.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1882914..3649e89e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,6 @@ jobs: name: cypress-images path: cypress/integration/__image_snapshots__/ - run: node scripts/outdatedGitPackages.mjs - if: github.ref == 'refs/heads/next' + if: ${{ github.ref == 'refs/heads/next' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/fix-lint.yml b/.github/workflows/fix-lint.yml new file mode 100644 index 00000000..990de3fa --- /dev/null +++ b/.github/workflows/fix-lint.yml @@ -0,0 +1,23 @@ +name: Fix Lint Command +on: + issue_comment: + types: [created] +jobs: + deploy: + runs-on: ubuntu-latest + if: >- + github.event.issue.pull_request != '' && + ( + contains(github.event.comment.body, '/fix') + ) + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v2 + with: + ref: refs/pull/${{ github.event.issue.number }}/head + - run: pnpm lint --fix + - name: Push Changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml new file mode 100644 index 00000000..c7a66454 --- /dev/null +++ b/.github/workflows/merge-next.yml @@ -0,0 +1,24 @@ +name: Update Base Branch Command +on: + issue_comment: + types: [created] +jobs: + deploy: + runs-on: ubuntu-latest + if: >- + github.event.issue.pull_request != '' && + ( + contains(github.event.comment.body, '/update') + ) + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v2 + with: + ref: refs/pull/${{ github.event.issue.number }}/head + - name: Merge From Next + run: git merge next --strategy-option=theirs + - name: Push Changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} From f9a4960c3182e523e5492172b699691c128e1898 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 18:28:55 +0300 Subject: [PATCH 071/865] ci: try to fix the commands --- .github/workflows/ci.yml | 2 ++ .github/workflows/merge-next.yml | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3649e89e..a57a1646 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,8 @@ jobs: with: name: cypress-images path: cypress/integration/__image_snapshots__/ + - name: print current ref + run: echo ${{ github.ref }} - run: node scripts/outdatedGitPackages.mjs if: ${{ github.ref == 'refs/heads/next' }} env: diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index c7a66454..8bc34607 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -15,9 +15,11 @@ jobs: steps: - uses: actions/checkout@v2 with: - ref: refs/pull/${{ github.event.issue.number }}/head + fetch-depth: 0 # Fetch all history so we can merge branches + - name: Fetch All Branches + run: git fetch --all - name: Merge From Next - run: git merge next --strategy-option=theirs + run: git merge origin/next --strategy-option=theirs - name: Push Changes uses: ad-m/github-push-action@master with: From e89196041e6eb9d7e998de603fc100619f47811f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 18:37:06 +0300 Subject: [PATCH 072/865] ci: update commands --- .github/workflows/ci.yml | 4 ++-- .github/workflows/merge-next.yml | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a57a1646..29b2f2d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,8 +37,8 @@ jobs: name: cypress-images path: cypress/integration/__image_snapshots__/ - name: print current ref - run: echo ${{ github.ref }} + run: echo ${{ github.event.pull_request.base.ref }} - run: node scripts/outdatedGitPackages.mjs - if: ${{ github.ref == 'refs/heads/next' }} + if: ${{ github.event.pull_request.base.ref == 'next' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index 8bc34607..c2e5ddf3 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -21,6 +21,4 @@ jobs: - name: Merge From Next run: git merge origin/next --strategy-option=theirs - name: Push Changes - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} + run: git push From f518dce04d0588d7fc580a37846f810a53bdb2eb Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 18:39:13 +0300 Subject: [PATCH 073/865] ci: checkout pr before merge --- .github/workflows/merge-next.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index c2e5ddf3..0b95e51e 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -18,6 +18,8 @@ jobs: fetch-depth: 0 # Fetch all history so we can merge branches - name: Fetch All Branches run: git fetch --all + - name: Checkout PR + run: git checkout ${{ refs/pull/${{ github.event.issue.number }}/head }} - name: Merge From Next run: git merge origin/next --strategy-option=theirs - name: Push Changes From a3ef16a81a5b385135ddd98ea458773547125c3f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 18:40:37 +0300 Subject: [PATCH 074/865] ci: checkout pr before merge --- .github/workflows/merge-next.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index 0b95e51e..bd84fb02 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -19,7 +19,7 @@ jobs: - name: Fetch All Branches run: git fetch --all - name: Checkout PR - run: git checkout ${{ refs/pull/${{ github.event.issue.number }}/head }} + run: git checkout ${{ github.event.issue.pull_request.head.ref }} - name: Merge From Next run: git merge origin/next --strategy-option=theirs - name: Push Changes From ad8dc1a21a64880d6f6900d4201a460d53d129aa Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 21:03:11 +0300 Subject: [PATCH 075/865] fix: fix compatibility with some versions of new region format files --- pnpm-lock.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 84ea8fa1..bb94a569 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -160,7 +160,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/0228b5252f48a0d6ad7f36d7189851c427fbe8c4(minecraft-data@3.65.0) + version: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699(minecraft-data@3.65.0) prosemirror-example-setup: specifier: ^1.2.2 version: 1.2.2 @@ -7514,8 +7514,8 @@ packages: prismarine-physics@1.8.0: resolution: {integrity: sha512-gbM+S+bmVtOKVv+Z0WGaHMeEeBHISIDsRDRlv8sr0dex3ZJRhuq8djA02CBreguXtI18ZKh6q3TSj2qDr45NHA==} - prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/0228b5252f48a0d6ad7f36d7189851c427fbe8c4: - resolution: {tarball: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/0228b5252f48a0d6ad7f36d7189851c427fbe8c4} + 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} version: 2.8.0 prismarine-realms@1.3.2: @@ -13385,7 +13385,7 @@ snapshots: prismarine-entity: 2.3.1 prismarine-item: 1.14.0 prismarine-nbt: 2.5.0 - prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/0228b5252f48a0d6ad7f36d7189851c427fbe8c4(minecraft-data@3.65.0) + prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699(minecraft-data@3.65.0) prismarine-windows: 2.9.0 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/187a87f6d71cba12881a7bbaa510ed9085bf6da7 rambda: 9.2.0 @@ -18623,7 +18623,7 @@ snapshots: prismarine-nbt: 2.5.0 vec3: 0.1.8 - prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/0228b5252f48a0d6ad7f36d7189851c427fbe8c4(minecraft-data@3.65.0): + prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699(minecraft-data@3.65.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.65.0) From 3fb872129ed98b677cc4190e332237309f53f771 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 10 Sep 2024 00:29:30 +0300 Subject: [PATCH 076/865] fix: update autojump module --- .github/workflows/merge-next.yml | 1 + package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index bd84fb02..9bed1b3d 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -12,6 +12,7 @@ jobs: ) permissions: pull-requests: write + contents: write steps: - uses: actions/checkout@v2 with: diff --git a/package.json b/package.json index 80c6fdac..0f72fefc 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@dimaka/interface": "0.0.3-alpha.0", "@floating-ui/react": "^0.26.1", "@mui/base": "5.0.0-beta.40", - "@nxg-org/mineflayer-auto-jump": "^0.7.7", + "@nxg-org/mineflayer-auto-jump": "^0.7.8", "@nxg-org/mineflayer-tracker": "^1.2.1", "@react-oauth/google": "^0.12.1", "@stylistic/eslint-plugin": "^2.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bb94a569..987a9dd3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,8 +45,8 @@ importers: specifier: 5.0.0-beta.40 version: 5.0.0-beta.40(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@nxg-org/mineflayer-auto-jump': - specifier: ^0.7.7 - version: 0.7.7 + specifier: ^0.7.8 + version: 0.7.8 '@nxg-org/mineflayer-tracker': specifier: ^1.2.1 version: 1.2.1 @@ -2110,8 +2110,8 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This functionality has been moved to @npmcli/fs - '@nxg-org/mineflayer-auto-jump@0.7.7': - resolution: {integrity: sha512-50FYsz5rxBuLzOh7wqmg9iN9zdVGD+QjuaPcw/mD7q8Bq6Bq+o1/DfXfpoNGIHaDag80q6FJSpc73MI3Scid8g==} + '@nxg-org/mineflayer-auto-jump@0.7.8': + resolution: {integrity: sha512-o3XVruz2siApRvJKMe9EjQYTMANTMhStM3mRUKpZ1ar/2QqJ6sgyqEZTD9sE/zNA7bcjr49sZfuB8bX4t07Hww==} '@nxg-org/mineflayer-physics-util@1.5.8': resolution: {integrity: sha512-KmCkAqpUo8BbuRdIBs6+V2hWHehz++PRz3lRwIsb47CuG0u4sgLYh37RY3ifAznC6uWvmPK+q3B4ZXwJzPy1MQ==} @@ -11514,7 +11514,7 @@ snapshots: rimraf: 3.0.2 optional: true - '@nxg-org/mineflayer-auto-jump@0.7.7': + '@nxg-org/mineflayer-auto-jump@0.7.8': dependencies: '@nxg-org/mineflayer-physics-util': 1.5.8 strict-event-emitter-types: 2.0.0 From c6c25a7bb94d9c20e48f9852c77831a860c1fc15 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 10 Sep 2024 00:52:39 +0300 Subject: [PATCH 077/865] up again --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 0f72fefc..dce99824 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@dimaka/interface": "0.0.3-alpha.0", "@floating-ui/react": "^0.26.1", "@mui/base": "5.0.0-beta.40", - "@nxg-org/mineflayer-auto-jump": "^0.7.8", + "@nxg-org/mineflayer-auto-jump": "^0.7.11", "@nxg-org/mineflayer-tracker": "^1.2.1", "@react-oauth/google": "^0.12.1", "@stylistic/eslint-plugin": "^2.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 987a9dd3..df10d754 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,8 +45,8 @@ importers: specifier: 5.0.0-beta.40 version: 5.0.0-beta.40(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@nxg-org/mineflayer-auto-jump': - specifier: ^0.7.8 - version: 0.7.8 + specifier: ^0.7.11 + version: 0.7.11 '@nxg-org/mineflayer-tracker': specifier: ^1.2.1 version: 1.2.1 @@ -2110,8 +2110,8 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This functionality has been moved to @npmcli/fs - '@nxg-org/mineflayer-auto-jump@0.7.8': - resolution: {integrity: sha512-o3XVruz2siApRvJKMe9EjQYTMANTMhStM3mRUKpZ1ar/2QqJ6sgyqEZTD9sE/zNA7bcjr49sZfuB8bX4t07Hww==} + '@nxg-org/mineflayer-auto-jump@0.7.11': + resolution: {integrity: sha512-ex6lYch+YXXZKs/TGIMkspZqWTZ3pkteX4ZZHnrx1D3Yw8xfLaeU/lZ4O/8lH2uInuZbsx5pLKtwChOwKmJTlg==} '@nxg-org/mineflayer-physics-util@1.5.8': resolution: {integrity: sha512-KmCkAqpUo8BbuRdIBs6+V2hWHehz++PRz3lRwIsb47CuG0u4sgLYh37RY3ifAznC6uWvmPK+q3B4ZXwJzPy1MQ==} @@ -11514,7 +11514,7 @@ snapshots: rimraf: 3.0.2 optional: true - '@nxg-org/mineflayer-auto-jump@0.7.8': + '@nxg-org/mineflayer-auto-jump@0.7.11': dependencies: '@nxg-org/mineflayer-physics-util': 1.5.8 strict-event-emitter-types: 2.0.0 From a5dddfaad53ee94769a102ae662e1d2449254768 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 10 Sep 2024 01:20:39 +0300 Subject: [PATCH 078/865] up mineflayer-auto-jump --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index dce99824..0eb733fb 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@dimaka/interface": "0.0.3-alpha.0", "@floating-ui/react": "^0.26.1", "@mui/base": "5.0.0-beta.40", - "@nxg-org/mineflayer-auto-jump": "^0.7.11", + "@nxg-org/mineflayer-auto-jump": "^0.7.12", "@nxg-org/mineflayer-tracker": "^1.2.1", "@react-oauth/google": "^0.12.1", "@stylistic/eslint-plugin": "^2.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df10d754..31adde17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,8 +45,8 @@ importers: specifier: 5.0.0-beta.40 version: 5.0.0-beta.40(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@nxg-org/mineflayer-auto-jump': - specifier: ^0.7.11 - version: 0.7.11 + specifier: ^0.7.12 + version: 0.7.12 '@nxg-org/mineflayer-tracker': specifier: ^1.2.1 version: 1.2.1 @@ -2110,8 +2110,8 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This functionality has been moved to @npmcli/fs - '@nxg-org/mineflayer-auto-jump@0.7.11': - resolution: {integrity: sha512-ex6lYch+YXXZKs/TGIMkspZqWTZ3pkteX4ZZHnrx1D3Yw8xfLaeU/lZ4O/8lH2uInuZbsx5pLKtwChOwKmJTlg==} + '@nxg-org/mineflayer-auto-jump@0.7.12': + resolution: {integrity: sha512-F5vX/lerlWx/5HVlkDNbvrtQ19PL6iG8i4ItPTIRtjGiFzusDefP7DI226zSFR8Wlaw45qHv0jn814p/4/qVdQ==} '@nxg-org/mineflayer-physics-util@1.5.8': resolution: {integrity: sha512-KmCkAqpUo8BbuRdIBs6+V2hWHehz++PRz3lRwIsb47CuG0u4sgLYh37RY3ifAznC6uWvmPK+q3B4ZXwJzPy1MQ==} @@ -11514,7 +11514,7 @@ snapshots: rimraf: 3.0.2 optional: true - '@nxg-org/mineflayer-auto-jump@0.7.11': + '@nxg-org/mineflayer-auto-jump@0.7.12': dependencies: '@nxg-org/mineflayer-physics-util': 1.5.8 strict-event-emitter-types: 2.0.0 From 2c971f331ed61c194ec6adf3b3c76025e63d62ab Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 10 Sep 2024 01:34:26 +0300 Subject: [PATCH 079/865] fix: update entities tracker which should fix playing walking animations when players are standing still --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 0eb733fb..c0ac0cc1 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@floating-ui/react": "^0.26.1", "@mui/base": "5.0.0-beta.40", "@nxg-org/mineflayer-auto-jump": "^0.7.12", - "@nxg-org/mineflayer-tracker": "^1.2.1", + "@nxg-org/mineflayer-tracker": "^1.2.3", "@react-oauth/google": "^0.12.1", "@stylistic/eslint-plugin": "^2.6.1", "@types/gapi": "^0.0.47", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31adde17..05b83f3c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,8 +48,8 @@ importers: specifier: ^0.7.12 version: 0.7.12 '@nxg-org/mineflayer-tracker': - specifier: ^1.2.1 - version: 1.2.1 + specifier: ^1.2.3 + version: 1.2.3 '@react-oauth/google': specifier: ^0.12.1 version: 0.12.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -2116,8 +2116,8 @@ packages: '@nxg-org/mineflayer-physics-util@1.5.8': resolution: {integrity: sha512-KmCkAqpUo8BbuRdIBs6+V2hWHehz++PRz3lRwIsb47CuG0u4sgLYh37RY3ifAznC6uWvmPK+q3B4ZXwJzPy1MQ==} - '@nxg-org/mineflayer-tracker@1.2.1': - resolution: {integrity: sha512-SI1ffF8zvg3/ZNE021Ja2W0FZPN+WbQDZf8yFqOcXtPRXAtM9W6HvoACdzXep8BZid7WYgYLIgjKpB+9RqvCNQ==} + '@nxg-org/mineflayer-tracker@1.2.3': + resolution: {integrity: sha512-E7Ik/scU117Rr6kQUHHMBk8qOGh63YlTCGN33jMfeP7L8xmLeSHN3JtV/fbog8Y+R+HgO99yfZiRAaV7z1T6gQ==} '@nxg-org/mineflayer-trajectories@1.1.1': resolution: {integrity: sha512-X103KXlX8+L3uMeK4jQxMUdTizv01sQRSfBizAF/iOAdfQZehRLXr3CYKeJzfwPYGLN0X0JCl++cMEcZVn4vbg==} @@ -11523,7 +11523,7 @@ snapshots: dependencies: '@nxg-org/mineflayer-util-plugin': 1.8.3 - '@nxg-org/mineflayer-tracker@1.2.1': + '@nxg-org/mineflayer-tracker@1.2.3': dependencies: '@nxg-org/mineflayer-trajectories': 1.1.1 '@nxg-org/mineflayer-util-plugin': 1.8.3 From 18bf1aa80a8c8bcbe472f5a6ae803bccdff31adf Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 10 Sep 2024 20:00:09 +0300 Subject: [PATCH 080/865] feat: The commit also adds a new keybind action for the 'F4' key, allowing the user to cycle through different game modes. Depending on the current game mode, the bot's chat command is updated accordingly. --- src/controls.ts | 70 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/src/controls.ts b/src/controls.ts index 39e4c8c7..d882e967 100644 --- a/src/controls.ts +++ b/src/controls.ts @@ -291,6 +291,27 @@ const alwaysPressedHandledCommand = (command: Command) => { hideCurrentModal() } } + if (command === 'advanced.lockUrl') { + lockUrl() + } +} + +function lockUrl () { + let newQs = '' + if (fsState.saveLoaded) { + const save = localServer!.options.worldFolder.split('/').at(-1) + newQs = `loadSave=${save}` + } else if (process.env.NODE_ENV === 'development') { + newQs = `reconnect=1` + } else { + const qs = new URLSearchParams() + const { server, version } = localStorage + qs.set('server', server) + if (version) qs.set('version', version) + newQs = String(qs.toString()) + } + + window.history.replaceState({}, '', `${window.location.pathname}?${newQs}`) } function cycleHotbarSlot (dir: 1 | -1) { @@ -390,24 +411,6 @@ contro.on('trigger', ({ command }) => { break } } - if (command === 'advanced.lockUrl') { - let newQs = '' - if (fsState.saveLoaded) { - const save = localServer!.options.worldFolder.split('/').at(-1) - newQs = `loadSave=${save}` - } else if (process.env.NODE_ENV === 'development') { - newQs = `reconnect=1` - } else { - const qs = new URLSearchParams() - const { server, version } = localStorage - qs.set('server', server) - if (version) qs.set('version', version) - newQs = String(qs.toString()) - } - - window.history.replaceState({}, '', `${window.location.pathname}?${newQs}`) - // return - } if (command === 'ui.pauseMenu') { showModal({ reactType: 'pause-screen' }) @@ -472,7 +475,36 @@ export const f3Keybinds = [ await completeTexturePackInstall('default', 'default') } }, - mobileTitle: 'Open Widget' + mobileTitle: 'Reload Textures' + }, + { + key: 'F4', + async action () { + switch (bot.game.gameMode) { + case 'creative': { + bot.chat('/gamemode survival') + + break + } + case 'survival': { + bot.chat('/gamemode adventure') + + break + } + case 'adventure': { + bot.chat('/gamemode spectator') + + break + } + case 'spectator': { + bot.chat('/gamemode creative') + + break + } + // No default + } + }, + mobileTitle: 'Cycle Game Mode' } ] From f9b87d5087629363c94008976b6a372429d9d2c9 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 01:07:46 +0300 Subject: [PATCH 081/865] move playground to rsbuild! now fast reloads! reload on workers change fix cd --- .github/workflows/next-deploy.yml | 5 +- .github/workflows/preview.yml | 5 +- .github/workflows/publish.yml | 5 +- package.json | 13 ++- prismarine-viewer/buildMesherWorker.mjs | 8 +- prismarine-viewer/esbuild.mjs | 96 ---------------------- prismarine-viewer/playground.html | 2 +- prismarine-viewer/rsbuild.config.ts | 37 +++++++++ prismarine-viewer/rsbuildSharedConfig.ts | 100 +++++++++++++++++++++++ rsbuild.config.ts | 84 +++---------------- 10 files changed, 171 insertions(+), 184 deletions(-) delete mode 100644 prismarine-viewer/esbuild.mjs create mode 100644 prismarine-viewer/rsbuild.config.ts create mode 100644 prismarine-viewer/rsbuildSharedConfig.ts diff --git a/.github/workflows/next-deploy.yml b/.github/workflows/next-deploy.yml index f6915839..a01e0bb8 100644 --- a/.github/workflows/next-deploy.yml +++ b/.github/workflows/next-deploy.yml @@ -24,7 +24,10 @@ jobs: run: vercel build --token=${{ secrets.VERCEL_TOKEN }} - run: pnpm build-storybook - name: Copy playground files - run: pnpm build-playground && cp prismarine-viewer/public/index.html .vercel/output/static/playground.html && cp prismarine-viewer/public/playground.js .vercel/output/static/playground.js + run: | + mkdir -p .vercel/output/static/playground + pnpm build-playground + cp -r prismarine-viewer/dist/* .vercel/output/static/playground/ - name: Download Generated Sounds map run: node scripts/downloadSoundsMap.mjs - name: Deploy Project Artifacts to Vercel diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index e0f9736a..c074d2e5 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -35,7 +35,10 @@ jobs: run: vercel build --token=${{ secrets.VERCEL_TOKEN }} - run: pnpm build-storybook - name: Copy playground files - run: pnpm build-playground && cp prismarine-viewer/public/index.html .vercel/output/static/playground.html && cp prismarine-viewer/public/playground.js .vercel/output/static/playground.js + run: | + mkdir -p .vercel/output/static/playground + pnpm build-playground + cp -r prismarine-viewer/dist/* .vercel/output/static/playground/ - name: Download Generated Sounds map run: node scripts/downloadSoundsMap.mjs - name: Deploy Project Artifacts to Vercel diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9472118b..fa1f0281 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,10 @@ jobs: - run: vercel build --token=${{ secrets.VERCEL_TOKEN }} --prod - run: pnpm build-storybook - name: Copy playground files - run: pnpm build-playground && cp prismarine-viewer/public/index.html .vercel/output/static/playground.html && cp prismarine-viewer/public/playground.js .vercel/output/static/playground.js + run: | + mkdir -p .vercel/output/static/playground + pnpm build-playground + cp -r prismarine-viewer/dist/* .vercel/output/static/playground/ - name: Download Generated Sounds map run: node scripts/downloadSoundsMap.mjs - name: Deploy Project to Vercel diff --git a/package.json b/package.json index c0ac0cc1..d715016a 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,8 @@ "dev-rsbuild": "rsbuild dev", "dev-proxy": "node server.js", "start": "run-p dev-rsbuild dev-proxy watch-mesher", - "start-watch-script": "nodemon -w rsbuild.config.ts --watch", - "build": "rsbuild build", - "build-analyze": "BUNDLE_ANALYZE=true rsbuild build", + "build": "pnpm build-other-workers && rsbuild build", + "build-analyze": "BUNDLE_ANALYZE=true rsbuild build && pnpm build-other-workers", "check-build": "tsx scripts/genShims.ts && tsc && pnpm build", "test:cypress": "cypress run", "test-unit": "vitest", @@ -20,13 +19,13 @@ "build-storybook": "storybook build && node scripts/build.js moveStorybookFiles", "start-experiments": "vite --config experiments/vite.config.ts --host", "watch-other-workers": "echo NOT IMPLEMENTED", + "build-other-workers": "echo NOT IMPLEMENTED", "build-mesher": "node prismarine-viewer/buildMesherWorker.mjs", "watch-mesher": "pnpm build-mesher -w", - "run-playground": "run-p watch-mesher watch-other-workers playground-server watch-playground", + "run-playground": "run-p watch-mesher watch-other-workers watch-playground", "run-all": "run-p start run-playground", - "playground-server": "live-server --port=9090 prismarine-viewer/public", - "build-playground": "node prismarine-viewer/esbuild.mjs", - "watch-playground": "node prismarine-viewer/esbuild.mjs -w" + "build-playground": "rsbuild build --root ./prismarine-viewer", + "watch-playground": "rsbuild dev --root ./prismarine-viewer" }, "keywords": [ "prismarine", diff --git a/prismarine-viewer/buildMesherWorker.mjs b/prismarine-viewer/buildMesherWorker.mjs index f70b8128..03b952b4 100644 --- a/prismarine-viewer/buildMesherWorker.mjs +++ b/prismarine-viewer/buildMesherWorker.mjs @@ -30,7 +30,7 @@ const buildOptions = { sourcemap: 'linked', write: false, metafile: true, - outdir: path.join(__dirname, './public'), + outdir: path.join(__dirname, './dist'), define: { 'process.env.BROWSER': '"true"', }, @@ -108,9 +108,9 @@ const buildOptions = { }) build.onEnd(({ metafile, outputFiles }) => { if (!metafile) return - fs.mkdirSync(path.join(__dirname, './public'), { recursive: true }) - fs.writeFileSync(path.join(__dirname, './public/metafile.json'), JSON.stringify(metafile)) - for (const outDir of ['../dist/', './public/']) { + fs.mkdirSync(path.join(__dirname, './dist'), { recursive: true }) + fs.writeFileSync(path.join(__dirname, './dist/metafile.json'), JSON.stringify(metafile)) + for (const outDir of ['../dist/', './dist/']) { for (const outputFile of outputFiles) { if (outDir === '../dist/' && outputFile.path.endsWith('.map')) { // skip writing & browser loading sourcemap there, worker debugging should be done in playground diff --git a/prismarine-viewer/esbuild.mjs b/prismarine-viewer/esbuild.mjs deleted file mode 100644 index e410c8b6..00000000 --- a/prismarine-viewer/esbuild.mjs +++ /dev/null @@ -1,96 +0,0 @@ -//@ts-check -import * as fs from 'fs' -import fsExtra from 'fs-extra' - -import * as esbuild from 'esbuild' -import { polyfillNode } from 'esbuild-plugin-polyfill-node' -import path, { dirname, join } from 'path' -import { fileURLToPath } from 'url' -import childProcess from 'child_process' -import supportedVersions from '../src/supportedVersions.mjs' - -const dev = process.argv.includes('-w') - -const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url))) - -const mcDataPath = join(__dirname, '../generated/minecraft-data-optimized.json') -if (!fs.existsSync(mcDataPath)) { - childProcess.execSync('tsx ../scripts/makeOptimizedMcData.mjs', { stdio: 'inherit', cwd: __dirname }) -} - -fs.copyFileSync(join(__dirname, 'playground.html'), join(__dirname, 'public/index.html')) - -/** @type {import('esbuild').BuildOptions} */ -const buildOptions = { - bundle: true, - entryPoints: [join(__dirname, './examples/playground.ts')], - // target: ['es2020'], - // logLevel: 'debug', - logLevel: 'info', - platform: 'browser', - sourcemap: dev ? 'inline' : false, - minify: !dev, - outfile: join(__dirname, 'public/playground.js'), - mainFields: [ - 'browser', 'module', 'main' - ], - keepNames: true, - banner: { - js: `globalThis.global = globalThis;globalThis.includedVersions = ${JSON.stringify(supportedVersions)};`, - }, - alias: { - events: 'events', - buffer: 'buffer', - 'fs': 'browserfs/dist/shims/fs.js', - http: 'http-browserify', - stream: 'stream-browserify', - net: 'net-browserify', - // 'mc-assets': '/Users/vitaly/Documents/mc-assets', - }, - inject: [], - metafile: true, - loader: { - '.png': 'dataurl', - '.obj': 'text', - }, - plugins: [ - { - name: 'minecraft-data', - setup(build) { - build.onLoad({ - filter: /minecraft-data[\/\\]data.js$/, - }, () => { - const defaultVersionsObj = {} - return { - contents: fs.readFileSync(join(__dirname, '../src/shims/minecraftData.ts'), 'utf8'), - loader: 'ts', - resolveDir: join(__dirname, '../src/shims'), - } - }) - build.onEnd((e) => { - if (e.errors.length) return - fs.writeFileSync(join(__dirname, './public/metafile.json'), JSON.stringify(e.metafile), 'utf8') - }) - } - }, - polyfillNode({ - polyfills: { - fs: false, - crypto: false, - events: false, - http: false, - stream: false, - buffer: false, - perf_hooks: false, - net: false, - }, - }) - ], -} -if (dev) { - (await esbuild.context(buildOptions)).watch() -} else { - await esbuild.build(buildOptions) -} - -// await ctx.rebuild() diff --git a/prismarine-viewer/playground.html b/prismarine-viewer/playground.html index f1b36015..92713c1c 100644 --- a/prismarine-viewer/playground.html +++ b/prismarine-viewer/playground.html @@ -31,6 +31,6 @@ - +
diff --git a/prismarine-viewer/rsbuild.config.ts b/prismarine-viewer/rsbuild.config.ts new file mode 100644 index 00000000..1299670d --- /dev/null +++ b/prismarine-viewer/rsbuild.config.ts @@ -0,0 +1,37 @@ +import { defineConfig, mergeRsbuildConfig } from '@rsbuild/core'; +import supportedVersions from '../src/supportedVersions.mjs' +import childProcess from 'child_process' +import path, { dirname, join } from 'path' +import { pluginReact } from '@rsbuild/plugin-react'; +import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill'; +import fs from 'fs' +import { appAndRendererSharedConfig, rspackViewerConfig } from './rsbuildSharedConfig'; + +const mcDataPath = join(__dirname, '../generated/minecraft-data-optimized.json') + +if (!fs.existsSync(mcDataPath)) { + childProcess.execSync('tsx ./scripts/makeOptimizedMcData.mjs', { stdio: 'inherit', cwd: path.join(__dirname, '..') }) +} + +export default mergeRsbuildConfig( + appAndRendererSharedConfig(), + defineConfig({ + html: { + template: './playground.html', + }, + output: { + cleanDistPath: false, + }, + server: { + port: 9090, + }, + source: { + entry: { + index: join(__dirname, './examples/playground.ts') + }, + define: { + 'globalThis.includedVersions': JSON.stringify(supportedVersions), + }, + }, + }) +) diff --git a/prismarine-viewer/rsbuildSharedConfig.ts b/prismarine-viewer/rsbuildSharedConfig.ts new file mode 100644 index 00000000..3fba1ef1 --- /dev/null +++ b/prismarine-viewer/rsbuildSharedConfig.ts @@ -0,0 +1,100 @@ +import { defineConfig, ModifyRspackConfigUtils } from '@rsbuild/core'; +import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill'; +import { pluginReact } from '@rsbuild/plugin-react'; +import path from 'path' + +export const appAndRendererSharedConfig = () => defineConfig({ + dev: { + progressBar: true, + writeToDisk: true, + watchFiles: { + paths: [ + path.join(__dirname, './dist/webgpuRendererWorker.js'), + path.join(__dirname, './dist/mesher.js'), + ] + }, + }, + output: { + polyfill: 'usage', + // 50kb limit for data uri + dataUriLimit: 50 * 1024 + }, + source: { + alias: { + fs: path.join(__dirname, `../src/shims/fs.js`), + http: 'http-browserify', + stream: 'stream-browserify', + net: 'net-browserify', + 'minecraft-protocol$': 'minecraft-protocol/src/index.js', + 'buffer$': 'buffer', + // avoid bundling, not used on client side + 'prismarine-auth': path.join(__dirname, `../src/shims/prismarineAuthReplacement.ts`), + perf_hooks: path.join(__dirname, `../src/shims/perf_hooks_replacement.js`), + crypto: path.join(__dirname, `../src/shims/crypto.js`), + dns: path.join(__dirname, `../src/shims/dns.js`), + yggdrasil: path.join(__dirname, `../src/shims/yggdrasilReplacement.ts`), + 'three$': 'three/src/Three.js', + 'stats.js$': 'stats.js/src/Stats.js', + }, + define: { + 'process.platform': '"browser"', + }, + decorators: { + version: 'legacy', // default is a lie + }, + }, + server: { + htmlFallback: false, + publicDir: false, + headers: { + // enable shared array buffer + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'require-corp', + }, + open: process.env.OPEN_BROWSER === 'true', + }, + plugins: [ + pluginReact(), + pluginNodePolyfill() + ], + tools: { + rspack (config, helpers) { + rspackViewerConfig(config, helpers) + } + }, +}) + +export const rspackViewerConfig = (config, { appendPlugins, addRules, rspack }: ModifyRspackConfigUtils) => { + appendPlugins(new rspack.NormalModuleReplacementPlugin(/data/, (resource) => { + let absolute: string + const request = resource.request.replaceAll('\\', '/') + absolute = path.join(resource.context, request).replaceAll('\\', '/') + if (request.includes('minecraft-data/data/pc/1.')) { + console.log('Error: incompatible resource', request, resource.contextInfo.issuer) + process.exit(1) + // throw new Error(`${resource.request} was requested by ${resource.contextInfo.issuer}`) + } + if (absolute.endsWith('/minecraft-data/data.js')) { + resource.request = path.join(__dirname, `../src/shims/minecraftData.ts`) + } + })) + addRules([ + { + test: /\.obj$/, + type: 'asset/source', + }, + { + test: /\.wgsl$/, + type: 'asset/source', + }, + { + test: /\.mp3$/, + type: 'asset/source', + } + ]) + config.ignoreWarnings = [ + /the request of a dependency is an expression/, + /Unsupported pseudo class or element: xr-overlay/ + ] + +} diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 588cc35d..f8a965dc 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, RsbuildPluginAPI } from '@rsbuild/core' +import { defineConfig, mergeRsbuildConfig, RsbuildPluginAPI } from '@rsbuild/core' import { pluginReact } from '@rsbuild/plugin-react' import { pluginTypedCSSModules } from '@rsbuild/plugin-typed-css-modules' import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill' @@ -10,6 +10,7 @@ import fsExtra from 'fs-extra' import { promisify } from 'util' import { generateSW } from 'workbox-build' import { getSwAdditionalEntries } from './scripts/build' +import { appAndRendererSharedConfig } from './prismarine-viewer/rsbuildSharedConfig' //@ts-ignore try { require('./localSettings.js') } catch { } @@ -20,16 +21,12 @@ const buildingVersion = new Date().toISOString().split(':')[0] const dev = process.env.NODE_ENV === 'development' -export default defineConfig({ - dev: { - progressBar: true, - writeToDisk: true - }, +// base options are in ./prismarine-viewer/rsbuildSharedConfig.ts +const appConfig = defineConfig({ html: { template: './index.html', }, output: { - polyfill: 'usage', externals: [ 'sharp' ], @@ -37,25 +34,8 @@ export default defineConfig({ js: 'source-map', css: true, }, - // 50kb limit for data uri - dataUriLimit: 50 * 1024 }, source: { - alias: { - fs: './src/shims/fs.js', - http: 'http-browserify', - stream: 'stream-browserify', - net: 'net-browserify', - 'minecraft-protocol$': 'minecraft-protocol/src/index.js', - 'buffer$': 'buffer', - // avoid bundling, not used on client side - 'prismarine-auth': './src/shims/prismarineAuthReplacement.ts', - perf_hooks: './src/shims/perf_hooks_replacement.js', - crypto: './src/shims/crypto.js', - dns: './src/shims/dns.js', - yggdrasil: './src/shims/yggdrasilReplacement.ts', - 'three$': 'three/src/Three.js' - }, entry: { index: './src/index.ts', }, @@ -65,36 +45,22 @@ export default defineConfig({ define: { 'process.env.BUILD_VERSION': JSON.stringify(!dev ? buildingVersion : 'undefined'), 'process.env.MAIN_MENU_LINKS': JSON.stringify(process.env.MAIN_MENU_LINKS), - 'process.platform': '"browser"', '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}`}`), 'process.env.DEPS_VERSIONS': JSON.stringify({}) }, - decorators: { - version: 'legacy', // default is a lie - }, }, server: { // strictPort: true, - htmlFallback: false, - publicDir: false, // publicDir: { // name: 'assets', // }, - headers: { - // enable shared array buffer - 'Cross-Origin-Opener-Policy': 'same-origin', - 'Cross-Origin-Embedder-Policy': 'require-corp', - }, - open: process.env.OPEN_BROWSER === 'true', proxy: { '/api': 'http://localhost:8080', }, }, plugins: [ - pluginReact(), pluginTypedCSSModules(), - pluginNodePolyfill(), { name: 'test', setup (build: RsbuildPluginAPI) { @@ -121,9 +87,9 @@ export default defineConfig({ // childProcess.execSync('./scripts/prepareSounds.mjs', { stdio: 'inherit' }) // childProcess.execSync('tsx ./scripts/genMcDataTypes.ts', { stdio: 'inherit' }) // childProcess.execSync('tsx ./scripts/genPixelartTypes.ts', { stdio: 'inherit' }) - if (fs.existsSync('./prismarine-viewer/public/mesher.js') && dev) { + if (fs.existsSync('./prismarine-viewer/dist/mesher.js') && dev) { // copy mesher - fs.copyFileSync('./prismarine-viewer/public/mesher.js', './dist/mesher.js') + fs.copyFileSync('./prismarine-viewer/dist/mesher.js', './dist/mesher.js') } else if (!dev) { await execAsync('pnpm run build-mesher') } @@ -150,39 +116,6 @@ export default defineConfig({ }, }, ], - tools: { - bundlerChain (chain, { CHAIN_ID }) { - }, - rspack (config, { addRules, appendPlugins, rspack }) { - appendPlugins(new rspack.NormalModuleReplacementPlugin(/data/, (resource) => { - let absolute: string - const request = resource.request.replaceAll('\\', '/') - absolute = path.join(resource.context, request).replaceAll('\\', '/') - if (request.includes('minecraft-data/data/pc/1.')) { - console.log('Error: incompatible resource', request, resource.contextInfo.issuer) - process.exit(1) - // throw new Error(`${resource.request} was requested by ${resource.contextInfo.issuer}`) - } - if (absolute.endsWith('/minecraft-data/data.js')) { - resource.request = path.join(__dirname, './src/shims/minecraftData.ts') - } - })) - addRules([ - { - test: /\.obj$/, - type: 'asset/source', - }, - { - test: /\.mp3$/, - type: 'asset/source', - } - ]) - config.ignoreWarnings = [ - /the request of a dependency is an expression/, - /Unsupported pseudo class or element: xr-overlay/ - ] - } - }, // performance: { // bundleAnalyze: { // analyzerMode: 'json', @@ -190,3 +123,8 @@ export default defineConfig({ // }, // }, }) + +export default mergeRsbuildConfig( + appAndRendererSharedConfig(), + appConfig +) From 1446ccc0a7a16b72facfe2c18f4229a8ca71e014 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 02:35:29 +0300 Subject: [PATCH 082/865] should fix playground build --- package.json | 2 +- prismarine-viewer/rsbuild.config.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d715016a..84c3951a 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "watch-mesher": "pnpm build-mesher -w", "run-playground": "run-p watch-mesher watch-other-workers watch-playground", "run-all": "run-p start run-playground", - "build-playground": "rsbuild build --root ./prismarine-viewer", + "build-playground": "rsbuild build --config prismarine-viewer/rsbuild.config.ts", "watch-playground": "rsbuild dev --root ./prismarine-viewer" }, "keywords": [ diff --git a/prismarine-viewer/rsbuild.config.ts b/prismarine-viewer/rsbuild.config.ts index 1299670d..cea001db 100644 --- a/prismarine-viewer/rsbuild.config.ts +++ b/prismarine-viewer/rsbuild.config.ts @@ -17,10 +17,13 @@ export default mergeRsbuildConfig( appAndRendererSharedConfig(), defineConfig({ html: { - template: './playground.html', + template: join(__dirname, './playground.html'), }, output: { cleanDistPath: false, + distPath: { + root: join(__dirname, './dist'), + }, }, server: { port: 9090, From 4d3c92f6111b78af9d91813250aedda5329b6bcb Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 03:19:24 +0300 Subject: [PATCH 083/865] some playground fixes --- README.MD | 2 +- assets/playground.html | 4 ++++ prismarine-viewer/rsbuild.config.ts | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 assets/playground.html diff --git a/README.MD b/README.MD index ae23b60b..6a9df6db 100644 --- a/README.MD +++ b/README.MD @@ -87,7 +87,7 @@ To open the console, press `F12`, or if you are on mobile, you can type `#dev` i It should be easy to build/start the project locally. See [CONTRIBUTING.MD](./CONTRIBUTING.md) for more info. Also you can look at Dockerfile for reference. -There is world renderer playground ([link](https://mcon.vercel.app/playground.html)). +There is world renderer playground ([link](https://mcon.vercel.app/playground)). However, there are many things that can be done in online production version (like debugging actual source code). Also you can access some global variables in the console and there are a few useful examples: diff --git a/assets/playground.html b/assets/playground.html new file mode 100644 index 00000000..e4bb88ec --- /dev/null +++ b/assets/playground.html @@ -0,0 +1,4 @@ + + diff --git a/prismarine-viewer/rsbuild.config.ts b/prismarine-viewer/rsbuild.config.ts index cea001db..fa666d99 100644 --- a/prismarine-viewer/rsbuild.config.ts +++ b/prismarine-viewer/rsbuild.config.ts @@ -24,6 +24,7 @@ export default mergeRsbuildConfig( distPath: { root: join(__dirname, './dist'), }, + assetPrefix: './', }, server: { port: 9090, From 76bed4d496c1b28744e182a7f308ddf8ea8c4e11 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 03:34:55 +0300 Subject: [PATCH 084/865] eslint: ignore dist linting! --- .eslintignore | 1 + assets/playground.html | 2 +- rsbuild.config.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.eslintignore b/.eslintignore index ce8d8d37..bf3201cf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,4 +3,5 @@ rsbuild.config.ts *.module.css.d.ts *.generated.ts generated +dist public diff --git a/assets/playground.html b/assets/playground.html index e4bb88ec..d4179621 100644 --- a/assets/playground.html +++ b/assets/playground.html @@ -1,4 +1,4 @@ diff --git a/rsbuild.config.ts b/rsbuild.config.ts index f8a965dc..88280acf 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -77,6 +77,7 @@ const appConfig = defineConfig({ fsExtra.copySync('./node_modules/mc-assets/dist/other-textures/latest/entity', './dist/textures/entity') fsExtra.copySync('./assets/background', './dist/background') fs.copyFileSync('./assets/favicon.png', './dist/favicon.png') + fs.copyFileSync('./assets/playground.html', './dist/playground.html') fs.copyFileSync('./assets/manifest.json', './dist/manifest.json') fs.copyFileSync('./assets/loading-bg.jpg', './dist/loading-bg.jpg') const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8')) From 74fe84e10d9d3684b1eacdeca9247c4bd6804551 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Wed, 11 Sep 2024 19:27:17 +0300 Subject: [PATCH 085/865] fix playground on windows: rsbuild does not implement --root option for win --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84c3951a..4b41660a 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "run-playground": "run-p watch-mesher watch-other-workers watch-playground", "run-all": "run-p start run-playground", "build-playground": "rsbuild build --config prismarine-viewer/rsbuild.config.ts", - "watch-playground": "rsbuild dev --root ./prismarine-viewer" + "watch-playground": "rsbuild dev --config prismarine-viewer/rsbuild.config.ts" }, "keywords": [ "prismarine", From 25db002bc3ede0620b938e2d41f28938b3d8f7da Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 21:39:15 +0300 Subject: [PATCH 086/865] redirect to correct playground url --- .github/workflows/preview.yml | 2 +- README.MD | 2 +- assets/playground.html | 4 ++-- prismarine-viewer/playground.html | 6 ++++++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index c074d2e5..526705e9 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -51,7 +51,7 @@ jobs: allow-repeats: true message: | Deployed to Vercel Preview: ${{ steps.deploy.outputs.stdout }} - [Playground](${{ steps.deploy.outputs.stdout }}/playground.html) + [Playground](${{ steps.deploy.outputs.stdout }}/playground/) [Storybook](${{ steps.deploy.outputs.stdout }}/storybook/) # - run: git checkout next scripts/githubActions.mjs - name: Get deployment alias diff --git a/README.MD b/README.MD index 6a9df6db..9b07bdb5 100644 --- a/README.MD +++ b/README.MD @@ -87,7 +87,7 @@ To open the console, press `F12`, or if you are on mobile, you can type `#dev` i It should be easy to build/start the project locally. See [CONTRIBUTING.MD](./CONTRIBUTING.md) for more info. Also you can look at Dockerfile for reference. -There is world renderer playground ([link](https://mcon.vercel.app/playground)). +There is world renderer playground ([link](https://mcon.vercel.app/playground/)). However, there are many things that can be done in online production version (like debugging actual source code). Also you can access some global variables in the console and there are a few useful examples: diff --git a/assets/playground.html b/assets/playground.html index d4179621..8c394f91 100644 --- a/assets/playground.html +++ b/assets/playground.html @@ -1,4 +1,4 @@ - + diff --git a/prismarine-viewer/playground.html b/prismarine-viewer/playground.html index 92713c1c..c8ea00d2 100644 --- a/prismarine-viewer/playground.html +++ b/prismarine-viewer/playground.html @@ -29,6 +29,12 @@ src: url(../../../assets/mojangles.ttf); } +
From 755eead9764c5b0c9140cf38847f5b04b0f7b9b8 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 21:51:12 +0300 Subject: [PATCH 087/865] correctly capture screenshots of cypress --- .eslintignore | 1 + .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintignore b/.eslintignore index bf3201cf..4c431739 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,3 +5,4 @@ rsbuild.config.ts generated dist public +**/*/rsbuildSharedConfig.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 29b2f2d5..e2117da3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: if: failure() with: name: cypress-images - path: cypress/integration/__image_snapshots__/ + path: cypress/screenshots/ - name: print current ref run: echo ${{ github.event.pull_request.base.ref }} - run: node scripts/outdatedGitPackages.mjs From 5a3fb6f2253fa54c227e5e06b84bdf4db85e489f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 22:06:14 +0300 Subject: [PATCH 088/865] disable Java integration test because of issues with downloading server --- cypress.config.ts | 1 + cypress/e2e/index.spec.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cypress.config.ts b/cypress.config.ts index f9bd9478..861931e3 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -3,6 +3,7 @@ import { defineConfig } from 'cypress' export default defineConfig({ video: false, chromeWebSecurity: false, + screenshotOnRunFailure: true, // Enable screenshots on test failures e2e: { // We've imported your old cypress plugins here. // You may want to clean this up later by importing these. diff --git a/cypress/e2e/index.spec.ts b/cypress/e2e/index.spec.ts index cb1b6880..fc67ad21 100644 --- a/cypress/e2e/index.spec.ts +++ b/cypress/e2e/index.spec.ts @@ -49,7 +49,7 @@ it('Joins to local flying-squid server', () => { testWorldLoad() }) -it('Joins to local latest Java vanilla server', () => { +it.skip('Joins to local latest Java vanilla server', () => { const version = supportedVersions.at(-1)! cy.task('startServer', [version, 25_590]).then(() => { visit('/?ip=localhost:25590&username=bot') From 16bb43c7d929a211b15b474f50b1316eece2e323 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 22:16:26 +0300 Subject: [PATCH 089/865] update current ref --- .github/workflows/ci.yml | 2 +- .github/workflows/merge-next.yml | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2117da3..d5d77dc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,6 @@ jobs: - name: print current ref run: echo ${{ github.event.pull_request.base.ref }} - run: node scripts/outdatedGitPackages.mjs - if: ${{ github.event.pull_request.base.ref == 'next' }} + if: ${{ github.event.pull_request.base.ref == 'release' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index 9bed1b3d..ee02789b 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -17,10 +17,11 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # Fetch all history so we can merge branches + ref: refs/pull/${{ github.event.issue.number }}/head - name: Fetch All Branches run: git fetch --all - - name: Checkout PR - run: git checkout ${{ github.event.issue.pull_request.head.ref }} + # - name: Checkout PR + # run: git checkout ${{ github.event.issue.pull_request.head.ref }} - name: Merge From Next run: git merge origin/next --strategy-option=theirs - name: Push Changes From d0b921a48e50fea48dd2046c23b802e69a3b71ff Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 22:39:51 +0300 Subject: [PATCH 090/865] revert update current ref --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5d77dc7..e2117da3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,6 @@ jobs: - name: print current ref run: echo ${{ github.event.pull_request.base.ref }} - run: node scripts/outdatedGitPackages.mjs - if: ${{ github.event.pull_request.base.ref == 'release' }} + if: ${{ github.event.pull_request.base.ref == 'next' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 33437823f3d408e2c4b1beece255e9a26a3ce102 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 22:40:41 +0300 Subject: [PATCH 091/865] disable outdated packages check for now --- .github/workflows/ci.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2117da3..2208e460 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,9 +36,7 @@ jobs: with: name: cypress-images path: cypress/screenshots/ - - name: print current ref - run: echo ${{ github.event.pull_request.base.ref }} - - run: node scripts/outdatedGitPackages.mjs - if: ${{ github.event.pull_request.base.ref == 'next' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # - run: node scripts/outdatedGitPackages.mjs + # if: ${{ github.event.pull_request.base.ref == 'release' }} + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 18a6f2c1f5a41bbf67d53436bbc9f5490862fc01 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 12 Sep 2024 04:32:37 +0300 Subject: [PATCH 092/865] fix: rare case where digging animation was not cancelled after actual dig cancel after respawn --- src/worldInteractions.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts index cef65b96..dd5f0aec 100644 --- a/src/worldInteractions.ts +++ b/src/worldInteractions.ts @@ -48,7 +48,7 @@ class WorldInteraction { breakTextures: THREE.Texture[] lastDigged: number lineMaterial: LineMaterial - debugStatus: string + debugDigStatus: string oneTimeInit () { const loader = new THREE.TextureLoader() @@ -129,11 +129,11 @@ class WorldInteraction { // 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.debugStatus = 'done' + this.debugDigStatus = 'done' }) bot.on('diggingAborted', (block) => { if (!this.cursorBlock?.position.equals(block.position)) return - this.debugStatus = 'aborted' + this.debugDigStatus = 'aborted' // if (this.lastDugBlock) this.breakStartTime = undefined if (this.buttons[0]) { @@ -326,7 +326,8 @@ class WorldInteraction { // We stopped breaking if ((!this.buttons[0] && this.lastButtons[0])) { this.lastDugBlock = null - this.debugStatus = 'cancelled' + this.breakStartTime = undefined + this.debugDigStatus = 'cancelled' } const onGround = bot.entity.onGround || bot.game.gameMode === 'creative' @@ -340,7 +341,7 @@ class WorldInteraction { && (!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.debugStatus = 'breaking' + 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)] From 7da41b02c95b61873fff8fd13f45712eb545c7aa Mon Sep 17 00:00:00 2001 From: Valery-a <83373303+Valery-a@users.noreply.github.com> Date: Thu, 12 Sep 2024 23:00:58 +0300 Subject: [PATCH 093/865] fix: fixed zombies and husks not having texture (#203) --- prismarine-viewer/viewer/lib/entities.ts | 3 +- .../viewer/lib/entity/EntityMesh.js | 10 + .../viewer/lib/entity/exportedModels.js | 1 + .../viewer/lib/entity/models/zombie.obj | 472 +++++++++--------- 4 files changed, 248 insertions(+), 238 deletions(-) diff --git a/prismarine-viewer/viewer/lib/entities.ts b/prismarine-viewer/viewer/lib/entities.ts index 281a442e..ee659263 100644 --- a/prismarine-viewer/viewer/lib/entities.ts +++ b/prismarine-viewer/viewer/lib/entities.ts @@ -340,9 +340,8 @@ export class Entities extends EventEmitter { } update (entity: import('prismarine-entity').Entity & { delete?; pos }, overrides) { - let isPlayerModel = entity.name === 'player' + const isPlayerModel = entity.name === 'player' if (entity.name === 'zombie' || entity.name === 'zombie_villager' || entity.name === 'husk') { - isPlayerModel = true overrides.texture = `textures/1.16.4/entity/${entity.name === 'zombie_villager' ? 'zombie_villager/zombie_villager.png' : `zombie/${entity.name}.png`}` } if (!this.entities[entity.id] && !entity.delete) { diff --git a/prismarine-viewer/viewer/lib/entity/EntityMesh.js b/prismarine-viewer/viewer/lib/entity/EntityMesh.js index 39b2d62d..1387d2cc 100644 --- a/prismarine-viewer/viewer/lib/entity/EntityMesh.js +++ b/prismarine-viewer/viewer/lib/entity/EntityMesh.js @@ -1,6 +1,7 @@ //@ts-check import * as THREE from 'three' import { OBJLoader } from 'three-stdlib' +import huskPng from 'mc-assets/dist/other-textures/latest/entity/zombie/husk.png' import entities from './entities.json' import { externalModels } from './objModels' import externalTexturesJson from './externalTextures.json' @@ -290,6 +291,10 @@ const getEntity = (name) => { // zombie_villager: 'zombie_villager/zombie_villager' // } +const scaleEntity = { + zombie: 1.9, + husk: 1.9 +} // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class EntityMesh { constructor(version, type, scene, /** @type {{textures?, rotation?: Record}} */overrides = {}) { @@ -303,6 +308,9 @@ export class EntityMesh { if (originalType === 'zombie_horse') { texturePath = `textures/${version}/entity/horse/horse_zombie.png` } + if (originalType === 'husk') { + texturePath = huskPng + } if (originalType === 'skeleton_horse') { texturePath = `textures/${version}/entity/horse/horse_skeleton.png` } @@ -325,6 +333,8 @@ export class EntityMesh { alphaTest: 0.1 }) const obj = objLoader.parse(externalModels[type]) + const scale = scaleEntity[originalType] + if (scale) obj.scale.set(scale, scale, scale) if (type === 'boat') obj.position.y = -1 // todo, should not be hardcoded obj.traverse((child) => { if (child instanceof THREE.Mesh) { diff --git a/prismarine-viewer/viewer/lib/entity/exportedModels.js b/prismarine-viewer/viewer/lib/entity/exportedModels.js index b43658ec..53d55d5e 100644 --- a/prismarine-viewer/viewer/lib/entity/exportedModels.js +++ b/prismarine-viewer/viewer/lib/entity/exportedModels.js @@ -35,4 +35,5 @@ export { default as witch } from './models/witch.obj' export { default as wolf } from './models/wolf.obj' export { default as zombie_villager } from './models/zombie_villager.obj' export { default as zombie } from './models/zombie.obj' +export { default as husk } from './models/zombie.obj' export { default as boat } from './models/boat.obj' diff --git a/prismarine-viewer/viewer/lib/entity/models/zombie.obj b/prismarine-viewer/viewer/lib/entity/models/zombie.obj index bcd7444c..42f1e722 100644 --- a/prismarine-viewer/viewer/lib/entity/models/zombie.obj +++ b/prismarine-viewer/viewer/lib/entity/models/zombie.obj @@ -1,322 +1,322 @@ -# Made in Blockbench 4.9.4 +# Made in Blockbench 4.10.4 mtllib materials.mtl -o Body -v 0.25 1.5 0.125 -v 0.25 1.5 -0.125 -v 0.25 0.75 0.125 -v 0.25 0.75 -0.125 -v -0.25 1.5 -0.125 -v -0.25 1.5 0.125 -v -0.25 0.75 -0.125 -v -0.25 0.75 0.125 -vt 0.3125 0.375 -vt 0.4375 0.375 -vt 0.4375 0 -vt 0.3125 0 -vt 0.25 0.375 -vt 0.3125 0.375 -vt 0.3125 0 -vt 0.25 0 -vt 0.5 0.375 -vt 0.625 0.375 -vt 0.625 0 -vt 0.5 0 -vt 0.4375 0.375 -vt 0.5 0.375 -vt 0.5 0 -vt 0.4375 0 -vt 0.4375 0.375 -vt 0.3125 0.375 -vt 0.3125 0.5 -vt 0.4375 0.5 -vt 0.5625 0.5 -vt 0.4375 0.5 -vt 0.4375 0.375 -vt 0.5625 0.375 +o /head +v 0.125 1 0.125 +v 0.125 1 -0.125 +v 0.125 0.75 0.125 +v 0.125 0.75 -0.125 +v -0.125 1 -0.125 +v -0.125 1 0.125 +v -0.125 0.75 -0.125 +v -0.125 0.75 0.125 +vt 0.125 0.875 +vt 0.25 0.875 +vt 0.25 0.75 +vt 0.125 0.75 +vt 0 0.875 +vt 0.125 0.875 +vt 0.125 0.75 +vt 0 0.75 +vt 0.375 0.875 +vt 0.5 0.875 +vt 0.5 0.75 +vt 0.375 0.75 +vt 0.25 0.875 +vt 0.375 0.875 +vt 0.375 0.75 +vt 0.25 0.75 +vt 0.25 0.875 +vt 0.125 0.875 +vt 0.125 1 +vt 0.25 1 +vt 0.25 1 +vt 0.375 1 +vt 0.375 0.875 +vt 0.25 0.875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 4/4/1 7/3/1 5/2/1 2/1/1 f 3/8/2 4/7/2 2/6/2 1/5/2 f 8/12/3 3/11/3 1/10/3 6/9/3 f 7/16/4 8/15/4 6/14/4 5/13/4 f 6/20/5 1/19/5 2/18/5 5/17/5 f 7/24/6 4/23/6 3/22/6 8/21/6 -o Head -v 0.25 2 0.25 -v 0.25 2 -0.25 -v 0.25 1.5 0.25 -v 0.25 1.5 -0.25 -v -0.25 2 -0.25 -v -0.25 2 0.25 -v -0.25 1.5 -0.25 -v -0.25 1.5 0.25 -vt 0.125 0.75 -vt 0.25 0.75 -vt 0.25 0.5 -vt 0.125 0.5 -vt 0 0.75 -vt 0.125 0.75 -vt 0.125 0.5 -vt 0 0.5 -vt 0.375 0.75 -vt 0.5 0.75 -vt 0.5 0.5 -vt 0.375 0.5 -vt 0.25 0.75 -vt 0.375 0.75 -vt 0.375 0.5 -vt 0.25 0.5 -vt 0.25 0.75 -vt 0.125 0.75 -vt 0.125 1 -vt 0.25 1 -vt 0.375 1 -vt 0.25 1 -vt 0.25 0.75 -vt 0.375 0.75 +o /right_arm +v -0.125 0.75 0.0625 +v -0.125 0.75 -0.0625 +v -0.125 0.375 0.0625 +v -0.125 0.375 -0.0625 +v -0.25 0.75 -0.0625 +v -0.25 0.75 0.0625 +v -0.25 0.375 -0.0625 +v -0.25 0.375 0.0625 +vt 0.6875 0.6875 +vt 0.75 0.6875 +vt 0.75 0.5 +vt 0.6875 0.5 +vt 0.625 0.6875 +vt 0.6875 0.6875 +vt 0.6875 0.5 +vt 0.625 0.5 +vt 0.8125 0.6875 +vt 0.875 0.6875 +vt 0.875 0.5 +vt 0.8125 0.5 +vt 0.75 0.6875 +vt 0.8125 0.6875 +vt 0.8125 0.5 +vt 0.75 0.5 +vt 0.75 0.6875 +vt 0.6875 0.6875 +vt 0.6875 0.75 +vt 0.75 0.75 +vt 0.75 0.75 +vt 0.8125 0.75 +vt 0.8125 0.6875 +vt 0.75 0.6875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 12/28/7 15/27/7 13/26/7 10/25/7 f 11/32/8 12/31/8 10/30/8 9/29/8 f 16/36/9 11/35/9 9/34/9 14/33/9 f 15/40/10 16/39/10 14/38/10 13/37/10 f 14/44/11 9/43/11 10/42/11 13/41/11 f 15/48/12 12/47/12 11/46/12 16/45/12 -o Hat Layer -v 0.28125 2.03125 0.28125 -v 0.28125 2.03125 -0.28125 -v 0.28125 1.46875 0.28125 -v 0.28125 1.46875 -0.28125 -v -0.28125 2.03125 -0.28125 -v -0.28125 2.03125 0.28125 -v -0.28125 1.46875 -0.28125 -v -0.28125 1.46875 0.28125 -vt 0.625 0.75 -vt 0.75 0.75 -vt 0.75 0.5 -vt 0.625 0.5 -vt 0.5 0.75 -vt 0.625 0.75 -vt 0.625 0.5 -vt 0.5 0.5 -vt 0.875 0.75 -vt 1 0.75 -vt 1 0.5 -vt 0.875 0.5 -vt 0.75 0.75 -vt 0.875 0.75 -vt 0.875 0.5 -vt 0.75 0.5 -vt 0.75 0.75 -vt 0.625 0.75 -vt 0.625 1 -vt 0.75 1 -vt 0.875 1 -vt 0.75 1 -vt 0.75 0.75 -vt 0.875 0.75 +o /left_leg +v 0.12187499999999996 0.375 0.0625 +v 0.12187499999999996 0.375 -0.0625 +v 0.12187499999999996 0 0.0625 +v 0.12187499999999996 0 -0.0625 +v -0.003124999999999989 0.375 -0.0625 +v -0.003124999999999989 0.375 0.0625 +v -0.003124999999999989 0 -0.0625 +v -0.003124999999999989 0 0.0625 +vt 0.0625 0.6875 +vt 0.125 0.6875 +vt 0.125 0.5 +vt 0.0625 0.5 +vt 0 0.6875 +vt 0.0625 0.6875 +vt 0.0625 0.5 +vt 0 0.5 +vt 0.1875 0.6875 +vt 0.25 0.6875 +vt 0.25 0.5 +vt 0.1875 0.5 +vt 0.125 0.6875 +vt 0.1875 0.6875 +vt 0.1875 0.5 +vt 0.125 0.5 +vt 0.125 0.6875 +vt 0.0625 0.6875 +vt 0.0625 0.75 +vt 0.125 0.75 +vt 0.125 0.75 +vt 0.1875 0.75 +vt 0.1875 0.6875 +vt 0.125 0.6875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 20/52/13 23/51/13 21/50/13 18/49/13 f 19/56/14 20/55/14 18/54/14 17/53/14 f 24/60/15 19/59/15 17/58/15 22/57/15 f 23/64/16 24/63/16 22/62/16 21/61/16 f 22/68/17 17/67/17 18/66/17 21/65/17 f 23/72/18 20/71/18 19/70/18 24/69/18 -o RightArm -v 0.5 1.5 0.125 -v 0.5 1.5 -0.125 -v 0.5 0.75 0.125 -v 0.5 0.75 -0.125 -v 0.25 1.5 -0.125 -v 0.25 1.5 0.125 -v 0.25 0.75 -0.125 -v 0.25 0.75 0.125 -vt 0.6875 0.375 -vt 0.75 0.375 -vt 0.75 0 -vt 0.6875 0 -vt 0.625 0.375 -vt 0.6875 0.375 -vt 0.6875 0 -vt 0.625 0 -vt 0.8125 0.375 -vt 0.875 0.375 -vt 0.875 0 -vt 0.8125 0 -vt 0.75 0.375 -vt 0.8125 0.375 -vt 0.8125 0 -vt 0.75 0 -vt 0.75 0.375 -vt 0.6875 0.375 -vt 0.6875 0.5 +o /left_arm +v 0.25 0.75 0.0625 +v 0.25 0.75 -0.0625 +v 0.25 0.375 0.0625 +v 0.25 0.375 -0.0625 +v 0.125 0.75 -0.0625 +v 0.125 0.75 0.0625 +v 0.125 0.375 -0.0625 +v 0.125 0.375 0.0625 +vt 0.6875 0.6875 +vt 0.75 0.6875 vt 0.75 0.5 +vt 0.6875 0.5 +vt 0.625 0.6875 +vt 0.6875 0.6875 +vt 0.6875 0.5 +vt 0.625 0.5 +vt 0.8125 0.6875 +vt 0.875 0.6875 +vt 0.875 0.5 +vt 0.8125 0.5 +vt 0.75 0.6875 +vt 0.8125 0.6875 vt 0.8125 0.5 vt 0.75 0.5 -vt 0.75 0.375 -vt 0.8125 0.375 +vt 0.75 0.6875 +vt 0.6875 0.6875 +vt 0.6875 0.75 +vt 0.75 0.75 +vt 0.75 0.75 +vt 0.8125 0.75 +vt 0.8125 0.6875 +vt 0.75 0.6875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 28/76/19 31/75/19 29/74/19 26/73/19 f 27/80/20 28/79/20 26/78/20 25/77/20 f 32/84/21 27/83/21 25/82/21 30/81/21 f 31/88/22 32/87/22 30/86/22 29/85/22 f 30/92/23 25/91/23 26/90/23 29/89/23 f 31/96/24 28/95/24 27/94/24 32/93/24 -o LeftArm -v -0.25 1.5 0.125 -v -0.25 1.5 -0.125 -v -0.25 0.75 0.125 -v -0.25 0.75 -0.125 -v -0.5 1.5 -0.125 -v -0.5 1.5 0.125 -v -0.5 0.75 -0.125 -v -0.5 0.75 0.125 -vt 0.75 0.375 -vt 0.6875 0.375 -vt 0.6875 0 -vt 0.75 0 -vt 0.8125 0.375 -vt 0.75 0.375 -vt 0.75 0 -vt 0.8125 0 -vt 0.875 0.375 -vt 0.8125 0.375 -vt 0.8125 0 -vt 0.875 0 -vt 0.6875 0.375 -vt 0.625 0.375 -vt 0.625 0 -vt 0.6875 0 -vt 0.6875 0.375 -vt 0.75 0.375 -vt 0.75 0.5 -vt 0.6875 0.5 -vt 0.75 0.5 -vt 0.8125 0.5 -vt 0.8125 0.375 -vt 0.75 0.375 +o /right_leg +v 0.0031250000000000444 0.375 0.0625 +v 0.0031250000000000444 0.375 -0.0625 +v 0.0031250000000000444 0 0.0625 +v 0.0031250000000000444 0 -0.0625 +v -0.12187500000000001 0.375 -0.0625 +v -0.12187500000000001 0.375 0.0625 +v -0.12187500000000001 0 -0.0625 +v -0.12187500000000001 0 0.0625 +vt 0.0625 0.6875 +vt 0.125 0.6875 +vt 0.125 0.5 +vt 0.0625 0.5 +vt 0 0.6875 +vt 0.0625 0.6875 +vt 0.0625 0.5 +vt 0 0.5 +vt 0.1875 0.6875 +vt 0.25 0.6875 +vt 0.25 0.5 +vt 0.1875 0.5 +vt 0.125 0.6875 +vt 0.1875 0.6875 +vt 0.1875 0.5 +vt 0.125 0.5 +vt 0.125 0.6875 +vt 0.0625 0.6875 +vt 0.0625 0.75 +vt 0.125 0.75 +vt 0.125 0.75 +vt 0.1875 0.75 +vt 0.1875 0.6875 +vt 0.125 0.6875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 36/100/25 39/99/25 37/98/25 34/97/25 f 35/104/26 36/103/26 34/102/26 33/101/26 f 40/108/27 35/107/27 33/106/27 38/105/27 f 39/112/28 40/111/28 38/110/28 37/109/28 f 38/116/29 33/115/29 34/114/29 37/113/29 f 39/120/30 36/119/30 35/118/30 40/117/30 -o RightLeg -v 0.24375000000000002 0.75 0.125 -v 0.24375000000000002 0.75 -0.125 -v 0.24375000000000002 0 0.125 -v 0.24375000000000002 0 -0.125 -v -0.006249999999999978 0.75 -0.125 -v -0.006249999999999978 0.75 0.125 -v -0.006249999999999978 0 -0.125 -v -0.006249999999999978 0 0.125 -vt 0.0625 0.375 -vt 0.125 0.375 -vt 0.125 0 -vt 0.0625 0 -vt 0 0.375 -vt 0.0625 0.375 -vt 0.0625 0 -vt 0 0 -vt 0.1875 0.375 -vt 0.25 0.375 -vt 0.25 0 -vt 0.1875 0 -vt 0.125 0.375 -vt 0.1875 0.375 -vt 0.1875 0 -vt 0.125 0 -vt 0.125 0.375 -vt 0.0625 0.375 -vt 0.0625 0.5 -vt 0.125 0.5 -vt 0.1875 0.5 -vt 0.125 0.5 -vt 0.125 0.375 -vt 0.1875 0.375 +o /hat +v 0.140625 1.015625 0.140625 +v 0.140625 1.015625 -0.140625 +v 0.140625 0.734375 0.140625 +v 0.140625 0.734375 -0.140625 +v -0.140625 1.015625 -0.140625 +v -0.140625 1.015625 0.140625 +v -0.140625 0.734375 -0.140625 +v -0.140625 0.734375 0.140625 +vt 0.625 0.875 +vt 0.75 0.875 +vt 0.75 0.75 +vt 0.625 0.75 +vt 0.5 0.875 +vt 0.625 0.875 +vt 0.625 0.75 +vt 0.5 0.75 +vt 0.875 0.875 +vt 1 0.875 +vt 1 0.75 +vt 0.875 0.75 +vt 0.75 0.875 +vt 0.875 0.875 +vt 0.875 0.75 +vt 0.75 0.75 +vt 0.75 0.875 +vt 0.625 0.875 +vt 0.625 1 +vt 0.75 1 +vt 0.75 1 +vt 0.875 1 +vt 0.875 0.875 +vt 0.75 0.875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 44/124/31 47/123/31 45/122/31 42/121/31 f 43/128/32 44/127/32 42/126/32 41/125/32 f 48/132/33 43/131/33 41/130/33 46/129/33 f 47/136/34 48/135/34 46/134/34 45/133/34 f 46/140/35 41/139/35 42/138/35 45/137/35 f 47/144/36 44/143/36 43/142/36 48/141/36 -o LeftLeg -v 0.006249999999999978 0.75 0.125 -v 0.006249999999999978 0.75 -0.125 -v 0.006249999999999978 0 0.125 -v 0.006249999999999978 0 -0.125 -v -0.24375000000000002 0.75 -0.125 -v -0.24375000000000002 0.75 0.125 -v -0.24375000000000002 0 -0.125 -v -0.24375000000000002 0 0.125 -vt 0.125 0.375 -vt 0.0625 0.375 -vt 0.0625 0 -vt 0.125 0 -vt 0.1875 0.375 -vt 0.125 0.375 -vt 0.125 0 -vt 0.1875 0 -vt 0.25 0.375 -vt 0.1875 0.375 -vt 0.1875 0 -vt 0.25 0 -vt 0.0625 0.375 -vt 0 0.375 -vt 0 0 -vt 0.0625 0 -vt 0.0625 0.375 -vt 0.125 0.375 -vt 0.125 0.5 -vt 0.0625 0.5 -vt 0.125 0.5 -vt 0.1875 0.5 -vt 0.1875 0.375 -vt 0.125 0.375 +o /body +v 0.125 0.75 0.0625 +v 0.125 0.75 -0.0625 +v 0.125 0.375 0.0625 +v 0.125 0.375 -0.0625 +v -0.125 0.75 -0.0625 +v -0.125 0.75 0.0625 +v -0.125 0.375 -0.0625 +v -0.125 0.375 0.0625 +vt 0.3125 0.6875 +vt 0.4375 0.6875 +vt 0.4375 0.5 +vt 0.3125 0.5 +vt 0.25 0.6875 +vt 0.3125 0.6875 +vt 0.3125 0.5 +vt 0.25 0.5 +vt 0.5 0.6875 +vt 0.625 0.6875 +vt 0.625 0.5 +vt 0.5 0.5 +vt 0.4375 0.6875 +vt 0.5 0.6875 +vt 0.5 0.5 +vt 0.4375 0.5 +vt 0.4375 0.6875 +vt 0.3125 0.6875 +vt 0.3125 0.75 +vt 0.4375 0.75 +vt 0.4375 0.75 +vt 0.5625 0.75 +vt 0.5625 0.6875 +vt 0.4375 0.6875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 52/148/37 55/147/37 53/146/37 50/145/37 f 51/152/38 52/151/38 50/150/38 49/149/38 f 56/156/39 51/155/39 49/154/39 54/153/39 From 9bac681c293da03e364a6c49eab7fed92ed1fbf7 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 12 Sep 2024 23:38:45 +0300 Subject: [PATCH 094/865] use correct zombie model --- .../viewer/lib/entity/EntityMesh.js | 14 +- .../viewer/lib/entity/models/zombie.obj | 174 +++++++++--------- 2 files changed, 98 insertions(+), 90 deletions(-) diff --git a/prismarine-viewer/viewer/lib/entity/EntityMesh.js b/prismarine-viewer/viewer/lib/entity/EntityMesh.js index 1387d2cc..a518171f 100644 --- a/prismarine-viewer/viewer/lib/entity/EntityMesh.js +++ b/prismarine-viewer/viewer/lib/entity/EntityMesh.js @@ -2,6 +2,7 @@ import * as THREE from 'three' import { OBJLoader } from 'three-stdlib' import huskPng from 'mc-assets/dist/other-textures/latest/entity/zombie/husk.png' +import { Vec3 } from 'vec3' import entities from './entities.json' import { externalModels } from './objModels' import externalTexturesJson from './externalTextures.json' @@ -292,9 +293,15 @@ const getEntity = (name) => { // } const scaleEntity = { - zombie: 1.9, - husk: 1.9 + zombie: 1.85, + husk: 1.85 } +const offsetEntity = { + zombie: new Vec3(0, 1, 0), + husk: new Vec3(0, 1, 0), + boat: new Vec3(0, -1, 0), +} + // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class EntityMesh { constructor(version, type, scene, /** @type {{textures?, rotation?: Record}} */overrides = {}) { @@ -335,7 +342,8 @@ export class EntityMesh { const obj = objLoader.parse(externalModels[type]) const scale = scaleEntity[originalType] if (scale) obj.scale.set(scale, scale, scale) - if (type === 'boat') obj.position.y = -1 // todo, should not be hardcoded + const offset = offsetEntity[originalType] + if (offset) obj.position.set(offset.x, offset.y, offset.z) obj.traverse((child) => { if (child instanceof THREE.Mesh) { child.material = material diff --git a/prismarine-viewer/viewer/lib/entity/models/zombie.obj b/prismarine-viewer/viewer/lib/entity/models/zombie.obj index 42f1e722..fa5d8f4d 100644 --- a/prismarine-viewer/viewer/lib/entity/models/zombie.obj +++ b/prismarine-viewer/viewer/lib/entity/models/zombie.obj @@ -2,14 +2,14 @@ mtllib materials.mtl o /head -v 0.125 1 0.125 -v 0.125 1 -0.125 -v 0.125 0.75 0.125 -v 0.125 0.75 -0.125 -v -0.125 1 -0.125 -v -0.125 1 0.125 -v -0.125 0.75 -0.125 -v -0.125 0.75 0.125 +v 0.125 0.5 0.125 +v 0.125 0.5 -0.125 +v 0.125 0.25 0.125 +v 0.125 0.25 -0.125 +v -0.125 0.5 -0.125 +v -0.125 0.5 0.125 +v -0.125 0.25 -0.125 +v -0.125 0.25 0.125 vt 0.125 0.875 vt 0.25 0.875 vt 0.25 0.75 @@ -48,38 +48,38 @@ f 7/16/4 8/15/4 6/14/4 5/13/4 f 6/20/5 1/19/5 2/18/5 5/17/5 f 7/24/6 4/23/6 3/22/6 8/21/6 o /right_arm -v -0.125 0.75 0.0625 -v -0.125 0.75 -0.0625 -v -0.125 0.375 0.0625 -v -0.125 0.375 -0.0625 -v -0.25 0.75 -0.0625 -v -0.25 0.75 0.0625 -v -0.25 0.375 -0.0625 -v -0.25 0.375 0.0625 -vt 0.6875 0.6875 +v -0.125 0.25 0.0625 +v -0.125 0.25 -0.3125 +v -0.125 0.125 0.0625 +v -0.125 0.125 -0.3125 +v -0.25 0.25 -0.3125 +v -0.25 0.25 0.0625 +v -0.25 0.125 -0.3125 +v -0.25 0.125 0.0625 +vt 0.8125 0.6875 vt 0.75 0.6875 -vt 0.75 0.5 -vt 0.6875 0.5 -vt 0.625 0.6875 +vt 0.75 0.75 +vt 0.8125 0.75 vt 0.6875 0.6875 vt 0.6875 0.5 vt 0.625 0.5 -vt 0.8125 0.6875 -vt 0.875 0.6875 -vt 0.875 0.5 -vt 0.8125 0.5 -vt 0.75 0.6875 -vt 0.8125 0.6875 -vt 0.8125 0.5 -vt 0.75 0.5 +vt 0.625 0.6875 vt 0.75 0.6875 vt 0.6875 0.6875 vt 0.6875 0.75 vt 0.75 0.75 -vt 0.75 0.75 -vt 0.8125 0.75 -vt 0.8125 0.6875 +vt 0.75 0.5 vt 0.75 0.6875 +vt 0.8125 0.6875 +vt 0.8125 0.5 +vt 0.75 0.5 +vt 0.6875 0.5 +vt 0.6875 0.6875 +vt 0.75 0.6875 +vt 0.8125 0.6875 +vt 0.875 0.6875 +vt 0.875 0.5 +vt 0.8125 0.5 vn 0 0 -1 vn 1 0 0 vn 0 0 1 @@ -94,14 +94,14 @@ f 15/40/10 16/39/10 14/38/10 13/37/10 f 14/44/11 9/43/11 10/42/11 13/41/11 f 15/48/12 12/47/12 11/46/12 16/45/12 o /left_leg -v 0.12187499999999996 0.375 0.0625 -v 0.12187499999999996 0.375 -0.0625 -v 0.12187499999999996 0 0.0625 -v 0.12187499999999996 0 -0.0625 -v -0.003124999999999989 0.375 -0.0625 -v -0.003124999999999989 0.375 0.0625 -v -0.003124999999999989 0 -0.0625 -v -0.003124999999999989 0 0.0625 +v 0.12187499999999996 -0.125 0.0625 +v 0.12187499999999996 -0.125 -0.0625 +v 0.12187499999999996 -0.5 0.0625 +v 0.12187499999999996 -0.5 -0.0625 +v -0.0031250000000000444 -0.125 -0.0625 +v -0.0031250000000000444 -0.125 0.0625 +v -0.0031250000000000444 -0.5 -0.0625 +v -0.0031250000000000444 -0.5 0.0625 vt 0.0625 0.6875 vt 0.125 0.6875 vt 0.125 0.5 @@ -140,38 +140,38 @@ f 23/64/16 24/63/16 22/62/16 21/61/16 f 22/68/17 17/67/17 18/66/17 21/65/17 f 23/72/18 20/71/18 19/70/18 24/69/18 o /left_arm -v 0.25 0.75 0.0625 -v 0.25 0.75 -0.0625 -v 0.25 0.375 0.0625 -v 0.25 0.375 -0.0625 -v 0.125 0.75 -0.0625 -v 0.125 0.75 0.0625 -v 0.125 0.375 -0.0625 -v 0.125 0.375 0.0625 -vt 0.6875 0.6875 +v 0.25 0.25 0.0625 +v 0.25 0.25 -0.3125 +v 0.25 0.125 0.0625 +v 0.25 0.125 -0.3125 +v 0.125 0.25 -0.3125 +v 0.125 0.25 0.0625 +v 0.125 0.125 -0.3125 +v 0.125 0.125 0.0625 +vt 0.8125 0.6875 vt 0.75 0.6875 -vt 0.75 0.5 -vt 0.6875 0.5 -vt 0.625 0.6875 +vt 0.75 0.75 +vt 0.8125 0.75 vt 0.6875 0.6875 vt 0.6875 0.5 vt 0.625 0.5 -vt 0.8125 0.6875 -vt 0.875 0.6875 -vt 0.875 0.5 -vt 0.8125 0.5 -vt 0.75 0.6875 -vt 0.8125 0.6875 -vt 0.8125 0.5 -vt 0.75 0.5 +vt 0.625 0.6875 vt 0.75 0.6875 vt 0.6875 0.6875 vt 0.6875 0.75 vt 0.75 0.75 -vt 0.75 0.75 -vt 0.8125 0.75 -vt 0.8125 0.6875 +vt 0.75 0.5 vt 0.75 0.6875 +vt 0.8125 0.6875 +vt 0.8125 0.5 +vt 0.75 0.5 +vt 0.6875 0.5 +vt 0.6875 0.6875 +vt 0.75 0.6875 +vt 0.8125 0.6875 +vt 0.875 0.6875 +vt 0.875 0.5 +vt 0.8125 0.5 vn 0 0 -1 vn 1 0 0 vn 0 0 1 @@ -186,14 +186,14 @@ f 31/88/22 32/87/22 30/86/22 29/85/22 f 30/92/23 25/91/23 26/90/23 29/89/23 f 31/96/24 28/95/24 27/94/24 32/93/24 o /right_leg -v 0.0031250000000000444 0.375 0.0625 -v 0.0031250000000000444 0.375 -0.0625 -v 0.0031250000000000444 0 0.0625 -v 0.0031250000000000444 0 -0.0625 -v -0.12187500000000001 0.375 -0.0625 -v -0.12187500000000001 0.375 0.0625 -v -0.12187500000000001 0 -0.0625 -v -0.12187500000000001 0 0.0625 +v 0.0031250000000000444 -0.125 0.0625 +v 0.0031250000000000444 -0.125 -0.0625 +v 0.0031250000000000444 -0.5 0.0625 +v 0.0031250000000000444 -0.5 -0.0625 +v -0.12187499999999996 -0.125 -0.0625 +v -0.12187499999999996 -0.125 0.0625 +v -0.12187499999999996 -0.5 -0.0625 +v -0.12187499999999996 -0.5 0.0625 vt 0.0625 0.6875 vt 0.125 0.6875 vt 0.125 0.5 @@ -232,14 +232,14 @@ f 39/112/28 40/111/28 38/110/28 37/109/28 f 38/116/29 33/115/29 34/114/29 37/113/29 f 39/120/30 36/119/30 35/118/30 40/117/30 o /hat -v 0.140625 1.015625 0.140625 -v 0.140625 1.015625 -0.140625 -v 0.140625 0.734375 0.140625 -v 0.140625 0.734375 -0.140625 -v -0.140625 1.015625 -0.140625 -v -0.140625 1.015625 0.140625 -v -0.140625 0.734375 -0.140625 -v -0.140625 0.734375 0.140625 +v 0.140625 0.515625 0.140625 +v 0.140625 0.515625 -0.140625 +v 0.140625 0.234375 0.140625 +v 0.140625 0.234375 -0.140625 +v -0.140625 0.515625 -0.140625 +v -0.140625 0.515625 0.140625 +v -0.140625 0.234375 -0.140625 +v -0.140625 0.234375 0.140625 vt 0.625 0.875 vt 0.75 0.875 vt 0.75 0.75 @@ -278,14 +278,14 @@ f 47/136/34 48/135/34 46/134/34 45/133/34 f 46/140/35 41/139/35 42/138/35 45/137/35 f 47/144/36 44/143/36 43/142/36 48/141/36 o /body -v 0.125 0.75 0.0625 -v 0.125 0.75 -0.0625 -v 0.125 0.375 0.0625 -v 0.125 0.375 -0.0625 -v -0.125 0.75 -0.0625 -v -0.125 0.75 0.0625 -v -0.125 0.375 -0.0625 -v -0.125 0.375 0.0625 +v 0.125 0.25 0.0625 +v 0.125 0.25 -0.0625 +v 0.125 -0.125 0.0625 +v 0.125 -0.125 -0.0625 +v -0.125 0.25 -0.0625 +v -0.125 0.25 0.0625 +v -0.125 -0.125 -0.0625 +v -0.125 -0.125 0.0625 vt 0.3125 0.6875 vt 0.4375 0.6875 vt 0.4375 0.5 @@ -322,4 +322,4 @@ f 51/152/38 52/151/38 50/150/38 49/149/38 f 56/156/39 51/155/39 49/154/39 54/153/39 f 55/160/40 56/159/40 54/158/40 53/157/40 f 54/164/41 49/163/41 50/162/41 53/161/41 -f 55/168/42 52/167/42 51/166/42 56/165/42 \ No newline at end of file +f 55/168/42 52/167/42 51/166/42 56/165/42 From 3ea95d509ac436236dc9113292396b1791512dbf Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 19 Sep 2024 02:34:45 +0300 Subject: [PATCH 095/865] fix: fix github pages main deploy --- prismarine-viewer/rsbuild.config.ts | 1 - prismarine-viewer/rsbuildSharedConfig.ts | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prismarine-viewer/rsbuild.config.ts b/prismarine-viewer/rsbuild.config.ts index fa666d99..cea001db 100644 --- a/prismarine-viewer/rsbuild.config.ts +++ b/prismarine-viewer/rsbuild.config.ts @@ -24,7 +24,6 @@ export default mergeRsbuildConfig( distPath: { root: join(__dirname, './dist'), }, - assetPrefix: './', }, server: { port: 9090, diff --git a/prismarine-viewer/rsbuildSharedConfig.ts b/prismarine-viewer/rsbuildSharedConfig.ts index 3fba1ef1..0c248f6c 100644 --- a/prismarine-viewer/rsbuildSharedConfig.ts +++ b/prismarine-viewer/rsbuildSharedConfig.ts @@ -17,7 +17,8 @@ export const appAndRendererSharedConfig = () => defineConfig({ output: { polyfill: 'usage', // 50kb limit for data uri - dataUriLimit: 50 * 1024 + dataUriLimit: 50 * 1024, + assetPrefix: './', }, source: { alias: { From 40f81d84cd7c337bd661b15e74358a7c80c6bf59 Mon Sep 17 00:00:00 2001 From: Valery-a <83373303+Valery-a@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:43:47 -0700 Subject: [PATCH 096/865] server change (#207) --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index e4f86060..33095707 100644 --- a/config.json +++ b/config.json @@ -10,9 +10,9 @@ "description": "Chaos and destruction server. Free for everyone." }, { - "ip": "go.mineberry.org", + "ip": "play.applemc.fun", "version": "1.18.2", - "description": "One of the best servers here. Join now!" + "description": "Very nice server. Try it now!" }, { "ip": "sus.shhnowisnottheti.me", From 00150dda1ddb9cb27426e68cebdc0f4b44529554 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 28 Sep 2024 02:57:18 +0300 Subject: [PATCH 097/865] fix: inventory UI crash in some cases with some specific window titles fix: client messages were not displayed on the latest version --- src/botUtils.ts | 20 ++++++++++++++++++++ src/builtinCommands.ts | 5 ++--- src/inventoryWindows.ts | 23 ++++++++++++++++------- src/worldInteractions.ts | 7 ++----- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/botUtils.ts b/src/botUtils.ts index 79b10118..e98d1e84 100644 --- a/src/botUtils.ts +++ b/src/botUtils.ts @@ -2,6 +2,7 @@ import { fromFormattedString, TextComponent } from '@xmcl/text-component' import type { IndexedData } from 'minecraft-data' +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' export type MessageFormatPart = Pick & { text: string @@ -120,3 +121,22 @@ export const getItemFromBlock = (block: import('prismarine-block').Block) => { const item = global.loadedData.itemsByName[blockToItemRemaps[block.name] ?? block.name] return item } + +export const displayClientChat = (text: string) => { + const message = { + text + } + if (versionToNumber(bot.version) >= versionToNumber('1.19')) { + bot._client.emit('systemChat', { + formattedMessage: JSON.stringify(message), + position: 0, + sender: 'minecraft:chat' + }) + return + } + bot._client.write('chat', { + message: JSON.stringify(message), + position: 0, + sender: 'minecraft:chat' + }) +} diff --git a/src/builtinCommands.ts b/src/builtinCommands.ts index 21b06ca6..ede5480e 100644 --- a/src/builtinCommands.ts +++ b/src/builtinCommands.ts @@ -6,6 +6,7 @@ import { closeWan, openToWanAndCopyJoinLink } from './localServerMultiplayer' import { copyFilesAsync, uniqueFileNameFromWorldName } from './browserfs' import { saveServer } from './flyingSquidUtils' import { setLoadingScreenStatus } from './utils' +import { displayClientChat } from './botUtils' const notImplemented = () => { return 'Not implemented yet' @@ -75,9 +76,7 @@ const exportLoadedWorld = async () => { window.exportWorld = exportLoadedWorld const writeText = (text) => { - bot._client.emit('chat', { - message: JSON.stringify({ text }) - }) + displayClientChat(text) } const commands: Array<{ diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 23e898a5..0665b7a6 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -11,12 +11,13 @@ import PItem, { Item } from 'prismarine-item' import { ItemsRenderer } from 'mc-assets/dist/itemsRenderer' import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' import { getRenamedData } from 'flying-squid/dist/blockRenames' +import PrismarineChatLoader from 'prismarine-chat' import Generic95 from '../assets/generic_95.png' import { appReplacableResources } from './generated/resources' import { activeModalStack, hideCurrentModal, hideModal, miscUiState, showModal } from './globalState' import { options } from './optionsStorage' import { assertDefined, inGameError } from './utils' -import { MessageFormatPart } from './botUtils' +import { displayClientChat, MessageFormatPart } from './botUtils' import { currentScaling } from './scaleInterface' import { getItemDescription } from './itemsDescriptions' @@ -59,11 +60,7 @@ export const onGameLoad = (onLoad) => { openWindow('ChestWin') } else { // todo format - bot._client.emit('chat', { - message: JSON.stringify({ - text: `[client error] cannot open unimplemented window ${win.id} (${win.type}). Slots: ${win.slots.map(item => getItemName(item)).filter(Boolean).join(', ')}` - }) - }) + displayClientChat(`[client error] cannot open unimplemented window ${win.id} (${win.type}). Slots: ${win.slots.map(item => getItemName(item)).filter(Boolean).join(', ')}`) bot.currentWindow?.['close']() } }) @@ -288,6 +285,7 @@ const implementedContainersGuiMap = { 'minecraft:furnace': 'FurnaceWin', 'minecraft:smoker': 'FurnaceWin', 'minecraft:crafting': 'CraftingWin', + 'minecraft:crafting3x3': 'CraftingWin', // todo different result slot 'minecraft:anvil': 'AnvilWin', // enchant 'minecraft:enchanting_table': 'EnchantingWin', @@ -365,7 +363,18 @@ const openWindow = (type: string | undefined) => { cleanLoadedImagesCache() const inv = openItemsCanvas(type) inv.canvasManager.children[0].mobileHelpers = miscUiState.currentTouch - inv.canvasManager.children[0].customTitleText = bot.currentWindow?.title ? fromFormattedString(bot.currentWindow.title).text : undefined + const title = bot.currentWindow?.title + const PrismarineChat = PrismarineChatLoader(bot.version) + try { + inv.canvasManager.children[0].customTitleText = title ? + typeof title === 'string' ? + fromFormattedString(title).text : + new PrismarineChat(title).toString() : + undefined + } catch (err) { + reportError?.(err) + inv.canvasManager.children[0].customTitleText = undefined + } // todo inv.canvasManager.setScale(currentScaling.scale === 1 ? 1.5 : currentScaling.scale) inv.canvas.style.zIndex = '10' diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts index dd5f0aec..dcd5dc3d 100644 --- a/src/worldInteractions.ts +++ b/src/worldInteractions.ts @@ -22,6 +22,7 @@ import { assertDefined } from './utils' import { options } from './optionsStorage' import { itemBeingUsed } from './react/Crosshair' import { isCypress } from './standaloneUtils' +import { displayClientChat } from './botUtils' function getViewDirection (pitch, yaw) { const csPitch = Math.cos(pitch) @@ -263,11 +264,7 @@ class WorldInteraction { hideCurrentModal() } // if (e.message === 'bot is not sleeping') return - bot._client.emit('chat', { - message: JSON.stringify({ - text: e.message, - }) - }) + displayClientChat(e.message) }) setTimeout(() => { cancelSleep = false From 2953554c5302dce09922352cc48c3fb3cdbd3772 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 28 Sep 2024 03:28:27 +0300 Subject: [PATCH 098/865] fix(regression): player walking animation was broken --- package.json | 2 +- pnpm-lock.yaml | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 4b41660a..f8bd1557 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@floating-ui/react": "^0.26.1", "@mui/base": "5.0.0-beta.40", "@nxg-org/mineflayer-auto-jump": "^0.7.12", - "@nxg-org/mineflayer-tracker": "^1.2.3", + "@nxg-org/mineflayer-tracker": "1.2.1", "@react-oauth/google": "^0.12.1", "@stylistic/eslint-plugin": "^2.6.1", "@types/gapi": "^0.0.47", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05b83f3c..6aca0d12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,8 +48,8 @@ importers: specifier: ^0.7.12 version: 0.7.12 '@nxg-org/mineflayer-tracker': - specifier: ^1.2.3 - version: 1.2.3 + specifier: 1.2.1 + version: 1.2.1 '@react-oauth/google': specifier: ^0.12.1 version: 0.12.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -2116,8 +2116,8 @@ packages: '@nxg-org/mineflayer-physics-util@1.5.8': resolution: {integrity: sha512-KmCkAqpUo8BbuRdIBs6+V2hWHehz++PRz3lRwIsb47CuG0u4sgLYh37RY3ifAznC6uWvmPK+q3B4ZXwJzPy1MQ==} - '@nxg-org/mineflayer-tracker@1.2.3': - resolution: {integrity: sha512-E7Ik/scU117Rr6kQUHHMBk8qOGh63YlTCGN33jMfeP7L8xmLeSHN3JtV/fbog8Y+R+HgO99yfZiRAaV7z1T6gQ==} + '@nxg-org/mineflayer-tracker@1.2.1': + resolution: {integrity: sha512-SI1ffF8zvg3/ZNE021Ja2W0FZPN+WbQDZf8yFqOcXtPRXAtM9W6HvoACdzXep8BZid7WYgYLIgjKpB+9RqvCNQ==} '@nxg-org/mineflayer-trajectories@1.1.1': resolution: {integrity: sha512-X103KXlX8+L3uMeK4jQxMUdTizv01sQRSfBizAF/iOAdfQZehRLXr3CYKeJzfwPYGLN0X0JCl++cMEcZVn4vbg==} @@ -5372,7 +5372,7 @@ packages: resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} engines: {node: '>= 4.0'} os: [darwin] - deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2 + deprecated: Upgrade to fsevents v2 to mitigate potential security issues fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -7747,6 +7747,7 @@ packages: range@0.0.3: resolution: {integrity: sha512-OxK2nY2bmeEB4NxoBraQIBOOeOIxoBvm6yt8MA1kLappgkG3SyLf173iOtT5woWycrtESDD2g0Nl2yt8YPoUnw==} engines: {node: '>=0.8'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. raw-body@2.5.1: resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} @@ -8217,6 +8218,10 @@ packages: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + sentence-case@3.0.4: resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} @@ -11523,7 +11528,7 @@ snapshots: dependencies: '@nxg-org/mineflayer-util-plugin': 1.8.3 - '@nxg-org/mineflayer-tracker@1.2.3': + '@nxg-org/mineflayer-tracker@1.2.1': dependencies: '@nxg-org/mineflayer-trajectories': 1.1.1 '@nxg-org/mineflayer-util-plugin': 1.8.3 @@ -17188,7 +17193,7 @@ snapshots: object-assign: 4.1.1 opn: 6.0.0 proxy-middleware: 0.15.0 - send: 0.18.0 + send: 0.19.0 serve-index: 1.9.1 transitivePeerDependencies: - supports-color @@ -19502,6 +19507,24 @@ snapshots: transitivePeerDependencies: - supports-color + send@0.19.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + sentence-case@3.0.4: dependencies: no-case: 3.0.4 From ab5f6ab448377cf9cf01ee0b000dc53e83e68578 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 1 Oct 2024 01:34:42 +0300 Subject: [PATCH 099/865] fix: add fallback peerjs discovery server to bypass geo restrictions and because sometimes official server is down feat: allow to use custom peerjs server via config --- config.json | 2 + prismarine-viewer/viewer/lib/entities.ts | 1 + src/connect.ts | 1 + src/entities.ts | 7 +- src/globalState.ts | 3 + src/index.ts | 11 ++- src/localServerMultiplayer.ts | 86 +++++++++++++++++++++--- src/react/PauseScreen.tsx | 9 ++- 8 files changed, 101 insertions(+), 19 deletions(-) diff --git a/config.json b/config.json index 33095707..7813b591 100644 --- a/config.json +++ b/config.json @@ -3,6 +3,8 @@ "defaultHost": "", "defaultProxy": "proxy.mcraft.fun", "mapsProvider": "https://maps.mcraft.fun/", + "peerJsServer": "", + "peerJsServerFallback": "https://p2p.mcraft.fun", "promoteServers": [ { "ip": "kaboom.pw", diff --git a/prismarine-viewer/viewer/lib/entities.ts b/prismarine-viewer/viewer/lib/entities.ts index ee659263..c7476f6c 100644 --- a/prismarine-viewer/viewer/lib/entities.ts +++ b/prismarine-viewer/viewer/lib/entities.ts @@ -340,6 +340,7 @@ export class Entities extends EventEmitter { } update (entity: import('prismarine-entity').Entity & { delete?; pos }, overrides) { + console.log('entity', entity) const isPlayerModel = entity.name === 'player' if (entity.name === 'zombie' || entity.name === 'zombie_villager' || entity.name === 'husk') { overrides.texture = `textures/1.16.4/entity/${entity.name === 'zombie_villager' ? 'zombie_villager/zombie_villager.png' : `zombie/${entity.name}.png`}` diff --git a/src/connect.ts b/src/connect.ts index 12a1fc5b..40a47669 100644 --- a/src/connect.ts +++ b/src/connect.ts @@ -15,4 +15,5 @@ export type ConnectOptions = { serverIndex?: string /** If true, will show a UI to authenticate with a new account */ authenticatedAccount?: AuthenticatedAccount | true + peerOptions?: any } diff --git a/src/entities.ts b/src/entities.ts index de97fb57..26641f5a 100644 --- a/src/entities.ts +++ b/src/entities.ts @@ -75,10 +75,9 @@ customEvents.on('gameLoaded', () => { const isWalking = Math.abs(speed.x) > WALKING_SPEED || Math.abs(speed.z) > WALKING_SPEED const isSprinting = Math.abs(speed.x) > SPRINTING_SPEED || Math.abs(speed.z) > SPRINTING_SPEED const newAnimation = isWalking ? (isSprinting ? 'running' : 'walking') : 'idle' - const username = e.username! - if (newAnimation !== playerPerAnimation[username]) { + if (newAnimation !== playerPerAnimation[id]) { viewer.entities.playAnimation(e.id, newAnimation) - playerPerAnimation[username] = newAnimation + playerPerAnimation[id] = newAnimation } } }) @@ -122,7 +121,7 @@ customEvents.on('gameLoaded', () => { } viewer.entities.addListener('remove', (e) => { loadedSkinEntityIds.delete(e.id) - playerPerAnimation[e.username] = '' + playerPerAnimation[e.id] = '' bot.tracker.stopTrackingEntity(e, true) }) diff --git a/src/globalState.ts b/src/globalState.ts index b0a447f2..cefa7810 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -109,6 +109,8 @@ export type AppConfig = { defaultProxy?: string // defaultProxySave?: string // defaultVersion?: string + peerJsServer?: string + peerJsServerFallback?: string promoteServers?: Array<{ ip, description, version? }> mapsProvider?: string } @@ -120,6 +122,7 @@ export const miscUiState = proxy({ singleplayer: false, flyingSquid: false, wanOpened: false, + wanOpening: false, /** wether game hud is shown (in playing state) */ gameLoaded: false, showUI: true, diff --git a/src/index.ts b/src/index.ts index db855960..b26619c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -72,7 +72,7 @@ import defaultServerOptions from './defaultLocalServerOptions' import dayCycle from './dayCycle' import { onAppLoad, resourcepackReload } from './resourcePack' -import { connectToPeer } from './localServerMultiplayer' +import { ConnectPeerOptions, connectToPeer } from './localServerMultiplayer' import CustomChannelClient from './customClient' import { loadScript } from 'prismarine-viewer/viewer/lib/utils' import { registerServiceWorker } from './serviceWorker' @@ -486,7 +486,7 @@ async function connect (connectOptions: ConnectOptions) { port: server.port ? +server.port : undefined, version: connectOptions.botVersion || false, ...p2pMultiplayer ? { - stream: await connectToPeer(connectOptions.peerId!), + stream: await connectToPeer(connectOptions.peerId!, connectOptions.peerOptions), } : {}, ...singleplayer || p2pMultiplayer ? { keepAlive: false, @@ -1022,6 +1022,10 @@ downloadAndOpenFile().then((downloadAction) => { void Promise.resolve().then(() => { // try to connect to peer const peerId = qs.get('connectPeer') + const peerOptions = {} as ConnectPeerOptions + if (qs.get('server')) { + peerOptions.server = qs.get('server')! + } const version = qs.get('peerVersion') if (peerId) { let username: string | null = options.guestUsername @@ -1031,7 +1035,8 @@ downloadAndOpenFile().then((downloadAction) => { void connect({ username, botVersion: version || undefined, - peerId + peerId, + peerOptions }) } }) diff --git a/src/localServerMultiplayer.ts b/src/localServerMultiplayer.ts index c8e4bc5e..7d147d0d 100644 --- a/src/localServerMultiplayer.ts +++ b/src/localServerMultiplayer.ts @@ -19,6 +19,8 @@ class CustomDuplex extends Duplex { let peerInstance: Peer | undefined +let overridePeerJsServer = null as string | null + export const getJoinLink = () => { if (!peerInstance) return const url = new URL(window.location.href) @@ -27,6 +29,11 @@ export const getJoinLink = () => { } url.searchParams.set('connectPeer', peerInstance.id) url.searchParams.set('peerVersion', localServer!.options.version) + const host = (overridePeerJsServer ?? miscUiState.appConfig?.peerJsServer) ?? undefined + if (host) { + // TODO! use miscUiState.appConfig.peerJsServer + url.searchParams.set('server', host) + } return url.toString() } @@ -46,8 +53,12 @@ export const openToWanAndCopyJoinLink = async (writeText: (text) => void, doCopy if (doCopy) await copyJoinLink() return 'Already opened to wan. Join link copied' } + miscUiState.wanOpening = true + const host = (overridePeerJsServer ?? miscUiState.appConfig?.peerJsServer) || undefined + const params = host ? parseUrl(host) : undefined const peer = new Peer({ debug: 3, + ...params }) peerInstance = peer peer.on('connection', (connection) => { @@ -83,34 +94,91 @@ export const openToWanAndCopyJoinLink = async (writeText: (text) => void, doCopy connection.on('close', disconnected) connection.on('error', disconnected) }) + const fallbackServer = miscUiState.appConfig?.peerJsServerFallback + const hasFallback = fallbackServer && peer.options.host !== fallbackServer + let hadErrorReported = false peer.on('error', (error) => { - console.error(error) - writeText(error.message) + console.error('peerJS error', error) + if (error.type === 'server-error' && hasFallback) { + return + } + hadErrorReported = true + writeText(error.message || JSON.stringify(error)) }) - return new Promise(resolve => { + let timeout + const destroy = () => { + clearTimeout(timeout) + timeout = undefined + peer.destroy() + peerInstance = undefined + } + + const result = await new Promise(resolve => { peer.on('open', async () => { await copyJoinLink() resolve('Copied join link to clipboard') }) - setTimeout(() => { + timeout = setTimeout(() => { + if (!hadErrorReported && timeout !== undefined) { + writeText('timeout') + } resolve('Failed to open to wan (timeout)') - }, 5000) + }, 6000) + + // fallback + peer.on('error', async (error) => { + if (!peer.open) { + if (hasFallback) { + destroy() + + overridePeerJsServer = fallbackServer + console.log('Trying fallback server', fallbackServer) + resolve((await openToWanAndCopyJoinLink(writeText, doCopy))!) + } + } + }) }) + if (!peerInstance.open) { + destroy() + } + miscUiState.wanOpening = false + return result +} + +const parseUrl = (url: string) => { + // peerJS does this internally for some reason: const url = new URL(`${protocol}://${host}:${port}${path}${key}/${method}`) + if (!url.startsWith('http')) url = `${location.protocol}//${url}` + const urlObj = new URL(url) + const key = urlObj.searchParams.get('key') + return { + host: urlObj.hostname, + path: urlObj.pathname, + protocol: urlObj.protocol.slice(0, -1), + ...urlObj.port ? { port: +urlObj.port } : {}, + ...key ? { key } : {}, + } } export const closeWan = () => { - if (!peerInstance) return - peerInstance.destroy() + peerInstance?.destroy() peerInstance = undefined miscUiState.wanOpened = false - return 'Closed to wan' + return 'Closed WAN' } -export const connectToPeer = async (peerId: string) => { +export type ConnectPeerOptions = { + server?: string +} + +export const connectToPeer = async (peerId: string, options: ConnectPeerOptions = {}) => { setLoadingScreenStatus('Connecting to peer server') // todo destroy connection on error + // TODO! use miscUiState.appConfig.peerJsServer + const host = options.server + const params = host ? parseUrl(host) : undefined const peer = new Peer({ debug: 3, + ...params }) await resolveTimeout(new Promise(resolve => { peer.once('open', resolve) diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index 1c4fdcd4..19f1385c 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -151,7 +151,7 @@ export default () => { const isModalActive = useIsModalActive('pause-screen') const fsStateSnap = useSnapshot(fsState) const activeModalStackSnap = useSnapshot(activeModalStack) - const { singleplayer, wanOpened } = useSnapshot(miscUiState) + const { singleplayer, wanOpened, wanOpening } = useSnapshot(miscUiState) const handlePointerLockChange = () => { if (!pointerLock.hasPointerLock && activeModalStack.length === 0) { @@ -188,7 +188,10 @@ export default () => { return } if (!wanOpened || !qr) { - await openToWanAndCopyJoinLink(() => { }, !qr) + await openToWanAndCopyJoinLink((err) => { + if (!miscUiState.wanOpening) return + alert(`Something went wrong: ${err}`) + }, !qr) } if (qr) { const joinLink = getJoinLink() @@ -230,7 +233,7 @@ export default () => { {singleplayer ? (
{(navigator.share as typeof navigator.share | undefined) ? ( {(navigator.share as typeof navigator.share | undefined) ? ( ) + } 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 401/865] 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 402/865] 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 403/865] 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 404/865] 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 405/865] 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 406/865] 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 407/865] 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 408/865] 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 409/865] 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 410/865] 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 411/865] 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 412/865] 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 413/865] 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 414/865] 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 415/865] 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 416/865] 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 417/865] 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 418/865] 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 419/865] 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 420/865] 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 421/865] 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 422/865] 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 423/865] 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 424/865] 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 425/865] 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 454/865] 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 455/865] 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 456/865] 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 459/865] 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 460/865] 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 461/865] 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 462/865] 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) => ( -