diff --git a/prismarine-viewer/viewer/lib/worldDataEmitter.ts b/prismarine-viewer/viewer/lib/worldDataEmitter.ts index d832d3db2..92a8ac4fb 100644 --- a/prismarine-viewer/viewer/lib/worldDataEmitter.ts +++ b/prismarine-viewer/viewer/lib/worldDataEmitter.ts @@ -5,7 +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 { getItemFromBlock } from '../../../src/chatUtils' import { chunkPos } from './simpleUtils' export type ChunkPosKey = string diff --git a/src/botUtils.ts b/src/botUtils.ts index e98d1e84a..f474d5325 100644 --- a/src/botUtils.ts +++ b/src/botUtils.ts @@ -1,127 +1,5 @@ -// this should actually be moved to mineflayer / prismarine-viewer - -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 - color?: string - bold?: boolean - italic?: boolean - underlined?: boolean - strikethrough?: boolean - obfuscated?: boolean -} - -type MessageInput = { - text?: string - translate?: string - with?: Array - color?: string - bold?: boolean - italic?: boolean - underlined?: boolean - strikethrough?: boolean - obfuscated?: boolean - extra?: MessageInput[] - json?: any -} - -const global = globalThis as any - -// todo move to sign-renderer, replace with prismarine-chat, fix mcData issue! -export const formatMessage = (message: MessageInput, mcData: IndexedData = global.loadedData) => { - let msglist: MessageFormatPart[] = [] - - const readMsg = (msg: MessageInput) => { - const styles = { - color: msg.color, - bold: !!msg.bold, - italic: !!msg.italic, - underlined: !!msg.underlined, - strikethrough: !!msg.strikethrough, - obfuscated: !!msg.obfuscated - } - - if (msg.text) { - msglist.push({ - ...msg, - text: msg.text, - ...styles - }) - } else if (msg.translate) { - const tText = mcData?.language[msg.translate] ?? msg.translate - - if (msg.with) { - const splitted = tText.split(/%s|%\d+\$s/g) - - let i = 0 - for (const [j, part] of splitted.entries()) { - msglist.push({ text: part, ...styles }) - - if (j + 1 < splitted.length) { - if (msg.with[i]) { - const msgWith = msg.with[i] - if (typeof msgWith === 'string') { - readMsg({ - ...styles, - text: msgWith - }) - } else { - readMsg({ - ...styles, - ...msgWith - }) - } - } - i++ - } - } - } else { - msglist.push({ - ...msg, - text: tText, - ...styles - }) - } - } - - if (msg.extra) { - for (const ex of msg.extra) { - readMsg({ ...styles, ...ex }) - } - } - } - - readMsg(message) - - const flat = (msg) => { - return [msg, msg.extra?.flatMap(flat) ?? []] - } - - msglist = msglist.map(msg => { - // normalize § - if (!msg.text.includes?.('§')) return msg - const newMsg = fromFormattedString(msg.text) - return flat(newMsg) - }).flat(Infinity) - - return msglist -} - -const blockToItemRemaps = { - water: 'water_bucket', - lava: 'lava_bucket', - redstone_wire: 'redstone', - tripwire: 'tripwire_hook' -} - -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 diff --git a/src/botUtils.test.ts b/src/chatUtils.test.ts similarity index 96% rename from src/botUtils.test.ts rename to src/chatUtils.test.ts index 99aa07b45..e717da284 100644 --- a/src/botUtils.test.ts +++ b/src/chatUtils.test.ts @@ -1,6 +1,6 @@ import { test, expect } from 'vitest' import mcData from 'minecraft-data' -import { formatMessage } from './botUtils' +import { formatMessage } from './chatUtils' //@ts-expect-error globalThis.loadedData ??= mcData('1.20.1') diff --git a/src/chatUtils.ts b/src/chatUtils.ts new file mode 100644 index 000000000..e1cd7df21 --- /dev/null +++ b/src/chatUtils.ts @@ -0,0 +1,123 @@ +// this should actually be moved to mineflayer / prismarine-viewer + +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 + color?: string + bold?: boolean + italic?: boolean + underlined?: boolean + strikethrough?: boolean + obfuscated?: boolean +} + +type MessageInput = { + text?: string + translate?: string + with?: Array + color?: string + bold?: boolean + italic?: boolean + underlined?: boolean + strikethrough?: boolean + obfuscated?: boolean + extra?: MessageInput[] + json?: any +} + +const global = globalThis as any + +// todo move to sign-renderer, replace with prismarine-chat, fix mcData issue! +export const formatMessage = (message: MessageInput, mcData: IndexedData = global.loadedData) => { + let msglist: MessageFormatPart[] = [] + + const readMsg = (msg: MessageInput) => { + const styles = { + color: msg.color, + bold: !!msg.bold, + italic: !!msg.italic, + underlined: !!msg.underlined, + strikethrough: !!msg.strikethrough, + obfuscated: !!msg.obfuscated + } + + if (msg.text) { + msglist.push({ + ...msg, + text: msg.text, + ...styles + }) + } else if (msg.translate) { + const tText = mcData?.language[msg.translate] ?? msg.translate + + if (msg.with) { + const splitted = tText.split(/%s|%\d+\$s/g) + + let i = 0 + for (const [j, part] of splitted.entries()) { + msglist.push({ text: part, ...styles }) + + if (j + 1 < splitted.length) { + if (msg.with[i]) { + const msgWith = msg.with[i] + if (typeof msgWith === 'string') { + readMsg({ + ...styles, + text: msgWith + }) + } else { + readMsg({ + ...styles, + ...msgWith + }) + } + } + i++ + } + } + } else { + msglist.push({ + ...msg, + text: tText, + ...styles + }) + } + } + + if (msg.extra) { + for (const ex of msg.extra) { + readMsg({ ...styles, ...ex }) + } + } + } + + readMsg(message) + + const flat = (msg) => { + return [msg, msg.extra?.flatMap(flat) ?? []] + } + + msglist = msglist.map(msg => { + // normalize § + if (!msg.text.includes?.('§')) return msg + const newMsg = fromFormattedString(msg.text) + return flat(newMsg) + }).flat(Infinity) + + return msglist +} + +const blockToItemRemaps = { + water: 'water_bucket', + lava: 'lava_bucket', + redstone_wire: 'redstone', + tripwire: 'tripwire_hook' +} + +export const getItemFromBlock = (block: import('prismarine-block').Block) => { + const item = global.loadedData.itemsByName[blockToItemRemaps[block.name] ?? block.name] + return item +} diff --git a/src/controls.ts b/src/controls.ts index d882e9673..2edfdf53f 100644 --- a/src/controls.ts +++ b/src/controls.ts @@ -17,7 +17,7 @@ import { customCommandsConfig } from './customCommands' import type { CustomCommand } from './react/KeybindingsCustom' import { showOptionsModal } from './react/SelectOption' import widgets from './react/widgets' -import { getItemFromBlock } from './botUtils' +import { getItemFromBlock } from './chatUtils' import { gamepadUiCursorState, moveGamepadCursorByPx } from './react/GamepadUiCursor' import { completeTexturePackInstall, resourcePackState } from './resourcePack' import { showNotification } from './react/NotificationProvider' diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 0665b7a67..bf847098b 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -17,9 +17,10 @@ import { appReplacableResources } from './generated/resources' import { activeModalStack, hideCurrentModal, hideModal, miscUiState, showModal } from './globalState' import { options } from './optionsStorage' import { assertDefined, inGameError } from './utils' -import { displayClientChat, MessageFormatPart } from './botUtils' +import { displayClientChat } from './botUtils' import { currentScaling } from './scaleInterface' import { getItemDescription } from './itemsDescriptions' +import { MessageFormatPart } from './chatUtils' const loadedImagesCache = new Map() const cleanLoadedImagesCache = () => { diff --git a/src/react/Chat.stories.tsx b/src/react/Chat.stories.tsx index 35fbc9fbd..192d5cb4e 100644 --- a/src/react/Chat.stories.tsx +++ b/src/react/Chat.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react' import { useEffect, useState } from 'react' -import { formatMessage } from '../botUtils' +import { formatMessage } from '../chatUtils' import Chat, { fadeMessage, chatInputValueGlobal } from './Chat' import Button from './Button' diff --git a/src/react/Chat.tsx b/src/react/Chat.tsx index bd9a1714c..106b0a2f8 100644 --- a/src/react/Chat.tsx +++ b/src/react/Chat.tsx @@ -1,6 +1,6 @@ import { proxy, subscribe } from 'valtio' import { useEffect, useMemo, useRef, useState } from 'react' -import { MessageFormatPart } from '../botUtils' +import { MessageFormatPart } from '../chatUtils' import { MessagePart } from './MessageFormatted' import './Chat.css' import { isIos, reactKeyForMessage } from './utils' diff --git a/src/react/ChatProvider.tsx b/src/react/ChatProvider.tsx index 3ff39e4f0..892360ce6 100644 --- a/src/react/ChatProvider.tsx +++ b/src/react/ChatProvider.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { useSnapshot } from 'valtio' -import { formatMessage } from '../botUtils' +import { formatMessage } from '../chatUtils' import { getBuiltinCommandsList, tryHandleBuiltinCommand } from '../builtinCommands' import { hideCurrentModal, loadedGameState, miscUiState } from '../globalState' import { options } from '../optionsStorage' diff --git a/src/react/DeathScreen.tsx b/src/react/DeathScreen.tsx index ea4dc7262..8f4c3f005 100644 --- a/src/react/DeathScreen.tsx +++ b/src/react/DeathScreen.tsx @@ -1,5 +1,5 @@ import './deathScreen.css' -import type { MessageFormatPart } from '../botUtils' +import type { MessageFormatPart } from '../chatUtils' import MessageFormatted from './MessageFormatted' import Button from './Button' diff --git a/src/react/DeathScreenProvider.tsx b/src/react/DeathScreenProvider.tsx index 8d5ab1c87..d3b598649 100644 --- a/src/react/DeathScreenProvider.tsx +++ b/src/react/DeathScreenProvider.tsx @@ -1,7 +1,7 @@ import { useEffect } from 'react' import { proxy, useSnapshot } from 'valtio' import { disconnect } from '../flyingSquidUtils' -import { MessageFormatPart, formatMessage } from '../botUtils' +import { MessageFormatPart, formatMessage } from '../chatUtils' import { showModal, hideModal } from '../globalState' import { options } from '../optionsStorage' import DeathScreen from './DeathScreen' diff --git a/src/react/MessageFormatted.tsx b/src/react/MessageFormatted.tsx index ebfaf2edc..3b727fc8a 100644 --- a/src/react/MessageFormatted.tsx +++ b/src/react/MessageFormatted.tsx @@ -3,7 +3,7 @@ import { render } from '@xmcl/text-component' import { noCase } from 'change-case' import mojangson from 'mojangson' import { openURL } from 'prismarine-viewer/viewer/lib/simpleUtils' -import { MessageFormatPart } from '../botUtils' +import { MessageFormatPart } from '../chatUtils' import { chatInputValueGlobal } from './Chat' import './MessageFormatted.css' diff --git a/src/react/MessageFormattedString.tsx b/src/react/MessageFormattedString.tsx index 32b8fad6a..ac261889e 100644 --- a/src/react/MessageFormattedString.tsx +++ b/src/react/MessageFormattedString.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react' import { fromFormattedString } from '@xmcl/text-component' -import { formatMessage } from '../botUtils' +import { formatMessage } from '../chatUtils' import MessageFormatted from './MessageFormatted' /** like MessageFormatted, but receives raw string or json instead, uses window.loadedData */