From d8261c2659acd6f90e6d95decbd45b665a7aac2d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 7 Jul 2024 19:18:48 +0300 Subject: [PATCH 001/909] fix: bundle pixealrticons with the app instead of fetching them from remote location in runtime --- .storybook/preview.tsx | 8 +- README.MD | 12 +- esbuild.mjs | 3 + package.json | 5 +- patches/pixelarticons@1.8.1.patch | 27 + pnpm-lock.yaml | 26 +- scripts/genPixelartTypes.ts | 16 + src/index.ts | 1 - src/packetsPatcher.ts | 16 +- src/react/Button.tsx | 5 +- src/react/ConceptCommandsGui.stories.tsx | 3 +- src/react/IndicatorEffects.stories.tsx | 12 +- src/react/KeybindingsScreen.tsx | 21 +- src/react/MainMenu.tsx | 5 +- src/react/PixelartIcon.tsx | 22 +- src/react/ServersList.stories.tsx | 1 - src/react/globals.d.ts | 9 +- src/react/pixelartIcons.generated.ts | 974 +++++++++++++++++++++++ src/styles.css | 5 + 19 files changed, 1102 insertions(+), 69 deletions(-) create mode 100644 patches/pixelarticons@1.8.1.patch create mode 100644 scripts/genPixelartTypes.ts create mode 100644 src/react/pixelartIcons.generated.ts diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index a8a219f1..74b0f05c 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,6 +1,6 @@ import React from 'react' -import type { Preview } from "@storybook/react"; +import type { Preview } from "@storybook/react" import '../src/styles.css' import './storybook.css' @@ -11,7 +11,7 @@ const preview: Preview = { const noScaling = c.parameters.noScaling return
-
; + }, ], parameters: { @@ -23,6 +23,6 @@ const preview: Preview = { }, }, }, -}; +} -export default preview; +export default preview diff --git a/README.MD b/README.MD index ad120dbf..9a4ba24a 100644 --- a/README.MD +++ b/README.MD @@ -4,14 +4,14 @@ A true Minecraft client running in your browser! A port of the original game to the web, written in JavaScript using modern web technologies. -If you encounter any bugs or usability issues, please report them! +If you encounter any bugs or usability issues, please report them! For development, see [development](#development--debugging). You can try this out at [mcraft.fun](https://mcraft.fun/), [pcm.gg](https://pcm.gg) (short link) [mcon.vercel.app](https://mcon.vercel.app/) or the GitHub pages deploy. Every commit from the `develop` (default) branch is deployed to [s.mcraft.fun](https://s.mcraft.fun/) - so it's usually newer, but might be less stable. ### Big Features - Open any zip world file or even folder in read-write mode! -- Connect to cracked servers* (it's possible because of proxy servers, see below) +- Connect to Java servers running in both offline (cracked) and online mode* (it's possible because of proxy servers, see below) - Singleplayer mode with simple world generations! - Works offline - Play with friends over internet! (P2P is powered by Peer.js discovery servers) @@ -39,9 +39,9 @@ Whatever offline mode you used (zip, folder, just single player), you can always ### Servers -You can play almost on any server, supporting offline connections. +You can play almost on any Java server, vanilla servers are fully supported. See the [Mineflayer](https://github.com/PrismarineJS/mineflayer) repo for the list of supported versions (should support majority of versions). -There is a builtin proxy, but you can also host a your one! Just clone the repo, run `pnpm i` (following CONTRIBUTING.MD) and run `pnpm prod-start`, then you can specify `http://localhost:8080` in the proxy field. +There is a builtin proxy, but you can also host your one! Just clone the repo, run `pnpm i` (following CONTRIBUTING.MD) and run `pnpm prod-start`, then you can specify `http://localhost:8080` in the proxy field. MS account authentication will be supported soon. ### Rendering @@ -67,9 +67,9 @@ There are many many settings, that are not exposed in the UI yet. You can find o To open the console, press `F12`, or if you are on mobile, you can type `#debug` in the URL (browser address bar), it wont't reload the page, but you will see a button to open the console. This way you can change advanced settings and see all errors or warnings. Also this way you can access global variables (described below). -### Debugging +### Development & Debugging -It should be easy to build/start the project locally. See [CONTRIBUTING.MD](./CONTRIBUTING.md) for more info. +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)). diff --git a/esbuild.mjs b/esbuild.mjs index 28b233bf..1f075a62 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -85,6 +85,9 @@ const buildOptions = { '.vert': 'text', '.frag': 'text', '.obj': 'text', + '.woff': 'dataurl', + '.woff2': 'dataurl', + '.ttf': 'dataurl', }, write: false, // todo would be better to enable? diff --git a/package.json b/package.json index 5bf32a69..07e9ff0b 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "flying-squid": "npm:@zardoy/flying-squid@^0.0.33", "fs-extra": "^11.1.1", "google-drive-browserfs": "github:zardoy/browserfs#google-drive", - "iconify-icon": "^1.0.8", "jszip": "^3.10.1", "lodash-es": "^4.17.21", "minecraft-assets": "^1.12.2", @@ -77,6 +76,7 @@ "net-browserify": "github:zardoy/prismarinejs-net-browserify", "node-gzip": "^1.1.2", "peerjs": "^1.5.0", + "pixelarticons": "^1.8.1", "pretty-bytes": "^6.1.1", "prismarine-provider-anvil": "github:zardoy/prismarine-provider-anvil#everything", "prosemirror-example-setup": "^1.2.2", @@ -169,7 +169,8 @@ }, "patchedDependencies": { "minecraft-protocol@1.47.0": "patches/minecraft-protocol@1.47.0.patch", - "three@0.154.0": "patches/three@0.154.0.patch" + "three@0.154.0": "patches/three@0.154.0.patch", + "pixelarticons@1.8.1": "patches/pixelarticons@1.8.1.patch" } }, "packageManager": "pnpm@9.0.4" diff --git a/patches/pixelarticons@1.8.1.patch b/patches/pixelarticons@1.8.1.patch new file mode 100644 index 00000000..10044536 --- /dev/null +++ b/patches/pixelarticons@1.8.1.patch @@ -0,0 +1,27 @@ +diff --git a/fonts/pixelart-icons-font.css b/fonts/pixelart-icons-font.css +index 3b2ebe839370d96bf93ef5ca94a827f07e49378d..103ab4d6b9f3b5c9f41d1407e3cbf4ac392fbf41 100644 +--- a/fonts/pixelart-icons-font.css ++++ b/fonts/pixelart-icons-font.css +@@ -1,16 +1,13 @@ + @font-face { + font-family: "pixelart-icons-font"; +- src: url('pixelart-icons-font.eot?t=1711815892278'); /* IE9*/ +- src: url('pixelart-icons-font.eot?t=1711815892278#iefix') format('embedded-opentype'), /* IE6-IE8 */ ++ src: + url("pixelart-icons-font.woff2?t=1711815892278") format("woff2"), + url("pixelart-icons-font.woff?t=1711815892278") format("woff"), + url('pixelart-icons-font.ttf?t=1711815892278') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ +- url('pixelart-icons-font.svg?t=1711815892278#pixelart-icons-font') format('svg'); /* iOS 4.1- */ + } + + [class^="pixelart-icons-font-"], [class*=" pixelart-icons-font-"] { + font-family: 'pixelart-icons-font' !important; +- font-size:24px; + font-style:normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +@@ -503,4 +500,3 @@ + .pixelart-icons-font-zap:before { content: "\ebe4"; } + .pixelart-icons-font-zoom-in:before { content: "\ebe5"; } + .pixelart-icons-font-zoom-out:before { content: "\ebe6"; } +- diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf890476..2208e681 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ patchedDependencies: minecraft-protocol@1.47.0: hash: 7otpchsbv7hxsuis4rrrwdtbve path: patches/minecraft-protocol@1.47.0.patch + pixelarticons@1.8.1: + hash: cclg2qo6d4yjs4evj64nr2mbwa + path: patches/pixelarticons@1.8.1.patch three@0.154.0: hash: sj7ocb4p23jym6bkfgueanti2e path: patches/three@0.154.0.patch @@ -115,9 +118,6 @@ importers: google-drive-browserfs: specifier: github:zardoy/browserfs#google-drive version: browserfs@https://codeload.github.com/zardoy/browserfs/tar.gz/ab58ae8ef00e3a31db01909e365e6cb5188436e0 - iconify-icon: - specifier: ^1.0.8 - version: 1.0.8 jszip: specifier: ^3.10.1 version: 3.10.1 @@ -148,6 +148,9 @@ importers: peerjs: specifier: ^1.5.0 version: 1.5.0 + pixelarticons: + specifier: ^1.8.1 + version: 1.8.1(patch_hash=cclg2qo6d4yjs4evj64nr2mbwa) pretty-bytes: specifier: ^6.1.1 version: 6.1.1 @@ -1723,9 +1726,6 @@ packages: '@humanwhocodes/object-schema@1.2.1': resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} - '@iconify/types@2.0.0': - resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -5158,9 +5158,6 @@ packages: hyphenate-style-name@1.0.4: resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} - iconify-icon@1.0.8: - resolution: {integrity: sha512-jvbUKHXf8EnGGArmhlP2IG8VqQLFFyTvTqb9LVL2TKTh7/eCCD1o2HHE9thpbJJb6B8hzhcFb6rOKhvo7reNKA==} - iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -6633,6 +6630,9 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} + pixelarticons@1.8.1: + resolution: {integrity: sha512-4taoDCleft9RtzVHLA73VDnRBwJNqlwbW8ShO6S0G9b+bM5ArGe1MVFW9xpromuPvQgVUYCSjRxNAQuNtADqyA==} + pixelmatch@4.0.2: resolution: {integrity: sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==} hasBin: true @@ -9985,8 +9985,6 @@ snapshots: '@humanwhocodes/object-schema@1.2.1': {} - '@iconify/types@2.0.0': {} - '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -14722,10 +14720,6 @@ snapshots: hyphenate-style-name@1.0.4: {} - iconify-icon@1.0.8: - dependencies: - '@iconify/types': 2.0.0 - iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -16433,6 +16427,8 @@ snapshots: pirates@4.0.6: {} + pixelarticons@1.8.1(patch_hash=cclg2qo6d4yjs4evj64nr2mbwa): {} + pixelmatch@4.0.2: dependencies: pngjs: 3.4.0 diff --git a/scripts/genPixelartTypes.ts b/scripts/genPixelartTypes.ts new file mode 100644 index 00000000..e7c9649a --- /dev/null +++ b/scripts/genPixelartTypes.ts @@ -0,0 +1,16 @@ +import fs from 'fs' + +const icons = fs.readdirSync('node_modules/pixelarticons/svg') + +const addIconPath = '../../node_modules/pixelarticons/svg/' + +let str = 'export type PixelartIconsGenerated = {\n' +for (const icon of icons) { + const name = icon.replace('.svg', '') + // jsdoc + const jsdocImage = '![image](' + addIconPath + icon + ')' + str += ` /** ${jsdocImage} */\n` + str += ` '${name}': string;\n` +} +str += '}\n' +fs.writeFileSync('./src/react/pixelartIcons.generated.ts', str, 'utf8') diff --git a/src/index.ts b/src/index.ts index e73e8240..e34e81ab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,6 @@ import './importsWorkaround' import './styles.css' import './globals' -import 'iconify-icon' import './devtools' import './entities' import './globalDomListeners' diff --git a/src/packetsPatcher.ts b/src/packetsPatcher.ts index f877d6ed..587c41c9 100644 --- a/src/packetsPatcher.ts +++ b/src/packetsPatcher.ts @@ -2,13 +2,13 @@ // todo these fixes should be ported to mineflayer export default () => { - customEvents.on('mineflayerBotCreated', () => { - bot._client.on('packet', (data, meta) => { - if (meta.name === 'map_chunk') { - if (data.groundUp && data.bitMap === 1 && data.chunkData.every(x => x === 0)) { - data.chunkData = Buffer.from(Array.from({ length: 12_544 }).fill(0) as any) - } - } - }) + customEvents.on('mineflayerBotCreated', () => { + bot._client.on('packet', (data, meta) => { + if (meta.name === 'map_chunk') { + if (data.groundUp && data.bitMap === 1 && data.chunkData.every(x => x === 0)) { + data.chunkData = Buffer.from(Array.from({ length: 12_544 }).fill(0) as any) + } + } }) + }) } diff --git a/src/react/Button.tsx b/src/react/Button.tsx index 20dc0801..10ff2fb0 100644 --- a/src/react/Button.tsx +++ b/src/react/Button.tsx @@ -2,6 +2,7 @@ import classNames from 'classnames' import { createContext, FC, Ref, useContext } from 'react' import buttonCss from './button.module.css' import SharedHudVars from './SharedHudVars' +import PixelartIcon from './PixelartIcon' // testing in storybook from deathscreen @@ -17,7 +18,7 @@ const ButtonContext = createContext({ onClick () { }, }) -export const ButtonProvider: FC<{children, onClick}> = ({ children, onClick }) => { +export const ButtonProvider: FC<{ children, onClick }> = ({ children, onClick }) => { return {children} } @@ -39,7 +40,7 @@ export default (({ label, icon, children, inScreen, rootRef, type = 'button', .. return diff --git a/src/react/ConceptCommandsGui.stories.tsx b/src/react/ConceptCommandsGui.stories.tsx index 89cb6eff..4f3586d4 100644 --- a/src/react/ConceptCommandsGui.stories.tsx +++ b/src/react/ConceptCommandsGui.stories.tsx @@ -11,7 +11,6 @@ const Button2 = ({ title, icon }) => {
{title}
- {/* { // const sessionEndpoint = 'http://localhost:3000/session' let authEndpoint = '' let sessionEndpoint = '' + if (!proxyBaseUrl.startsWith('http')) proxyBaseUrl = `${isPageSecure() ? 'https' : 'http'}://${proxyBaseUrl}` + const url = proxyBaseUrl + '/api/vm/net/connect' + let result: Response + try { + result = await fetch(url) + } catch (err) { + throw new Error(`Selected proxy server ${proxyBaseUrl} most likely is down`) + } try { - if (!proxyBaseUrl.startsWith('http')) proxyBaseUrl = `${isPageSecure() ? 'https' : 'http'}://${proxyBaseUrl}` - const url = proxyBaseUrl + '/api/vm/net/connect' - const result = await fetch(url) const json = await result.json() authEndpoint = urlWithBase(json.capabilities.authEndpoint, proxyBaseUrl) sessionEndpoint = urlWithBase(json.capabilities.sessionEndpoint, proxyBaseUrl) diff --git a/src/react/AppStatus.tsx b/src/react/AppStatus.tsx index 10cc5793..9ebecf93 100644 --- a/src/react/AppStatus.tsx +++ b/src/react/AppStatus.tsx @@ -28,6 +28,7 @@ export default ({ status, isError, hideDots = false, lastStatus = '', backAction return ( diff --git a/src/react/Chat.css b/src/react/Chat.css index 41917783..cba0a1aa 100644 --- a/src/react/Chat.css +++ b/src/react/Chat.css @@ -24,6 +24,13 @@ div.chat-wrapper { left: 1px; box-sizing: border-box; background-color: rgba(0, 0, 0, 0); + display: flex; + align-items: center; + gap: 1px; +} + +.chat-input-wrapper form { + display: flex; } .chat-input { @@ -103,7 +110,8 @@ div.chat-wrapper { } .input-mobile #chatinput { - height: 20px; + height: 24px; + font-size: 13px; } .display-mobile { diff --git a/src/react/Chat.tsx b/src/react/Chat.tsx index 89b2aa8b..60467171 100644 --- a/src/react/Chat.tsx +++ b/src/react/Chat.tsx @@ -4,6 +4,8 @@ import { MessageFormatPart } from '../botUtils' import { MessagePart } from './MessageFormatted' import './Chat.css' import { isIos, reactKeyForMessage } from './utils' +import Button from './Button' +import { pixelartIcons } from './PixelartIcon' export type Message = { parts: MessageFormatPart[], @@ -221,6 +223,8 @@ export default ({ diff --git a/src/react/EnterFullscreenButton.tsx b/src/react/EnterFullscreenButton.tsx index ad78ddad..3901b9ae 100644 --- a/src/react/EnterFullscreenButton.tsx +++ b/src/react/EnterFullscreenButton.tsx @@ -1,6 +1,11 @@ import { useEffect, useState } from 'react' +import { useSnapshot } from 'valtio' +import { activeModalStack, miscUiState } from '../globalState' import Button from './Button' import { useUsingTouch } from './utilsApp' +import { pixelartIcons } from './PixelartIcon' + +const hideOnModals = new Set(['chat']) export default () => { const [fullScreen, setFullScreen] = useState(false) @@ -9,16 +14,23 @@ export default () => { setFullScreen(!!document.fullscreenElement) }) }, []) + const { gameLoaded } = useSnapshot(miscUiState) + + const activeStack = useSnapshot(activeModalStack) + + const inMainMenu = activeStack.length === 0 && !gameLoaded const usingTouch = useUsingTouch() - if (!usingTouch || !document.documentElement.requestFullscreen || fullScreen) return null + const hideButton = activeStack.some(x => hideOnModals.has(x.reactType)) + + if (hideButton || !usingTouch || !document.documentElement.requestFullscreen || fullScreen) return null return + {!lockConnect && <> + + } } From d02008c2ef4bfff93fad0edbeaffca3d60399bcc Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 11 Jul 2024 18:33:16 +0300 Subject: [PATCH 011/909] fix(regression): signs lighting was compltely broken --- 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 ab77d516..c978c240 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -624,7 +624,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { delete attr.t_uvs attr.positions = new Float32Array(attr.positions) as any - attr.normals = new Int8Array(attr.normals) as any + attr.normals = new Float32Array(attr.normals) as any attr.colors = new Float32Array(attr.colors) as any attr.uvs = new Float32Array(attr.uvs) as any From d0205a970b823ed49e4a01dea237e175a096d777 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 11 Jul 2024 20:08:10 +0300 Subject: [PATCH 012/909] up mineflayer for better vehicle move --- pnpm-lock.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45ebdda4..9d370f17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -314,7 +314,7 @@ importers: version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/c50afc54e39817f7e4d313ce0f6fdaad71e7e4f4(@types/react@18.2.20)(react@18.2.0) mineflayer: specifier: github:zardoy/mineflayer - version: https://codeload.github.com/zardoy/mineflayer/tar.gz/dddc683544317f117172077a9245a07be1b12479(encoding@0.1.13) + version: https://codeload.github.com/zardoy/mineflayer/tar.gz/c03c76ef2acfc747bfe8cd1e290106c81f399848(encoding@0.1.13) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -6086,8 +6086,8 @@ packages: resolution: {integrity: sha512-QMMNPx4IyZE7ydAzjvGLQLCnQNUOfkk1qVZKxTTS9q3qPTAewz4GhsVUBtbQ8LSbHthe5RcQ1Sgxs4wlIma/Qw==} engines: {node: '>=18'} - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/dddc683544317f117172077a9245a07be1b12479: - resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/dddc683544317f117172077a9245a07be1b12479} + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/c03c76ef2acfc747bfe8cd1e290106c81f399848: + resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/c03c76ef2acfc747bfe8cd1e290106c81f399848} version: 4.20.1 engines: {node: '>=18'} @@ -15825,7 +15825,7 @@ snapshots: - encoding - supports-color - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/dddc683544317f117172077a9245a07be1b12479(encoding@0.1.13): + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/c03c76ef2acfc747bfe8cd1e290106c81f399848(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) From 07002a743748899f58767011c3bf99da2658c63b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 13 Jul 2024 05:49:38 +0300 Subject: [PATCH 013/909] fix: remove entities from the scene on login packet (usually on the world switch) --- pnpm-lock.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9d370f17..9deb9a7e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -314,7 +314,7 @@ importers: version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/c50afc54e39817f7e4d313ce0f6fdaad71e7e4f4(@types/react@18.2.20)(react@18.2.0) mineflayer: specifier: github:zardoy/mineflayer - version: https://codeload.github.com/zardoy/mineflayer/tar.gz/c03c76ef2acfc747bfe8cd1e290106c81f399848(encoding@0.1.13) + version: https://codeload.github.com/zardoy/mineflayer/tar.gz/7f65e46a048f1bc2b57775d84b32400dce707321(encoding@0.1.13) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -6086,8 +6086,8 @@ packages: resolution: {integrity: sha512-QMMNPx4IyZE7ydAzjvGLQLCnQNUOfkk1qVZKxTTS9q3qPTAewz4GhsVUBtbQ8LSbHthe5RcQ1Sgxs4wlIma/Qw==} engines: {node: '>=18'} - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/c03c76ef2acfc747bfe8cd1e290106c81f399848: - resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/c03c76ef2acfc747bfe8cd1e290106c81f399848} + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/7f65e46a048f1bc2b57775d84b32400dce707321: + resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/7f65e46a048f1bc2b57775d84b32400dce707321} version: 4.20.1 engines: {node: '>=18'} @@ -15825,7 +15825,7 @@ snapshots: - encoding - supports-color - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/c03c76ef2acfc747bfe8cd1e290106c81f399848(encoding@0.1.13): + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/7f65e46a048f1bc2b57775d84b32400dce707321(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) From 04b5d1ac3f03e5053df6a6142ede7d0c4539d480 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 13 Jul 2024 06:17:47 +0300 Subject: [PATCH 014/909] add handled packets stats TODO make auto-updated --- docs-assets/handled-packets.md | 169 +++++++++++++++++++++++++++++++ scripts/updateHandledPackets.mjs | 60 +++++++++++ 2 files changed, 229 insertions(+) create mode 100644 docs-assets/handled-packets.md create mode 100644 scripts/updateHandledPackets.mjs diff --git a/docs-assets/handled-packets.md b/docs-assets/handled-packets.md new file mode 100644 index 00000000..0671987c --- /dev/null +++ b/docs-assets/handled-packets.md @@ -0,0 +1,169 @@ +# Handled Packets + +## Server -> Client + +❌ statistics +❌ advancements +❌ face_player +❌ nbt_query_response +❌ chat_suggestions +❌ trade_list +❌ vehicle_move +❌ open_book +❌ craft_recipe_response +❌ end_combat_event +❌ enter_combat_event +❌ unlock_recipes +❌ camera +❌ update_view_position +❌ update_view_distance +❌ entity_sound_effect +❌ stop_sound +❌ feature_flags +❌ select_advancement_tab +❌ declare_recipes +❌ tags +❌ acknowledge_player_digging +❌ initialize_world_border +❌ world_border_center +❌ world_border_lerp_size +❌ world_border_size +❌ world_border_warning_delay +❌ world_border_warning_reach +❌ simulation_distance +❌ chunk_biomes +❌ damage_event +❌ hurt_animation +✅ spawn_entity +✅ spawn_entity_experience_orb +✅ named_entity_spawn +✅ animation +✅ block_break_animation +✅ tile_entity_data +✅ block_action +✅ block_change +✅ boss_bar +✅ difficulty +✅ tab_complete +✅ declare_commands +✅ multi_block_change +✅ close_window +✅ open_window +✅ window_items +✅ craft_progress_bar +✅ set_slot +✅ set_cooldown +✅ custom_payload +✅ hide_message +✅ kick_disconnect +✅ profileless_chat +✅ entity_status +✅ explosion +✅ unload_chunk +✅ game_state_change +✅ open_horse_window +✅ keep_alive +✅ map_chunk +✅ world_event +✅ world_particles +✅ update_light +✅ login +✅ map +✅ rel_entity_move +✅ entity_move_look +✅ entity_look +✅ open_sign_entity +✅ abilities +✅ player_chat +✅ death_combat_event +✅ player_remove +✅ player_info +✅ position +✅ entity_destroy +✅ remove_entity_effect +✅ resource_pack_send +✅ respawn +✅ entity_head_rotation +✅ held_item_slot +✅ scoreboard_display_objective +✅ entity_metadata +✅ attach_entity +✅ entity_velocity +✅ entity_equipment +✅ experience +✅ update_health +✅ scoreboard_objective +✅ set_passengers +✅ teams +✅ scoreboard_score +✅ spawn_position +✅ update_time +✅ sound_effect +✅ system_chat +✅ playerlist_header +✅ collect +✅ entity_teleport +✅ entity_update_attributes +✅ entity_effect +✅ server_data +✅ clear_titles +✅ action_bar +✅ ping +✅ set_title_subtitle +✅ set_title_text +✅ set_title_time +✅ packet + +## Client -> Server + +❌ query_block_nbt +❌ set_difficulty +❌ query_entity_nbt +❌ pick_item +❌ set_beacon_effect +❌ update_command_block_minecart +❌ update_structure_block +❌ generate_structure +❌ lock_difficulty +❌ craft_recipe_request +❌ displayed_recipe +❌ recipe_book +❌ update_jigsaw_block +❌ spectate +❌ advancement_tab +✅ teleport_confirm +✅ chat_command +✅ chat_message +✅ message_acknowledgement +✅ edit_book +✅ name_item +✅ select_trade +✅ update_command_block +✅ tab_complete +✅ client_command +✅ settings +✅ enchant_item +✅ window_click +✅ close_window +✅ custom_payload +✅ use_entity +✅ keep_alive +✅ position +✅ position_look +✅ look +✅ flying +✅ vehicle_move +✅ steer_boat +✅ abilities +✅ block_dig +✅ entity_action +✅ steer_vehicle +✅ resource_pack_receive +✅ held_item_slot +✅ set_creative_slot +✅ update_sign +✅ arm_animation +✅ block_place +✅ use_item +✅ pong +✅ chat_session_update diff --git a/scripts/updateHandledPackets.mjs b/scripts/updateHandledPackets.mjs new file mode 100644 index 00000000..080eaf44 --- /dev/null +++ b/scripts/updateHandledPackets.mjs @@ -0,0 +1,60 @@ +import fs from 'fs' +import path from 'path' +import minecraftData from 'minecraft-data' + +const lastVersion = minecraftData.versions.pc[0] +// console.log('last proto ver', lastVersion.minecraftVersion) +const allPackets = minecraftData(lastVersion.minecraftVersion).protocol +const getPackets = ({ types }) => { + return Object.keys(types).map(type => type.replace('packet_', '')) +} +// todo test against all versions +const allFromServerPackets = getPackets(allPackets.play.toClient) +const allToServerPackets = getPackets(allPackets.play.toServer).filter(x => !['packet'].includes(x)) + +const buildFile = './dist/index.js' + +const file = fs.readFileSync(buildFile, 'utf8') + +const packetsReceiveRegex = /client\.on\("(\w+)"/g +const packetsReceiveSend = /client\.write\("(\w+)"/g + +let allSupportedReceive = [...new Set([...file.matchAll(packetsReceiveRegex)].map(x => x[1]))] +let allSupportedSend = [...new Set([...file.matchAll(packetsReceiveSend)].map(x => x[1]))] + +let md = '# Handled Packets\n' + +md += '\n## Server -> Client\n\n' +let notSupportedRows = [] +let supportedRows = [] +for (const packet of allFromServerPackets) { + const includes = allSupportedReceive.includes(packet); + (includes ? supportedRows : notSupportedRows).push(packet) +} + +for (const row of notSupportedRows) { + md += `❌ ${row}\n` +} +for (const row of supportedRows) { + md += `✅ ${row}\n` +} + +md += '\n' + +notSupportedRows = [] +supportedRows = [] + +md += '## Client -> Server\n\n' +for (const packet of allToServerPackets) { + const includes = allSupportedSend.includes(packet); + (includes ? supportedRows : notSupportedRows).push(packet) +} + +for (const row of notSupportedRows) { + md += `❌ ${row}\n` +} +for (const row of supportedRows) { + md += `✅ ${row}\n` +} + +fs.writeFileSync('./docs-assets/handled-packets.md', md) From 6c5f72e1f286f2b73f8b940d117b9cec4b197ae1 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 13 Jul 2024 20:31:27 +0300 Subject: [PATCH 015/909] feat: implement pitch which was required for note blocks to work properly --- prismarine-viewer/viewer/lib/viewer.ts | 5 +++-- src/soundSystem.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/prismarine-viewer/viewer/lib/viewer.ts b/prismarine-viewer/viewer/lib/viewer.ts index c95127f2..504c4637 100644 --- a/prismarine-viewer/viewer/lib/viewer.ts +++ b/prismarine-viewer/viewer/lib/viewer.ts @@ -32,7 +32,7 @@ export class Viewer { this.world.camera = camera } - constructor(public renderer: THREE.WebGLRenderer, worldConfig = defaultWorldRendererConfig) { + constructor (public renderer: THREE.WebGLRenderer, worldConfig = defaultWorldRendererConfig) { // https://discourse.threejs.org/t/updates-to-color-management-in-three-js-r152/50791 THREE.ColorManagement.enabled = false renderer.outputColorSpace = THREE.LinearSRGBColorSpace @@ -118,7 +118,7 @@ export class Viewer { this.world.updateCamera(pos?.offset(0, yOffset, 0) ?? null, yaw, pitch) } - playSound (position: Vec3, path: string, volume = 1) { + playSound (position: Vec3, path: string, volume = 1, pitch = 1) { if (!this.audioListener) { this.audioListener = new THREE.AudioListener() this.camera.add(this.audioListener) @@ -134,6 +134,7 @@ export class Viewer { sound.setBuffer(buffer) sound.setRefDistance(20) sound.setVolume(volume) + sound.setPlaybackRate(pitch) // set the pitch this.scene.add(sound) // set sound position sound.position.set(position.x, position.y, position.z) diff --git a/src/soundSystem.ts b/src/soundSystem.ts index e7fdaee7..d49b6a8c 100644 --- a/src/soundSystem.ts +++ b/src/soundSystem.ts @@ -50,7 +50,7 @@ subscribeKey(miscUiState, 'gameLoaded', async () => { const isMuted = options.mutedSounds.includes(soundKey) || options.volume === 0 if (position) { if (!isMuted) { - viewer.playSound(position, url, soundVolume * Math.max(Math.min(volume, 1), 0) * (options.volume / 100)) + viewer.playSound(position, url, soundVolume * Math.max(Math.min(volume, 1), 0) * (options.volume / 100), Math.max(Math.min(pitch ?? 1, 2), 0.5)) } if (getDistance(bot.entity.position, position) < 4 * 16) { lastPlayedSounds.lastServerPlayed[soundKey] ??= { count: 0, last: 0 } From b13ef443bb84d4f2f84a4d11bc0f25ffc8d4840c Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 13 Jul 2024 21:30:44 +0300 Subject: [PATCH 016/909] feat: rework sound system. Now sounds are 100% implemented for all current & future supported versions --- prismarine-viewer/viewer/prepare/utils.ts | 14 ++++++ src/globals.d.ts | 2 +- src/soundSystem.ts | 53 ++++++++++++----------- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/prismarine-viewer/viewer/prepare/utils.ts b/prismarine-viewer/viewer/prepare/utils.ts index a33909a9..958273f3 100644 --- a/prismarine-viewer/viewer/prepare/utils.ts +++ b/prismarine-viewer/viewer/prepare/utils.ts @@ -2,3 +2,17 @@ export const versionToNumber = (ver: string) => { const [x, y = '0', z = '0'] = ver.split('.') return +`${x.padStart(2, '0')}${y.padStart(2, '0')}${z.padStart(2, '0')}` } + +export const versionToMajor = (version: string) => { + const [x, y = '0'] = version.split('.') + return `${x.padStart(2, '0')}.${y.padStart(2, '0')}` +} + +export const versionsMapToMajor = (versionsMap: Record) => { + const majorVersions = {} as Record + for (const [ver, data] of Object.entries(versionsMap)) { + const major = versionToMajor(ver) + majorVersions[major] = data + } + return majorVersions +} diff --git a/src/globals.d.ts b/src/globals.d.ts index 05b29a14..f8699f21 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -17,7 +17,7 @@ declare const worldView: import('prismarine-viewer/viewer/lib/worldDataEmitter') declare const localServer: import('flying-squid/dist/index').FullServer & { options } | undefined /** all currently loaded mc data */ declare const mcData: Record -declare const loadedData: import('minecraft-data').IndexedData +declare const loadedData: import('minecraft-data').IndexedData & { sounds: Record } declare const customEvents: import('typed-emitter').default<{ /** Singleplayer load requested */ singleplayer (): void diff --git a/src/soundSystem.ts b/src/soundSystem.ts index d49b6a8c..0187a9b0 100644 --- a/src/soundSystem.ts +++ b/src/soundSystem.ts @@ -1,6 +1,6 @@ import { subscribeKey } from 'valtio/utils' import { Vec3 } from 'vec3' -import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' +import { versionToMajor, versionToNumber, versionsMapToMajor } from 'prismarine-viewer/viewer/prepare/utils' import { loadScript } from 'prismarine-viewer/viewer/lib/utils' import type { Block } from 'prismarine-block' import { miscUiState } from './globalState' @@ -22,32 +22,26 @@ subscribeKey(miscUiState, 'gameLoaded', async () => { return } - // todo also use major versioned hardcoded sounds - const soundsMap = allSoundsMap[bot.version] + const allSoundsMajor = versionsMapToMajor(allSoundsMap) + const soundsMap = allSoundsMajor[versionToMajor(bot.version)] ?? Object.values(allSoundsMajor)[0] - if (!soundsMap || !miscUiState.gameLoaded || !soundsMap) { + if (!soundsMap || !miscUiState.gameLoaded || !loadedData.sounds) { return } - const soundsPerId = Object.fromEntries(Object.entries(soundsMap).map(([id, sound]) => [+id.split(';')[0], sound])) + // const soundsPerId = Object.fromEntries(Object.entries(soundsMap).map(([id, sound]) => [+id.split(';')[0], sound])) const soundsPerName = Object.fromEntries(Object.entries(soundsMap).map(([id, sound]) => [id.split(';')[1], sound])) - const soundIdToName = Object.fromEntries(Object.entries(soundsMap).map(([id, sound]) => [+id.split(';')[0], id.split(';')[1]])) - const playGeneralSound = async (soundId: string, soundString: string | undefined, position?: Vec3, volume = 1, pitch?: number) => { - if (!soundString) { - console.warn('Unknown sound received from server to play', soundId) - return - } + const playGeneralSound = async (soundKey: string, position?: Vec3, volume = 1, pitch?: number) => { if (!options.volume) return - const parts = soundString.split(';') - const soundVolume = +parts[0]! - const soundName = parts[1]! - // console.log('pitch', pitch) - const versionedSound = getVersionedSound(bot.version, soundName, Object.entries(soundsLegacyMap)) + const soundStaticData = soundsPerName[soundKey]?.split(';') + if (!soundStaticData) return + const soundVolume = +soundStaticData[0]! + const soundPath = soundStaticData[1]! + const versionedSound = getVersionedSound(bot.version, soundPath, Object.entries(soundsLegacyMap)) // todo test versionedSound - const url = allSoundsMeta.baseUrl.replace(/\/$/, '') + (versionedSound ? `/${versionedSound}` : '') + '/minecraft/sounds/' + soundName + '.' + allSoundsMeta.format - const soundKey = soundIdToName[+soundId] ?? soundId - const isMuted = options.mutedSounds.includes(soundKey) || options.volume === 0 + const url = allSoundsMeta.baseUrl.replace(/\/$/, '') + (versionedSound ? `/${versionedSound}` : '') + '/minecraft/sounds/' + soundPath + '.' + allSoundsMeta.format + const isMuted = options.mutedSounds.includes(soundKey) || options.mutedSounds.includes(soundPath) || options.volume === 0 if (position) { if (!isMuted) { viewer.playSound(position, url, soundVolume * Math.max(Math.min(volume, 1), 0) * (options.volume / 100), Math.max(Math.min(pitch ?? 1, 2), 0.5)) @@ -67,17 +61,24 @@ subscribeKey(miscUiState, 'gameLoaded', async () => { } } } - const playHardcodedSound = async (soundId: string, position?: Vec3, volume = 1, pitch?: number) => { - const sound = soundsPerName[soundId] - await playGeneralSound(soundId, sound, position, volume, pitch) + const playHardcodedSound = async (soundKey: string, position?: Vec3, volume = 1, pitch?: number) => { + await playGeneralSound(soundKey, position, volume, pitch) } bot.on('soundEffectHeard', async (soundId, position, volume, pitch) => { - console.debug('soundEffectHeard', soundId, volume) await playHardcodedSound(soundId, position, volume, pitch) }) - bot.on('hardcodedSoundEffectHeard', async (soundId, soundCategory, position, volume, pitch) => { - const sound = soundsPerId[soundId] - await playGeneralSound(soundId.toString(), sound, position, volume, pitch) + bot.on('hardcodedSoundEffectHeard', async (soundIdNum, soundCategory, position, volume, pitch) => { + const fixOffset = versionToNumber('1.20.4') === versionToNumber(bot.version) ? -1 : 0 + const soundKey = loadedData.sounds[soundIdNum + fixOffset]?.name + if (soundKey === undefined) return + await playGeneralSound(soundKey, position, volume, pitch) + }) + // workaround as mineflayer doesn't support soundEvent + bot._client.on('sound_effect', async (packet) => { + const soundResource = packet['soundEvent']?.resource as string | undefined + if (packet.soundId !== 0 || !soundResource) return + const pos = new Vec3(packet.x / 8, packet.y / 8, packet.z / 8) + await playHardcodedSound(soundResource.replace('minecraft:', ''), pos, packet.volume, packet.pitch) }) bot.on('entityHurt', async (entity) => { if (entity.id === bot.entity.id) { From 698779f6b5302165dd3dff731de0b223ac837513 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 13 Jul 2024 21:31:29 +0300 Subject: [PATCH 017/909] fix: toggle entity visiiblity even after it's creation --- prismarine-viewer/viewer/lib/entities.js | 10 +++++----- prismarine-viewer/viewer/lib/worldDataEmitter.ts | 9 ++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/prismarine-viewer/viewer/lib/entities.js b/prismarine-viewer/viewer/lib/entities.js index 94f06e0c..d36b6936 100644 --- a/prismarine-viewer/viewer/lib/entities.js +++ b/prismarine-viewer/viewer/lib/entities.js @@ -405,14 +405,14 @@ export class Entities extends EventEmitter { } //@ts-ignore + // set visibility const isInvisible = entity.metadata?.[0] & 0x20 - if (isInvisible) { - for (const child of this.entities[entity.id].children.find(c => c.name === 'mesh').children) { - if (child.name !== 'nametag') { - child.visible = false - } + for (const child of this.entities[entity.id].children.find(c => c.name === 'mesh').children) { + if (child.name !== 'nametag') { + child.visible = !isInvisible } } + // --- // not player const displayText = entity.metadata?.[3] && this.parseEntityLabel(entity.metadata[2]) if (entity.name !== 'player' && displayText) { diff --git a/prismarine-viewer/viewer/lib/worldDataEmitter.ts b/prismarine-viewer/viewer/lib/worldDataEmitter.ts index 5df5cc73..761509c2 100644 --- a/prismarine-viewer/viewer/lib/worldDataEmitter.ts +++ b/prismarine-viewer/viewer/lib/worldDataEmitter.ts @@ -44,7 +44,14 @@ export class WorldDataEmitter extends EventEmitter { listenToBot (bot: typeof __type_bot) { const emitEntity = (e) => { if (!e || e === bot.entity) return - this.emitter.emit('entity', { ...e, pos: e.position, username: e.username }) + this.emitter.emit('entity', { + ...e, + pos: e.position, + username: e.username, + // set debugTree (obj) { + // e.debugTree = obj + // } + }) } this.eventListeners[bot.username] = { From 512bc09477aa96352f4d19a165a7e110d5c76f81 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 14 Jul 2024 01:55:41 +0300 Subject: [PATCH 018/909] make main docker build 5x smaller, add only proxy dockerfile --- Dockerfile | 32 +++++++++++++++++++++----------- Dockerfile.proxy | 11 +++++++++++ README.MD | 15 +++++++++++---- 3 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 Dockerfile.proxy diff --git a/Dockerfile b/Dockerfile index 086240b5..0ae1c6e0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,32 @@ -FROM node:18-alpine +# ---- Build Stage ---- +FROM node:18-alpine AS build # Without git installing the npm packages fails -RUN apk add git -RUN mkdir /app +RUN apk add --no-cache git python3 make g++ cairo-dev pango-dev jpeg-dev giflib-dev librsvg-dev WORKDIR /app COPY . /app -# install python and other dependencies -RUN apk add python3 make g++ cairo-dev pango-dev jpeg-dev giflib-dev librsvg-dev # install pnpm RUN npm i -g pnpm@9.0.4 RUN pnpm install -# only for prod -RUN pnpm run build -# --- -EXPOSE 8080 -# uncomment for development + +# TODO for development # EXPOSE 9090 # VOLUME /app/src # VOLUME /app/prismarine-viewer # ENTRYPOINT ["pnpm", "run", "run-all"] + # only for prod -ENTRYPOINT ["npm", "run", "prod-start"] +RUN pnpm run build + +# ---- Run Stage ---- +FROM node:18-alpine +RUN apk add git +WORKDIR /app +# Copy build artifacts from the build stage +COPY --from=build /app/dist /app/dist +COPY server.js /app/server.js +# Install express +RUN npm i -g pnpm@9.0.4 +RUN npm init -yp +RUN pnpm i express github:zardoy/prismarinejs-net-browserify compression cors +EXPOSE 8080 +ENTRYPOINT ["node", "server.js"] diff --git a/Dockerfile.proxy b/Dockerfile.proxy new file mode 100644 index 00000000..746eef72 --- /dev/null +++ b/Dockerfile.proxy @@ -0,0 +1,11 @@ +# ---- Run Stage ---- +FROM node:18-alpine +RUN apk add git +WORKDIR /app +COPY server.js /app/server.js +# Install server dependencies +RUN npm i -g pnpm@9.0.4 +RUN npm init -yp +RUN pnpm i express github:zardoy/prismarinejs-net-browserify compression cors +EXPOSE 8080 +ENTRYPOINT ["node", "server.js"] diff --git a/README.MD b/README.MD index 9a4ba24a..7576f6ba 100644 --- a/README.MD +++ b/README.MD @@ -44,6 +44,13 @@ See the [Mineflayer](https://github.com/PrismarineJS/mineflayer) repo for the li There is a builtin proxy, but you can also host your one! Just clone the repo, run `pnpm i` (following CONTRIBUTING.MD) and run `pnpm prod-start`, then you can specify `http://localhost:8080` in the proxy field. MS account authentication will be supported soon. +### Docker Files + +- [Main Dockerfile](./Dockerfile) - for production build & offline/private usage. Includes full web-app + proxy server for connecting to Minecraft servers. +- [Proxy Dockerfile](./Dockerfile.proxy) - for proxy server only - that one you would be able to specify in the proxy field on the client (`docker build . -f Dockerfile.proxy -t minecraft-web-proxy`) + +In case of using docker, you don't have to follow preparation steps from CONTRIBUTING.MD, like installing Node.js, pnpm, etc. + ### Rendering #### Three.js Renderer @@ -55,10 +62,6 @@ MS account authentication will be supported soon. -### Things that are not planned yet - -- Mods, plugins (basically JARs) support, shaders - since they all are related to specific game pipelines - ### Advanced Settings There are many many settings, that are not exposed in the UI yet. You can find or change them by opening the browser console and typing `options`. You can also change them by typing `options. = `. @@ -131,6 +134,10 @@ Press `Y` to set query parameters to url of your current game state. - [Peer.js](https://peerjs.com/) - P2P networking (when you open to wan) - [Three.js](https://threejs.org/) - Helping in 3D rendering +### Things that are not planned yet + +- Mods, plugins (basically JARs) support, shaders - since they all are related to specific game pipelines + ### Alternatives - [https://github.com/ClassiCube/ClassiCube](ClassiCube - Better C# Rewrite) [DEMO](https://www.classicube.net/server/play/?warned=true) From b49e9880906f269b1be6456abcdb24f78e45f21e Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 16 Jul 2024 10:35:01 +0300 Subject: [PATCH 019/909] fix: inventory didn't update after dropping an item --- src/controls.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/controls.ts b/src/controls.ts index a3a22029..98cc6afb 100644 --- a/src/controls.ts +++ b/src/controls.ts @@ -353,7 +353,7 @@ contro.on('trigger', ({ command }) => { document.exitPointerLock?.() openPlayerInventory() break - case 'general.drop': + case 'general.drop': { // if (bot.heldItem/* && ctrl */) bot.tossStack(bot.heldItem) bot._client.write('block_dig', { 'status': 4, @@ -365,7 +365,14 @@ contro.on('trigger', ({ command }) => { 'face': 0, sequence: 0 }) + const slot = bot.inventory.hotbarStart + bot.quickBarSlot + const item = bot.inventory.slots[slot] + if (item) { + item.count-- + bot.inventory.updateSlot(slot, item.count > 0 ? item : null!) + } break + } case 'general.chat': showModal({ reactType: 'chat' }) break From 9ee67cc8270e79e5b2d9025a30148bb083e8c36d Mon Sep 17 00:00:00 2001 From: Wolf2323 Date: Tue, 16 Jul 2024 10:37:01 +0200 Subject: [PATCH 020/909] fix: ignoring exception ResizeObserver loop exceptions (#161) --- src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.ts b/src/index.ts index e34e81ab..2a190785 100644 --- a/src/index.ts +++ b/src/index.ts @@ -330,6 +330,9 @@ async function connect (connectOptions: ConnectOptions) { } const handleError = (err) => { console.error(err) + if (err === 'ResizeObserver loop completed with undelivered notifications.') { + return + } errorAbortController.abort() if (isCypress()) throw err miscUiState.hasErrors = true From cda1d59d3bd59580d91f4107a8b4a8bfae57c976 Mon Sep 17 00:00:00 2001 From: Wolf2323 Date: Tue, 16 Jul 2024 11:05:57 +0200 Subject: [PATCH 021/909] improved query parameters handling (#162) Co-authored-by: Vitaly --- README.MD | 25 ++++++++++------- src/react/AddServerOrConnect.tsx | 46 +++++++++++++++++++------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/README.MD b/README.MD index 7576f6ba..cafa3ac9 100644 --- a/README.MD +++ b/README.MD @@ -110,19 +110,26 @@ world chunks have a *yellow* border, hostile mobs have a *red* outline, passive Press `Y` to set query parameters to url of your current game state. -- `?ip=` - Display connect screen to the server on load -- `?username=` - Set the username for server -- `?proxy=` - Set the proxy server address to use for server -- `?version=` - Set the version for server -- `?lockConnect=true` - Disable cancel / save buttons, useful for integrates iframes +There are some parameters you can set in the url to archive some specific behaviors: + +Server specific: +- `?ip=` - Display connect screen to the server on load with predefined server ip. `:` is optional and can be added to the ip. +- `?name=` - Set the server name for saving to the server list +- `?version=` - Set the version for the server +- `?proxy=` - Set the proxy server address to use for the server +- `?username=` - Set the username for the server +- `?lockConnect=true` - Only works then `ip` parameter is set. Disables cancel/save buttons and all inputs in the connect screen already set as parameters. Useful for integrates iframes. - `?reconnect=true` - Reconnect to the server on page reloads. Available in **dev mode only** and very useful on server testing. + +Single player specific: - `?loadSave=` - Load the save on load with the specified folder name (not title) - `?singleplayer=1` - Create empty world on load. Nothing will be saved -- `?noSave=true` - Disable auto save on unload / disconnect / export. Only manual save with `/save` command will work - - +- `?version=` - Set the version for the singleplayer world (when used with `?singleplayer=1`) +- `?noSave=true` - Disable auto save on unload / disconnect / export whenever a world is loaded. Only manual save with `/save` command will work. - `?map=` - Load the map from ZIP. You can use any url, but it must be CORS enabled. -- `?setting=:` - Set the and lock the setting on load. You can set multiple settings by separating them with `&` e.g. `?setting=autoParkour:true&setting=renderDistance:4` + +General: +- `?setting=:` - Set and lock the setting on load. You can set multiple settings by separating them with `&` e.g. `?setting=autoParkour:true&setting=renderDistance:4` ### Notable Things that Power this Project diff --git a/src/react/AddServerOrConnect.tsx b/src/react/AddServerOrConnect.tsx index 729f1ec2..535e3ee9 100644 --- a/src/react/AddServerOrConnect.tsx +++ b/src/react/AddServerOrConnect.tsx @@ -30,19 +30,25 @@ const ELEMENTS_WIDTH = 190 export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQs, onQsConnect, defaults, accounts, authenticatedAccounts }: Props) => { const qsParams = parseQs ? new URLSearchParams(window.location.search) : undefined + const qsParamName = qsParams?.get('name') + const qsParamIp = qsParams?.get('ip') + const qsParamVersion = qsParams?.get('version') + const qsParamProxy = qsParams?.get('proxy') + const qsParamUsername = qsParams?.get('username') + const qsParamLockConnect = qsParams?.get('lockConnect') - const [serverName, setServerName] = React.useState(initialData?.name ?? qsParams?.get('name') ?? '') + const qsIpParts = qsParamIp?.split(':') + const ipParts = initialData?.ip.split(':') - const ipWithoutPort = initialData?.ip.split(':')[0] - const port = initialData?.ip.split(':')[1] + const [serverName, setServerName] = React.useState(initialData?.name ?? qsParamName ?? '') + const [serverIp, setServerIp] = React.useState(ipParts?.[0] ?? qsIpParts?.[0] ?? '') + const [serverPort, setServerPort] = React.useState(ipParts?.[1] ?? qsIpParts?.[1] ?? '') + const [versionOverride, setVersionOverride] = React.useState(initialData?.versionOverride ?? /* legacy */ initialData?.['version'] ?? qsParamVersion ?? '') + const [proxyOverride, setProxyOverride] = React.useState(initialData?.proxyOverride ?? qsParamProxy ?? '') + const [usernameOverride, setUsernameOverride] = React.useState(initialData?.usernameOverride ?? qsParamUsername ?? '') + const lockConnect = qsParamLockConnect === 'true' - const [serverIp, setServerIp] = React.useState(ipWithoutPort ?? qsParams?.get('ip') ?? '') - const [serverPort, setServerPort] = React.useState(port ?? '') - const [versionOverride, setVersionOverride] = React.useState(initialData?.versionOverride ?? /* legacy */ initialData?.['version'] ?? qsParams?.get('version') ?? '') - const [proxyOverride, setProxyOverride] = React.useState(initialData?.proxyOverride ?? qsParams?.get('proxy') ?? '') - const [usernameOverride, setUsernameOverride] = React.useState(initialData?.usernameOverride ?? qsParams?.get('username') ?? '') const smallWidth = useIsSmallWidth() - const lockConnect = qsParams?.get('lockConnect') === 'true' const initialAccount = initialData?.authenticatedAccountOverride const [accountIndex, setAccountIndex] = React.useState(initialAccount === true ? -2 : initialAccount ? (accounts?.includes(initialAccount) ? accounts.indexOf(initialAccount) : -2) : -1) @@ -61,7 +67,7 @@ export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQ authenticatedAccountOverride, } - return + return
-
- setServerName(value)} placeholder='Defaults to IP' /> -
- setServerIp(value)} /> - setServerPort(value)} placeholder='25565' /> + {!lockConnect && <> +
+ setServerName(value)} placeholder='Defaults to IP' /> +
+ } + setServerIp(value)} /> + setServerPort(value)} placeholder='25565' />
Overrides:
- setVersionOverride(value)} placeholder='Optional, but recommended to specify' /> - setProxyOverride(value)} placeholder={defaults?.proxyOverride} /> - setUsernameOverride(value)} placeholder={defaults?.usernameOverride} disabled={!noAccountSelected} /> + setVersionOverride(value)} placeholder='Optional, but recommended to specify' /> + setProxyOverride(value)} placeholder={defaults?.proxyOverride} /> + setUsernameOverride(value)} placeholder={defaults?.usernameOverride} />