No big features yet (#52)
This commit is contained in:
commit
59b689d931
15 changed files with 143 additions and 72 deletions
1
.github/workflows/publish.yml
vendored
1
.github/workflows/publish.yml
vendored
|
|
@ -19,6 +19,7 @@ jobs:
|
|||
- run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
|
||||
# will install + build to .vercel/output/static
|
||||
- run: vercel build --token=${{ secrets.VERCEL_TOKEN }} --prod
|
||||
- run: pnpm build-storybook
|
||||
- name: Copy playground files
|
||||
run: node prismarine-viewer/esbuild.mjs && cp prismarine-viewer/public/index.html .vercel/output/static/playground.html && cp prismarine-viewer/public/playground.js .vercel/output/static/playground.js
|
||||
- name: Download Generated Sounds map
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
"test-mc-server": "tsx cypress/minecraft-server.mjs",
|
||||
"lint": "eslint \"{src,cypress}/**/*.{ts,js,jsx,tsx}\"",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build",
|
||||
"build-storybook": "storybook build && node scripts/build.js moveStorybookFiles",
|
||||
"start-experiments": "vite --config experiments/vite.config.ts",
|
||||
"watch-worker": "node prismarine-viewer/buildWorker.mjs -w"
|
||||
},
|
||||
|
|
@ -98,7 +98,7 @@
|
|||
"http-server": "^14.1.1",
|
||||
"https-browserify": "^1.0.0",
|
||||
"minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next",
|
||||
"mineflayer": "github:zardoy/mineflayer#custom",
|
||||
"mineflayer": "github:PrismarineJS/mineflayer",
|
||||
"mineflayer-pathfinder": "^2.4.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"os-browserify": "^0.3.0",
|
||||
|
|
|
|||
60
pnpm-lock.yaml
generated
60
pnpm-lock.yaml
generated
|
|
@ -226,8 +226,8 @@ importers:
|
|||
specifier: github:zardoy/minecraft-inventory-gui#next
|
||||
version: github.com/zardoy/minecraft-inventory-gui/69003692b3041d94a420a65c7d3cc1b37737e838(@types/react@18.2.20)(react@18.2.0)
|
||||
mineflayer:
|
||||
specifier: github:zardoy/mineflayer#custom
|
||||
version: github.com/zardoy/mineflayer/e828c161aab120f2d926fba48de3b4d57c361710
|
||||
specifier: github:PrismarineJS/mineflayer
|
||||
version: github.com/PrismarineJS/mineflayer/5c71edf48bb2f2dfa16cddb9af5baa0c4d55cf0d
|
||||
mineflayer-pathfinder:
|
||||
specifier: ^2.4.4
|
||||
version: 2.4.4
|
||||
|
|
@ -14846,6 +14846,34 @@ packages:
|
|||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
github.com/PrismarineJS/mineflayer/5c71edf48bb2f2dfa16cddb9af5baa0c4d55cf0d:
|
||||
resolution: {tarball: https://codeload.github.com/PrismarineJS/mineflayer/tar.gz/5c71edf48bb2f2dfa16cddb9af5baa0c4d55cf0d}
|
||||
name: mineflayer
|
||||
version: 4.17.0
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
minecraft-data: 3.58.0
|
||||
minecraft-protocol: github.com/zardoy/minecraft-protocol/436e0f2945d82408cfd1eb4262535c205bcba8d0
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.58.0)(prismarine-registry@1.7.0)
|
||||
prismarine-block: github.com/zardoy/prismarine-block/00cd810ca6853024b2e73ff0d405d1b1e397defc
|
||||
prismarine-chat: 1.9.1
|
||||
prismarine-chunk: 1.35.0(minecraft-data@3.58.0)
|
||||
prismarine-entity: 2.3.1
|
||||
prismarine-item: 1.14.0
|
||||
prismarine-nbt: 2.2.1
|
||||
prismarine-physics: 1.8.0
|
||||
prismarine-recipe: 1.3.1(prismarine-registry@1.7.0)
|
||||
prismarine-registry: 1.7.0
|
||||
prismarine-windows: 2.8.0
|
||||
prismarine-world: github.com/zardoy/prismarine-world/c358222204d21fe7d45379fbfcefb047f926c786
|
||||
protodef: 1.15.0
|
||||
typed-emitter: 1.4.0
|
||||
vec3: 0.1.8
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
github.com/PrismarineJS/node-process/380d0b4f4c86f1b65b216c311bf00431f314e88e:
|
||||
resolution: {tarball: https://codeload.github.com/PrismarineJS/node-process/tar.gz/380d0b4f4c86f1b65b216c311bf00431f314e88e}
|
||||
name: process
|
||||
|
|
@ -14912,34 +14940,6 @@ packages:
|
|||
- encoding
|
||||
- supports-color
|
||||
|
||||
github.com/zardoy/mineflayer/e828c161aab120f2d926fba48de3b4d57c361710:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/e828c161aab120f2d926fba48de3b4d57c361710}
|
||||
name: mineflayer
|
||||
version: 4.14.0
|
||||
engines: {node: '>=14'}
|
||||
dependencies:
|
||||
minecraft-data: 3.58.0
|
||||
minecraft-protocol: github.com/zardoy/minecraft-protocol/436e0f2945d82408cfd1eb4262535c205bcba8d0
|
||||
prismarine-biome: 1.3.0(minecraft-data@3.58.0)(prismarine-registry@1.7.0)
|
||||
prismarine-block: github.com/zardoy/prismarine-block/00cd810ca6853024b2e73ff0d405d1b1e397defc
|
||||
prismarine-chat: 1.9.1
|
||||
prismarine-chunk: 1.35.0(minecraft-data@3.58.0)
|
||||
prismarine-entity: 2.3.1
|
||||
prismarine-item: 1.14.0
|
||||
prismarine-nbt: 2.2.1
|
||||
prismarine-physics: 1.8.0
|
||||
prismarine-recipe: 1.3.1(prismarine-registry@1.7.0)
|
||||
prismarine-registry: 1.7.0
|
||||
prismarine-windows: 2.8.0
|
||||
prismarine-world: github.com/zardoy/prismarine-world/c358222204d21fe7d45379fbfcefb047f926c786
|
||||
protodef: 1.15.0
|
||||
typed-emitter: 1.4.0
|
||||
vec3: 0.1.8
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
github.com/zardoy/prismarine-block/00cd810ca6853024b2e73ff0d405d1b1e397defc:
|
||||
resolution: {tarball: https://codeload.github.com/zardoy/prismarine-block/tar.gz/00cd810ca6853024b2e73ff0d405d1b1e397defc}
|
||||
name: prismarine-block
|
||||
|
|
|
|||
|
|
@ -91,6 +91,10 @@ exports.getSwAdditionalEntries = () => {
|
|||
return output
|
||||
}
|
||||
|
||||
exports.moveStorybookFiles = () => {
|
||||
fs.renameSync('storybook-static', 'dist/storybook')
|
||||
}
|
||||
|
||||
const fn = require.main === module && exports[process.argv[2]]
|
||||
|
||||
if (fn) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// this should actually be moved to mineflayer / prismarine-viewer
|
||||
|
||||
import { fromFormattedString } from '@xmcl/text-component'
|
||||
import { fromFormattedString, TextComponent } from '@xmcl/text-component'
|
||||
|
||||
export type MessageFormatPart = {
|
||||
export type MessageFormatPart = Pick<TextComponent, 'hoverEvent' | 'clickEvent'> & {
|
||||
text: string
|
||||
color?: string
|
||||
bold?: boolean
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { isGameActive, showModal, gameAdditionalState, activeModalStack, hideCur
|
|||
import { goFullscreen, pointerLock, reloadChunks } from './utils'
|
||||
import { options } from './optionsStorage'
|
||||
import { openPlayerInventory } from './playerWindows'
|
||||
import { initialChatOpenValue } from './react/ChatContainer'
|
||||
import { chatInputValueGlobal } from './react/ChatContainer'
|
||||
import { fsState } from './loadSave'
|
||||
|
||||
// doesnt seem to work for now
|
||||
|
|
@ -226,7 +226,7 @@ contro.on('trigger', ({ command }) => {
|
|||
showModal({ reactType: 'chat' })
|
||||
break
|
||||
case 'general.command':
|
||||
initialChatOpenValue.value = '/'
|
||||
chatInputValueGlobal.value = '/'
|
||||
showModal({ reactType: 'chat' })
|
||||
break
|
||||
case 'general.selectItem':
|
||||
|
|
|
|||
|
|
@ -187,6 +187,9 @@ export const showNotification = (newNotification: Partial<typeof notification>)
|
|||
// todo restore auto-save on interval for player data! (or implement it in flying squid since there is already auto-save for world)
|
||||
|
||||
window.addEventListener('unload', (e) => {
|
||||
if (!window.justReloaded) {
|
||||
sessionStorage.justReloaded = false
|
||||
}
|
||||
void saveServer()
|
||||
})
|
||||
|
||||
|
|
@ -201,6 +204,10 @@ window.inspectPlayer = () => require('fs').promises.readFile('/world/playerdata/
|
|||
|
||||
// todo move from global state
|
||||
window.addEventListener('beforeunload', (event) => {
|
||||
if (!window.justReloaded) {
|
||||
sessionStorage.justReloaded = false
|
||||
}
|
||||
|
||||
// todo-low maybe exclude chat?
|
||||
if (!isGameActive(true) && activeModalStack.at(-1)?.elem?.id !== 'chat') return
|
||||
if (sessionStorage.lastReload && !options.preventDevReloadWhilePlaying) return
|
||||
|
|
|
|||
|
|
@ -433,6 +433,7 @@ async function connect (connectOptions: {
|
|||
noPongTimeout: 240 * 1000,
|
||||
closeTimeout: 240 * 1000,
|
||||
respawn: options.autoRespawn,
|
||||
maxCatchupTicks: 0,
|
||||
async versionSelectedHook (client) {
|
||||
// todo keep in sync with esbuild preload, expose cache ideally
|
||||
if (client.version === '1.20.1') {
|
||||
|
|
@ -447,12 +448,12 @@ async function connect (connectOptions: {
|
|||
if (singleplayer || p2pMultiplayer) {
|
||||
// in case of p2pMultiplayer there is still flying-squid on the host side
|
||||
const _supportFeature = bot.supportFeature
|
||||
bot.supportFeature = (feature) => {
|
||||
bot.supportFeature = ((feature) => {
|
||||
if (unsupportedLocalServerFeatures.includes(feature)) {
|
||||
return false
|
||||
}
|
||||
return _supportFeature(feature)
|
||||
}
|
||||
}) as typeof bot.supportFeature
|
||||
|
||||
bot.emit('inject_allowed')
|
||||
bot._client.emit('connect')
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'
|
|||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { formatMessage } from '../botUtils'
|
||||
import Chat, { fadeMessage, initialChatOpenValue } from './ChatContainer'
|
||||
import Chat, { fadeMessage, chatInputValueGlobal } from './ChatContainer'
|
||||
import Button from './Button'
|
||||
|
||||
window.spamMessage = window.spamMessage ?? ''
|
||||
|
|
@ -20,7 +20,7 @@ const meta: Meta<typeof Chat> = {
|
|||
const abortController = new AbortController()
|
||||
addEventListener('keyup', (e) => {
|
||||
if (e.code === 'KeyY') {
|
||||
initialChatOpenValue.value = '/'
|
||||
chatInputValueGlobal.value = '/'
|
||||
setOpen(true)
|
||||
e.stopImmediatePropagation()
|
||||
}
|
||||
|
|
@ -103,7 +103,6 @@ export const Primary: Story = {
|
|||
'underlined': false,
|
||||
'strikethrough': false,
|
||||
'obfuscated': false,
|
||||
//@ts-expect-error
|
||||
'json': {
|
||||
'insertion': 'pviewer672',
|
||||
'clickEvent': {
|
||||
|
|
@ -129,6 +128,7 @@ export const Primary: Story = {
|
|||
},
|
||||
'hoverEvent': {
|
||||
'action': 'show_entity',
|
||||
//@ts-expect-error
|
||||
'contents': {
|
||||
'type': 'minecraft:player',
|
||||
'id': 'ecd0eeb1-625e-3fea-b16e-cb449dcfa434',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useUsingTouch } from '@dimaka/interface'
|
||||
import { proxy, subscribe } from 'valtio'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { isCypress } from '../standaloneUtils'
|
||||
import { MessageFormatPart } from '../botUtils'
|
||||
|
|
@ -12,7 +13,7 @@ export type Message = {
|
|||
faded?: boolean
|
||||
}
|
||||
|
||||
const MessageLine = ({ message }) => {
|
||||
const MessageLine = ({ message }: {message: Message}) => {
|
||||
const classes = {
|
||||
'chat-message-fadeout': message.fading,
|
||||
'chat-message-fade': message.fading,
|
||||
|
|
@ -35,9 +36,9 @@ type Props = {
|
|||
// width?: number
|
||||
}
|
||||
|
||||
export const initialChatOpenValue = {
|
||||
export const chatInputValueGlobal = proxy({
|
||||
value: ''
|
||||
}
|
||||
})
|
||||
|
||||
export const fadeMessage = (message: Message, initialTimeout: boolean, requestUpdate: () => void) => {
|
||||
setTimeout(() => {
|
||||
|
|
@ -100,11 +101,18 @@ export default ({ messages, opacity = 1, fetchCompletionItems, opened, sendMessa
|
|||
|
||||
useEffect(() => {
|
||||
if (opened) {
|
||||
updateInputValue(initialChatOpenValue.value)
|
||||
initialChatOpenValue.value = ''
|
||||
updateInputValue(chatInputValueGlobal.value)
|
||||
chatInputValueGlobal.value = ''
|
||||
if (!usingTouch) {
|
||||
chatInput.current.focus()
|
||||
}
|
||||
const unsubscribe = subscribe(chatInputValueGlobal, () => {
|
||||
if (!chatInputValueGlobal.value) return
|
||||
updateInputValue(chatInputValueGlobal.value)
|
||||
chatInputValueGlobal.value = ''
|
||||
chatInput.current.focus()
|
||||
})
|
||||
return unsubscribe
|
||||
}
|
||||
if (!opened && chatMessages.current) {
|
||||
chatMessages.current.scrollTop = chatMessages.current.scrollHeight
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import MessageFormatted from './MessageFormatted'
|
|||
import Button from './Button'
|
||||
|
||||
type Props = {
|
||||
dieReasonMessage: readonly MessageFormatPart[]
|
||||
dieReasonMessage: MessageFormatPart[]
|
||||
respawnCallback: () => void
|
||||
disconnectCallback: () => void
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export default () => {
|
|||
if (!isModalActive || !dieReasonMessage || options.autoRespawn) return null
|
||||
|
||||
return <DeathScreen
|
||||
dieReasonMessage={dieReasonMessage}
|
||||
dieReasonMessage={dieReasonMessage as MessageFormatPart[]}
|
||||
respawnCallback={() => {
|
||||
bot._client.write('client_command', bot.supportFeature('respawnIsPayload') ? { payload: 0 } : { actionId: 0 })
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ interface Props {
|
|||
const refreshApp = async () => {
|
||||
const registration = await navigator.serviceWorker.getRegistration()
|
||||
await registration?.unregister()
|
||||
window.justReloaded = true
|
||||
sessionStorage.justReloaded = true
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +38,13 @@ export default ({ connectToServerAction, mapsProvider, singleplayerAction, optio
|
|||
fetch('./version.txt').then(async (f) => {
|
||||
if (f.status === 404) return
|
||||
const contents = await f.text()
|
||||
setVersionStatus(`(${contents === process.env.BUILD_VERSION ? 'latest' : 'new version available'})`)
|
||||
const isLatest = contents === process.env.BUILD_VERSION
|
||||
if (!isLatest && sessionStorage.justReloaded) {
|
||||
// try to force bypass cache
|
||||
location.search = '?update=true'
|
||||
}
|
||||
sessionStorage.justReloaded = false
|
||||
setVersionStatus(`(${isLatest ? 'latest' : 'new version available'})`)
|
||||
setVersionTitle(`Loaded: ${process.env.BUILD_VERSION}. Remote: ${contents}`)
|
||||
}, () => { })
|
||||
}
|
||||
|
|
@ -114,7 +122,10 @@ export default ({ connectToServerAction, mapsProvider, singleplayerAction, optio
|
|||
<div className={styles['bottom-info']}>
|
||||
<span
|
||||
title={`${versionTitle} (click to reload)`}
|
||||
onClick={refreshApp}
|
||||
onClick={async () => {
|
||||
setVersionStatus('(reloading)')
|
||||
await refreshApp()
|
||||
}}
|
||||
className={styles['product-info']}
|
||||
>
|
||||
Prismarine Web Client {versionStatus}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,63 @@
|
|||
import { ComponentProps } from 'react'
|
||||
import { render } from '@xmcl/text-component'
|
||||
import { noCase } from 'change-case'
|
||||
import { MessageFormatPart } from '../botUtils'
|
||||
import { openURL } from '../menus/components/common'
|
||||
import { chatInputValueGlobal } from './ChatContainer'
|
||||
|
||||
const hoverItemToText = (hoverEvent: MessageFormatPart['hoverEvent']) => {
|
||||
if (!hoverEvent) return undefined
|
||||
const contents = hoverEvent['contents'] ?? hoverEvent.value
|
||||
if (typeof contents === 'string') return contents
|
||||
// if (hoverEvent.action === 'show_text') {
|
||||
// return contents
|
||||
// }
|
||||
if (hoverEvent.action === 'show_item') {
|
||||
return contents.id
|
||||
}
|
||||
if (hoverEvent.action === 'show_entity') {
|
||||
let str = noCase(contents.type.replace('minecraft:', ''))
|
||||
if (contents.name) str += `: ${contents.name.text}`
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
const clickEventToProps = (clickEvent: MessageFormatPart['clickEvent']) => {
|
||||
if (!clickEvent) return
|
||||
if (clickEvent.action === 'run_command' || clickEvent.action === 'suggest_command') {
|
||||
return {
|
||||
onClick () {
|
||||
chatInputValueGlobal.value = clickEvent.value
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clickEvent.action === 'open_url') {
|
||||
return {
|
||||
onClick () {
|
||||
const confirm = window.confirm(`Open ${clickEvent.value}?`)
|
||||
if (confirm) {
|
||||
openURL(clickEvent.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//@ts-expect-error todo
|
||||
if (clickEvent.action === 'copy_to_clipboard') {
|
||||
return {
|
||||
onClick () {
|
||||
navigator.clipboard.writeText(clickEvent.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const MessagePart = ({ part, ...props }: { part: MessageFormatPart } & ComponentProps<'span'>) => {
|
||||
const { color, italic, bold, underlined, strikethrough, text } = part
|
||||
|
||||
const { color, italic, bold, underlined, strikethrough, text, clickEvent, hoverEvent, obfuscated } = part
|
||||
|
||||
const clickProps = clickEventToProps(clickEvent)
|
||||
const hoverMessageRaw = hoverItemToText(hoverEvent)
|
||||
const hoverItemText = hoverMessageRaw && typeof hoverMessageRaw !== 'string' ? render(hoverMessageRaw).children.map(child => child.component.text).join('') : hoverMessageRaw
|
||||
|
||||
const applyStyles = [
|
||||
color ? colorF(color.toLowerCase()) + `; text-shadow: 1px 1px 0px ${getColorShadow(colorF(color.toLowerCase()).replace('color:', ''))}` : messageFormatStylesMap.white,
|
||||
|
|
@ -10,10 +65,11 @@ export const MessagePart = ({ part, ...props }: { part: MessageFormatPart } & Co
|
|||
bold && messageFormatStylesMap.bold,
|
||||
italic && messageFormatStylesMap.italic,
|
||||
underlined && messageFormatStylesMap.underlined,
|
||||
strikethrough && messageFormatStylesMap.strikethrough
|
||||
strikethrough && messageFormatStylesMap.strikethrough,
|
||||
obfuscated && messageFormatStylesMap.obfuscated
|
||||
].filter(Boolean)
|
||||
|
||||
return <span style={parseInlineStyle(applyStyles.join(' '))} {...props}>{text}</span>
|
||||
return <span title={hoverItemText} style={parseInlineStyle(applyStyles.join(' '))} {...clickProps} {...props}>{text}</span>
|
||||
}
|
||||
|
||||
export default ({ parts }: { parts: readonly MessageFormatPart[] }) => {
|
||||
|
|
@ -69,5 +125,6 @@ export const messageFormatStylesMap = {
|
|||
bold: 'font-weight:900',
|
||||
strikethrough: 'text-decoration:line-through',
|
||||
underlined: 'text-decoration:underline',
|
||||
italic: 'font-style:italic'
|
||||
italic: 'font-style:italic',
|
||||
obfuscated: 'color: #222326;background-color: #222326;'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import OptionsGroup from './OptionsGroup'
|
||||
|
||||
const meta: Meta<typeof OptionsGroup> = {
|
||||
component: OptionsGroup,
|
||||
// render: () => <OptionsGroup />
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof OptionsGroup>;
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
group: 'controls',
|
||||
backButtonAction () { }
|
||||
},
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue