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 1/6] server change (#207) --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index e4f860606..330957078 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 2/6] 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 79b10118e..e98d1e84a 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 21b06ca6c..ede5480e8 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 23e898a5a..0665b7a67 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 dd5f0aec4..dcd5dc3d0 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 3/6] 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 4b41660a6..f8bd15576 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 05b83f3cf..6aca0d129 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 4/6] 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 330957078..7813b5911 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 ee659263a..c7476f6c3 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 12a1fc5bf..40a476693 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 de97fb578..26641f5ac 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 b0a447f21..cefa78103 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 db855960f..b26619c33 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 c8e4bc5e5..7d147d0d7 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 type ConnectPeerOptions = { + server?: string } -export const connectToPeer = async (peerId: 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 1c4fdcd4e..19f1385cb 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) ? (