From f7554676fd958f44a587bcec4af383a4e2ee93a5 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 11 Aug 2024 06:01:35 +0300 Subject: [PATCH 001/851] start opt --- scripts/optimizeJson.ts | 195 ++++++++++++++++++++++++++++++++++++++++ scripts/prepareNew.mjs | 181 +++++++++++++++++++++++++++++++++++++ 2 files changed, 376 insertions(+) create mode 100644 scripts/optimizeJson.ts create mode 100644 scripts/prepareNew.mjs diff --git a/scripts/optimizeJson.ts b/scripts/optimizeJson.ts new file mode 100644 index 00000000..05351890 --- /dev/null +++ b/scripts/optimizeJson.ts @@ -0,0 +1,195 @@ +type IdMap = Record + +type DiffData = { + removed: number[], + changed: any[], + added +} + +type SourceData = { + keys: IdMap, + properties: IdMap + source + arrKey? +} + +export default class JsonOptimizer { + keys = {} as IdMap + properties = {} as IdMap + source = {} + arrKey? + ignoreChanges = false + ignoreRemoved = false + + export () { + const { keys, properties, source, arrKey } = this + return { + keys, + properties, + source, + arrKey, + } satisfies SourceData + } + + diffObj (diffing): DiffData { + const removed = [] as number[] + const changed = [] as any[] + const { source, arrKey, ignoreChanges, ignoreRemoved } = this + const added = arrKey ? [] : {} as any + // const added = {} as any + + if (!source || !diffing || typeof source !== 'object' || typeof diffing !== 'object') throw new Error('something is not object') + if (Array.isArray(source) !== Array.isArray(diffing)) throw new Error('something is arr and something is not') + if (Array.isArray(source) && !arrKey) throw new Error('arrKey is required for arrays') + const sourceObj = Array.isArray(source) ? Object.fromEntries(source.map(x => { + const key = x[arrKey] + return [key, x] + })) : source + const diffingObj = Array.isArray(diffing) ? Object.fromEntries(diffing.map(x => { + const key = x[arrKey] + return [key, x] + })) : diffing + + const curKeysMerged = [...new Set([...Object.keys(sourceObj), ...Object.keys(diffingObj)])] + this.keys ??= {} + this.properties ??= {} + let lastRootKeyId = Object.values(this.keys).length + let lastItemKeyId = Object.values(this.properties).length + for (const key of curKeysMerged) { + if (!this.keys[key]) this.keys[key] = lastRootKeyId++ + } + + const addDiff = (key, newVal, prevVal) => { + // const valueMapped = {} as Record + const valueMapped = [] as any[] + if (typeof newVal === 'object' && newVal) { + for (const [key, val] of Object.entries(newVal)) { + if (!isEqualStructured(newVal[key], prevVal[key])) { + let keyMapped = this.properties[key] + if (!keyMapped) { + this.properties[key] = lastItemKeyId++ + keyMapped = this.properties[key] + } + // valueMapped[keyMapped] = newVal[key] + // valueMapped.push(key, newVal[key]) + valueMapped.push(keyMapped, newVal[key]) + } + } + } else { + throw new Error('item is not an object') + } + changed.push(this.keys[key], valueMapped) + // changed.push(key, valueMapped) + } + for (const [key, val] of Object.entries(sourceObj)) { + const diffVal = diffingObj[key]; + if (!diffVal && !ignoreRemoved) { + removed.push(this.keys[key]) + continue + } + if (!ignoreChanges) { + if (!isEqualStructured(val, diffVal)) { + addDiff(key, val, diffVal) + } + } + } + for (const [key, val] of Object.entries(diffingObj)) { + if (!sourceObj[key]) { + if (arrKey) added.push(val) + else added[key] = val + // added[key] = val + continue + } + } + + return { + removed, + changed, + added + } + } + + static restoreData ({ keys, properties, source, arrKey }: SourceData, { removed, changed, added }: DiffData) { + const data = source + if (arrKey) { + data.push(...added ?? []) + } else { + Object.assign(data, added ?? {}) + } + const changeData = (id, newData) => { + const key = keys[id] + if (arrKey) { + const index = data.findIndex(a => a[arrKey] === key) + const oldData = data[index] + let newDataMapped + if (newData && typeof newDataMapped === 'object') { + for (let i = 0; i < newData.length / 2; i++) { + let id = newData[i] + let val = newData[i + 1] + const key = properties[id] + newDataMapped[key] = val + } + newDataMapped + } + data.splice(index, 1, newDataMapped ? { ...oldData, ...newDataMapped } : newDataMapped) + } else { + data[key] = newData + } + } + for (const id of removed) { + changeData(id, undefined) + } + } + + static resolveDefaults (arr) { + if (!Array.isArray(arr)) throw Error('not an array') + const propsValueCount = {} as { + [key: string]: { + [val: string]: number + } + } + for (const obj of arr) { + if (typeof obj !== 'object' || !obj) continue + for (const [key, val] of Object.entries(obj)) { + const valJson = JSON.stringify(val); + propsValueCount[key] ??= {} + propsValueCount[key][valJson] ??= 0 + propsValueCount[key][valJson] += 1 + } + } + const defaults = Object.fromEntries(Object.entries(propsValueCount).map(([prop, values]) => { + const defaultValue = Object.entries(values).sort(([, count1], [, count2]) => count2 - count1)[0][0] + return [prop, defaultValue] + })) + + const newData = [] as any[] + const noData = {} + for (const [i, obj] of arr.entries()) { + if (typeof obj !== 'object' || !obj) { + newData.push(obj) + continue + } + for (const key of Object.keys(defaults)) { + const val = obj[key] + if (!val) { + noData[key] ??= [] + noData[key].push(key) + continue + } + if (defaults[key] === JSON.stringify(val)) { + delete obj[key] + } + } + newData.push(obj) + } + + return { + data: newData, + defaults + } + } +} + +const isEqualStructured = (val1, val2) => { + return JSON.stringify(val1) === JSON.stringify(val2) +} diff --git a/scripts/prepareNew.mjs b/scripts/prepareNew.mjs new file mode 100644 index 00000000..7396fa22 --- /dev/null +++ b/scripts/prepareNew.mjs @@ -0,0 +1,181 @@ +//@ts-check +import { build } from 'esbuild' +import { existsSync } from 'node:fs' +import Module from "node:module" +import { dirname } from 'node:path' +import supportedVersions from '../src/supportedVersions.mjs' +import { gzipSizeFromFileSync } from 'gzip-size' +import fs from 'fs' +import {default as _JsonOptimizer} from './optimizeJson' +import { buildSync } from 'esbuild' + +/** @type {typeof _JsonOptimizer} */ +//@ts-ignore +const JsonOptimizer = _JsonOptimizer.default + +// console.log(a.diff_main(JSON.stringify({ a: 1 }), JSON.stringify({ a: 1, b: 2 }))) + +const require = Module.createRequire(import.meta.url) + +const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') + +function toMajor (version) { + const [a, b] = (version + '').split('.') + return `${a}.${b}` +} + +const versions = {} +const dataTypes = new Set() + +for (const [version, dataSet] of Object.entries(dataPaths.pc)) { + if (!supportedVersions.includes(version)) continue + for (const type of Object.keys(dataSet)) { + dataTypes.add(type) + } + versions[version] = dataSet +} + +const versionToNumber = (ver) => { + const [x, y = '0', z = '0'] = ver.split('.') + return +`${x.padStart(2, '0')}${y.padStart(2, '0')}${z.padStart(2, '0')}` +} + +// if not included here (even as {}) will not be bundled & accessible! +const dataTypeBundling = { + language: { + ignoreRemoved: true, + ignoreChanges: true + }, + blocks: { + arrKey: 'name', + // ignoreRemoved: true, + // genChanges (source, diff) { + // const diffs = {} + // const newItems = {} + // for (const [key, val] of Object.entries(diff)) { + // const src = source[key] + // if (!src) { + // newItems[key] = val + // continue + // } + // const { minStateId, defaultState, maxStateId } = val + // if (defaultState === undefined || minStateId === src.minStateId || maxStateId === src.maxStateId || defaultState === src.defaultState) continue + // diffs[key] = [minStateId, defaultState, maxStateId] + // } + // return { + // stateChanges: diffs + // } + // }, + // ignoreChanges: true + }, + items: { + arrKey: 'name' + }, + attributes: { + arrKey: 'name' + }, + particles: { + arrKey: 'name' + }, + effects: { + arrKey: 'name' + }, + enchantments: { + arrKey: 'name' + }, + instruments: { + arrKey: 'name' + }, + foods: { + arrKey: 'name' + }, + entities: { + arrKey: 'name' + }, + materials: {}, + windows: { + arrKey: 'name' + }, + version: { + raw: true + }, + tints: {}, + biomes: { + arrKey: 'name' + }, + entityLoot: { + arrKey: 'name' + }, + blockLoot: { + arrKey: 'name' + }, + recipes: {}, // todo + blockCollisionShapes: {}, + loginPacket: {}, + protocol: {}, + sounds: { + arrKey: 'name' + } +} + +const notBundling = [...dataTypes.keys()].filter(x => !Object.keys(dataTypeBundling).includes(x)) +console.log("Not bundling minecraft-data data:", notBundling) + +let contents = 'Object.assign(window.mcData, {\n' +let previousData = {} +const diffSources = {} +const versionsArr = Object.entries(versions) +// const versionsArr = Object.entries(versions).slice(-1) +for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) { + // console.log(i, '/', versionsArr.length) + contents += ` '${version}': {\n` + for (const [dataType, dataPath] of Object.entries(dataSet)) { + const config = dataTypeBundling[dataType] + if (!config) continue + if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { + contents += ` get ${dataType} () { return window.globalGetCollisionShapes?.("${version}") },\n` + continue + } + const loc = `minecraft-data/data/${dataPath}/` + const dataPathAbsolute = require.resolve(`minecraft-data/${loc}${dataType}`) + // const data = fs.readFileSync(dataPathAbsolute, 'utf8') + const dataRaw = require(dataPathAbsolute) + let data + if (config.raw) { + data = dataRaw + } else { + if (!diffSources[dataType]) { + diffSources[dataType] = new JsonOptimizer() + diffSources[dataType].source = dataRaw + if (config.arrKey) diffSources[dataType].arrKey = config.arrKey + if (config.ignoreChanges) diffSources[dataType].ignoreChanges = config.ignoreChanges + if (config.ignoreRemoved) diffSources[dataType].ignoreRemoved = config.ignoreRemoved + } + data = diffSources[dataType].diffObj(dataRaw) + } + if (config.genChanges && previousData[dataType]) { + const changes = config.genChanges(previousData[dataType], dataRaw) + Object.assign(data, changes) + } + previousData[dataType] = dataRaw + contents += ` get ${dataType} () { return ${JSON.stringify(data)} },\n` + } + contents += ' },\n' +} +contents += '})' +contents += `\n\nwindow.sources = ${JSON.stringify(Object.fromEntries(Object.entries(diffSources).map(x => [x[0], x[1].export()])), null, 4)}` + +const filePath = './generated/new.js' +buildSync({ + bundle: true, + minify: true, + outfile: filePath, + stdin: { + contents, + + loader: 'js', + }, +}) +// fs.writeFileSync(filePath, contents, 'utf8') + +console.log('size', fs.lstatSync(filePath).size / 1024 / 1024, gzipSizeFromFileSync(filePath) / 1024 / 1024) From d1aa155b79116634810229a06a135bbe41e0314b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 11 Aug 2024 22:57:09 +0300 Subject: [PATCH 002/851] some final progress, but not interested anymore: result 2mb compressed --- experiments/decode.html | 1 + experiments/decode.ts | 26 +++++++ scripts/optimizeJson.ts | 150 +++++++++++++++++++++++----------------- scripts/prepareNew.mjs | 98 +++++++++++++++++++------- 4 files changed, 187 insertions(+), 88 deletions(-) create mode 100644 experiments/decode.html create mode 100644 experiments/decode.ts diff --git a/experiments/decode.html b/experiments/decode.html new file mode 100644 index 00000000..fd55e622 --- /dev/null +++ b/experiments/decode.html @@ -0,0 +1 @@ + diff --git a/experiments/decode.ts b/experiments/decode.ts new file mode 100644 index 00000000..6d0f876d --- /dev/null +++ b/experiments/decode.ts @@ -0,0 +1,26 @@ +// Include the pako library +import pako from 'pako'; +import compressedJsRaw from './compressed.js?raw' + +function decompressFromBase64(input) { + // Decode the Base64 string + const binaryString = atob(input); + const len = binaryString.length; + const bytes = new Uint8Array(len); + + // Convert the binary string to a byte array + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + // Decompress the byte array + const decompressedData = pako.inflate(bytes, { to: 'string' }); + + return decompressedData; +} + +// Use the function +console.time('decompress'); +const decompressedData = decompressFromBase64(compressedJsRaw); +console.timeEnd('decompress') +console.log(decompressedData) diff --git a/scripts/optimizeJson.ts b/scripts/optimizeJson.ts index 05351890..89d6ee41 100644 --- a/scripts/optimizeJson.ts +++ b/scripts/optimizeJson.ts @@ -9,64 +9,65 @@ type DiffData = { type SourceData = { keys: IdMap, properties: IdMap - source + source: Record + diffs: Record arrKey? } export default class JsonOptimizer { keys = {} as IdMap + idToKey = {} as Record properties = {} as IdMap source = {} - arrKey? - ignoreChanges = false - ignoreRemoved = false + previousKeys = [] as number[] + diffs = {} as Record + + constructor(public arrKey?: string, public ignoreChanges = false, public ignoreRemoved = false) { } export () { - const { keys, properties, source, arrKey } = this + const { keys, properties, source, arrKey, diffs } = this return { keys, properties, source, arrKey, + diffs } satisfies SourceData } diffObj (diffing): DiffData { const removed = [] as number[] const changed = [] as any[] - const { source, arrKey, ignoreChanges, ignoreRemoved } = this - const added = arrKey ? [] : {} as any + const { arrKey, ignoreChanges, ignoreRemoved } = this + const added = [] as number[] // const added = {} as any - if (!source || !diffing || typeof source !== 'object' || typeof diffing !== 'object') throw new Error('something is not object') - if (Array.isArray(source) !== Array.isArray(diffing)) throw new Error('something is arr and something is not') - if (Array.isArray(source) && !arrKey) throw new Error('arrKey is required for arrays') - const sourceObj = Array.isArray(source) ? Object.fromEntries(source.map(x => { - const key = x[arrKey] - return [key, x] - })) : source + if (!diffing || typeof diffing !== 'object') throw new Error('diffing data is not object') + if (Array.isArray(diffing) && !arrKey) throw new Error('arrKey is required for arrays') const diffingObj = Array.isArray(diffing) ? Object.fromEntries(diffing.map(x => { const key = x[arrKey] return [key, x] })) : diffing - const curKeysMerged = [...new Set([...Object.keys(sourceObj), ...Object.keys(diffingObj)])] + const possiblyNewKeys = Object.keys(diffingObj) this.keys ??= {} this.properties ??= {} let lastRootKeyId = Object.values(this.keys).length let lastItemKeyId = Object.values(this.properties).length - for (const key of curKeysMerged) { - if (!this.keys[key]) this.keys[key] = lastRootKeyId++ + for (const key of possiblyNewKeys) { + this.keys[key] ??= lastRootKeyId++ + this.idToKey[this.keys[key]] = key } const addDiff = (key, newVal, prevVal) => { // const valueMapped = {} as Record const valueMapped = [] as any[] - if (typeof newVal === 'object' && newVal) { + const isItemObj = typeof newVal === 'object' && newVal + if (isItemObj) { for (const [key, val] of Object.entries(newVal)) { if (!isEqualStructured(newVal[key], prevVal[key])) { let keyMapped = this.properties[key] - if (!keyMapped) { + if (keyMapped === undefined) { this.properties[key] = lastItemKeyId++ keyMapped = this.properties[key] } @@ -75,33 +76,44 @@ export default class JsonOptimizer { valueMapped.push(keyMapped, newVal[key]) } } - } else { - throw new Error('item is not an object') } - changed.push(this.keys[key], valueMapped) + changed.push(this.keys[key], isItemObj ? valueMapped : newVal) // changed.push(key, valueMapped) } - for (const [key, val] of Object.entries(sourceObj)) { - const diffVal = diffingObj[key]; - if (!diffVal && !ignoreRemoved) { - removed.push(this.keys[key]) - continue - } - if (!ignoreChanges) { + for (const [id, val] of Object.entries(this.source)) { + const key = this.idToKey[id] + const diffVal = diffingObj[key] + if (!ignoreChanges && diffVal !== undefined) { if (!isEqualStructured(val, diffVal)) { addDiff(key, val, diffVal) } } } for (const [key, val] of Object.entries(diffingObj)) { - if (!sourceObj[key]) { - if (arrKey) added.push(val) - else added[key] = val - // added[key] = val + const id = this.keys[key] + if (!this.source[id]) { + this.source[id] = val continue } + added.push(id) } + for (const previousKey of this.previousKeys) { + const key = this.idToKey[previousKey] + if (!diffingObj[key] && !ignoreRemoved) { + removed.push(previousKey) + this.previousKeys.splice(this.previousKeys.indexOf(previousKey), 1) + } + } + + for (const previousKey of this.previousKeys) { + const index = added.indexOf(previousKey) + if (index === -1) continue + added.splice(index, 1) + } + + this.previousKeys = [...this.previousKeys, ...added] + return { removed, changed, @@ -109,36 +121,50 @@ export default class JsonOptimizer { } } - static restoreData ({ keys, properties, source, arrKey }: SourceData, { removed, changed, added }: DiffData) { - const data = source - if (arrKey) { - data.push(...added ?? []) - } else { - Object.assign(data, added ?? {}) - } - const changeData = (id, newData) => { - const key = keys[id] - if (arrKey) { - const index = data.findIndex(a => a[arrKey] === key) - const oldData = data[index] - let newDataMapped - if (newData && typeof newDataMapped === 'object') { - for (let i = 0; i < newData.length / 2; i++) { - let id = newData[i] - let val = newData[i + 1] - const key = properties[id] - newDataMapped[key] = val - } - newDataMapped - } - data.splice(index, 1, newDataMapped ? { ...oldData, ...newDataMapped } : newDataMapped) - } else { - data[key] = newData - } - } - for (const id of removed) { - changeData(id, undefined) + recordDiff (key: string, diffObj: string) { + const diff = this.diffObj(diffObj) + this.diffs[key] = diff + } + + static isOptimizedChangeDiff(changePossiblyArrDiff) { + if (!Array.isArray(changePossiblyArrDiff)) return false + if (changePossiblyArrDiff.length % 2 !== 0) return false + for (let i = 0; i < changePossiblyArrDiff.length; i += 2) { + if (typeof changePossiblyArrDiff[i] !== 'number') return false } + return true + } + + static restoreData ({ keys, properties, source, arrKey }: SourceData, key: string) { + // const data = arrKey ? [] : {} + // if (arrKey) { + // data.push(...added ?? []) + // } else { + // Object.assign(data, added ?? {}) + // } + // const changeData = (id, newData) => { + // const key = keys[id] + // if (arrKey) { + // const index = data.findIndex(a => a[arrKey] === key) + // const oldData = data[index] + // const isOptimizedChange = JsonOptimizer.isOptimizedChangeDiff(newData) + // let newDataMapped = {} as Record + // if (isOptimizedChange) { + // for (let i = 0; i < newData.length / 2; i++) { + // let id = newData[i] + // let val = newData[i + 1] + // const key = properties[id] + // newDataMapped[key] = val + // } + // } + // data.splice(index, 1, isOptimizedChange ? { ...oldData, ...newDataMapped } : newData) + // } else { + // data[key] = newData + // } + // } + // for (const id of removed) { + // changeData(id, undefined) + // } } static resolveDefaults (arr) { diff --git a/scripts/prepareNew.mjs b/scripts/prepareNew.mjs index 7396fa22..cce108ee 100644 --- a/scripts/prepareNew.mjs +++ b/scripts/prepareNew.mjs @@ -8,6 +8,7 @@ import { gzipSizeFromFileSync } from 'gzip-size' import fs from 'fs' import {default as _JsonOptimizer} from './optimizeJson' import { buildSync } from 'esbuild' +import { gzipSync } from 'zlib'; /** @type {typeof _JsonOptimizer} */ //@ts-ignore @@ -41,11 +42,19 @@ const versionToNumber = (ver) => { } // if not included here (even as {}) will not be bundled & accessible! +const minify = true +// const dataTypeBundling = { +// recipes: { +// arrKey: 'name', +// // ignoreRemoved: true, +// // ignoreChanges: true +// } +// } const dataTypeBundling = { - language: { - ignoreRemoved: true, - ignoreChanges: true - }, + // language: { + // ignoreRemoved: true, + // ignoreChanges: true + // }, blocks: { arrKey: 'name', // ignoreRemoved: true, @@ -123,8 +132,10 @@ console.log("Not bundling minecraft-data data:", notBundling) let contents = 'Object.assign(window.mcData, {\n' let previousData = {} +// /** @type {Record} */ const diffSources = {} const versionsArr = Object.entries(versions) +const sizePerDataType = {} // const versionsArr = Object.entries(versions).slice(-1) for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) { // console.log(i, '/', versionsArr.length) @@ -140,42 +151,77 @@ for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) { const dataPathAbsolute = require.resolve(`minecraft-data/${loc}${dataType}`) // const data = fs.readFileSync(dataPathAbsolute, 'utf8') const dataRaw = require(dataPathAbsolute) - let data + let injectCode = '' + let rawData = dataRaw if (config.raw) { - data = dataRaw + rawData = dataRaw } else { if (!diffSources[dataType]) { - diffSources[dataType] = new JsonOptimizer() - diffSources[dataType].source = dataRaw - if (config.arrKey) diffSources[dataType].arrKey = config.arrKey - if (config.ignoreChanges) diffSources[dataType].ignoreChanges = config.ignoreChanges - if (config.ignoreRemoved) diffSources[dataType].ignoreRemoved = config.ignoreRemoved + diffSources[dataType] = new JsonOptimizer(config.arrKey, config.ignoreChanges, config.ignoreRemoved) + } + try { + diffSources[dataType].recordDiff(version, dataRaw) + injectCode = `restoreDiff(sources, ${JSON.stringify(dataType)}, ${JSON.stringify(version)})` + } catch (err) { + const error = new Error(`Failed to diff ${dataType} for ${version}: ${err.message}`) + error.stack = err.stack + throw error } - data = diffSources[dataType].diffObj(dataRaw) } + sizePerDataType[dataType] ??= 0 + sizePerDataType[dataType] += Buffer.byteLength(JSON.stringify(injectCode || rawData), 'utf8') if (config.genChanges && previousData[dataType]) { const changes = config.genChanges(previousData[dataType], dataRaw) - Object.assign(data, changes) + // Object.assign(data, changes) } previousData[dataType] = dataRaw - contents += ` get ${dataType} () { return ${JSON.stringify(data)} },\n` + contents += ` get ${dataType} () { return ${injectCode || JSON.stringify(rawData)} },\n` } contents += ' },\n' } contents += '})' -contents += `\n\nwindow.sources = ${JSON.stringify(Object.fromEntries(Object.entries(diffSources).map(x => [x[0], x[1].export()])), null, 4)}` +contents += `\n\nconst sources = ${JSON.stringify(Object.fromEntries(Object.entries(diffSources).map(x => { + const data = x[1].export(); + // const data = {} + sizePerDataType[x[0]] += Buffer.byteLength(JSON.stringify(data), 'utf8') + return [x[0], data]; +})), null, 4)}` + +const totalSize = Object.values(sizePerDataType).reduce((acc, val) => acc + val, 0) +console.log('total size (mb)', totalSize / 1024 / 1024) +console.log( + 'size per data type (mb, %)', + Object.fromEntries(Object.entries(sizePerDataType).map(([dataType, size]) => { + return [dataType, [size / 1024 / 1024, Math.round(size / totalSize * 100)]]; + }).sort((a, b) => { + //@ts-ignore + return b[1][1] - a[1][1]; + })) +) + +function compressToBase64(input) { + const buffer = gzipSync(input); + return buffer.toString('base64'); +} const filePath = './generated/new.js' -buildSync({ - bundle: true, - minify: true, - outfile: filePath, - stdin: { - contents, +if (minify) { + buildSync({ + bundle: true, + minify: true, + outfile: filePath, + stdin: { + contents, - loader: 'js', - }, -}) -// fs.writeFileSync(filePath, contents, 'utf8') + loader: 'js', + }, + }) + const minizedCompressed = compressToBase64(fs.readFileSync(filePath)) + console.log('size of compressed', Buffer.byteLength(minizedCompressed, 'utf8') / 1000 / 1000) + const compressedFilePath = './experiments/compressed.js' + fs.writeFileSync(compressedFilePath, minizedCompressed, 'utf8') +} else { + fs.writeFileSync(filePath, contents, 'utf8') +} -console.log('size', fs.lstatSync(filePath).size / 1024 / 1024, gzipSizeFromFileSync(filePath) / 1024 / 1024) +console.log('size', fs.lstatSync(filePath).size / 1000 / 1000, gzipSizeFromFileSync(filePath) / 1000 / 1000) From 7c68ead81bacd08227f0870fa5723b10483d79f1 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 18 Aug 2024 14:28:30 +0300 Subject: [PATCH 003/851] finishing mc-data optim --- scripts/prepareNew.mjs | 48 ++++++------- scripts/testOptimizedJson.ts | 90 +++++++++++++++++++++++ {scripts => src}/optimizeJson.ts | 118 ++++++++++++++++++------------- 3 files changed, 178 insertions(+), 78 deletions(-) create mode 100644 scripts/testOptimizedJson.ts rename {scripts => src}/optimizeJson.ts (58%) diff --git a/scripts/prepareNew.mjs b/scripts/prepareNew.mjs index cce108ee..14e7b150 100644 --- a/scripts/prepareNew.mjs +++ b/scripts/prepareNew.mjs @@ -6,7 +6,7 @@ import { dirname } from 'node:path' import supportedVersions from '../src/supportedVersions.mjs' import { gzipSizeFromFileSync } from 'gzip-size' import fs from 'fs' -import {default as _JsonOptimizer} from './optimizeJson' +import {default as _JsonOptimizer} from '../src/optimizeJson' import { buildSync } from 'esbuild' import { gzipSync } from 'zlib'; @@ -42,9 +42,9 @@ const versionToNumber = (ver) => { } // if not included here (even as {}) will not be bundled & accessible! -const minify = true +const minifyTestOutput = false // const dataTypeBundling = { -// recipes: { +// biomes: { // arrKey: 'name', // // ignoreRemoved: true, // // ignoreChanges: true @@ -130,21 +130,22 @@ const dataTypeBundling = { const notBundling = [...dataTypes.keys()].filter(x => !Object.keys(dataTypeBundling).includes(x)) console.log("Not bundling minecraft-data data:", notBundling) -let contents = 'Object.assign(window.mcData, {\n' +// let contents = 'Object.assign(window.mcData, {\n' let previousData = {} // /** @type {Record} */ const diffSources = {} const versionsArr = Object.entries(versions) const sizePerDataType = {} +const rawDataVersions = {} // const versionsArr = Object.entries(versions).slice(-1) for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) { // console.log(i, '/', versionsArr.length) - contents += ` '${version}': {\n` + // contents += ` '${version}': {\n` for (const [dataType, dataPath] of Object.entries(dataSet)) { const config = dataTypeBundling[dataType] if (!config) continue if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { - contents += ` get ${dataType} () { return window.globalGetCollisionShapes?.("${version}") },\n` + // contents += ` get ${dataType} () { return window.globalGetCollisionShapes?.("${version}") },\n` continue } const loc = `minecraft-data/data/${dataPath}/` @@ -154,6 +155,8 @@ for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) { let injectCode = '' let rawData = dataRaw if (config.raw) { + rawDataVersions[dataType] ??= {} + rawDataVersions[dataType][version] = rawData rawData = dataRaw } else { if (!diffSources[dataType]) { @@ -175,17 +178,19 @@ for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) { // Object.assign(data, changes) } previousData[dataType] = dataRaw - contents += ` get ${dataType} () { return ${injectCode || JSON.stringify(rawData)} },\n` + // contents += ` get ${dataType} () { return ${injectCode || JSON.stringify(rawData)} },\n` } - contents += ' },\n' + // contents += ' },\n' } -contents += '})' -contents += `\n\nconst sources = ${JSON.stringify(Object.fromEntries(Object.entries(diffSources).map(x => { - const data = x[1].export(); +// contents += '})' +const sources = Object.fromEntries(Object.entries(diffSources).map(x => { + const data = x[1].export() // const data = {} sizePerDataType[x[0]] += Buffer.byteLength(JSON.stringify(data), 'utf8') - return [x[0], data]; -})), null, 4)}` + return [x[0], data] +})) +Object.assign(sources, rawDataVersions) +// contents += `\n\nconst sources = ${JSON.stringify(sources, null, 4)}` const totalSize = Object.values(sizePerDataType).reduce((acc, val) => acc + val, 0) console.log('total size (mb)', totalSize / 1024 / 1024) @@ -204,24 +209,13 @@ function compressToBase64(input) { return buffer.toString('base64'); } -const filePath = './generated/new.js' -if (minify) { - buildSync({ - bundle: true, - minify: true, - outfile: filePath, - stdin: { - contents, - - loader: 'js', - }, - }) +const filePath = './generated/minecraft-data-optimized.json' +fs.writeFileSync(filePath, JSON.stringify(sources), 'utf8') +if (minifyTestOutput) { const minizedCompressed = compressToBase64(fs.readFileSync(filePath)) console.log('size of compressed', Buffer.byteLength(minizedCompressed, 'utf8') / 1000 / 1000) const compressedFilePath = './experiments/compressed.js' fs.writeFileSync(compressedFilePath, minizedCompressed, 'utf8') -} else { - fs.writeFileSync(filePath, contents, 'utf8') } console.log('size', fs.lstatSync(filePath).size / 1000 / 1000, gzipSizeFromFileSync(filePath) / 1000 / 1000) diff --git a/scripts/testOptimizedJson.ts b/scripts/testOptimizedJson.ts new file mode 100644 index 00000000..6cff7555 --- /dev/null +++ b/scripts/testOptimizedJson.ts @@ -0,0 +1,90 @@ +import JsonOptimizer from '../src/optimizeJson'; +import fs from 'fs' +import minecraftData from 'minecraft-data' + +// const data = minecraftData('1.20.4') +const json = JSON.parse(fs.readFileSync('./generated/minecraft-data-optimized.json', 'utf8')) + +const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') + +const validateData = (ver, type) => { + const target = JsonOptimizer.restoreData(json[type], ver) + const arrKey = json[type].arrKey + const originalPath = dataPaths.pc[ver][type] + const original = require(`minecraft-data/minecraft-data/data/${originalPath}/${type}.json`) + if (arrKey) { + const originalKeys = original.map(a => a[arrKey]) as string[] + for (const [i, item] of originalKeys.entries()) { + if (originalKeys.indexOf(item) !== i) { + console.warn(`${type} ${ver} Incorrect source, duplicated arrKey (${arrKey}) ${item}. Ignoring!`) // todo should span instead + const index = originalKeys.indexOf(item); + original.splice(index, 1) + originalKeys.splice(index, 1) + } + } + // if (target.length !== originalKeys.length) { + // throw new Error(`wrong arr length: ${target.length} !== ${original.length}`) + // } + checkKeys(originalKeys, target.map(a => a[arrKey])) + for (const item of target as any[]) { + const keys = Object.entries(item).map(a => a[0]) + const origItem = original.find(a => a[arrKey] === item[arrKey]); + const keysSource = Object.entries(origItem).map(a => a[0]) + checkKeys(keysSource, keys, true, 'prop keys', true) + checkObj(origItem, item) + } + } else { + const keysOriginal = Object.keys(original) + const keysTarget = Object.keys(target) + checkKeys(keysOriginal, keysTarget) + for (const key of keysTarget) { + checkObj(original[key], target[key]) + } + } +} + +const checkObj = (source, diffing) => { + for (const [key, val] of Object.entries(source)) { + if (JSON.stringify(val) !== JSON.stringify(diffing[key])) { + throw new Error(`different value: ${val} ${diffing[key]}`) + } + } +} + +const checkKeys = (source, diffing, isUniq = true, msg = '', redunantOk = false) => { + if (isUniq) { + for (const [i, item] of diffing.entries()) { + if (diffing.indexOf(item) !== i) { + throw new Error(`Duplicate: ${item}: ${i} ${diffing.indexOf(item)} ${msg}`) + } + } + } + for (const key of source) { + if (!diffing.includes(key)) { + throw new Error(`Diffing does not include ${key} ${msg}`) + } + } + if (!redunantOk) { + for (const key of diffing) { + if (!source.includes(key)) { + throw new Error(`Source does not include ${key} ${msg}`) + } + } + } +} + +// console.log(JsonOptimizer.restoreData(json['blocks'], '1.16.2').slice(0, 5)) +// console.log(data.blocksByName.melon_stem.drops) +// test all types + all versions + +for (const type of Object.keys(json)) { + if (!json[type].__IS_OPTIMIZED__) continue + console.log('validating', type) + const source = json[type] + let checkedVer = 0 + for (const ver of Object.keys(source.diffs)) { + validateData(ver, type) + checkedVer++ + } + console.log('Checked versions:', checkedVer) +} diff --git a/scripts/optimizeJson.ts b/src/optimizeJson.ts similarity index 58% rename from scripts/optimizeJson.ts rename to src/optimizeJson.ts index 89d6ee41..7e3cfd1a 100644 --- a/scripts/optimizeJson.ts +++ b/src/optimizeJson.ts @@ -1,3 +1,5 @@ +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' + type IdMap = Record type DiffData = { @@ -12,6 +14,7 @@ type SourceData = { source: Record diffs: Record arrKey? + __IS_OPTIMIZED__: true } export default class JsonOptimizer { @@ -20,9 +23,10 @@ export default class JsonOptimizer { properties = {} as IdMap source = {} previousKeys = [] as number[] + previousValues = {} as Record diffs = {} as Record - constructor(public arrKey?: string, public ignoreChanges = false, public ignoreRemoved = false) { } + constructor (public arrKey?: string, public ignoreChanges = false, public ignoreRemoved = false) { } export () { const { keys, properties, source, arrKey, diffs } = this @@ -31,7 +35,8 @@ export default class JsonOptimizer { properties, source, arrKey, - diffs + diffs, + '__IS_OPTIMIZED__': true } satisfies SourceData } @@ -40,12 +45,11 @@ export default class JsonOptimizer { const changed = [] as any[] const { arrKey, ignoreChanges, ignoreRemoved } = this const added = [] as number[] - // const added = {} as any if (!diffing || typeof diffing !== 'object') throw new Error('diffing data is not object') if (Array.isArray(diffing) && !arrKey) throw new Error('arrKey is required for arrays') const diffingObj = Array.isArray(diffing) ? Object.fromEntries(diffing.map(x => { - const key = x[arrKey] + const key = x[arrKey!] return [key, x] })) : diffing @@ -58,42 +62,43 @@ export default class JsonOptimizer { this.keys[key] ??= lastRootKeyId++ this.idToKey[this.keys[key]] = key } + const DEBUG = false const addDiff = (key, newVal, prevVal) => { - // const valueMapped = {} as Record const valueMapped = [] as any[] const isItemObj = typeof newVal === 'object' && newVal if (isItemObj) { - for (const [key, val] of Object.entries(newVal)) { - if (!isEqualStructured(newVal[key], prevVal[key])) { - let keyMapped = this.properties[key] + for (const [prop, val] of Object.entries(newVal)) { + // mc-data: why push only changed props? eg for blocks only stateId are different between all versions so we skip a lot of duplicated data like block props + if (!isEqualStructured(newVal[prop], prevVal[prop])) { + let keyMapped = this.properties[prop] if (keyMapped === undefined) { - this.properties[key] = lastItemKeyId++ - keyMapped = this.properties[key] + this.properties[prop] = lastItemKeyId++ + keyMapped = this.properties[prop] } - // valueMapped[keyMapped] = newVal[key] - // valueMapped.push(key, newVal[key]) - valueMapped.push(keyMapped, newVal[key]) + valueMapped.push(DEBUG ? prop : keyMapped, newVal[prop]) } } } - changed.push(this.keys[key], isItemObj ? valueMapped : newVal) - // changed.push(key, valueMapped) + const keyId = this.keys[key] + changed.push(DEBUG ? key : keyId, isItemObj ? valueMapped : newVal) } - for (const [id, val] of Object.entries(this.source)) { + for (const [id, sourceVal] of Object.entries(this.source)) { const key = this.idToKey[id] const diffVal = diffingObj[key] if (!ignoreChanges && diffVal !== undefined) { - if (!isEqualStructured(val, diffVal)) { - addDiff(key, val, diffVal) + this.previousValues[id] ??= this.source[id] + const prevVal = this.previousValues[id] + if (!isEqualStructured(prevVal, diffVal)) { + addDiff(key, diffVal, prevVal) } + this.previousValues[id] = diffVal } } for (const [key, val] of Object.entries(diffingObj)) { const id = this.keys[key] if (!this.source[id]) { this.source[id] = val - continue } added.push(id) } @@ -102,10 +107,13 @@ export default class JsonOptimizer { const key = this.idToKey[previousKey] if (!diffingObj[key] && !ignoreRemoved) { removed.push(previousKey) - this.previousKeys.splice(this.previousKeys.indexOf(previousKey), 1) } } + for (const toRemove of removed) { + this.previousKeys.splice(this.previousKeys.indexOf(toRemove), 1) + } + for (const previousKey of this.previousKeys) { const index = added.indexOf(previousKey) if (index === -1) continue @@ -126,7 +134,7 @@ export default class JsonOptimizer { this.diffs[key] = diff } - static isOptimizedChangeDiff(changePossiblyArrDiff) { + static isOptimizedChangeDiff (changePossiblyArrDiff) { if (!Array.isArray(changePossiblyArrDiff)) return false if (changePossiblyArrDiff.length % 2 !== 0) return false for (let i = 0; i < changePossiblyArrDiff.length; i += 2) { @@ -135,36 +143,44 @@ export default class JsonOptimizer { return true } - static restoreData ({ keys, properties, source, arrKey }: SourceData, key: string) { - // const data = arrKey ? [] : {} - // if (arrKey) { - // data.push(...added ?? []) - // } else { - // Object.assign(data, added ?? {}) - // } - // const changeData = (id, newData) => { - // const key = keys[id] - // if (arrKey) { - // const index = data.findIndex(a => a[arrKey] === key) - // const oldData = data[index] - // const isOptimizedChange = JsonOptimizer.isOptimizedChangeDiff(newData) - // let newDataMapped = {} as Record - // if (isOptimizedChange) { - // for (let i = 0; i < newData.length / 2; i++) { - // let id = newData[i] - // let val = newData[i + 1] - // const key = properties[id] - // newDataMapped[key] = val - // } - // } - // data.splice(index, 1, isOptimizedChange ? { ...oldData, ...newDataMapped } : newData) - // } else { - // data[key] = newData - // } - // } - // for (const id of removed) { - // changeData(id, undefined) - // } + static restoreData ({ keys, properties, source, arrKey, diffs }: SourceData, targetKey: string) { + // if (!diffs[targetKey]) throw new Error(`The requested data to restore with key ${targetKey} does not exist`) + const keysById = Object.fromEntries(Object.entries(keys).map(x => [x[1], x[0]])) + const propertiesById = Object.fromEntries(Object.entries(properties).map(x => [x[1], x[0]])) + const dataByKeys = {} as Record + for (const [versionKey, { added, changed, removed }] of Object.entries(diffs)) { + if (versionToNumber(versionKey) >= versionToNumber(targetKey)) { + for (const toAdd of added) { + dataByKeys[toAdd] = source[toAdd] + } + for (const toRemove of removed) { + delete dataByKeys[toRemove] + } + for (let i = 0; i < changed.length; i += 2) { + const key = changed[i] + const change = changed[i+1] + const isOptimizedChange = JsonOptimizer.isOptimizedChangeDiff(change) + if (isOptimizedChange) { + // apply optimized diff + for (let k = 0; k < change.length; k += 2) { + const propId = change[k] + const newVal = change[k + 1] + const prop = propertiesById[propId]; + // const prop = propId + if (prop === undefined) throw new Error(`Property id change is undefined: ${propId}`) + dataByKeys[key][prop] = newVal + } + } else { + dataByKeys[key] = change + } + } + } + } + if (arrKey) { + return Object.values(dataByKeys) + } else { + return Object.fromEntries(Object.entries(dataByKeys).map(([key, val]) => [keysById[key], val])) + } } static resolveDefaults (arr) { From c90b7fdea633a1150cc0bbd993fd8b09f49a912d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 26 Aug 2024 18:11:16 +0300 Subject: [PATCH 004/851] add shim --- src/shims/minecraftData.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/shims/minecraftData.ts diff --git a/src/shims/minecraftData.ts b/src/shims/minecraftData.ts new file mode 100644 index 00000000..6f2d3b81 --- /dev/null +++ b/src/shims/minecraftData.ts @@ -0,0 +1,27 @@ +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' +import JsonOptimizer from '../optimizeJson' +import minecraftDataOptimizedJson from '../generated/minecraft-data-optimized.json' + +const resolver = Promise.withResolvers() +window._MC_DATA_READY = resolver.promise + +window.mcData = new Proxy({}, { + get (t, version: string) { + const dataTypes = Object.keys(minecraftDataOptimizedJson) + console.log(`restoring data for ${version}: ${dataTypes.join(', ')}`) + const allRestored = {} + for (const dataType of dataTypes) { + if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { + return window.globalGetCollisionShapes?.(version) + } + + const data = dataType[dataType] + if (data.__IS_OPTIMIZED__) { + allRestored[dataType] = JsonOptimizer.restoreData(data, version) + } else { + allRestored[dataType] = data + } + } + return allRestored + } +}) From 7247a47e645be87d56fb0b0b0f13307cfd72d3b8 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 26 Aug 2024 18:14:28 +0300 Subject: [PATCH 005/851] rename --- scripts/{testOptimizedJson.ts => testOptimizedMcdata.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts/{testOptimizedJson.ts => testOptimizedMcdata.ts} (100%) diff --git a/scripts/testOptimizedJson.ts b/scripts/testOptimizedMcdata.ts similarity index 100% rename from scripts/testOptimizedJson.ts rename to scripts/testOptimizedMcdata.ts From cd277169f4acabfa140a5840e42d932e9c23bb69 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 26 Aug 2024 19:07:48 +0300 Subject: [PATCH 006/851] fix lint --- src/optimizeJson.ts | 408 ++++++++++++++++++++++---------------------- 1 file changed, 204 insertions(+), 204 deletions(-) diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts index 7e3cfd1a..67769571 100644 --- a/src/optimizeJson.ts +++ b/src/optimizeJson.ts @@ -3,235 +3,235 @@ import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' type IdMap = Record type DiffData = { - removed: number[], - changed: any[], - added + removed: number[], + changed: any[], + added } type SourceData = { - keys: IdMap, - properties: IdMap - source: Record - diffs: Record - arrKey? - __IS_OPTIMIZED__: true + keys: IdMap, + properties: IdMap + source: Record + diffs: Record + arrKey? + __IS_OPTIMIZED__: true } export default class JsonOptimizer { - keys = {} as IdMap - idToKey = {} as Record - properties = {} as IdMap - source = {} - previousKeys = [] as number[] - previousValues = {} as Record - diffs = {} as Record + keys = {} as IdMap + idToKey = {} as Record + properties = {} as IdMap + source = {} + previousKeys = [] as number[] + previousValues = {} as Record + diffs = {} as Record - constructor (public arrKey?: string, public ignoreChanges = false, public ignoreRemoved = false) { } + constructor (public arrKey?: string, public ignoreChanges = false, public ignoreRemoved = false) { } - export () { - const { keys, properties, source, arrKey, diffs } = this - return { - keys, - properties, - source, - arrKey, - diffs, - '__IS_OPTIMIZED__': true - } satisfies SourceData + export () { + const { keys, properties, source, arrKey, diffs } = this + return { + keys, + properties, + source, + arrKey, + diffs, + '__IS_OPTIMIZED__': true + } satisfies SourceData + } + + diffObj (diffing): DiffData { + const removed = [] as number[] + const changed = [] as any[] + const { arrKey, ignoreChanges, ignoreRemoved } = this + const added = [] as number[] + + if (!diffing || typeof diffing !== 'object') throw new Error('diffing data is not object') + if (Array.isArray(diffing) && !arrKey) throw new Error('arrKey is required for arrays') + const diffingObj = Array.isArray(diffing) ? Object.fromEntries(diffing.map(x => { + const key = x[arrKey!] + return [key, x] + })) : diffing + + const possiblyNewKeys = Object.keys(diffingObj) + this.keys ??= {} + this.properties ??= {} + let lastRootKeyId = Object.values(this.keys).length + let lastItemKeyId = Object.values(this.properties).length + for (const key of possiblyNewKeys) { + this.keys[key] ??= lastRootKeyId++ + this.idToKey[this.keys[key]] = key + } + const DEBUG = false + + const addDiff = (key, newVal, prevVal) => { + const valueMapped = [] as any[] + const isItemObj = typeof newVal === 'object' && newVal + if (isItemObj) { + for (const [prop, val] of Object.entries(newVal)) { + // mc-data: why push only changed props? eg for blocks only stateId are different between all versions so we skip a lot of duplicated data like block props + if (!isEqualStructured(newVal[prop], prevVal[prop])) { + let keyMapped = this.properties[prop] + if (keyMapped === undefined) { + this.properties[prop] = lastItemKeyId++ + keyMapped = this.properties[prop] + } + valueMapped.push(DEBUG ? prop : keyMapped, newVal[prop]) + } + } + } + const keyId = this.keys[key] + changed.push(DEBUG ? key : keyId, isItemObj ? valueMapped : newVal) + } + for (const [id, sourceVal] of Object.entries(this.source)) { + const key = this.idToKey[id] + const diffVal = diffingObj[key] + if (!ignoreChanges && diffVal !== undefined) { + this.previousValues[id] ??= this.source[id] + const prevVal = this.previousValues[id] + if (!isEqualStructured(prevVal, diffVal)) { + addDiff(key, diffVal, prevVal) + } + this.previousValues[id] = diffVal + } + } + for (const [key, val] of Object.entries(diffingObj)) { + const id = this.keys[key] + if (!this.source[id]) { + this.source[id] = val + } + added.push(id) } - diffObj (diffing): DiffData { - const removed = [] as number[] - const changed = [] as any[] - const { arrKey, ignoreChanges, ignoreRemoved } = this - const added = [] as number[] + for (const previousKey of this.previousKeys) { + const key = this.idToKey[previousKey] + if (!diffingObj[key] && !ignoreRemoved) { + removed.push(previousKey) + } + } - if (!diffing || typeof diffing !== 'object') throw new Error('diffing data is not object') - if (Array.isArray(diffing) && !arrKey) throw new Error('arrKey is required for arrays') - const diffingObj = Array.isArray(diffing) ? Object.fromEntries(diffing.map(x => { - const key = x[arrKey!] - return [key, x] - })) : diffing + for (const toRemove of removed) { + this.previousKeys.splice(this.previousKeys.indexOf(toRemove), 1) + } - const possiblyNewKeys = Object.keys(diffingObj) - this.keys ??= {} - this.properties ??= {} - let lastRootKeyId = Object.values(this.keys).length - let lastItemKeyId = Object.values(this.properties).length - for (const key of possiblyNewKeys) { - this.keys[key] ??= lastRootKeyId++ - this.idToKey[this.keys[key]] = key - } - const DEBUG = false + for (const previousKey of this.previousKeys) { + const index = added.indexOf(previousKey) + if (index === -1) continue + added.splice(index, 1) + } - const addDiff = (key, newVal, prevVal) => { - const valueMapped = [] as any[] - const isItemObj = typeof newVal === 'object' && newVal - if (isItemObj) { - for (const [prop, val] of Object.entries(newVal)) { - // mc-data: why push only changed props? eg for blocks only stateId are different between all versions so we skip a lot of duplicated data like block props - if (!isEqualStructured(newVal[prop], prevVal[prop])) { - let keyMapped = this.properties[prop] - if (keyMapped === undefined) { - this.properties[prop] = lastItemKeyId++ - keyMapped = this.properties[prop] - } - valueMapped.push(DEBUG ? prop : keyMapped, newVal[prop]) - } - } - } - const keyId = this.keys[key] - changed.push(DEBUG ? key : keyId, isItemObj ? valueMapped : newVal) - } - for (const [id, sourceVal] of Object.entries(this.source)) { - const key = this.idToKey[id] - const diffVal = diffingObj[key] - if (!ignoreChanges && diffVal !== undefined) { - this.previousValues[id] ??= this.source[id] - const prevVal = this.previousValues[id] - if (!isEqualStructured(prevVal, diffVal)) { - addDiff(key, diffVal, prevVal) - } - this.previousValues[id] = diffVal - } - } - for (const [key, val] of Object.entries(diffingObj)) { - const id = this.keys[key] - if (!this.source[id]) { - this.source[id] = val - } - added.push(id) - } + this.previousKeys = [...this.previousKeys, ...added] - for (const previousKey of this.previousKeys) { - const key = this.idToKey[previousKey] - if (!diffingObj[key] && !ignoreRemoved) { - removed.push(previousKey) - } - } + return { + removed, + changed, + added + } + } + recordDiff (key: string, diffObj: string) { + const diff = this.diffObj(diffObj) + this.diffs[key] = diff + } + + static isOptimizedChangeDiff (changePossiblyArrDiff) { + if (!Array.isArray(changePossiblyArrDiff)) return false + if (changePossiblyArrDiff.length % 2 !== 0) return false + for (let i = 0; i < changePossiblyArrDiff.length; i += 2) { + if (typeof changePossiblyArrDiff[i] !== 'number') return false + } + return true + } + + static restoreData ({ keys, properties, source, arrKey, diffs }: SourceData, targetKey: string) { + // if (!diffs[targetKey]) throw new Error(`The requested data to restore with key ${targetKey} does not exist`) + const keysById = Object.fromEntries(Object.entries(keys).map(x => [x[1], x[0]])) + const propertiesById = Object.fromEntries(Object.entries(properties).map(x => [x[1], x[0]])) + const dataByKeys = {} as Record + for (const [versionKey, { added, changed, removed }] of Object.entries(diffs)) { + if (versionToNumber(versionKey) >= versionToNumber(targetKey)) { + for (const toAdd of added) { + dataByKeys[toAdd] = source[toAdd] + } for (const toRemove of removed) { - this.previousKeys.splice(this.previousKeys.indexOf(toRemove), 1) + delete dataByKeys[toRemove] } - - for (const previousKey of this.previousKeys) { - const index = added.indexOf(previousKey) - if (index === -1) continue - added.splice(index, 1) + for (let i = 0; i < changed.length; i += 2) { + const key = changed[i] + const change = changed[i + 1] + const isOptimizedChange = JsonOptimizer.isOptimizedChangeDiff(change) + if (isOptimizedChange) { + // apply optimized diff + for (let k = 0; k < change.length; k += 2) { + const propId = change[k] + const newVal = change[k + 1] + const prop = propertiesById[propId] + // const prop = propId + if (prop === undefined) throw new Error(`Property id change is undefined: ${propId}`) + dataByKeys[key][prop] = newVal + } + } else { + dataByKeys[key] = change + } } + } + } + if (arrKey) { + return Object.values(dataByKeys) + } else { + return Object.fromEntries(Object.entries(dataByKeys).map(([key, val]) => [keysById[key], val])) + } + } - this.previousKeys = [...this.previousKeys, ...added] + static resolveDefaults (arr) { + if (!Array.isArray(arr)) throw new Error('not an array') + const propsValueCount = {} as { + [key: string]: { + [val: string]: number + } + } + for (const obj of arr) { + if (typeof obj !== 'object' || !obj) continue + for (const [key, val] of Object.entries(obj)) { + const valJson = JSON.stringify(val) + propsValueCount[key] ??= {} + propsValueCount[key][valJson] ??= 0 + propsValueCount[key][valJson] += 1 + } + } + const defaults = Object.fromEntries(Object.entries(propsValueCount).map(([prop, values]) => { + const defaultValue = Object.entries(values).sort(([, count1], [, count2]) => count2 - count1)[0][0] + return [prop, defaultValue] + })) - return { - removed, - changed, - added + const newData = [] as any[] + const noData = {} + for (const [i, obj] of arr.entries()) { + if (typeof obj !== 'object' || !obj) { + newData.push(obj) + continue + } + for (const key of Object.keys(defaults)) { + const val = obj[key] + if (!val) { + noData[key] ??= [] + noData[key].push(key) + continue } + if (defaults[key] === JSON.stringify(val)) { + delete obj[key] + } + } + newData.push(obj) } - recordDiff (key: string, diffObj: string) { - const diff = this.diffObj(diffObj) - this.diffs[key] = diff - } - - static isOptimizedChangeDiff (changePossiblyArrDiff) { - if (!Array.isArray(changePossiblyArrDiff)) return false - if (changePossiblyArrDiff.length % 2 !== 0) return false - for (let i = 0; i < changePossiblyArrDiff.length; i += 2) { - if (typeof changePossiblyArrDiff[i] !== 'number') return false - } - return true - } - - static restoreData ({ keys, properties, source, arrKey, diffs }: SourceData, targetKey: string) { - // if (!diffs[targetKey]) throw new Error(`The requested data to restore with key ${targetKey} does not exist`) - const keysById = Object.fromEntries(Object.entries(keys).map(x => [x[1], x[0]])) - const propertiesById = Object.fromEntries(Object.entries(properties).map(x => [x[1], x[0]])) - const dataByKeys = {} as Record - for (const [versionKey, { added, changed, removed }] of Object.entries(diffs)) { - if (versionToNumber(versionKey) >= versionToNumber(targetKey)) { - for (const toAdd of added) { - dataByKeys[toAdd] = source[toAdd] - } - for (const toRemove of removed) { - delete dataByKeys[toRemove] - } - for (let i = 0; i < changed.length; i += 2) { - const key = changed[i] - const change = changed[i+1] - const isOptimizedChange = JsonOptimizer.isOptimizedChangeDiff(change) - if (isOptimizedChange) { - // apply optimized diff - for (let k = 0; k < change.length; k += 2) { - const propId = change[k] - const newVal = change[k + 1] - const prop = propertiesById[propId]; - // const prop = propId - if (prop === undefined) throw new Error(`Property id change is undefined: ${propId}`) - dataByKeys[key][prop] = newVal - } - } else { - dataByKeys[key] = change - } - } - } - } - if (arrKey) { - return Object.values(dataByKeys) - } else { - return Object.fromEntries(Object.entries(dataByKeys).map(([key, val]) => [keysById[key], val])) - } - } - - static resolveDefaults (arr) { - if (!Array.isArray(arr)) throw Error('not an array') - const propsValueCount = {} as { - [key: string]: { - [val: string]: number - } - } - for (const obj of arr) { - if (typeof obj !== 'object' || !obj) continue - for (const [key, val] of Object.entries(obj)) { - const valJson = JSON.stringify(val); - propsValueCount[key] ??= {} - propsValueCount[key][valJson] ??= 0 - propsValueCount[key][valJson] += 1 - } - } - const defaults = Object.fromEntries(Object.entries(propsValueCount).map(([prop, values]) => { - const defaultValue = Object.entries(values).sort(([, count1], [, count2]) => count2 - count1)[0][0] - return [prop, defaultValue] - })) - - const newData = [] as any[] - const noData = {} - for (const [i, obj] of arr.entries()) { - if (typeof obj !== 'object' || !obj) { - newData.push(obj) - continue - } - for (const key of Object.keys(defaults)) { - const val = obj[key] - if (!val) { - noData[key] ??= [] - noData[key].push(key) - continue - } - if (defaults[key] === JSON.stringify(val)) { - delete obj[key] - } - } - newData.push(obj) - } - - return { - data: newData, - defaults - } + return { + data: newData, + defaults } + } } const isEqualStructured = (val1, val2) => { - return JSON.stringify(val1) === JSON.stringify(val2) + return JSON.stringify(val1) === JSON.stringify(val2) } From 1d255935741d09b6e00a38a1bc4bc68d0426e073 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 26 Aug 2024 19:08:38 +0300 Subject: [PATCH 007/851] diff arr key --- scripts/prepareNew.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepareNew.mjs b/scripts/prepareNew.mjs index 14e7b150..3ddf4ac5 100644 --- a/scripts/prepareNew.mjs +++ b/scripts/prepareNew.mjs @@ -99,7 +99,7 @@ const dataTypeBundling = { arrKey: 'name' }, entities: { - arrKey: 'name' + arrKey: 'id' }, materials: {}, windows: { From 6fde5cac9bf50e4c44dc0f27894b81d28f4c5009 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 26 Aug 2024 20:12:16 +0300 Subject: [PATCH 008/851] fix all remaining issues with data bundling. all validation passing. result: 6mb uncompressed and 0.9mb compressed --- scripts/prepareNew.mjs | 16 ++++++++-------- scripts/testOptimizedMcdata.ts | 17 ++++++++++++----- src/optimizeJson.ts | 8 ++++++-- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/scripts/prepareNew.mjs b/scripts/prepareNew.mjs index 3ddf4ac5..e95022b9 100644 --- a/scripts/prepareNew.mjs +++ b/scripts/prepareNew.mjs @@ -51,10 +51,10 @@ const minifyTestOutput = false // } // } const dataTypeBundling = { - // language: { - // ignoreRemoved: true, - // ignoreChanges: true - // }, + language: { + ignoreRemoved: true, + ignoreChanges: true + }, blocks: { arrKey: 'name', // ignoreRemoved: true, @@ -99,7 +99,7 @@ const dataTypeBundling = { arrKey: 'name' }, entities: { - arrKey: 'id' + arrKey: 'id+type' }, materials: {}, windows: { @@ -113,12 +113,12 @@ const dataTypeBundling = { arrKey: 'name' }, entityLoot: { - arrKey: 'name' + arrKey: 'entity' }, blockLoot: { - arrKey: 'name' + arrKey: 'block' }, - recipes: {}, // todo + recipes: {}, // todo we can do better blockCollisionShapes: {}, loginPacket: {}, protocol: {}, diff --git a/scripts/testOptimizedMcdata.ts b/scripts/testOptimizedMcdata.ts index 6cff7555..e53dbdbb 100644 --- a/scripts/testOptimizedMcdata.ts +++ b/scripts/testOptimizedMcdata.ts @@ -13,7 +13,7 @@ const validateData = (ver, type) => { const originalPath = dataPaths.pc[ver][type] const original = require(`minecraft-data/minecraft-data/data/${originalPath}/${type}.json`) if (arrKey) { - const originalKeys = original.map(a => a[arrKey]) as string[] + const originalKeys = original.map(a => JsonOptimizer.getByArrKey(a, arrKey)) as string[] for (const [i, item] of originalKeys.entries()) { if (originalKeys.indexOf(item) !== i) { console.warn(`${type} ${ver} Incorrect source, duplicated arrKey (${arrKey}) ${item}. Ignoring!`) // todo should span instead @@ -25,10 +25,10 @@ const validateData = (ver, type) => { // if (target.length !== originalKeys.length) { // throw new Error(`wrong arr length: ${target.length} !== ${original.length}`) // } - checkKeys(originalKeys, target.map(a => a[arrKey])) + checkKeys(originalKeys, target.map(a => JsonOptimizer.getByArrKey(a, arrKey))) for (const item of target as any[]) { const keys = Object.entries(item).map(a => a[0]) - const origItem = original.find(a => a[arrKey] === item[arrKey]); + const origItem = original.find(a => JsonOptimizer.getByArrKey(a, arrKey) === JsonOptimizer.getByArrKey(item, arrKey)); const keysSource = Object.entries(origItem).map(a => a[0]) checkKeys(keysSource, keys, true, 'prop keys', true) checkObj(origItem, item) @@ -46,7 +46,7 @@ const validateData = (ver, type) => { const checkObj = (source, diffing) => { for (const [key, val] of Object.entries(source)) { if (JSON.stringify(val) !== JSON.stringify(diffing[key])) { - throw new Error(`different value: ${val} ${diffing[key]}`) + throw new Error(`different value of ${key}: ${val} ${diffing[key]}`) } } } @@ -79,11 +79,18 @@ const checkKeys = (source, diffing, isUniq = true, msg = '', redunantOk = false) for (const type of Object.keys(json)) { if (!json[type].__IS_OPTIMIZED__) continue + if (type === 'language') continue // we have loose data for language for size reasons console.log('validating', type) const source = json[type] let checkedVer = 0 for (const ver of Object.keys(source.diffs)) { - validateData(ver, type) + try { + validateData(ver, type) + } catch (err) { + const error = new Error(`Failed to validate ${type} for ${ver}: ${err.message}`); + error.stack = err.stack + throw error + } checkedVer++ } console.log('Checked versions:', checkedVer) diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts index 67769571..d1a7ef82 100644 --- a/src/optimizeJson.ts +++ b/src/optimizeJson.ts @@ -49,7 +49,7 @@ export default class JsonOptimizer { if (!diffing || typeof diffing !== 'object') throw new Error('diffing data is not object') if (Array.isArray(diffing) && !arrKey) throw new Error('arrKey is required for arrays') const diffingObj = Array.isArray(diffing) ? Object.fromEntries(diffing.map(x => { - const key = x[arrKey!] + const key = JsonOptimizer.getByArrKey(x, arrKey!) return [key, x] })) : diffing @@ -105,7 +105,7 @@ export default class JsonOptimizer { for (const previousKey of this.previousKeys) { const key = this.idToKey[previousKey] - if (!diffingObj[key] && !ignoreRemoved) { + if (diffingObj[key] === undefined && !ignoreRemoved) { removed.push(previousKey) } } @@ -183,6 +183,10 @@ export default class JsonOptimizer { } } + static getByArrKey (item: any, arrKey: string) { + return arrKey.split('+').map(x => item[x]).join('+') + } + static resolveDefaults (arr) { if (!Array.isArray(arr)) throw new Error('not an array') const propsValueCount = {} as { From 1e20f98d16090ba5341c8d15b65427a41b6d110d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 27 Aug 2024 00:24:10 +0300 Subject: [PATCH 009/851] fix more bugs with data diffs, make it all work in game & playground! --- prismarine-viewer/esbuild.mjs | 18 ++-- prismarine-viewer/examples/playground.ts | 29 +++--- prismarine-viewer/package.json | 3 + .../viewer/lib/worldrendererCommon.ts | 2 +- rsbuild.config.ts | 8 +- scripts/build.js | 1 - scripts/genShims.ts | 17 ---- ...prepareNew.mjs => makeOptimizedMcData.mjs} | 28 ++++-- scripts/prepareData.mjs | 72 -------------- scripts/testOptimizedMcdata.ts | 10 +- src/getCollisionInteractionShapes.ts | 17 ++++ src/getCollisionShapes.ts | 17 +--- src/index.ts | 6 +- src/optimizeJson.ts | 27 ++++- src/shims/minecraftData.ts | 99 +++++++++++++++---- 15 files changed, 178 insertions(+), 176 deletions(-) rename scripts/{prepareNew.mjs => makeOptimizedMcData.mjs} (92%) delete mode 100644 scripts/prepareData.mjs create mode 100644 src/getCollisionInteractionShapes.ts diff --git a/prismarine-viewer/esbuild.mjs b/prismarine-viewer/esbuild.mjs index 90741ee1..e410c8b6 100644 --- a/prismarine-viewer/esbuild.mjs +++ b/prismarine-viewer/esbuild.mjs @@ -6,20 +6,19 @@ import * as esbuild from 'esbuild' import { polyfillNode } from 'esbuild-plugin-polyfill-node' import path, { dirname, join } from 'path' import { fileURLToPath } from 'url' +import childProcess from 'child_process' +import supportedVersions from '../src/supportedVersions.mjs' const dev = process.argv.includes('-w') const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url))) -const mcDataPath = join(__dirname, '../dist/mc-data') +const mcDataPath = join(__dirname, '../generated/minecraft-data-optimized.json') if (!fs.existsSync(mcDataPath)) { - // shouldn't it be in the viewer instead? - await import('../scripts/prepareData.mjs') + childProcess.execSync('tsx ../scripts/makeOptimizedMcData.mjs', { stdio: 'inherit', cwd: __dirname }) } fs.copyFileSync(join(__dirname, 'playground.html'), join(__dirname, 'public/index.html')) -fsExtra.copySync(mcDataPath, join(__dirname, 'public/mc-data')) -const availableVersions = fs.readdirSync(mcDataPath).map(ver => ver.replace('.js', '')) /** @type {import('esbuild').BuildOptions} */ const buildOptions = { @@ -37,7 +36,7 @@ const buildOptions = { ], keepNames: true, banner: { - js: `globalThis.global = globalThis;globalThis.includedVersions = ${JSON.stringify(availableVersions)};`, + js: `globalThis.global = globalThis;globalThis.includedVersions = ${JSON.stringify(supportedVersions)};`, }, alias: { events: 'events', @@ -63,13 +62,14 @@ const buildOptions = { }, () => { const defaultVersionsObj = {} return { - contents: `window.mcData ??= ${JSON.stringify(defaultVersionsObj)};module.exports = { pc: window.mcData }`, - loader: 'js', + contents: fs.readFileSync(join(__dirname, '../src/shims/minecraftData.ts'), 'utf8'), + loader: 'ts', + resolveDir: join(__dirname, '../src/shims'), } }) build.onEnd((e) => { if (e.errors.length) return - // fs.writeFileSync(join(__dirname, 'dist/metafile.json'), JSON.stringify(e.metafile), 'utf8') + fs.writeFileSync(join(__dirname, './public/metafile.json'), JSON.stringify(e.metafile), 'utf8') }) } }, diff --git a/prismarine-viewer/examples/playground.ts b/prismarine-viewer/examples/playground.ts index 28dcceef..1726fe86 100644 --- a/prismarine-viewer/examples/playground.ts +++ b/prismarine-viewer/examples/playground.ts @@ -15,6 +15,7 @@ import { loadScript } from '../viewer/lib/utils' import { TWEEN_DURATION } from '../viewer/lib/entities' import { EntityMesh } from '../viewer/lib/entity/EntityMesh' import { WorldDataEmitter, Viewer } from '../viewer' +import '../../src/getCollisionShapes' import { toMajorVersion } from '../../src/utils' window.THREE = THREE @@ -65,21 +66,21 @@ async function main () { let continuousRender = false const { version } = params + await window._LOAD_MC_DATA() // temporary solution until web worker is here, cache data for faster reloads - const globalMcData = window['mcData'] - if (!globalMcData['version']) { - const major = toMajorVersion(version) - const sessionKey = `mcData-${major}` - if (sessionStorage[sessionKey]) { - Object.assign(globalMcData, JSON.parse(sessionStorage[sessionKey])) - } else { - if (sessionStorage.length > 1) sessionStorage.clear() - await loadScript(`./mc-data/${major}.js`) - try { - sessionStorage[sessionKey] = JSON.stringify(Object.fromEntries(Object.entries(globalMcData).filter(([ver]) => ver.startsWith(major)))) - } catch { } - } - } + // const globalMcData = window['mcData'] + // if (!globalMcData['version']) { + // const major = toMajorVersion(version) + // const sessionKey = `mcData-${major}` + // if (sessionStorage[sessionKey]) { + // Object.assign(globalMcData, JSON.parse(sessionStorage[sessionKey])) + // } else { + // if (sessionStorage.length > 1) sessionStorage.clear() + // try { + // sessionStorage[sessionKey] = JSON.stringify(Object.fromEntries(Object.entries(globalMcData).filter(([ver]) => ver.startsWith(major)))) + // } catch { } + // } + // } const mcData: IndexedData = require('minecraft-data')(version) window['loadedData'] = mcData diff --git a/prismarine-viewer/package.json b/prismarine-viewer/package.json index d0ea3826..02b0a304 100644 --- a/prismarine-viewer/package.json +++ b/prismarine-viewer/package.json @@ -38,5 +38,8 @@ "optionalDependencies": { "canvas": "^2.11.2", "node-canvas-webgl": "^0.3.0" + }, + "devDependencies": { + "live-server": "^1.2.2" } } diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index fbc6db46..995f31ce 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -2,7 +2,7 @@ import { EventEmitter } from 'events' import { Vec3 } from 'vec3' import * as THREE from 'three' -import mcDataRaw from 'minecraft-data/data.js' // handled correctly in esbuild plugin +import mcDataRaw from 'minecraft-data/data.js' // note: using alias import blocksAtlases from 'mc-assets/dist/blocksAtlases.json' import blocksAtlasLatest from 'mc-assets/dist/blocksAtlasLatest.png' import blocksAtlasLegacy from 'mc-assets/dist/blocksAtlasLegacy.png' diff --git a/rsbuild.config.ts b/rsbuild.config.ts index bb5750f7..e3c45e2b 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -100,9 +100,10 @@ export default defineConfig({ const prep = async () => { console.time('total-prep') fs.mkdirSync('./generated', { recursive: true }) - if (!fs.existsSync('./generated/minecraft-data-data.js')) { - childProcess.execSync('tsx ./scripts/genShims.ts', { stdio: 'inherit' }) + if (!fs.existsSync('./generated/minecraft-data-optimized.json') || require('./generated/minecraft-data-optimized.json').versionKey !== require('minecraft-data/package.json').version) { + childProcess.execSync('tsx ./scripts/makeOptimizedMcData.mjs', { stdio: 'inherit' }) } + childProcess.execSync('tsx ./scripts/genShims.ts', { stdio: 'inherit' }) if (!fs.existsSync('./generated/latestBlockCollisionsShapes.json')) { childProcess.execSync('tsx ./scripts/optimizeBlockCollisions.ts', { stdio: 'inherit' }) } @@ -116,7 +117,6 @@ export default defineConfig({ configJson.defaultProxy = ':8080' } fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8') - childProcess.execSync('node ./scripts/prepareData.mjs', { stdio: 'inherit' }) // childProcess.execSync('./scripts/prepareSounds.mjs', { stdio: 'inherit' }) // childProcess.execSync('tsx ./scripts/genMcDataTypes.ts', { stdio: 'inherit' }) // childProcess.execSync('tsx ./scripts/genPixelartTypes.ts', { stdio: 'inherit' }) @@ -163,7 +163,7 @@ export default defineConfig({ // throw new Error(`${resource.request} was requested by ${resource.contextInfo.issuer}`) } if (absolute.endsWith('/minecraft-data/data.js')) { - resource.request = path.join(__dirname, './generated/minecraft-data-data.js') + resource.request = path.join(__dirname, './src/shims/minecraftData.ts') } })) addRules([ diff --git a/scripts/build.js b/scripts/build.js index 7d3dc3fc..9b8a85a5 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -45,7 +45,6 @@ exports.getSwAdditionalEntries = () => { // need to be careful with this const filesToCachePatterns = [ 'index.html', - `mc-data/${defaultLocalServerOptions.versionMajor}.js`, 'background/**', // todo-low copy from assets '*.mp3', diff --git a/scripts/genShims.ts b/scripts/genShims.ts index e688e525..0ee8d91e 100644 --- a/scripts/genShims.ts +++ b/scripts/genShims.ts @@ -1,24 +1,7 @@ import fs from 'fs' -import MinecraftData from 'minecraft-data' -import MCProtocol from 'minecraft-protocol' import { appReplacableResources } from '../src/resourcesSource' -const { supportedVersions, defaultVersion } = MCProtocol - -// gen generated/minecraft-data-data.js - -const data = MinecraftData(defaultVersion) -const defaultVersionObj = { - [defaultVersion]: { - version: data.version, - protocol: data.protocol, - } -} - -const mcDataContents = `window.mcData ??= ${JSON.stringify(defaultVersionObj)};module.exports = { pc: window.mcData }` - fs.mkdirSync('./generated', { recursive: true }) -fs.writeFileSync('./generated/minecraft-data-data.js', mcDataContents, 'utf8') // app resources diff --git a/scripts/prepareNew.mjs b/scripts/makeOptimizedMcData.mjs similarity index 92% rename from scripts/prepareNew.mjs rename to scripts/makeOptimizedMcData.mjs index e95022b9..cda75125 100644 --- a/scripts/prepareNew.mjs +++ b/scripts/makeOptimizedMcData.mjs @@ -7,8 +7,9 @@ import supportedVersions from '../src/supportedVersions.mjs' import { gzipSizeFromFileSync } from 'gzip-size' import fs from 'fs' import {default as _JsonOptimizer} from '../src/optimizeJson' -import { buildSync } from 'esbuild' import { gzipSync } from 'zlib'; +import MinecraftData from 'minecraft-data' +import MCProtocol from 'minecraft-protocol' /** @type {typeof _JsonOptimizer} */ //@ts-ignore @@ -42,7 +43,7 @@ const versionToNumber = (ver) => { } // if not included here (even as {}) will not be bundled & accessible! -const minifyTestOutput = false +const compressedOutput = false // const dataTypeBundling = { // biomes: { // arrKey: 'name', @@ -130,7 +131,6 @@ const dataTypeBundling = { const notBundling = [...dataTypes.keys()].filter(x => !Object.keys(dataTypeBundling).includes(x)) console.log("Not bundling minecraft-data data:", notBundling) -// let contents = 'Object.assign(window.mcData, {\n' let previousData = {} // /** @type {Record} */ const diffSources = {} @@ -139,8 +139,6 @@ const sizePerDataType = {} const rawDataVersions = {} // const versionsArr = Object.entries(versions).slice(-1) for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) { - // console.log(i, '/', versionsArr.length) - // contents += ` '${version}': {\n` for (const [dataType, dataPath] of Object.entries(dataSet)) { const config = dataTypeBundling[dataType] if (!config) continue @@ -178,11 +176,8 @@ for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) { // Object.assign(data, changes) } previousData[dataType] = dataRaw - // contents += ` get ${dataType} () { return ${injectCode || JSON.stringify(rawData)} },\n` } - // contents += ' },\n' } -// contents += '})' const sources = Object.fromEntries(Object.entries(diffSources).map(x => { const data = x[1].export() // const data = {} @@ -190,7 +185,7 @@ const sources = Object.fromEntries(Object.entries(diffSources).map(x => { return [x[0], data] })) Object.assign(sources, rawDataVersions) -// contents += `\n\nconst sources = ${JSON.stringify(sources, null, 4)}` +sources.versionKey = require('minecraft-data/package.json').version const totalSize = Object.values(sizePerDataType).reduce((acc, val) => acc + val, 0) console.log('total size (mb)', totalSize / 1024 / 1024) @@ -211,7 +206,7 @@ function compressToBase64(input) { const filePath = './generated/minecraft-data-optimized.json' fs.writeFileSync(filePath, JSON.stringify(sources), 'utf8') -if (minifyTestOutput) { +if (compressedOutput) { const minizedCompressed = compressToBase64(fs.readFileSync(filePath)) console.log('size of compressed', Buffer.byteLength(minizedCompressed, 'utf8') / 1000 / 1000) const compressedFilePath = './experiments/compressed.js' @@ -219,3 +214,16 @@ if (minifyTestOutput) { } console.log('size', fs.lstatSync(filePath).size / 1000 / 1000, gzipSizeFromFileSync(filePath) / 1000 / 1000) + +// always bundled + +const { defaultVersion } = MCProtocol +const data = MinecraftData(defaultVersion) +const defaultVersionObj = { + [defaultVersion]: { + version: data.version, + protocol: data.protocol, + } +} + +fs.writeFileSync('./generated/minecraft-initial-data.json', JSON.stringify(defaultVersionObj), 'utf8') diff --git a/scripts/prepareData.mjs b/scripts/prepareData.mjs deleted file mode 100644 index ab92499e..00000000 --- a/scripts/prepareData.mjs +++ /dev/null @@ -1,72 +0,0 @@ -//@ts-check -import { build } from 'esbuild' -import { existsSync } from 'node:fs' -import Module from "node:module" -import { dirname } from 'node:path' -import supportedVersions from '../src/supportedVersions.mjs' - -if (existsSync('dist/mc-data') && !process.argv.includes('-f')) { - console.log('using cached prepared data') - process.exit(0) -} - -const require = Module.createRequire(import.meta.url) - -const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') - -function toMajor (version) { - const [a, b] = (version + '').split('.') - return `${a}.${b}` -} - -const grouped = {} - -for (const [version, data] of Object.entries(dataPaths.pc)) { - if (!supportedVersions.includes(version)) continue - const major = toMajor(version) - grouped[major] ??= {} - grouped[major][version] = data -} - -const versionToNumber = (ver) => { - const [x, y = '0', z = '0'] = ver.split('.') - return +`${x.padStart(2, '0')}${y.padStart(2, '0')}${z.padStart(2, '0')}` -} - -console.log('preparing data') -console.time('data prepared') -let builds = [] -for (const [major, versions] of Object.entries(grouped)) { - // if (major !== '1.19') continue - let contents = 'Object.assign(window.mcData, {\n' - for (const [version, dataSet] of Object.entries(versions)) { - contents += ` '${version}': {\n` - for (const [dataType, dataPath] of Object.entries(dataSet)) { - if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { - contents += ` get ${dataType} () { return window.globalGetCollisionShapes?.("${version}") },\n` - continue - } - const loc = `minecraft-data/data/${dataPath}/` - contents += ` get ${dataType} () { return require("./${loc}${dataType}.json") },\n` - } - contents += ' },\n' - } - contents += '})' - - const promise = build({ - bundle: true, - outfile: `dist/mc-data/${major}.js`, - stdin: { - contents, - - resolveDir: dirname(require.resolve('minecraft-data')), - sourcefile: `mcData${major}.js`, - loader: 'js', - }, - metafile: true, - }) - // require('fs').writeFileSync('dist/mc-data/metafile.json', JSON.stringify(promise.metafile), 'utf8') - builds.push(promise) -} -await Promise.all(builds) -console.timeEnd('data prepared') diff --git a/scripts/testOptimizedMcdata.ts b/scripts/testOptimizedMcdata.ts index e53dbdbb..f9196b53 100644 --- a/scripts/testOptimizedMcdata.ts +++ b/scripts/testOptimizedMcdata.ts @@ -44,6 +44,7 @@ const validateData = (ver, type) => { } const checkObj = (source, diffing) => { + checkKeys(Object.keys(source), Object.keys(diffing)) for (const [key, val] of Object.entries(source)) { if (JSON.stringify(val) !== JSON.stringify(diffing[key])) { throw new Error(`different value of ${key}: ${val} ${diffing[key]}`) @@ -61,13 +62,13 @@ const checkKeys = (source, diffing, isUniq = true, msg = '', redunantOk = false) } for (const key of source) { if (!diffing.includes(key)) { - throw new Error(`Diffing does not include ${key} ${msg}`) + throw new Error(`Diffing does not include "${key}" (${msg})`) } } if (!redunantOk) { for (const key of diffing) { if (!source.includes(key)) { - throw new Error(`Source does not include ${key} ${msg}`) + throw new Error(`Source does not include "${key}" (${msg})`) } } } @@ -87,9 +88,8 @@ for (const type of Object.keys(json)) { try { validateData(ver, type) } catch (err) { - const error = new Error(`Failed to validate ${type} for ${ver}: ${err.message}`); - error.stack = err.stack - throw error + err.message = `Failed to validate ${type} for ${ver}: ${err.message}` + throw err; } checkedVer++ } diff --git a/src/getCollisionInteractionShapes.ts b/src/getCollisionInteractionShapes.ts new file mode 100644 index 00000000..9dead22b --- /dev/null +++ b/src/getCollisionInteractionShapes.ts @@ -0,0 +1,17 @@ +import { getRenamedData } from 'flying-squid/dist/blockRenames' +import outputInteractionShapesJson from './interactionShapesGenerated.json' +import './getCollisionShapes' + +export default () => { + customEvents.on('gameLoaded', () => { + // todo also remap block states (e.g. redstone)! + const renamedBlocksInteraction = getRenamedData('blocks', Object.keys(outputInteractionShapesJson), '1.20.2', bot.version) + const interactionShapes = { + ...outputInteractionShapesJson, + ...Object.fromEntries(Object.entries(outputInteractionShapesJson).map(([block, shape], i) => [renamedBlocksInteraction[i], shape])) + } + interactionShapes[''] = interactionShapes['air'] + // todo make earlier + window.interactionShapes = interactionShapes + }) +} diff --git a/src/getCollisionShapes.ts b/src/getCollisionShapes.ts index 4ee0e802..3596cca2 100644 --- a/src/getCollisionShapes.ts +++ b/src/getCollisionShapes.ts @@ -1,6 +1,5 @@ -import { getRenamedData } from 'flying-squid/dist/blockRenames' import collisionShapesInit from '../generated/latestBlockCollisionsShapes.json' -import outputInteractionShapesJson from './interactionShapesGenerated.json' +import { getRenamedData } from 'flying-squid/dist/blockRenames' // defining globally to be used in loaded data, not sure of better workaround window.globalGetCollisionShapes = (version) => { @@ -13,17 +12,3 @@ window.globalGetCollisionShapes = (version) => { } return collisionShapes } - -export default () => { - customEvents.on('gameLoaded', () => { - // todo also remap block states (e.g. redstone)! - const renamedBlocksInteraction = getRenamedData('blocks', Object.keys(outputInteractionShapesJson), '1.20.2', bot.version) - const interactionShapes = { - ...outputInteractionShapesJson, - ...Object.fromEntries(Object.entries(outputInteractionShapesJson).map(([block, shape], i) => [renamedBlocksInteraction[i], shape])) - } - interactionShapes[''] = interactionShapes['air'] - // todo make earlier - window.interactionShapes = interactionShapes - }) -} diff --git a/src/index.ts b/src/index.ts index 39626d54..e233c242 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import './globals' import './devtools' import './entities' import './globalDomListeners' -import initCollisionShapes from './getCollisionShapes' +import initCollisionShapes from './getCollisionInteractionShapes' import { onGameLoad } from './inventoryWindows' import { supportedVersions } from 'minecraft-protocol' import protocolMicrosoftAuth from 'minecraft-protocol/src/client/microsoftAuth' @@ -393,13 +393,13 @@ async function connect (connectOptions: ConnectOptions) { // ignore cache hit versionsByMinecraftVersion.pc[lastVersion]!['dataVersion']!++ } + setLoadingScreenStatus(`Loading data for ${version}`) if (!document.fonts.check('1em mojangles')) { // todo instead re-render signs on load await document.fonts.load('1em mojangles').catch(() => { }) } - setLoadingScreenStatus(`Downloading data for ${version}`) + await window._LOAD_MC_DATA() await downloadSoundsIfNeeded() - await loadScript(`./mc-data/${toMajorVersion(version)}.js`) miscUiState.loadedDataVersion = version try { await resourcepackReload(version) diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts index d1a7ef82..7c757143 100644 --- a/src/optimizeJson.ts +++ b/src/optimizeJson.ts @@ -5,6 +5,7 @@ type IdMap = Record type DiffData = { removed: number[], changed: any[], + removedProps: Array<[number, number[]]>, added } @@ -43,6 +44,7 @@ export default class JsonOptimizer { diffObj (diffing): DiffData { const removed = [] as number[] const changed = [] as any[] + const removedProps = [] as any[] const { arrKey, ignoreChanges, ignoreRemoved } = this const added = [] as number[] @@ -67,7 +69,9 @@ export default class JsonOptimizer { const addDiff = (key, newVal, prevVal) => { const valueMapped = [] as any[] const isItemObj = typeof newVal === 'object' && newVal + const keyId = this.keys[key] if (isItemObj) { + const removedPropsLocal = [] as any[] for (const [prop, val] of Object.entries(newVal)) { // mc-data: why push only changed props? eg for blocks only stateId are different between all versions so we skip a lot of duplicated data like block props if (!isEqualStructured(newVal[prop], prevVal[prop])) { @@ -79,8 +83,18 @@ export default class JsonOptimizer { valueMapped.push(DEBUG ? prop : keyMapped, newVal[prop]) } } + // also add undefined for removed props + for (const prop of Object.keys(prevVal)) { + if (prop in newVal) continue + let keyMapped = this.properties[prop] + if (keyMapped === undefined) { + this.properties[prop] = lastItemKeyId++ + keyMapped = this.properties[prop] + } + removedPropsLocal.push(DEBUG ? prop : keyMapped) + } + removedProps.push([keyId, removedPropsLocal]) } - const keyId = this.keys[key] changed.push(DEBUG ? key : keyId, isItemObj ? valueMapped : newVal) } for (const [id, sourceVal] of Object.entries(this.source)) { @@ -125,7 +139,8 @@ export default class JsonOptimizer { return { removed, changed, - added + added, + removedProps } } @@ -148,7 +163,7 @@ export default class JsonOptimizer { const keysById = Object.fromEntries(Object.entries(keys).map(x => [x[1], x[0]])) const propertiesById = Object.fromEntries(Object.entries(properties).map(x => [x[1], x[0]])) const dataByKeys = {} as Record - for (const [versionKey, { added, changed, removed }] of Object.entries(diffs)) { + for (const [versionKey, { added, changed, removed, removedProps }] of Object.entries(diffs)) { if (versionToNumber(versionKey) >= versionToNumber(targetKey)) { for (const toAdd of added) { dataByKeys[toAdd] = source[toAdd] @@ -174,6 +189,12 @@ export default class JsonOptimizer { dataByKeys[key] = change } } + for (const [key, removePropsId] of removedProps) { + for (const removePropId of removePropsId) { + const removeProp = propertiesById[removePropId] + delete dataByKeys[key][removeProp] + } + } } } if (arrKey) { diff --git a/src/shims/minecraftData.ts b/src/shims/minecraftData.ts index 6f2d3b81..5b8718e4 100644 --- a/src/shims/minecraftData.ts +++ b/src/shims/minecraftData.ts @@ -1,27 +1,84 @@ import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' import JsonOptimizer from '../optimizeJson' -import minecraftDataOptimizedJson from '../generated/minecraft-data-optimized.json' +import minecraftInitialDataJson from '../../generated/minecraft-initial-data.json' +import { toMajorVersion } from '../utils' -const resolver = Promise.withResolvers() -window._MC_DATA_READY = resolver.promise - -window.mcData = new Proxy({}, { - get (t, version: string) { - const dataTypes = Object.keys(minecraftDataOptimizedJson) - console.log(`restoring data for ${version}: ${dataTypes.join(', ')}`) - const allRestored = {} - for (const dataType of dataTypes) { - if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { - return window.globalGetCollisionShapes?.(version) - } - - const data = dataType[dataType] - if (data.__IS_OPTIMIZED__) { - allRestored[dataType] = JsonOptimizer.restoreData(data, version) - } else { - allRestored[dataType] = data - } +const customResolver = () => { + const resolver = Promise.withResolvers() + let resolvedData + return { + ...resolver, + get resolvedData () { + return resolvedData + }, + resolve (data) { + resolver.resolve(data) + resolvedData = data } - return allRestored + } +} + +const optimizedDataResolver = customResolver() +window._MC_DATA_RESOLVER = optimizedDataResolver +window._LOAD_MC_DATA = async () => { + if (optimizedDataResolver.resolvedData) return + optimizedDataResolver.resolve(await import('../../generated/minecraft-data-optimized.json')) +} + +// 30 seconds +const cacheTtl = 30 * 1000 +const cache = new Map() +const cacheTime = new Map() +window.allLoadedMcData = new Proxy({}, { + get (t, version: string) { + // special properties like $typeof + if (version.includes('$')) return + if (cache.has(version)) { + return cache.get(version) + } + const inner = () => { + if (minecraftInitialDataJson[version] && !optimizedDataResolver.resolvedData) { + return minecraftInitialDataJson[version] + } + if (!optimizedDataResolver.resolvedData) { + throw new Error(`Data for ${version} is not ready yet`) + } + const dataTypes = Object.keys(optimizedDataResolver.resolvedData) + const allRestored = {} + for (const dataType of dataTypes) { + if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { + const shapes = window.globalGetCollisionShapes?.(version) + if (shapes) { + allRestored[dataType] = shapes + continue + } + } + + const data = optimizedDataResolver.resolvedData[dataType] + if (data.__IS_OPTIMIZED__) { + allRestored[dataType] = JsonOptimizer.restoreData(data, version) + } else { + allRestored[dataType] = data[version] ?? data[toMajorVersion(version)] + } + } + return allRestored + } + const data = inner() + cache.set(version, data) + cacheTime.set(version, Date.now()) + return data } }) + +setInterval(() => { + const now = Date.now() + for (const [version, time] of cacheTime) { + if (now - time > cacheTtl) { + cache.delete(version) + cacheTime.delete(version) + } + } +}, 1000) + +export const pc = window.allLoadedMcData +export default { pc } From a6c288aa22900ec65bb35693a43d262d5c4b1cd0 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 27 Aug 2024 00:46:57 +0300 Subject: [PATCH 010/851] fix: parse correct versions diffs for the requested version! --- src/optimizeJson.ts | 57 +++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts index 7c757143..0b237307 100644 --- a/src/optimizeJson.ts +++ b/src/optimizeJson.ts @@ -164,38 +164,39 @@ export default class JsonOptimizer { const propertiesById = Object.fromEntries(Object.entries(properties).map(x => [x[1], x[0]])) const dataByKeys = {} as Record for (const [versionKey, { added, changed, removed, removedProps }] of Object.entries(diffs)) { - if (versionToNumber(versionKey) >= versionToNumber(targetKey)) { - for (const toAdd of added) { - dataByKeys[toAdd] = source[toAdd] - } - for (const toRemove of removed) { - delete dataByKeys[toRemove] - } - for (let i = 0; i < changed.length; i += 2) { - const key = changed[i] - const change = changed[i + 1] - const isOptimizedChange = JsonOptimizer.isOptimizedChangeDiff(change) - if (isOptimizedChange) { - // apply optimized diff - for (let k = 0; k < change.length; k += 2) { - const propId = change[k] - const newVal = change[k + 1] - const prop = propertiesById[propId] - // const prop = propId - if (prop === undefined) throw new Error(`Property id change is undefined: ${propId}`) - dataByKeys[key][prop] = newVal - } - } else { - dataByKeys[key] = change + for (const toAdd of added) { + dataByKeys[toAdd] = source[toAdd] + } + for (const toRemove of removed) { + delete dataByKeys[toRemove] + } + for (let i = 0; i < changed.length; i += 2) { + const key = changed[i] + const change = changed[i + 1] + const isOptimizedChange = JsonOptimizer.isOptimizedChangeDiff(change) + if (isOptimizedChange) { + // apply optimized diff + for (let k = 0; k < change.length; k += 2) { + const propId = change[k] + const newVal = change[k + 1] + const prop = propertiesById[propId] + // const prop = propId + if (prop === undefined) throw new Error(`Property id change is undefined: ${propId}`) + dataByKeys[key][prop] = newVal } + } else { + dataByKeys[key] = change } - for (const [key, removePropsId] of removedProps) { - for (const removePropId of removePropsId) { - const removeProp = propertiesById[removePropId] - delete dataByKeys[key][removeProp] - } + } + for (const [key, removePropsId] of removedProps) { + for (const removePropId of removePropsId) { + const removeProp = propertiesById[removePropId] + delete dataByKeys[key][removeProp] } } + if (versionToNumber(versionKey) <= versionToNumber(targetKey)) { + break + } } if (arrKey) { return Object.values(dataByKeys) From 25cfbf458867f3d3a3c28700a940bef89ac7d5de Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 27 Aug 2024 00:48:17 +0300 Subject: [PATCH 011/851] up lockfile --- pnpm-lock.yaml | 1011 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1007 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 486eebe6..337856b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -461,6 +461,10 @@ importers: node-canvas-webgl: specifier: ^0.3.0 version: 0.3.0(encoding@0.1.13) + devDependencies: + live-server: + specifier: ^1.2.2 + version: 1.2.2 prismarine-viewer/viewer/sign-renderer: dependencies: @@ -3535,10 +3539,21 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + anymatch@2.0.0: + resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + apache-crypt@1.2.6: + resolution: {integrity: sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==} + engines: {node: '>=8'} + + apache-md5@1.1.8: + resolution: {integrity: sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==} + engines: {node: '>=8'} + app-root-dir@1.0.2: resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} @@ -3568,6 +3583,18 @@ packages: resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} engines: {node: '>=10'} + arr-diff@4.0.0: + resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} + engines: {node: '>=0.10.0'} + + arr-flatten@1.1.0: + resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} + engines: {node: '>=0.10.0'} + + arr-union@3.1.0: + resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} + engines: {node: '>=0.10.0'} + array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -3590,6 +3617,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + array-unique@0.3.2: + resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} + engines: {node: '>=0.10.0'} + array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} @@ -3645,6 +3676,10 @@ packages: assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + assign-symbols@1.0.0: + resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} + engines: {node: '>=0.10.0'} + ast-types@0.14.2: resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} engines: {node: '>=4'} @@ -3661,6 +3696,9 @@ packages: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} + async-each@1.0.6: + resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==} + async-limiter@1.0.1: resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} @@ -3677,6 +3715,11 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} + atob@2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true + available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -3745,13 +3788,23 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} + base@0.11.2: + resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} + engines: {node: '>=0.10.0'} + basic-auth@2.0.1: resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} engines: {node: '>= 0.8'} + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + bcryptjs@2.4.3: + resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==} + better-opn@3.0.2: resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} engines: {node: '>=12.0.0'} @@ -3760,6 +3813,10 @@ packages: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} + binary-extensions@1.13.1: + resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} + engines: {node: '>=0.10.0'} + binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -3812,6 +3869,10 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + braces@2.3.2: + resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} + engines: {node: '>=0.10.0'} + braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -3916,6 +3977,10 @@ packages: resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + cache-base@1.0.1: + resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} + engines: {node: '>=0.10.0'} + cachedir@2.4.0: resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} engines: {node: '>=6'} @@ -3997,6 +4062,10 @@ packages: resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} engines: {node: '>= 0.8.0'} + chokidar@2.1.8: + resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} + deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies + chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -4019,6 +4088,10 @@ packages: cipher-base@1.0.4: resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + class-utils@0.3.6: + resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} + engines: {node: '>=0.10.0'} + classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -4069,6 +4142,10 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + collection-visit@1.0.0: + resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} + engines: {node: '>=0.10.0'} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -4152,6 +4229,10 @@ packages: confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + console-browserify@1.2.0: resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} @@ -4193,6 +4274,10 @@ packages: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} + copy-descriptor@0.1.1: + resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} + engines: {node: '>=0.10.0'} + copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} @@ -4366,6 +4451,10 @@ packages: decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + decompress-response@4.2.1: resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} engines: {node: '>=8'} @@ -4412,6 +4501,18 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + define-property@0.2.5: + resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} + engines: {node: '>=0.10.0'} + + define-property@1.0.0: + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} + engines: {node: '>=0.10.0'} + + define-property@2.0.2: + resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==} + engines: {node: '>=0.10.0'} + defu@6.1.2: resolution: {integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==} @@ -4426,6 +4527,10 @@ packages: delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -4961,6 +5066,9 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -4997,6 +5105,10 @@ packages: resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} engines: {node: '>=6'} + expand-brackets@2.1.4: + resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} + engines: {node: '>=0.10.0'} + expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} @@ -5014,9 +5126,21 @@ packages: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} engines: {node: '>= 0.10.0'} + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extend-shallow@3.0.2: + resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} + engines: {node: '>=0.10.0'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extglob@2.0.4: + resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} + engines: {node: '>=0.10.0'} + extract-zip@1.7.0: resolution: {integrity: sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==} hasBin: true @@ -5055,6 +5179,10 @@ packages: fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -5092,10 +5220,18 @@ packages: resolution: {integrity: sha512-6RS9gDchbn+qWmtV2uSjo5vmKizgfCQeb5jKmqx8HyzA3MoLqqyQxN+QcjkGBJt7FjJ9qFce67Auyya5rRRbpw==} engines: {node: '>= 10.4.0'} + fill-range@4.0.0: + resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} + engines: {node: '>=0.10.0'} + fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} + finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + finalhandler@1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} engines: {node: '>= 0.8'} @@ -5158,6 +5294,10 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-in@1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + foreground-child@2.0.0: resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} engines: {node: '>=8.0.0'} @@ -5188,10 +5328,17 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} + fragment-cache@0.2.1: + resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} + engines: {node: '>=0.10.0'} + fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -5221,6 +5368,12 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@1.2.13: + resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} + engines: {node: '>= 4.0'} + os: [darwin] + deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2 + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -5305,6 +5458,10 @@ packages: get-tsconfig@4.7.2: resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + get-value@2.0.6: + resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} + engines: {node: '>=0.10.0'} + getos@3.2.1: resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==} @@ -5325,6 +5482,9 @@ packages: resolution: {integrity: sha512-yBbfpChOtFvg5D+KtMaBFvj6yt3vUnheNAH+UrQH2TfDB8kr0tERdL0Tjhe0W7xJ6jR6ftQBluTZR9jXUnKe8g==} engines: {node: '>=14.0.0'} + glob-parent@3.1.0: + resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -5458,6 +5618,22 @@ packages: has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + has-value@0.3.1: + resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} + engines: {node: '>=0.10.0'} + + has-value@1.0.0: + resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} + engines: {node: '>=0.10.0'} + + has-values@0.1.4: + resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} + engines: {node: '>=0.10.0'} + + has-values@1.0.0: + resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} + engines: {node: '>=0.10.0'} + has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -5515,16 +5691,27 @@ packages: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} + http-auth@3.1.3: + resolution: {integrity: sha512-Jbx0+ejo2IOx+cRUYAGS1z6RGc6JfYUNkysZM4u4Sfk1uLlGv814F7/PIjQQAuThLdAWxb74JMGd5J8zex1VQg==} + engines: {node: '>=4.6.1'} + http-browserify@1.7.0: resolution: {integrity: sha512-Irf/LJXmE3cBzU1eaR4+NEX6bmVLqt1wkmDiA7kBwH7zmb0D8kBAXsDmQ88hhj/qv9iEZKlyGx/hrMcFi8sOHw==} http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + http-errors@1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + http-parser-js@0.5.8: + resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} + http-proxy-agent@5.0.0: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} engines: {node: '>= 6'} @@ -5626,6 +5813,9 @@ packages: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -5661,6 +5851,10 @@ packages: resolution: {integrity: sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==} engines: {node: '>=8'} + is-accessor-descriptor@1.0.1: + resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==} + engines: {node: '>= 0.10'} + is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -5685,6 +5879,10 @@ packages: is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-binary-path@1.0.1: + resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==} + engines: {node: '>=0.10.0'} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -5693,6 +5891,9 @@ packages: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + is-builtin-module@3.2.1: resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} engines: {node: '>=6'} @@ -5711,6 +5912,10 @@ packages: is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-data-descriptor@1.0.1: + resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} + engines: {node: '>= 0.4'} + is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} engines: {node: '>= 0.4'} @@ -5722,11 +5927,27 @@ packages: is-deflate@1.0.0: resolution: {integrity: sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==} + is-descriptor@0.1.7: + resolution: {integrity: sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==} + engines: {node: '>= 0.4'} + + is-descriptor@1.0.3: + resolution: {integrity: sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==} + engines: {node: '>= 0.4'} + is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} hasBin: true + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extendable@1.0.1: + resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} + engines: {node: '>=0.10.0'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -5745,6 +5966,10 @@ packages: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} + is-glob@3.1.0: + resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} + engines: {node: '>=0.10.0'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -5787,6 +6012,10 @@ packages: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} + is-number@3.0.0: + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} + engines: {node: '>=0.10.0'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -5876,6 +6105,14 @@ packages: resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} engines: {node: '>= 0.4'} + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-wsl@1.1.0: + resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==} + engines: {node: '>=4'} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -5895,6 +6132,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isobject@2.1.0: + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} + engines: {node: '>=0.10.0'} + isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} @@ -6076,6 +6317,14 @@ packages: keyv@4.5.3: resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + kind-of@3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + + kind-of@4.0.0: + resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} + engines: {node: '>=0.10.0'} + kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -6124,6 +6373,11 @@ packages: enquirer: optional: true + live-server@1.2.2: + resolution: {integrity: sha512-t28HXLjITRGoMSrCOv4eZ88viHaBVIjKjdI5PO92Vxlu+twbk6aE0t7dVIaz6ZWkjPilYFV6OSdMYl9ybN2B4w==} + engines: {node: '>=0.10.0'} + hasBin: true + load-bmfont@1.4.1: resolution: {integrity: sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==} @@ -6269,6 +6523,10 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -6280,6 +6538,13 @@ packages: map-or-similar@1.5.0: resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} + map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + + map-visit@1.0.0: + resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} + engines: {node: '>=0.10.0'} + markdown-it@14.0.0: resolution: {integrity: sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==} hasBin: true @@ -6431,6 +6696,10 @@ packages: micromark@4.0.0: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + micromatch@3.1.10: + resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==} + engines: {node: '>=0.10.0'} + micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -6578,6 +6847,10 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + mixin-deep@1.3.2: + resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} + engines: {node: '>=0.10.0'} + mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} @@ -6608,6 +6881,10 @@ packages: moo@0.5.2: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + morgan@1.10.0: + resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==} + engines: {node: '>= 0.8.0'} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -6643,6 +6920,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanomatch@1.2.13: + resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} + engines: {node: '>=0.10.0'} + napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} @@ -6753,6 +7034,10 @@ packages: resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} engines: {node: '>=10'} + normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -6782,6 +7067,10 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-copy@0.1.0: + resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} + engines: {node: '>=0.10.0'} + object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} @@ -6796,6 +7085,10 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + object-visit@1.0.1: + resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} + engines: {node: '>=0.10.0'} + object.assign@4.1.4: resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} engines: {node: '>= 0.4'} @@ -6816,6 +7109,10 @@ packages: resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} engines: {node: '>= 0.4'} + object.pick@1.3.0: + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} + engines: {node: '>=0.10.0'} + object.values@1.1.7: resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} @@ -6827,6 +7124,10 @@ packages: omggif@1.0.10: resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -6850,6 +7151,11 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true + opn@6.0.0: + resolution: {integrity: sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==} + engines: {node: '>=8'} + deprecated: The package has been renamed to `open` + optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -6948,12 +7254,19 @@ packages: pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + pascalcase@0.1.1: + resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} + engines: {node: '>=0.10.0'} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} path-case@3.0.4: resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} + path-dirname@1.0.2: + resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==} + path-exists-cli@2.0.0: resolution: {integrity: sha512-qGr0A87KYCznmvabblxyxnzA/MtPZ28wH+4SCMP4tjTFAbzqwvs5xpUZExAYzq5OgHe5vIswzdH5iosCb8YF/Q==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7007,6 +7320,9 @@ packages: pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pbkdf2@3.1.2: resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} engines: {node: '>=0.12'} @@ -7108,6 +7424,10 @@ packages: resolution: {integrity: sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==} engines: {node: '>= 0.12.0'} + posix-character-classes@0.1.1: + resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} + engines: {node: '>=0.10.0'} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -7322,6 +7642,10 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-middleware@0.15.0: + resolution: {integrity: sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==} + engines: {node: '>=0.8.0'} + psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} @@ -7604,6 +7928,10 @@ packages: resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdirp@2.2.1: + resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} + engines: {node: '>=0.10'} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -7646,6 +7974,10 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + regex-not@1.0.2: + resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} + engines: {node: '>=0.10.0'} + regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true @@ -7689,6 +8021,17 @@ packages: remark@15.0.1: resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + + repeat-element@1.1.4: + resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} + engines: {node: '>=0.10.0'} + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + request-progress@3.0.0: resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} @@ -7717,6 +8060,10 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve-url@0.2.1: + resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} + deprecated: https://github.com/lydell/resolve-url#deprecated + resolve@1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true @@ -7816,6 +8163,9 @@ packages: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safe-regex@1.1.0: + resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -7876,6 +8226,10 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + serve-index@1.9.1: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} @@ -7899,9 +8253,16 @@ packages: resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} engines: {node: '>=6.9'} + set-value@2.0.1: + resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} + engines: {node: '>=0.10.0'} + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -8001,6 +8362,18 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + snapdragon-node@2.1.1: + resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==} + engines: {node: '>=0.10.0'} + + snapdragon-util@3.0.1: + resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==} + engines: {node: '>=0.10.0'} + + snapdragon@0.8.2: + resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} + engines: {node: '>=0.10.0'} + socket.io-adapter@1.1.2: resolution: {integrity: sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==} @@ -8048,9 +8421,17 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-resolve@0.5.3: + resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated + source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map-url@0.4.1: + resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} + deprecated: See https://github.com/lydell/source-map-url#deprecated + source-map@0.5.6: resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} engines: {node: '>=0.10.0'} @@ -8086,6 +8467,13 @@ packages: spdx-license-ids@3.0.13: resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} + split-string@3.1.0: + resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} + engines: {node: '>=0.10.0'} + + split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -8113,12 +8501,20 @@ packages: stacktrace-js@2.0.2: resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + static-extend@0.1.2: + resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} + engines: {node: '>=0.10.0'} + stats-gl@1.0.5: resolution: {integrity: sha512-XimMxvwnf1Qf5KwebhcoA34kcX+fWEkIl0QjNkCbu4IpoyDMMsOajExn7FIq5w569k45+LhmsuRlGSrsvmGdNw==} stats.js@0.17.0: resolution: {integrity: sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==} + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -8136,6 +8532,9 @@ packages: stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + stream-http@3.2.0: resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} @@ -8410,10 +8809,22 @@ packages: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} + to-object-path@0.3.0: + resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} + engines: {node: '>=0.10.0'} + + to-regex-range@2.1.1: + resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} + engines: {node: '>=0.10.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + to-regex@3.0.2: + resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} + engines: {node: '>=0.10.0'} + tocbot@4.21.2: resolution: {integrity: sha512-R5Muhi/TUu4i4snWVrMgNoXyJm2f8sJfdgIkQvqb+cuIXQEIMAiWGWgCgYXHqX4+XiS/Bnm7IYZ9Zy6NVe6lhw==} @@ -8620,6 +9031,10 @@ packages: unified@11.0.4: resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + union-value@1.0.1: + resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} + engines: {node: '>=0.10.0'} + union@0.5.0: resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} engines: {node: '>= 0.8.0'} @@ -8669,6 +9084,9 @@ packages: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} + unix-crypt-td-js@1.1.4: + resolution: {integrity: sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -8676,6 +9094,10 @@ packages: unplugin@1.5.0: resolution: {integrity: sha512-9ZdRwbh/4gcm1JTOkp9lAkIDrtOyOxgHmY7cjuwI8L/2RTikMcVG25GsZwNAgRuap3iDw2jeq7eoqtAsz5rW3A==} + unset-value@1.0.0: + resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} + engines: {node: '>=0.10.0'} + untildify@4.0.0: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} engines: {node: '>=8'} @@ -8705,6 +9127,10 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + urix@0.1.0: + resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} + deprecated: Please see https://github.com/lydell/urix#deprecated + url-join@4.0.1: resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} @@ -8769,6 +9195,10 @@ packages: typescript: optional: true + use@3.1.1: + resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} + engines: {node: '>=0.10.0'} + utf8-byte-length@1.0.4: resolution: {integrity: sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==} @@ -8788,6 +9218,11 @@ packages: uuid-1345@1.0.2: resolution: {integrity: sha512-bA5zYZui+3nwAc0s3VdGQGBfbVsJLVX7Np7ch2aqcEWFi5lsAEcmO3+lx3djM1npgpZI8KY2FITZ2uYTnYUYyw==} + uuid@3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -8979,6 +9414,14 @@ packages: resolution: {integrity: sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==} engines: {node: '>=6.0.0', npm: '>=3.10.0'} + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} @@ -13096,11 +13539,24 @@ snapshots: any-promise@1.3.0: {} + anymatch@2.0.0: + dependencies: + micromatch: 3.1.10 + normalize-path: 2.1.1 + transitivePeerDependencies: + - supports-color + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 + apache-crypt@1.2.6: + dependencies: + unix-crypt-td-js: 1.1.4 + + apache-md5@1.1.8: {} + app-root-dir@1.0.2: {} aproba@2.0.0: @@ -13131,6 +13587,12 @@ snapshots: dependencies: tslib: 2.6.2 + arr-diff@4.0.0: {} + + arr-flatten@1.1.0: {} + + arr-union@3.1.0: {} + array-buffer-byte-length@1.0.0: dependencies: call-bind: 1.0.2 @@ -13162,6 +13624,8 @@ snapshots: array-union@2.1.0: {} + array-unique@0.3.2: {} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.7 @@ -13259,6 +13723,8 @@ snapshots: assertion-error@1.1.0: {} + assign-symbols@1.0.0: {} + ast-types@0.14.2: dependencies: tslib: 2.6.2 @@ -13274,6 +13740,8 @@ snapshots: astral-regex@2.0.0: optional: true + async-each@1.0.6: {} + async-limiter@1.0.1: {} async@2.6.4: @@ -13286,6 +13754,8 @@ snapshots: at-least-node@1.0.0: {} + atob@2.1.2: {} + available-typed-arrays@1.0.5: {} available-typed-arrays@1.0.7: @@ -13370,21 +13840,37 @@ snapshots: base64id@2.0.0: {} + base@0.11.2: + dependencies: + cache-base: 1.0.1 + class-utils: 0.3.6 + component-emitter: 1.3.0 + define-property: 1.0.0 + isobject: 3.0.1 + mixin-deep: 1.3.2 + pascalcase: 0.1.1 + basic-auth@2.0.1: dependencies: safe-buffer: 5.1.2 + batch@0.6.1: {} + bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 optional: true + bcryptjs@2.4.3: {} + better-opn@3.0.2: dependencies: open: 8.4.2 big-integer@1.6.51: {} + binary-extensions@1.13.1: {} + binary-extensions@2.2.0: {} bindings@1.5.0: @@ -13466,6 +13952,21 @@ snapshots: dependencies: balanced-match: 1.0.2 + braces@2.3.2: + dependencies: + arr-flatten: 1.1.0 + array-unique: 0.3.2 + extend-shallow: 2.0.1 + fill-range: 4.0.0 + isobject: 3.0.1 + repeat-element: 1.1.4 + snapdragon: 0.8.2 + snapdragon-node: 2.1.1 + split-string: 3.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + braces@3.0.2: dependencies: fill-range: 7.0.1 @@ -13612,6 +14113,18 @@ snapshots: - bluebird optional: true + cache-base@1.0.1: + dependencies: + collection-visit: 1.0.0 + component-emitter: 1.3.0 + get-value: 2.0.6 + has-value: 1.0.0 + isobject: 3.0.1 + set-value: 2.0.1 + to-object-path: 0.3.0 + union-value: 1.0.1 + unset-value: 1.0.0 + cachedir@2.4.0: optional: true @@ -13732,6 +14245,24 @@ snapshots: check-more-types@2.24.0: optional: true + chokidar@2.1.8: + dependencies: + anymatch: 2.0.0 + async-each: 1.0.6 + braces: 2.3.2 + glob-parent: 3.1.0 + inherits: 2.0.4 + is-binary-path: 1.0.1 + is-glob: 4.0.3 + normalize-path: 3.0.0 + path-is-absolute: 1.0.1 + readdirp: 2.2.1 + upath: 1.2.0 + optionalDependencies: + fsevents: 1.2.13 + transitivePeerDependencies: + - supports-color + chokidar@3.5.3: dependencies: anymatch: 3.1.3 @@ -13757,6 +14288,13 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 + class-utils@0.3.6: + dependencies: + arr-union: 3.1.0 + define-property: 0.2.5 + isobject: 3.0.1 + static-extend: 0.1.2 + classnames@2.5.1: {} clean-regexp@1.0.0: @@ -13807,6 +14345,11 @@ snapshots: clsx@2.1.1: {} + collection-visit@1.0.0: + dependencies: + map-visit: 1.0.0 + object-visit: 1.0.1 + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -13857,8 +14400,7 @@ snapshots: component-emitter@1.2.1: optional: true - component-emitter@1.3.0: - optional: true + component-emitter@1.3.0: {} component-inherit@0.0.3: optional: true @@ -13890,6 +14432,15 @@ snapshots: confusing-browser-globals@1.0.11: {} + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + console-browserify@1.2.0: {} console-control-strings@1.1.0: @@ -13930,6 +14481,8 @@ snapshots: cookie@0.5.0: {} + copy-descriptor@0.1.1: {} + copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 @@ -14183,6 +14736,8 @@ snapshots: dependencies: character-entities: 2.0.2 + decode-uri-component@0.2.2: {} + decompress-response@4.2.1: dependencies: mimic-response: 2.1.0 @@ -14231,6 +14786,19 @@ snapshots: has-property-descriptors: 1.0.0 object-keys: 1.1.1 + define-property@0.2.5: + dependencies: + is-descriptor: 0.1.7 + + define-property@1.0.0: + dependencies: + is-descriptor: 1.0.3 + + define-property@2.0.2: + dependencies: + is-descriptor: 1.0.3 + isobject: 3.0.1 + defu@6.1.2: {} del@6.1.1: @@ -14249,6 +14817,8 @@ snapshots: delegates@1.0.0: optional: true + depd@1.1.2: {} + depd@2.0.0: {} dequal@1.0.0: {} @@ -15068,6 +15638,16 @@ snapshots: etag@1.8.1: {} + event-stream@3.3.4: + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.1.0 + pause-stream: 0.0.11 + split: 0.3.3 + stream-combiner: 0.0.4 + through: 2.3.8 + event-target-shim@5.0.1: {} eventemitter2@6.4.7: @@ -15117,6 +15697,18 @@ snapshots: exit-hook@2.2.1: {} + expand-brackets@2.1.4: + dependencies: + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + posix-character-classes: 0.1.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + expand-template@2.0.3: {} exponential-backoff@3.1.1: @@ -15166,8 +15758,30 @@ snapshots: transitivePeerDependencies: - supports-color + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extend-shallow@3.0.2: + dependencies: + assign-symbols: 1.0.0 + is-extendable: 1.0.1 + extend@3.0.2: {} + extglob@2.0.4: + dependencies: + array-unique: 0.3.2 + define-property: 1.0.0 + expand-brackets: 2.1.4 + extend-shallow: 2.0.1 + fragment-cache: 0.2.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + extract-zip@1.7.0: dependencies: concat-stream: 1.6.2 @@ -15214,6 +15828,10 @@ snapshots: dependencies: reusify: 1.0.4 + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + fb-watchman@2.0.2: dependencies: bser: 2.1.1 @@ -15252,10 +15870,29 @@ snapshots: filesize@10.0.12: {} + fill-range@4.0.0: + dependencies: + extend-shallow: 2.0.1 + is-number: 3.0.0 + repeat-string: 1.6.1 + to-regex-range: 2.1.1 + fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 + finalhandler@1.1.2: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + finalhandler@1.2.0: dependencies: debug: 2.6.9 @@ -15320,6 +15957,8 @@ snapshots: dependencies: is-callable: 1.2.7 + for-in@1.0.2: {} + foreground-child@2.0.0: dependencies: cross-spawn: 7.0.3 @@ -15365,8 +16004,14 @@ snapshots: forwarded@0.2.0: {} + fragment-cache@0.2.1: + dependencies: + map-cache: 0.2.2 + fresh@0.5.2: {} + from@0.1.7: {} + fs-constants@1.0.0: {} fs-extra@10.1.0: @@ -15403,6 +16048,12 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@1.2.13: + dependencies: + bindings: 1.5.0 + nan: 2.18.0 + optional: true + fsevents@2.3.3: optional: true @@ -15496,6 +16147,8 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-value@2.0.6: {} + getos@3.2.1: dependencies: async: 3.2.5 @@ -15536,6 +16189,11 @@ snapshots: - supports-color optional: true + glob-parent@3.1.0: + dependencies: + is-glob: 3.1.0 + path-dirname: 1.0.2 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -15688,6 +16346,25 @@ snapshots: has-unicode@2.0.1: optional: true + has-value@0.3.1: + dependencies: + get-value: 2.0.6 + has-values: 0.1.4 + isobject: 2.1.0 + + has-value@1.0.0: + dependencies: + get-value: 2.0.6 + has-values: 1.0.0 + isobject: 3.0.1 + + has-values@0.1.4: {} + + has-values@1.0.0: + dependencies: + is-number: 3.0.0 + kind-of: 4.0.0 + has@1.0.3: dependencies: function-bind: 1.1.1 @@ -15750,6 +16427,13 @@ snapshots: html-tags@3.3.1: {} + http-auth@3.1.3: + dependencies: + apache-crypt: 1.2.6 + apache-md5: 1.1.8 + bcryptjs: 2.4.3 + uuid: 3.4.0 + http-browserify@1.7.0: dependencies: Base64: 0.2.1 @@ -15757,6 +16441,13 @@ snapshots: http-cache-semantics@4.1.1: {} + http-errors@1.6.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -15765,6 +16456,8 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-parser-js@0.5.8: {} + http-proxy-agent@5.0.0: dependencies: '@tootallnate/once': 2.0.0 @@ -15887,6 +16580,8 @@ snapshots: once: 1.4.0 wrappy: 1.0.2 + inherits@2.0.3: {} + inherits@2.0.4: {} ini@1.3.8: {} @@ -15921,6 +16616,10 @@ snapshots: is-absolute-url@3.0.3: {} + is-accessor-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + is-arguments@1.1.1: dependencies: call-bind: 1.0.2 @@ -15949,6 +16648,10 @@ snapshots: dependencies: has-bigints: 1.0.2 + is-binary-path@1.0.1: + dependencies: + binary-extensions: 1.13.1 + is-binary-path@2.1.0: dependencies: binary-extensions: 2.2.0 @@ -15958,6 +16661,8 @@ snapshots: call-bind: 1.0.2 has-tostringtag: 1.0.0 + is-buffer@1.1.6: {} + is-builtin-module@3.2.1: dependencies: builtin-modules: 3.3.0 @@ -15977,6 +16682,10 @@ snapshots: dependencies: hasown: 2.0.2 + is-data-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 @@ -15987,8 +16696,24 @@ snapshots: is-deflate@1.0.0: {} + is-descriptor@0.1.7: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + + is-descriptor@1.0.3: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + is-docker@2.2.1: {} + is-extendable@0.1.1: {} + + is-extendable@1.0.1: + dependencies: + is-plain-object: 2.0.4 + is-extglob@2.1.1: {} is-finalizationregistry@1.0.2: @@ -16004,6 +16729,10 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-glob@3.1.0: + dependencies: + is-extglob: 2.1.1 + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -16038,6 +16767,10 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-number@3.0.0: + dependencies: + kind-of: 3.2.2 + is-number@7.0.0: {} is-obj@1.0.1: {} @@ -16107,6 +16840,10 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 + is-windows@1.0.2: {} + + is-wsl@1.1.0: {} + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 @@ -16123,6 +16860,10 @@ snapshots: isexe@2.0.0: {} + isobject@2.1.0: + dependencies: + isarray: 1.0.0 + isobject@3.0.1: {} isstream@0.1.2: @@ -16378,6 +17119,14 @@ snapshots: dependencies: json-buffer: 3.0.1 + kind-of@3.2.2: + dependencies: + is-buffer: 1.1.6 + + kind-of@4.0.0: + dependencies: + is-buffer: 1.1.6 + kind-of@6.0.3: {} kleur@3.0.3: {} @@ -16426,6 +17175,24 @@ snapshots: enquirer: 2.4.1 optional: true + live-server@1.2.2: + dependencies: + chokidar: 2.1.8 + colors: 1.4.0 + connect: 3.7.0 + cors: 2.8.5 + event-stream: 3.3.4 + faye-websocket: 0.11.4 + http-auth: 3.1.3 + morgan: 1.10.0 + object-assign: 4.1.1 + opn: 6.0.0 + proxy-middleware: 0.15.0 + send: 0.18.0 + serve-index: 1.9.1 + transitivePeerDependencies: + - supports-color + load-bmfont@1.4.1: dependencies: buffer-equal: 0.0.1 @@ -16588,12 +17355,20 @@ snapshots: dependencies: tmpl: 1.0.5 + map-cache@0.2.2: {} + map-obj@1.0.1: {} map-obj@4.3.0: {} map-or-similar@1.5.0: {} + map-stream@0.1.0: {} + + map-visit@1.0.0: + dependencies: + object-visit: 1.0.1 + markdown-it@14.0.0: dependencies: argparse: 2.0.1 @@ -16839,6 +17614,24 @@ snapshots: transitivePeerDependencies: - supports-color + micromatch@3.1.10: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + braces: 2.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + extglob: 2.0.4 + fragment-cache: 0.2.1 + kind-of: 6.0.3 + nanomatch: 1.2.13 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + micromatch@4.0.5: dependencies: braces: 3.0.2 @@ -17066,6 +17859,11 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + mixin-deep@1.3.2: + dependencies: + for-in: 1.0.2 + is-extendable: 1.0.1 + mkdirp-classic@0.5.3: {} mkdirp@0.3.0: @@ -17092,6 +17890,16 @@ snapshots: moo@0.5.2: {} + morgan@1.10.0: + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 2.0.0 + on-finished: 2.3.0 + on-headers: 1.0.2 + transitivePeerDependencies: + - supports-color + mri@1.2.0: {} ms@2.0.0: {} @@ -17126,6 +17934,22 @@ snapshots: nanoid@3.3.7: {} + nanomatch@1.2.13: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + fragment-cache: 0.2.1 + is-windows: 1.0.2 + kind-of: 6.0.3 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + napi-build-utils@1.0.2: {} natural-compare-lite@1.4.0: {} @@ -17263,6 +18087,10 @@ snapshots: semver: 7.6.0 validate-npm-package-license: 3.0.4 + normalize-path@2.1.1: + dependencies: + remove-trailing-separator: 1.1.0 + normalize-path@3.0.0: {} npm-run-all@4.1.5: @@ -17303,6 +18131,12 @@ snapshots: object-assign@4.1.1: {} + object-copy@0.1.0: + dependencies: + copy-descriptor: 0.1.1 + define-property: 0.2.5 + kind-of: 3.2.2 + object-inspect@1.12.3: {} object-inspect@1.13.1: {} @@ -17314,6 +18148,10 @@ snapshots: object-keys@1.1.1: {} + object-visit@1.0.1: + dependencies: + isobject: 3.0.1 + object.assign@4.1.4: dependencies: call-bind: 1.0.2 @@ -17347,6 +18185,10 @@ snapshots: es-abstract: 1.23.3 es-object-atoms: 1.0.0 + object.pick@1.3.0: + dependencies: + isobject: 3.0.1 + object.values@1.1.7: dependencies: call-bind: 1.0.2 @@ -17362,6 +18204,10 @@ snapshots: omggif@1.0.10: optional: true + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -17384,6 +18230,10 @@ snapshots: opener@1.5.2: {} + opn@6.0.0: + dependencies: + is-wsl: 1.1.0 + optionator@0.9.3: dependencies: '@aashutoshrathi/word-wrap': 1.2.6 @@ -17503,6 +18353,8 @@ snapshots: no-case: 3.0.4 tslib: 2.6.2 + pascalcase@0.1.1: {} + path-browserify@1.0.1: {} path-case@3.0.4: @@ -17510,6 +18362,8 @@ snapshots: dot-case: 3.0.4 tslib: 2.6.2 + path-dirname@1.0.2: {} + path-exists-cli@2.0.0: dependencies: meow: 10.1.5 @@ -17546,6 +18400,10 @@ snapshots: pathval@1.1.1: {} + pause-stream@0.0.11: + dependencies: + through: 2.3.8 + pbkdf2@3.1.2: dependencies: create-hash: 1.2.0 @@ -17641,6 +18499,8 @@ snapshots: transitivePeerDependencies: - supports-color + posix-character-classes@0.1.1: {} + possible-typed-array-names@1.0.0: {} postcss@8.4.31: @@ -17947,6 +18807,8 @@ snapshots: proxy-from-env@1.1.0: {} + proxy-middleware@0.15.0: {} + psl@1.9.0: optional: true @@ -18304,6 +19166,14 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 + readdirp@2.2.1: + dependencies: + graceful-fs: 4.2.11 + micromatch: 3.1.10 + readable-stream: 2.3.8 + transitivePeerDependencies: + - supports-color + readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -18359,6 +19229,11 @@ snapshots: dependencies: '@babel/runtime': 7.24.5 + regex-not@1.0.2: + dependencies: + extend-shallow: 3.0.2 + safe-regex: 1.1.0 + regexp-tree@0.1.27: {} regexp.prototype.flags@1.5.1: @@ -18431,6 +19306,12 @@ snapshots: transitivePeerDependencies: - supports-color + remove-trailing-separator@1.1.0: {} + + repeat-element@1.1.4: {} + + repeat-string@1.6.1: {} + request-progress@3.0.0: dependencies: throttleit: 1.0.0 @@ -18450,6 +19331,8 @@ snapshots: resolve-pkg-maps@1.0.0: {} + resolve-url@0.2.1: {} + resolve@1.22.4: dependencies: is-core-module: 2.13.0 @@ -18558,6 +19441,10 @@ snapshots: es-errors: 1.3.0 is-regex: 1.1.4 + safe-regex@1.1.0: + dependencies: + ret: 0.1.15 + safer-buffer@2.1.2: {} sanitize-filename@1.6.3: @@ -18629,6 +19516,18 @@ snapshots: dependencies: randombytes: 2.1.0 + serve-index@1.9.1: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.6.3 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + serve-static@1.15.0: dependencies: encodeurl: 1.0.2 @@ -18665,8 +19564,17 @@ snapshots: set-harmonic-interval@1.0.1: {} + set-value@2.0.1: + dependencies: + extend-shallow: 2.0.1 + is-extendable: 0.1.1 + is-plain-object: 2.0.4 + split-string: 3.1.0 + setimmediate@1.0.5: {} + setprototypeof@1.1.0: {} + setprototypeof@1.2.0: {} sha.js@2.4.11: @@ -18786,6 +19694,29 @@ snapshots: dot-case: 3.0.4 tslib: 2.6.2 + snapdragon-node@2.1.1: + dependencies: + define-property: 1.0.0 + isobject: 3.0.1 + snapdragon-util: 3.0.1 + + snapdragon-util@3.0.1: + dependencies: + kind-of: 3.2.2 + + snapdragon@0.8.2: + dependencies: + base: 0.11.2 + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + map-cache: 0.2.2 + source-map: 0.5.7 + source-map-resolve: 0.5.3 + use: 3.1.1 + transitivePeerDependencies: + - supports-color + socket.io-adapter@1.1.2: optional: true @@ -18898,11 +19829,21 @@ snapshots: source-map-js@1.2.0: {} + source-map-resolve@0.5.3: + dependencies: + atob: 2.1.2 + decode-uri-component: 0.2.2 + resolve-url: 0.2.1 + source-map-url: 0.4.1 + urix: 0.1.0 + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 + source-map-url@0.4.1: {} + source-map@0.5.6: {} source-map@0.5.7: {} @@ -18931,6 +19872,14 @@ snapshots: spdx-license-ids@3.0.13: {} + split-string@3.1.0: + dependencies: + extend-shallow: 3.0.2 + + split@0.3.3: + dependencies: + through: 2.3.8 + sprintf-js@1.0.3: {} sshpk@1.17.0: @@ -18970,10 +19919,17 @@ snapshots: stack-generator: 2.0.10 stacktrace-gps: 3.1.2 + static-extend@0.1.2: + dependencies: + define-property: 0.2.5 + object-copy: 0.1.0 + stats-gl@1.0.5: {} stats.js@0.17.0: {} + statuses@1.5.0: {} + statuses@2.0.1: {} std-env@3.4.3: {} @@ -18994,6 +19950,10 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + stream-combiner@0.0.4: + dependencies: + duplexer: 0.1.2 + stream-http@3.2.0: dependencies: builtin-status-codes: 3.0.0 @@ -19279,8 +20239,7 @@ snapshots: readable-stream: 2.3.8 xtend: 4.0.2 - through@2.3.8: - optional: true + through@2.3.8: {} timers-browserify@2.0.12: dependencies: @@ -19316,10 +20275,26 @@ snapshots: to-fast-properties@2.0.0: {} + to-object-path@0.3.0: + dependencies: + kind-of: 3.2.2 + + to-regex-range@2.1.1: + dependencies: + is-number: 3.0.0 + repeat-string: 1.6.1 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + to-regex@3.0.2: + dependencies: + define-property: 2.0.2 + extend-shallow: 3.0.2 + regex-not: 1.0.2 + safe-regex: 1.1.0 + tocbot@4.21.2: {} toggle-selection@1.0.6: {} @@ -19532,6 +20507,13 @@ snapshots: trough: 2.2.0 vfile: 6.0.1 + union-value@1.0.1: + dependencies: + arr-union: 3.1.0 + get-value: 2.0.6 + is-extendable: 0.1.1 + set-value: 2.0.1 + union@0.5.0: dependencies: qs: 6.11.2 @@ -19590,6 +20572,8 @@ snapshots: universalify@2.0.0: {} + unix-crypt-td-js@1.1.4: {} + unpipe@1.0.0: {} unplugin@1.5.0: @@ -19599,6 +20583,11 @@ snapshots: webpack-sources: 3.2.3 webpack-virtual-modules: 0.5.0 + unset-value@1.0.0: + dependencies: + has-value: 0.3.1 + isobject: 3.0.1 + untildify@4.0.0: {} upath@1.2.0: {} @@ -19627,6 +20616,8 @@ snapshots: dependencies: punycode: 2.3.0 + urix@0.1.0: {} + url-join@4.0.1: {} url-parse@1.5.10: @@ -19684,6 +20675,8 @@ snapshots: optionalDependencies: typescript: 5.5.4 + use@3.1.1: {} + utf8-byte-length@1.0.4: {} utif@2.0.1: @@ -19707,6 +20700,8 @@ snapshots: dependencies: macaddress: 0.5.3 + uuid@3.4.0: {} + uuid@8.3.2: {} uuid@9.0.1: {} @@ -19914,6 +20909,14 @@ snapshots: dependencies: sdp: 3.2.0 + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.8 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + whatwg-encoding@2.0.0: dependencies: iconv-lite: 0.6.3 From 2203a8a32b1186d44ef4056cedeffef3a66f140d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 27 Aug 2024 01:12:25 +0300 Subject: [PATCH 012/851] fix lint --- src/getCollisionShapes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/getCollisionShapes.ts b/src/getCollisionShapes.ts index 3596cca2..383adc0e 100644 --- a/src/getCollisionShapes.ts +++ b/src/getCollisionShapes.ts @@ -1,5 +1,5 @@ -import collisionShapesInit from '../generated/latestBlockCollisionsShapes.json' import { getRenamedData } from 'flying-squid/dist/blockRenames' +import collisionShapesInit from '../generated/latestBlockCollisionsShapes.json' // defining globally to be used in loaded data, not sure of better workaround window.globalGetCollisionShapes = (version) => { From 294c69f3b53c5e6fb1e5314a93c926840c6c8e8d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 27 Aug 2024 03:27:25 +0300 Subject: [PATCH 013/851] fix protocol issue, optimize getter (delay uneeded resolvers), fix server connect data load --- .../viewer/lib/worldrendererCommon.ts | 8 +- scripts/makeOptimizedMcData.mjs | 11 +-- src/index.ts | 3 +- src/shims/minecraftData.ts | 76 ++++++++++--------- 4 files changed, 56 insertions(+), 42 deletions(-) diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index daa17335..58e5a6f5 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -223,8 +223,12 @@ export abstract class WorldRendererCommon sendMesherMcData () { const allMcData = mcDataRaw.pc[this.version] ?? mcDataRaw.pc[toMajorVersion(this.version)] - const mcData = Object.fromEntries(Object.entries(allMcData).filter(([key]) => dynamicMcDataFiles.includes(key))) - mcData.version = JSON.parse(JSON.stringify(mcData.version)) + const mcData = { + version: JSON.parse(JSON.stringify(allMcData.version)) + } + for (const key of dynamicMcDataFiles) { + mcData[key] = allMcData[key] + } for (const worker of this.workers) { worker.postMessage({ type: 'mcData', mcData, config: this.mesherConfig }) diff --git a/scripts/makeOptimizedMcData.mjs b/scripts/makeOptimizedMcData.mjs index cda75125..d0adcae2 100644 --- a/scripts/makeOptimizedMcData.mjs +++ b/scripts/makeOptimizedMcData.mjs @@ -45,8 +45,7 @@ const versionToNumber = (ver) => { // if not included here (even as {}) will not be bundled & accessible! const compressedOutput = false // const dataTypeBundling = { -// biomes: { -// arrKey: 'name', +// protocol: { // // ignoreRemoved: true, // // ignoreChanges: true // } @@ -122,7 +121,9 @@ const dataTypeBundling = { recipes: {}, // todo we can do better blockCollisionShapes: {}, loginPacket: {}, - protocol: {}, + protocol: { + raw: true + }, sounds: { arrKey: 'name' } @@ -219,11 +220,11 @@ console.log('size', fs.lstatSync(filePath).size / 1000 / 1000, gzipSizeFromFileS const { defaultVersion } = MCProtocol const data = MinecraftData(defaultVersion) -const defaultVersionObj = { +const initialMcData = { [defaultVersion]: { version: data.version, protocol: data.protocol, } } -fs.writeFileSync('./generated/minecraft-initial-data.json', JSON.stringify(defaultVersionObj), 'utf8') +fs.writeFileSync('./generated/minecraft-initial-data.json', JSON.stringify(initialMcData), 'utf8') diff --git a/src/index.ts b/src/index.ts index 00e12658..39a110a3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -380,6 +380,7 @@ async function connect (connectOptions: ConnectOptions) { try { const serverOptions = defaultsDeep({}, connectOptions.serverOverrides ?? {}, options.localServerOptions, defaultServerOptions) Object.assign(serverOptions, connectOptions.serverOverridesFlat ?? {}) + window._LOAD_MC_DATA() // start loading data (if not loaded yet) const downloadMcData = async (version: string) => { if (connectOptions.authenticatedAccount && versionToNumber(version) < versionToNumber('1.19.4')) { // todo support it (just need to fix .export crash) @@ -397,7 +398,7 @@ async function connect (connectOptions: ConnectOptions) { // todo instead re-render signs on load await document.fonts.load('1em mojangles').catch(() => { }) } - await window._LOAD_MC_DATA() + await window._MC_DATA_RESOLVER.promise // ensure data is loaded await downloadSoundsIfNeeded() miscUiState.loadedDataVersion = version try { diff --git a/src/shims/minecraftData.ts b/src/shims/minecraftData.ts index 5b8718e4..6edb5f48 100644 --- a/src/shims/minecraftData.ts +++ b/src/shims/minecraftData.ts @@ -29,44 +29,52 @@ window._LOAD_MC_DATA = async () => { const cacheTtl = 30 * 1000 const cache = new Map() const cacheTime = new Map() +const possiblyGetFromCache = (version: string) => { + if (minecraftInitialDataJson[version] && !optimizedDataResolver.resolvedData) { + return minecraftInitialDataJson[version] + } + if (cache.has(version)) { + return cache.get(version) + } + const inner = () => { + if (!optimizedDataResolver.resolvedData) { + throw new Error(`Data for ${version} is not ready yet`) + } + const dataTypes = Object.keys(optimizedDataResolver.resolvedData) + const allRestored = {} + for (const dataType of dataTypes) { + if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { + const shapes = window.globalGetCollisionShapes?.(version) + if (shapes) { + allRestored[dataType] = shapes + continue + } + } + + const data = optimizedDataResolver.resolvedData[dataType] + if (data.__IS_OPTIMIZED__) { + allRestored[dataType] = JsonOptimizer.restoreData(data, version) + } else { + allRestored[dataType] = data[version] ?? data[toMajorVersion(version)] + } + } + return allRestored + } + const data = inner() + cache.set(version, data) + cacheTime.set(version, Date.now()) + return data +} window.allLoadedMcData = new Proxy({}, { get (t, version: string) { // special properties like $typeof if (version.includes('$')) return - if (cache.has(version)) { - return cache.get(version) - } - const inner = () => { - if (minecraftInitialDataJson[version] && !optimizedDataResolver.resolvedData) { - return minecraftInitialDataJson[version] - } - if (!optimizedDataResolver.resolvedData) { - throw new Error(`Data for ${version} is not ready yet`) - } - const dataTypes = Object.keys(optimizedDataResolver.resolvedData) - const allRestored = {} - for (const dataType of dataTypes) { - if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { - const shapes = window.globalGetCollisionShapes?.(version) - if (shapes) { - allRestored[dataType] = shapes - continue - } - } - - const data = optimizedDataResolver.resolvedData[dataType] - if (data.__IS_OPTIMIZED__) { - allRestored[dataType] = JsonOptimizer.restoreData(data, version) - } else { - allRestored[dataType] = data[version] ?? data[toMajorVersion(version)] - } - } - return allRestored - } - const data = inner() - cache.set(version, data) - cacheTime.set(version, Date.now()) - return data + // todo enumerate all props + return new Proxy({}, { + get (target, prop) { + return possiblyGetFromCache(version)[prop] + }, + }) } }) From 037b4b48e20794a24c6dc7bdc635b9b00ec57afe Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 27 Aug 2024 03:36:06 +0300 Subject: [PATCH 014/851] remove outdated label --- src/react/CreateWorld.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/react/CreateWorld.tsx b/src/react/CreateWorld.tsx index 0ff34a39..da710be1 100644 --- a/src/react/CreateWorld.tsx +++ b/src/react/CreateWorld.tsx @@ -45,8 +45,8 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer placeholder='World name' /> { return { value: obj.version, label: obj.version === defaultVersion ? obj.version + ' (available offline)' : obj.version } })} - selected={{ value: defaultVersion, label: defaultVersion + ' (available offline)' }} + versions={versions.map((obj) => { return { value: obj.version, label: obj.version } })} + selected={{ value: defaultVersion, label: defaultVersion }} onChange={(value) => { creatingWorldState.version = value ?? defaultVersion }} From aa9400e88505fea11574187f57eb931bb34c4d06 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 29 Aug 2024 16:28:26 +0300 Subject: [PATCH 015/851] always enable cors so able to connect from prod domains --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index dd1ccaa1..4e541486 100644 --- a/server.js +++ b/server.js @@ -17,7 +17,7 @@ const app = express() const isProd = process.argv.includes('--prod') app.use(compression()) -// app.use(cors()) +app.use(cors()) app.use(netApi({ allowOrigin: '*' })) if (!isProd) { app.use('/sounds', express.static(path.join(__dirname, './generated/sounds/'))) From f32f30ca5a05e8a3fc9e487305c92dde4d56b3f7 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 29 Aug 2024 16:28:45 +0300 Subject: [PATCH 016/851] also watch mesher when running the main start script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 695aff89..e20c548a 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "dev-rsbuild": "rsbuild dev", "dev-proxy": "node server.js", - "start": "run-p dev-rsbuild dev-proxy", + "start": "run-p dev-rsbuild dev-proxy watch-mesher", "start-watch-script": "nodemon -w rsbuild.config.ts --watch", "build": "rsbuild build", "build-analyze": "BUNDLE_ANALYZE=true rsbuild build", From b472849c4745d21fe55d80750d430813171b33a3 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 31 Aug 2024 17:47:17 +0300 Subject: [PATCH 017/851] fix: packets replay crash on message send on latest versions --- src/packetsReplayBase.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/packetsReplayBase.ts b/src/packetsReplayBase.ts index 375da3cb..fda41ae2 100644 --- a/src/packetsReplayBase.ts +++ b/src/packetsReplayBase.ts @@ -20,7 +20,11 @@ export class PacketsLogger { } const diff = `+${Date.now() - this.lastPacketTime}` - const str = `${isFromServer ? 'S' : 'C'} ${packet.state}:${packet.name} ${diff} ${JSON.stringify(data)}` + // serialize bigint + const str = `${isFromServer ? 'S' : 'C'} ${packet.state}:${packet.name} ${diff} ${JSON.stringify(data, (key, value) => { + if (typeof value === 'bigint') return value.toString() + return value + })}` this.logStr(str) this.lastPacketTime = Date.now() } From 72a54989adc1f166f4cddf197ee9ed6506df23bd Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 31 Aug 2024 17:47:46 +0300 Subject: [PATCH 018/851] fix(important): chunk unload was never implemented --- prismarine-viewer/viewer/lib/worldDataEmitter.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prismarine-viewer/viewer/lib/worldDataEmitter.ts b/prismarine-viewer/viewer/lib/worldDataEmitter.ts index ea956b81..381526d9 100644 --- a/prismarine-viewer/viewer/lib/worldDataEmitter.ts +++ b/prismarine-viewer/viewer/lib/worldDataEmitter.ts @@ -72,6 +72,9 @@ export class WorldDataEmitter extends EventEmitter { chunkColumnLoad: (pos: Vec3) => { this.loadChunk(pos) }, + chunkColumnUnload: (pos: Vec3) => { + this.unloadChunk(pos) + }, blockUpdate: (oldBlock: any, newBlock: any) => { const stateId = newBlock.stateId ?? ((newBlock.type << 4) | newBlock.metadata) this.emitter.emit('blockUpdate', { pos: oldBlock.position, stateId }) From 7748e8c384fdeefc359ed3d3247b5cfcf9683290 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 31 Aug 2024 17:48:13 +0300 Subject: [PATCH 019/851] fix: cleanup entities in all cases on world switch --- pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5dffb3da..1810903f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -348,7 +348,7 @@ importers: version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/75e940a4cd50d89e0ba03db3733d5d704917a3c8(@types/react@18.2.20)(react@18.2.0) mineflayer: specifier: github:zardoy/mineflayer - version: https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502(encoding@0.1.13) + version: https://codeload.github.com/zardoy/mineflayer/tar.gz/c6755fc3758eeb3bbbb98479efe03b7935486247(encoding@0.1.13) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -6509,8 +6509,8 @@ packages: resolution: {integrity: sha512-QMMNPx4IyZE7ydAzjvGLQLCnQNUOfkk1qVZKxTTS9q3qPTAewz4GhsVUBtbQ8LSbHthe5RcQ1Sgxs4wlIma/Qw==} engines: {node: '>=18'} - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502: - resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502} + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/c6755fc3758eeb3bbbb98479efe03b7935486247: + resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/c6755fc3758eeb3bbbb98479efe03b7935486247} version: 4.20.1 engines: {node: '>=18'} From 17a3166f7d12163276095d69eb4695eabb7aeacb Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 31 Aug 2024 17:56:35 +0300 Subject: [PATCH 020/851] fix lockfile --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1810903f..16eba168 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16973,7 +16973,7 @@ snapshots: - encoding - supports-color - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/326c5a1681b09ee2bb783f20a562c6055788b502(encoding@0.1.13): + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/c6755fc3758eeb3bbbb98479efe03b7935486247(encoding@0.1.13): dependencies: minecraft-data: 3.65.0 minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/7057ad979b416192ada235f2f4e3b5eb26af5fa1(patch_hash=7otpchsbv7hxsuis4rrrwdtbve)(encoding@0.1.13) From 447f5eabc84e88a70ab97a81d6a427c5260c6183 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 31 Aug 2024 18:51:31 +0300 Subject: [PATCH 021/851] skip building worker in dev by default because of new scripts change --- rsbuild.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 422af603..fd05db8a 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -124,7 +124,7 @@ export default defineConfig({ if (fs.existsSync('./prismarine-viewer/public/mesher.js') && dev) { // copy mesher fs.copyFileSync('./prismarine-viewer/public/mesher.js', './dist/mesher.js') - } else { + } else if (!dev) { await execAsync('pnpm run build-mesher') } fs.writeFileSync('./dist/version.txt', buildingVersion, 'utf-8') From 0dc261258ad805f5bdeaeddd456bfd8ded17d6f3 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 31 Aug 2024 19:05:20 +0300 Subject: [PATCH 022/851] fix: fix bug ?singleplayer=1&version=1.20.4 didn't work in safari because of different setTimeout timing --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e20c548a..5337cebf 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "esbuild-plugin-polyfill-node": "^0.3.0", "express": "^4.18.2", "filesize": "^10.0.12", - "flying-squid": "npm:@zardoy/flying-squid@^0.0.35", + "flying-squid": "npm:@zardoy/flying-squid@^0.0.36", "fs-extra": "^11.1.1", "google-drive-browserfs": "github:zardoy/browserfs#google-drive", "jszip": "^3.10.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16eba168..6697f33b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,8 +117,8 @@ importers: specifier: ^10.0.12 version: 10.0.12 flying-squid: - specifier: npm:@zardoy/flying-squid@^0.0.35 - version: '@zardoy/flying-squid@0.0.35(encoding@0.1.13)' + specifier: npm:@zardoy/flying-squid@^0.0.36 + version: '@zardoy/flying-squid@0.0.36(encoding@0.1.13)' fs-extra: specifier: ^11.1.1 version: 11.1.1 @@ -3386,8 +3386,8 @@ packages: resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==} engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'} - '@zardoy/flying-squid@0.0.35': - resolution: {integrity: sha512-6cZdDi7yaqxh6KbOPhDueipcr9DBgJ3mJY+/QwAjaSzhP//5n1BLjyVGlx2Ncs/6Vns2grTOmeuDhJjMbVgjQg==} + '@zardoy/flying-squid@0.0.36': + resolution: {integrity: sha512-d4clMPDpw723SDF5P2mMVNfbthUFLX6OT+vTCECAMshX8/M7CyMq/q9BfBQoeJcBL0H9nplhwtFbnx3Edb2fzA==} engines: {node: '>=8'} hasBin: true @@ -12923,7 +12923,7 @@ snapshots: '@types/emscripten': 1.39.8 tslib: 1.14.1 - '@zardoy/flying-squid@0.0.35(encoding@0.1.13)': + '@zardoy/flying-squid@0.0.36(encoding@0.1.13)': dependencies: '@tootallnate/once': 2.0.0 change-case: 4.1.2 From 8e314992571007581217a3fcbeaf4545fa4cbc0b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 31 Aug 2024 19:37:21 +0300 Subject: [PATCH 023/851] fix important bug: was mutating old restored data obj --- scripts/testOptimizedMcdata.ts | 8 +++++--- src/optimizeJson.ts | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/testOptimizedMcdata.ts b/scripts/testOptimizedMcdata.ts index f9196b53..17b5f7ed 100644 --- a/scripts/testOptimizedMcdata.ts +++ b/scripts/testOptimizedMcdata.ts @@ -1,8 +1,8 @@ +import assert from 'assert' import JsonOptimizer from '../src/optimizeJson'; import fs from 'fs' import minecraftData from 'minecraft-data' -// const data = minecraftData('1.20.4') const json = JSON.parse(fs.readFileSync('./generated/minecraft-data-optimized.json', 'utf8')) const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') @@ -74,8 +74,10 @@ const checkKeys = (source, diffing, isUniq = true, msg = '', redunantOk = false) } } -// console.log(JsonOptimizer.restoreData(json['blocks'], '1.16.2').slice(0, 5)) -// console.log(data.blocksByName.melon_stem.drops) +// const data = minecraftData('1.20.4') +const oldId = JsonOptimizer.restoreData(json['blocks'], '1.20').find(x => x.name === 'brown_stained_glass').id; +const newId = JsonOptimizer.restoreData(json['blocks'], '1.20.4').find(x => x.name === 'brown_stained_glass').id; +assert(oldId !== newId) // test all types + all versions for (const type of Object.keys(json)) { diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts index 0b237307..442a72a1 100644 --- a/src/optimizeJson.ts +++ b/src/optimizeJson.ts @@ -160,6 +160,7 @@ export default class JsonOptimizer { static restoreData ({ keys, properties, source, arrKey, diffs }: SourceData, targetKey: string) { // if (!diffs[targetKey]) throw new Error(`The requested data to restore with key ${targetKey} does not exist`) + source = structuredClone(source) const keysById = Object.fromEntries(Object.entries(keys).map(x => [x[1], x[0]])) const propertiesById = Object.fromEntries(Object.entries(properties).map(x => [x[1], x[0]])) const dataByKeys = {} as Record From d3c592930f144d612be0bb2cf005b8e05fac22c1 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 31 Aug 2024 19:49:22 +0300 Subject: [PATCH 024/851] increase timeout --- cypress/e2e/index.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/index.spec.ts b/cypress/e2e/index.spec.ts index b0d7118b..cb1b6880 100644 --- a/cypress/e2e/index.spec.ts +++ b/cypress/e2e/index.spec.ts @@ -14,7 +14,7 @@ const compareRenderedFlatWorld = () => { } const testWorldLoad = () => { - return cy.document().then({ timeout: 25_000 }, doc => { + return cy.document().then({ timeout: 35_000 }, doc => { return new Cypress.Promise(resolve => { doc.addEventListener('cypress-world-ready', resolve) }) From eb0bc02647070f80cab7a7671fd747071d6b406f Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sat, 31 Aug 2024 19:50:33 +0300 Subject: [PATCH 025/851] feat: All versions now are available offline! (#174) --- cypress/e2e/index.spec.ts | 2 +- experiments/decode.html | 1 + experiments/decode.ts | 26 + pnpm-lock.yaml | 1011 ++++++++++++++++- prismarine-viewer/esbuild.mjs | 18 +- prismarine-viewer/examples/playground.ts | 29 +- prismarine-viewer/package.json | 3 + .../viewer/lib/worldrendererCommon.ts | 10 +- rsbuild.config.ts | 8 +- scripts/build.js | 1 - scripts/genShims.ts | 17 - scripts/makeOptimizedMcData.mjs | 230 ++++ scripts/prepareData.mjs | 72 -- scripts/testOptimizedMcdata.ts | 99 ++ src/getCollisionInteractionShapes.ts | 17 + src/getCollisionShapes.ts | 15 - src/index.ts | 7 +- src/optimizeJson.ts | 264 +++++ src/react/CreateWorld.tsx | 4 +- src/shims/minecraftData.ts | 92 ++ 20 files changed, 1781 insertions(+), 145 deletions(-) create mode 100644 experiments/decode.html create mode 100644 experiments/decode.ts create mode 100644 scripts/makeOptimizedMcData.mjs delete mode 100644 scripts/prepareData.mjs create mode 100644 scripts/testOptimizedMcdata.ts create mode 100644 src/getCollisionInteractionShapes.ts create mode 100644 src/optimizeJson.ts create mode 100644 src/shims/minecraftData.ts diff --git a/cypress/e2e/index.spec.ts b/cypress/e2e/index.spec.ts index b0d7118b..cb1b6880 100644 --- a/cypress/e2e/index.spec.ts +++ b/cypress/e2e/index.spec.ts @@ -14,7 +14,7 @@ const compareRenderedFlatWorld = () => { } const testWorldLoad = () => { - return cy.document().then({ timeout: 25_000 }, doc => { + return cy.document().then({ timeout: 35_000 }, doc => { return new Cypress.Promise(resolve => { doc.addEventListener('cypress-world-ready', resolve) }) diff --git a/experiments/decode.html b/experiments/decode.html new file mode 100644 index 00000000..fd55e622 --- /dev/null +++ b/experiments/decode.html @@ -0,0 +1 @@ + diff --git a/experiments/decode.ts b/experiments/decode.ts new file mode 100644 index 00000000..6d0f876d --- /dev/null +++ b/experiments/decode.ts @@ -0,0 +1,26 @@ +// Include the pako library +import pako from 'pako'; +import compressedJsRaw from './compressed.js?raw' + +function decompressFromBase64(input) { + // Decode the Base64 string + const binaryString = atob(input); + const len = binaryString.length; + const bytes = new Uint8Array(len); + + // Convert the binary string to a byte array + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + // Decompress the byte array + const decompressedData = pako.inflate(bytes, { to: 'string' }); + + return decompressedData; +} + +// Use the function +console.time('decompress'); +const decompressedData = decompressFromBase64(compressedJsRaw); +console.timeEnd('decompress') +console.log(decompressedData) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6697f33b..8075be60 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -461,6 +461,10 @@ importers: node-canvas-webgl: specifier: ^0.3.0 version: 0.3.0(encoding@0.1.13) + devDependencies: + live-server: + specifier: ^1.2.2 + version: 1.2.2 prismarine-viewer/viewer/sign-renderer: dependencies: @@ -3535,10 +3539,21 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + anymatch@2.0.0: + resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + apache-crypt@1.2.6: + resolution: {integrity: sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==} + engines: {node: '>=8'} + + apache-md5@1.1.8: + resolution: {integrity: sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==} + engines: {node: '>=8'} + app-root-dir@1.0.2: resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} @@ -3568,6 +3583,18 @@ packages: resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} engines: {node: '>=10'} + arr-diff@4.0.0: + resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} + engines: {node: '>=0.10.0'} + + arr-flatten@1.1.0: + resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} + engines: {node: '>=0.10.0'} + + arr-union@3.1.0: + resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} + engines: {node: '>=0.10.0'} + array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -3590,6 +3617,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + array-unique@0.3.2: + resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} + engines: {node: '>=0.10.0'} + array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} @@ -3645,6 +3676,10 @@ packages: assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + assign-symbols@1.0.0: + resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} + engines: {node: '>=0.10.0'} + ast-types@0.14.2: resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} engines: {node: '>=4'} @@ -3661,6 +3696,9 @@ packages: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} + async-each@1.0.6: + resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==} + async-limiter@1.0.1: resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} @@ -3677,6 +3715,11 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} + atob@2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true + available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -3745,13 +3788,23 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} + base@0.11.2: + resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} + engines: {node: '>=0.10.0'} + basic-auth@2.0.1: resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} engines: {node: '>= 0.8'} + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + bcryptjs@2.4.3: + resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==} + better-opn@3.0.2: resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} engines: {node: '>=12.0.0'} @@ -3760,6 +3813,10 @@ packages: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} + binary-extensions@1.13.1: + resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} + engines: {node: '>=0.10.0'} + binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -3812,6 +3869,10 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + braces@2.3.2: + resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} + engines: {node: '>=0.10.0'} + braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -3916,6 +3977,10 @@ packages: resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + cache-base@1.0.1: + resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} + engines: {node: '>=0.10.0'} + cachedir@2.4.0: resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} engines: {node: '>=6'} @@ -3997,6 +4062,10 @@ packages: resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} engines: {node: '>= 0.8.0'} + chokidar@2.1.8: + resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} + deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies + chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -4019,6 +4088,10 @@ packages: cipher-base@1.0.4: resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + class-utils@0.3.6: + resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} + engines: {node: '>=0.10.0'} + classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -4069,6 +4142,10 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + collection-visit@1.0.0: + resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} + engines: {node: '>=0.10.0'} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -4152,6 +4229,10 @@ packages: confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + console-browserify@1.2.0: resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} @@ -4193,6 +4274,10 @@ packages: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} + copy-descriptor@0.1.1: + resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} + engines: {node: '>=0.10.0'} + copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} @@ -4366,6 +4451,10 @@ packages: decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + decompress-response@4.2.1: resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} engines: {node: '>=8'} @@ -4412,6 +4501,18 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + define-property@0.2.5: + resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} + engines: {node: '>=0.10.0'} + + define-property@1.0.0: + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} + engines: {node: '>=0.10.0'} + + define-property@2.0.2: + resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==} + engines: {node: '>=0.10.0'} + defu@6.1.2: resolution: {integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==} @@ -4426,6 +4527,10 @@ packages: delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -4961,6 +5066,9 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -4997,6 +5105,10 @@ packages: resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} engines: {node: '>=6'} + expand-brackets@2.1.4: + resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} + engines: {node: '>=0.10.0'} + expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} @@ -5014,9 +5126,21 @@ packages: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} engines: {node: '>= 0.10.0'} + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extend-shallow@3.0.2: + resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} + engines: {node: '>=0.10.0'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extglob@2.0.4: + resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} + engines: {node: '>=0.10.0'} + extract-zip@1.7.0: resolution: {integrity: sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==} hasBin: true @@ -5055,6 +5179,10 @@ packages: fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -5092,10 +5220,18 @@ packages: resolution: {integrity: sha512-6RS9gDchbn+qWmtV2uSjo5vmKizgfCQeb5jKmqx8HyzA3MoLqqyQxN+QcjkGBJt7FjJ9qFce67Auyya5rRRbpw==} engines: {node: '>= 10.4.0'} + fill-range@4.0.0: + resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} + engines: {node: '>=0.10.0'} + fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} + finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + finalhandler@1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} engines: {node: '>= 0.8'} @@ -5158,6 +5294,10 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-in@1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + foreground-child@2.0.0: resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} engines: {node: '>=8.0.0'} @@ -5188,10 +5328,17 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} + fragment-cache@0.2.1: + resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} + engines: {node: '>=0.10.0'} + fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -5221,6 +5368,12 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@1.2.13: + resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} + engines: {node: '>= 4.0'} + os: [darwin] + deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2 + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -5305,6 +5458,10 @@ packages: get-tsconfig@4.7.2: resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + get-value@2.0.6: + resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} + engines: {node: '>=0.10.0'} + getos@3.2.1: resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==} @@ -5325,6 +5482,9 @@ packages: resolution: {integrity: sha512-yBbfpChOtFvg5D+KtMaBFvj6yt3vUnheNAH+UrQH2TfDB8kr0tERdL0Tjhe0W7xJ6jR6ftQBluTZR9jXUnKe8g==} engines: {node: '>=14.0.0'} + glob-parent@3.1.0: + resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -5458,6 +5618,22 @@ packages: has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + has-value@0.3.1: + resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} + engines: {node: '>=0.10.0'} + + has-value@1.0.0: + resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} + engines: {node: '>=0.10.0'} + + has-values@0.1.4: + resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} + engines: {node: '>=0.10.0'} + + has-values@1.0.0: + resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} + engines: {node: '>=0.10.0'} + has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -5515,16 +5691,27 @@ packages: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} + http-auth@3.1.3: + resolution: {integrity: sha512-Jbx0+ejo2IOx+cRUYAGS1z6RGc6JfYUNkysZM4u4Sfk1uLlGv814F7/PIjQQAuThLdAWxb74JMGd5J8zex1VQg==} + engines: {node: '>=4.6.1'} + http-browserify@1.7.0: resolution: {integrity: sha512-Irf/LJXmE3cBzU1eaR4+NEX6bmVLqt1wkmDiA7kBwH7zmb0D8kBAXsDmQ88hhj/qv9iEZKlyGx/hrMcFi8sOHw==} http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + http-errors@1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + http-parser-js@0.5.8: + resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} + http-proxy-agent@5.0.0: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} engines: {node: '>= 6'} @@ -5626,6 +5813,9 @@ packages: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -5661,6 +5851,10 @@ packages: resolution: {integrity: sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==} engines: {node: '>=8'} + is-accessor-descriptor@1.0.1: + resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==} + engines: {node: '>= 0.10'} + is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -5685,6 +5879,10 @@ packages: is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-binary-path@1.0.1: + resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==} + engines: {node: '>=0.10.0'} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -5693,6 +5891,9 @@ packages: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + is-builtin-module@3.2.1: resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} engines: {node: '>=6'} @@ -5711,6 +5912,10 @@ packages: is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-data-descriptor@1.0.1: + resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} + engines: {node: '>= 0.4'} + is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} engines: {node: '>= 0.4'} @@ -5722,11 +5927,27 @@ packages: is-deflate@1.0.0: resolution: {integrity: sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==} + is-descriptor@0.1.7: + resolution: {integrity: sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==} + engines: {node: '>= 0.4'} + + is-descriptor@1.0.3: + resolution: {integrity: sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==} + engines: {node: '>= 0.4'} + is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} hasBin: true + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extendable@1.0.1: + resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} + engines: {node: '>=0.10.0'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -5745,6 +5966,10 @@ packages: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} + is-glob@3.1.0: + resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} + engines: {node: '>=0.10.0'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -5787,6 +6012,10 @@ packages: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} + is-number@3.0.0: + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} + engines: {node: '>=0.10.0'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -5876,6 +6105,14 @@ packages: resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} engines: {node: '>= 0.4'} + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-wsl@1.1.0: + resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==} + engines: {node: '>=4'} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -5895,6 +6132,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isobject@2.1.0: + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} + engines: {node: '>=0.10.0'} + isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} @@ -6076,6 +6317,14 @@ packages: keyv@4.5.3: resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + kind-of@3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + + kind-of@4.0.0: + resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} + engines: {node: '>=0.10.0'} + kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -6124,6 +6373,11 @@ packages: enquirer: optional: true + live-server@1.2.2: + resolution: {integrity: sha512-t28HXLjITRGoMSrCOv4eZ88viHaBVIjKjdI5PO92Vxlu+twbk6aE0t7dVIaz6ZWkjPilYFV6OSdMYl9ybN2B4w==} + engines: {node: '>=0.10.0'} + hasBin: true + load-bmfont@1.4.1: resolution: {integrity: sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==} @@ -6269,6 +6523,10 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -6280,6 +6538,13 @@ packages: map-or-similar@1.5.0: resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} + map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + + map-visit@1.0.0: + resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} + engines: {node: '>=0.10.0'} + markdown-it@14.0.0: resolution: {integrity: sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==} hasBin: true @@ -6431,6 +6696,10 @@ packages: micromark@4.0.0: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + micromatch@3.1.10: + resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==} + engines: {node: '>=0.10.0'} + micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -6578,6 +6847,10 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + mixin-deep@1.3.2: + resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} + engines: {node: '>=0.10.0'} + mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} @@ -6608,6 +6881,10 @@ packages: moo@0.5.2: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + morgan@1.10.0: + resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==} + engines: {node: '>= 0.8.0'} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -6643,6 +6920,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanomatch@1.2.13: + resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} + engines: {node: '>=0.10.0'} + napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} @@ -6753,6 +7034,10 @@ packages: resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} engines: {node: '>=10'} + normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -6782,6 +7067,10 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-copy@0.1.0: + resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} + engines: {node: '>=0.10.0'} + object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} @@ -6796,6 +7085,10 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + object-visit@1.0.1: + resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} + engines: {node: '>=0.10.0'} + object.assign@4.1.4: resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} engines: {node: '>= 0.4'} @@ -6816,6 +7109,10 @@ packages: resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} engines: {node: '>= 0.4'} + object.pick@1.3.0: + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} + engines: {node: '>=0.10.0'} + object.values@1.1.7: resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} @@ -6827,6 +7124,10 @@ packages: omggif@1.0.10: resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -6850,6 +7151,11 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true + opn@6.0.0: + resolution: {integrity: sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==} + engines: {node: '>=8'} + deprecated: The package has been renamed to `open` + optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -6948,12 +7254,19 @@ packages: pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + pascalcase@0.1.1: + resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} + engines: {node: '>=0.10.0'} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} path-case@3.0.4: resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} + path-dirname@1.0.2: + resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==} + path-exists-cli@2.0.0: resolution: {integrity: sha512-qGr0A87KYCznmvabblxyxnzA/MtPZ28wH+4SCMP4tjTFAbzqwvs5xpUZExAYzq5OgHe5vIswzdH5iosCb8YF/Q==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7007,6 +7320,9 @@ packages: pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pbkdf2@3.1.2: resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} engines: {node: '>=0.12'} @@ -7108,6 +7424,10 @@ packages: resolution: {integrity: sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==} engines: {node: '>= 0.12.0'} + posix-character-classes@0.1.1: + resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} + engines: {node: '>=0.10.0'} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -7322,6 +7642,10 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-middleware@0.15.0: + resolution: {integrity: sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==} + engines: {node: '>=0.8.0'} + psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} @@ -7604,6 +7928,10 @@ packages: resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdirp@2.2.1: + resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} + engines: {node: '>=0.10'} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -7646,6 +7974,10 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + regex-not@1.0.2: + resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} + engines: {node: '>=0.10.0'} + regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true @@ -7689,6 +8021,17 @@ packages: remark@15.0.1: resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + + repeat-element@1.1.4: + resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} + engines: {node: '>=0.10.0'} + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + request-progress@3.0.0: resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} @@ -7717,6 +8060,10 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve-url@0.2.1: + resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} + deprecated: https://github.com/lydell/resolve-url#deprecated + resolve@1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true @@ -7816,6 +8163,9 @@ packages: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safe-regex@1.1.0: + resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -7876,6 +8226,10 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + serve-index@1.9.1: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} @@ -7899,9 +8253,16 @@ packages: resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} engines: {node: '>=6.9'} + set-value@2.0.1: + resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} + engines: {node: '>=0.10.0'} + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -8001,6 +8362,18 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + snapdragon-node@2.1.1: + resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==} + engines: {node: '>=0.10.0'} + + snapdragon-util@3.0.1: + resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==} + engines: {node: '>=0.10.0'} + + snapdragon@0.8.2: + resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} + engines: {node: '>=0.10.0'} + socket.io-adapter@1.1.2: resolution: {integrity: sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==} @@ -8048,9 +8421,17 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-resolve@0.5.3: + resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated + source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map-url@0.4.1: + resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} + deprecated: See https://github.com/lydell/source-map-url#deprecated + source-map@0.5.6: resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} engines: {node: '>=0.10.0'} @@ -8086,6 +8467,13 @@ packages: spdx-license-ids@3.0.13: resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} + split-string@3.1.0: + resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} + engines: {node: '>=0.10.0'} + + split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -8113,12 +8501,20 @@ packages: stacktrace-js@2.0.2: resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + static-extend@0.1.2: + resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} + engines: {node: '>=0.10.0'} + stats-gl@1.0.5: resolution: {integrity: sha512-XimMxvwnf1Qf5KwebhcoA34kcX+fWEkIl0QjNkCbu4IpoyDMMsOajExn7FIq5w569k45+LhmsuRlGSrsvmGdNw==} stats.js@0.17.0: resolution: {integrity: sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==} + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -8136,6 +8532,9 @@ packages: stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + stream-http@3.2.0: resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} @@ -8410,10 +8809,22 @@ packages: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} + to-object-path@0.3.0: + resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} + engines: {node: '>=0.10.0'} + + to-regex-range@2.1.1: + resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} + engines: {node: '>=0.10.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + to-regex@3.0.2: + resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} + engines: {node: '>=0.10.0'} + tocbot@4.21.2: resolution: {integrity: sha512-R5Muhi/TUu4i4snWVrMgNoXyJm2f8sJfdgIkQvqb+cuIXQEIMAiWGWgCgYXHqX4+XiS/Bnm7IYZ9Zy6NVe6lhw==} @@ -8620,6 +9031,10 @@ packages: unified@11.0.4: resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + union-value@1.0.1: + resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} + engines: {node: '>=0.10.0'} + union@0.5.0: resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} engines: {node: '>= 0.8.0'} @@ -8669,6 +9084,9 @@ packages: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} + unix-crypt-td-js@1.1.4: + resolution: {integrity: sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -8676,6 +9094,10 @@ packages: unplugin@1.5.0: resolution: {integrity: sha512-9ZdRwbh/4gcm1JTOkp9lAkIDrtOyOxgHmY7cjuwI8L/2RTikMcVG25GsZwNAgRuap3iDw2jeq7eoqtAsz5rW3A==} + unset-value@1.0.0: + resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} + engines: {node: '>=0.10.0'} + untildify@4.0.0: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} engines: {node: '>=8'} @@ -8705,6 +9127,10 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + urix@0.1.0: + resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} + deprecated: Please see https://github.com/lydell/urix#deprecated + url-join@4.0.1: resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} @@ -8769,6 +9195,10 @@ packages: typescript: optional: true + use@3.1.1: + resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} + engines: {node: '>=0.10.0'} + utf8-byte-length@1.0.4: resolution: {integrity: sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==} @@ -8788,6 +9218,11 @@ packages: uuid-1345@1.0.2: resolution: {integrity: sha512-bA5zYZui+3nwAc0s3VdGQGBfbVsJLVX7Np7ch2aqcEWFi5lsAEcmO3+lx3djM1npgpZI8KY2FITZ2uYTnYUYyw==} + uuid@3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -8979,6 +9414,14 @@ packages: resolution: {integrity: sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==} engines: {node: '>=6.0.0', npm: '>=3.10.0'} + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} @@ -13096,11 +13539,24 @@ snapshots: any-promise@1.3.0: {} + anymatch@2.0.0: + dependencies: + micromatch: 3.1.10 + normalize-path: 2.1.1 + transitivePeerDependencies: + - supports-color + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 + apache-crypt@1.2.6: + dependencies: + unix-crypt-td-js: 1.1.4 + + apache-md5@1.1.8: {} + app-root-dir@1.0.2: {} aproba@2.0.0: @@ -13131,6 +13587,12 @@ snapshots: dependencies: tslib: 2.6.2 + arr-diff@4.0.0: {} + + arr-flatten@1.1.0: {} + + arr-union@3.1.0: {} + array-buffer-byte-length@1.0.0: dependencies: call-bind: 1.0.2 @@ -13162,6 +13624,8 @@ snapshots: array-union@2.1.0: {} + array-unique@0.3.2: {} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.7 @@ -13259,6 +13723,8 @@ snapshots: assertion-error@1.1.0: {} + assign-symbols@1.0.0: {} + ast-types@0.14.2: dependencies: tslib: 2.6.2 @@ -13274,6 +13740,8 @@ snapshots: astral-regex@2.0.0: optional: true + async-each@1.0.6: {} + async-limiter@1.0.1: {} async@2.6.4: @@ -13286,6 +13754,8 @@ snapshots: at-least-node@1.0.0: {} + atob@2.1.2: {} + available-typed-arrays@1.0.5: {} available-typed-arrays@1.0.7: @@ -13370,21 +13840,37 @@ snapshots: base64id@2.0.0: {} + base@0.11.2: + dependencies: + cache-base: 1.0.1 + class-utils: 0.3.6 + component-emitter: 1.3.0 + define-property: 1.0.0 + isobject: 3.0.1 + mixin-deep: 1.3.2 + pascalcase: 0.1.1 + basic-auth@2.0.1: dependencies: safe-buffer: 5.1.2 + batch@0.6.1: {} + bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 optional: true + bcryptjs@2.4.3: {} + better-opn@3.0.2: dependencies: open: 8.4.2 big-integer@1.6.51: {} + binary-extensions@1.13.1: {} + binary-extensions@2.2.0: {} bindings@1.5.0: @@ -13466,6 +13952,21 @@ snapshots: dependencies: balanced-match: 1.0.2 + braces@2.3.2: + dependencies: + arr-flatten: 1.1.0 + array-unique: 0.3.2 + extend-shallow: 2.0.1 + fill-range: 4.0.0 + isobject: 3.0.1 + repeat-element: 1.1.4 + snapdragon: 0.8.2 + snapdragon-node: 2.1.1 + split-string: 3.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + braces@3.0.2: dependencies: fill-range: 7.0.1 @@ -13612,6 +14113,18 @@ snapshots: - bluebird optional: true + cache-base@1.0.1: + dependencies: + collection-visit: 1.0.0 + component-emitter: 1.3.0 + get-value: 2.0.6 + has-value: 1.0.0 + isobject: 3.0.1 + set-value: 2.0.1 + to-object-path: 0.3.0 + union-value: 1.0.1 + unset-value: 1.0.0 + cachedir@2.4.0: optional: true @@ -13732,6 +14245,24 @@ snapshots: check-more-types@2.24.0: optional: true + chokidar@2.1.8: + dependencies: + anymatch: 2.0.0 + async-each: 1.0.6 + braces: 2.3.2 + glob-parent: 3.1.0 + inherits: 2.0.4 + is-binary-path: 1.0.1 + is-glob: 4.0.3 + normalize-path: 3.0.0 + path-is-absolute: 1.0.1 + readdirp: 2.2.1 + upath: 1.2.0 + optionalDependencies: + fsevents: 1.2.13 + transitivePeerDependencies: + - supports-color + chokidar@3.5.3: dependencies: anymatch: 3.1.3 @@ -13757,6 +14288,13 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 + class-utils@0.3.6: + dependencies: + arr-union: 3.1.0 + define-property: 0.2.5 + isobject: 3.0.1 + static-extend: 0.1.2 + classnames@2.5.1: {} clean-regexp@1.0.0: @@ -13807,6 +14345,11 @@ snapshots: clsx@2.1.1: {} + collection-visit@1.0.0: + dependencies: + map-visit: 1.0.0 + object-visit: 1.0.1 + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -13857,8 +14400,7 @@ snapshots: component-emitter@1.2.1: optional: true - component-emitter@1.3.0: - optional: true + component-emitter@1.3.0: {} component-inherit@0.0.3: optional: true @@ -13890,6 +14432,15 @@ snapshots: confusing-browser-globals@1.0.11: {} + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + console-browserify@1.2.0: {} console-control-strings@1.1.0: @@ -13930,6 +14481,8 @@ snapshots: cookie@0.5.0: {} + copy-descriptor@0.1.1: {} + copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 @@ -14183,6 +14736,8 @@ snapshots: dependencies: character-entities: 2.0.2 + decode-uri-component@0.2.2: {} + decompress-response@4.2.1: dependencies: mimic-response: 2.1.0 @@ -14231,6 +14786,19 @@ snapshots: has-property-descriptors: 1.0.0 object-keys: 1.1.1 + define-property@0.2.5: + dependencies: + is-descriptor: 0.1.7 + + define-property@1.0.0: + dependencies: + is-descriptor: 1.0.3 + + define-property@2.0.2: + dependencies: + is-descriptor: 1.0.3 + isobject: 3.0.1 + defu@6.1.2: {} del@6.1.1: @@ -14249,6 +14817,8 @@ snapshots: delegates@1.0.0: optional: true + depd@1.1.2: {} + depd@2.0.0: {} dequal@1.0.0: {} @@ -15068,6 +15638,16 @@ snapshots: etag@1.8.1: {} + event-stream@3.3.4: + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.1.0 + pause-stream: 0.0.11 + split: 0.3.3 + stream-combiner: 0.0.4 + through: 2.3.8 + event-target-shim@5.0.1: {} eventemitter2@6.4.7: @@ -15117,6 +15697,18 @@ snapshots: exit-hook@2.2.1: {} + expand-brackets@2.1.4: + dependencies: + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + posix-character-classes: 0.1.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + expand-template@2.0.3: {} exponential-backoff@3.1.1: @@ -15166,8 +15758,30 @@ snapshots: transitivePeerDependencies: - supports-color + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extend-shallow@3.0.2: + dependencies: + assign-symbols: 1.0.0 + is-extendable: 1.0.1 + extend@3.0.2: {} + extglob@2.0.4: + dependencies: + array-unique: 0.3.2 + define-property: 1.0.0 + expand-brackets: 2.1.4 + extend-shallow: 2.0.1 + fragment-cache: 0.2.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + extract-zip@1.7.0: dependencies: concat-stream: 1.6.2 @@ -15214,6 +15828,10 @@ snapshots: dependencies: reusify: 1.0.4 + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + fb-watchman@2.0.2: dependencies: bser: 2.1.1 @@ -15252,10 +15870,29 @@ snapshots: filesize@10.0.12: {} + fill-range@4.0.0: + dependencies: + extend-shallow: 2.0.1 + is-number: 3.0.0 + repeat-string: 1.6.1 + to-regex-range: 2.1.1 + fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 + finalhandler@1.1.2: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + finalhandler@1.2.0: dependencies: debug: 2.6.9 @@ -15320,6 +15957,8 @@ snapshots: dependencies: is-callable: 1.2.7 + for-in@1.0.2: {} + foreground-child@2.0.0: dependencies: cross-spawn: 7.0.3 @@ -15365,8 +16004,14 @@ snapshots: forwarded@0.2.0: {} + fragment-cache@0.2.1: + dependencies: + map-cache: 0.2.2 + fresh@0.5.2: {} + from@0.1.7: {} + fs-constants@1.0.0: {} fs-extra@10.1.0: @@ -15403,6 +16048,12 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@1.2.13: + dependencies: + bindings: 1.5.0 + nan: 2.18.0 + optional: true + fsevents@2.3.3: optional: true @@ -15496,6 +16147,8 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-value@2.0.6: {} + getos@3.2.1: dependencies: async: 3.2.5 @@ -15536,6 +16189,11 @@ snapshots: - supports-color optional: true + glob-parent@3.1.0: + dependencies: + is-glob: 3.1.0 + path-dirname: 1.0.2 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -15688,6 +16346,25 @@ snapshots: has-unicode@2.0.1: optional: true + has-value@0.3.1: + dependencies: + get-value: 2.0.6 + has-values: 0.1.4 + isobject: 2.1.0 + + has-value@1.0.0: + dependencies: + get-value: 2.0.6 + has-values: 1.0.0 + isobject: 3.0.1 + + has-values@0.1.4: {} + + has-values@1.0.0: + dependencies: + is-number: 3.0.0 + kind-of: 4.0.0 + has@1.0.3: dependencies: function-bind: 1.1.1 @@ -15750,6 +16427,13 @@ snapshots: html-tags@3.3.1: {} + http-auth@3.1.3: + dependencies: + apache-crypt: 1.2.6 + apache-md5: 1.1.8 + bcryptjs: 2.4.3 + uuid: 3.4.0 + http-browserify@1.7.0: dependencies: Base64: 0.2.1 @@ -15757,6 +16441,13 @@ snapshots: http-cache-semantics@4.1.1: {} + http-errors@1.6.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -15765,6 +16456,8 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-parser-js@0.5.8: {} + http-proxy-agent@5.0.0: dependencies: '@tootallnate/once': 2.0.0 @@ -15887,6 +16580,8 @@ snapshots: once: 1.4.0 wrappy: 1.0.2 + inherits@2.0.3: {} + inherits@2.0.4: {} ini@1.3.8: {} @@ -15921,6 +16616,10 @@ snapshots: is-absolute-url@3.0.3: {} + is-accessor-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + is-arguments@1.1.1: dependencies: call-bind: 1.0.2 @@ -15949,6 +16648,10 @@ snapshots: dependencies: has-bigints: 1.0.2 + is-binary-path@1.0.1: + dependencies: + binary-extensions: 1.13.1 + is-binary-path@2.1.0: dependencies: binary-extensions: 2.2.0 @@ -15958,6 +16661,8 @@ snapshots: call-bind: 1.0.2 has-tostringtag: 1.0.0 + is-buffer@1.1.6: {} + is-builtin-module@3.2.1: dependencies: builtin-modules: 3.3.0 @@ -15977,6 +16682,10 @@ snapshots: dependencies: hasown: 2.0.2 + is-data-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 @@ -15987,8 +16696,24 @@ snapshots: is-deflate@1.0.0: {} + is-descriptor@0.1.7: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + + is-descriptor@1.0.3: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + is-docker@2.2.1: {} + is-extendable@0.1.1: {} + + is-extendable@1.0.1: + dependencies: + is-plain-object: 2.0.4 + is-extglob@2.1.1: {} is-finalizationregistry@1.0.2: @@ -16004,6 +16729,10 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-glob@3.1.0: + dependencies: + is-extglob: 2.1.1 + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -16038,6 +16767,10 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-number@3.0.0: + dependencies: + kind-of: 3.2.2 + is-number@7.0.0: {} is-obj@1.0.1: {} @@ -16107,6 +16840,10 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 + is-windows@1.0.2: {} + + is-wsl@1.1.0: {} + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 @@ -16123,6 +16860,10 @@ snapshots: isexe@2.0.0: {} + isobject@2.1.0: + dependencies: + isarray: 1.0.0 + isobject@3.0.1: {} isstream@0.1.2: @@ -16378,6 +17119,14 @@ snapshots: dependencies: json-buffer: 3.0.1 + kind-of@3.2.2: + dependencies: + is-buffer: 1.1.6 + + kind-of@4.0.0: + dependencies: + is-buffer: 1.1.6 + kind-of@6.0.3: {} kleur@3.0.3: {} @@ -16426,6 +17175,24 @@ snapshots: enquirer: 2.4.1 optional: true + live-server@1.2.2: + dependencies: + chokidar: 2.1.8 + colors: 1.4.0 + connect: 3.7.0 + cors: 2.8.5 + event-stream: 3.3.4 + faye-websocket: 0.11.4 + http-auth: 3.1.3 + morgan: 1.10.0 + object-assign: 4.1.1 + opn: 6.0.0 + proxy-middleware: 0.15.0 + send: 0.18.0 + serve-index: 1.9.1 + transitivePeerDependencies: + - supports-color + load-bmfont@1.4.1: dependencies: buffer-equal: 0.0.1 @@ -16588,12 +17355,20 @@ snapshots: dependencies: tmpl: 1.0.5 + map-cache@0.2.2: {} + map-obj@1.0.1: {} map-obj@4.3.0: {} map-or-similar@1.5.0: {} + map-stream@0.1.0: {} + + map-visit@1.0.0: + dependencies: + object-visit: 1.0.1 + markdown-it@14.0.0: dependencies: argparse: 2.0.1 @@ -16839,6 +17614,24 @@ snapshots: transitivePeerDependencies: - supports-color + micromatch@3.1.10: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + braces: 2.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + extglob: 2.0.4 + fragment-cache: 0.2.1 + kind-of: 6.0.3 + nanomatch: 1.2.13 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + micromatch@4.0.5: dependencies: braces: 3.0.2 @@ -17066,6 +17859,11 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + mixin-deep@1.3.2: + dependencies: + for-in: 1.0.2 + is-extendable: 1.0.1 + mkdirp-classic@0.5.3: {} mkdirp@0.3.0: @@ -17092,6 +17890,16 @@ snapshots: moo@0.5.2: {} + morgan@1.10.0: + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 2.0.0 + on-finished: 2.3.0 + on-headers: 1.0.2 + transitivePeerDependencies: + - supports-color + mri@1.2.0: {} ms@2.0.0: {} @@ -17126,6 +17934,22 @@ snapshots: nanoid@3.3.7: {} + nanomatch@1.2.13: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + fragment-cache: 0.2.1 + is-windows: 1.0.2 + kind-of: 6.0.3 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + napi-build-utils@1.0.2: {} natural-compare-lite@1.4.0: {} @@ -17263,6 +18087,10 @@ snapshots: semver: 7.6.0 validate-npm-package-license: 3.0.4 + normalize-path@2.1.1: + dependencies: + remove-trailing-separator: 1.1.0 + normalize-path@3.0.0: {} npm-run-all@4.1.5: @@ -17303,6 +18131,12 @@ snapshots: object-assign@4.1.1: {} + object-copy@0.1.0: + dependencies: + copy-descriptor: 0.1.1 + define-property: 0.2.5 + kind-of: 3.2.2 + object-inspect@1.12.3: {} object-inspect@1.13.1: {} @@ -17314,6 +18148,10 @@ snapshots: object-keys@1.1.1: {} + object-visit@1.0.1: + dependencies: + isobject: 3.0.1 + object.assign@4.1.4: dependencies: call-bind: 1.0.2 @@ -17347,6 +18185,10 @@ snapshots: es-abstract: 1.23.3 es-object-atoms: 1.0.0 + object.pick@1.3.0: + dependencies: + isobject: 3.0.1 + object.values@1.1.7: dependencies: call-bind: 1.0.2 @@ -17362,6 +18204,10 @@ snapshots: omggif@1.0.10: optional: true + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -17384,6 +18230,10 @@ snapshots: opener@1.5.2: {} + opn@6.0.0: + dependencies: + is-wsl: 1.1.0 + optionator@0.9.3: dependencies: '@aashutoshrathi/word-wrap': 1.2.6 @@ -17503,6 +18353,8 @@ snapshots: no-case: 3.0.4 tslib: 2.6.2 + pascalcase@0.1.1: {} + path-browserify@1.0.1: {} path-case@3.0.4: @@ -17510,6 +18362,8 @@ snapshots: dot-case: 3.0.4 tslib: 2.6.2 + path-dirname@1.0.2: {} + path-exists-cli@2.0.0: dependencies: meow: 10.1.5 @@ -17546,6 +18400,10 @@ snapshots: pathval@1.1.1: {} + pause-stream@0.0.11: + dependencies: + through: 2.3.8 + pbkdf2@3.1.2: dependencies: create-hash: 1.2.0 @@ -17641,6 +18499,8 @@ snapshots: transitivePeerDependencies: - supports-color + posix-character-classes@0.1.1: {} + possible-typed-array-names@1.0.0: {} postcss@8.4.31: @@ -17947,6 +18807,8 @@ snapshots: proxy-from-env@1.1.0: {} + proxy-middleware@0.15.0: {} + psl@1.9.0: optional: true @@ -18304,6 +19166,14 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 + readdirp@2.2.1: + dependencies: + graceful-fs: 4.2.11 + micromatch: 3.1.10 + readable-stream: 2.3.8 + transitivePeerDependencies: + - supports-color + readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -18359,6 +19229,11 @@ snapshots: dependencies: '@babel/runtime': 7.24.5 + regex-not@1.0.2: + dependencies: + extend-shallow: 3.0.2 + safe-regex: 1.1.0 + regexp-tree@0.1.27: {} regexp.prototype.flags@1.5.1: @@ -18431,6 +19306,12 @@ snapshots: transitivePeerDependencies: - supports-color + remove-trailing-separator@1.1.0: {} + + repeat-element@1.1.4: {} + + repeat-string@1.6.1: {} + request-progress@3.0.0: dependencies: throttleit: 1.0.0 @@ -18450,6 +19331,8 @@ snapshots: resolve-pkg-maps@1.0.0: {} + resolve-url@0.2.1: {} + resolve@1.22.4: dependencies: is-core-module: 2.13.0 @@ -18558,6 +19441,10 @@ snapshots: es-errors: 1.3.0 is-regex: 1.1.4 + safe-regex@1.1.0: + dependencies: + ret: 0.1.15 + safer-buffer@2.1.2: {} sanitize-filename@1.6.3: @@ -18629,6 +19516,18 @@ snapshots: dependencies: randombytes: 2.1.0 + serve-index@1.9.1: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.6.3 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + serve-static@1.15.0: dependencies: encodeurl: 1.0.2 @@ -18665,8 +19564,17 @@ snapshots: set-harmonic-interval@1.0.1: {} + set-value@2.0.1: + dependencies: + extend-shallow: 2.0.1 + is-extendable: 0.1.1 + is-plain-object: 2.0.4 + split-string: 3.1.0 + setimmediate@1.0.5: {} + setprototypeof@1.1.0: {} + setprototypeof@1.2.0: {} sha.js@2.4.11: @@ -18786,6 +19694,29 @@ snapshots: dot-case: 3.0.4 tslib: 2.6.2 + snapdragon-node@2.1.1: + dependencies: + define-property: 1.0.0 + isobject: 3.0.1 + snapdragon-util: 3.0.1 + + snapdragon-util@3.0.1: + dependencies: + kind-of: 3.2.2 + + snapdragon@0.8.2: + dependencies: + base: 0.11.2 + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + map-cache: 0.2.2 + source-map: 0.5.7 + source-map-resolve: 0.5.3 + use: 3.1.1 + transitivePeerDependencies: + - supports-color + socket.io-adapter@1.1.2: optional: true @@ -18898,11 +19829,21 @@ snapshots: source-map-js@1.2.0: {} + source-map-resolve@0.5.3: + dependencies: + atob: 2.1.2 + decode-uri-component: 0.2.2 + resolve-url: 0.2.1 + source-map-url: 0.4.1 + urix: 0.1.0 + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 + source-map-url@0.4.1: {} + source-map@0.5.6: {} source-map@0.5.7: {} @@ -18931,6 +19872,14 @@ snapshots: spdx-license-ids@3.0.13: {} + split-string@3.1.0: + dependencies: + extend-shallow: 3.0.2 + + split@0.3.3: + dependencies: + through: 2.3.8 + sprintf-js@1.0.3: {} sshpk@1.17.0: @@ -18970,10 +19919,17 @@ snapshots: stack-generator: 2.0.10 stacktrace-gps: 3.1.2 + static-extend@0.1.2: + dependencies: + define-property: 0.2.5 + object-copy: 0.1.0 + stats-gl@1.0.5: {} stats.js@0.17.0: {} + statuses@1.5.0: {} + statuses@2.0.1: {} std-env@3.4.3: {} @@ -18994,6 +19950,10 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + stream-combiner@0.0.4: + dependencies: + duplexer: 0.1.2 + stream-http@3.2.0: dependencies: builtin-status-codes: 3.0.0 @@ -19279,8 +20239,7 @@ snapshots: readable-stream: 2.3.8 xtend: 4.0.2 - through@2.3.8: - optional: true + through@2.3.8: {} timers-browserify@2.0.12: dependencies: @@ -19316,10 +20275,26 @@ snapshots: to-fast-properties@2.0.0: {} + to-object-path@0.3.0: + dependencies: + kind-of: 3.2.2 + + to-regex-range@2.1.1: + dependencies: + is-number: 3.0.0 + repeat-string: 1.6.1 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + to-regex@3.0.2: + dependencies: + define-property: 2.0.2 + extend-shallow: 3.0.2 + regex-not: 1.0.2 + safe-regex: 1.1.0 + tocbot@4.21.2: {} toggle-selection@1.0.6: {} @@ -19532,6 +20507,13 @@ snapshots: trough: 2.2.0 vfile: 6.0.1 + union-value@1.0.1: + dependencies: + arr-union: 3.1.0 + get-value: 2.0.6 + is-extendable: 0.1.1 + set-value: 2.0.1 + union@0.5.0: dependencies: qs: 6.11.2 @@ -19590,6 +20572,8 @@ snapshots: universalify@2.0.0: {} + unix-crypt-td-js@1.1.4: {} + unpipe@1.0.0: {} unplugin@1.5.0: @@ -19599,6 +20583,11 @@ snapshots: webpack-sources: 3.2.3 webpack-virtual-modules: 0.5.0 + unset-value@1.0.0: + dependencies: + has-value: 0.3.1 + isobject: 3.0.1 + untildify@4.0.0: {} upath@1.2.0: {} @@ -19627,6 +20616,8 @@ snapshots: dependencies: punycode: 2.3.0 + urix@0.1.0: {} + url-join@4.0.1: {} url-parse@1.5.10: @@ -19684,6 +20675,8 @@ snapshots: optionalDependencies: typescript: 5.5.4 + use@3.1.1: {} + utf8-byte-length@1.0.4: {} utif@2.0.1: @@ -19707,6 +20700,8 @@ snapshots: dependencies: macaddress: 0.5.3 + uuid@3.4.0: {} + uuid@8.3.2: {} uuid@9.0.1: {} @@ -19914,6 +20909,14 @@ snapshots: dependencies: sdp: 3.2.0 + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.8 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + whatwg-encoding@2.0.0: dependencies: iconv-lite: 0.6.3 diff --git a/prismarine-viewer/esbuild.mjs b/prismarine-viewer/esbuild.mjs index 90741ee1..e410c8b6 100644 --- a/prismarine-viewer/esbuild.mjs +++ b/prismarine-viewer/esbuild.mjs @@ -6,20 +6,19 @@ import * as esbuild from 'esbuild' import { polyfillNode } from 'esbuild-plugin-polyfill-node' import path, { dirname, join } from 'path' import { fileURLToPath } from 'url' +import childProcess from 'child_process' +import supportedVersions from '../src/supportedVersions.mjs' const dev = process.argv.includes('-w') const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url))) -const mcDataPath = join(__dirname, '../dist/mc-data') +const mcDataPath = join(__dirname, '../generated/minecraft-data-optimized.json') if (!fs.existsSync(mcDataPath)) { - // shouldn't it be in the viewer instead? - await import('../scripts/prepareData.mjs') + childProcess.execSync('tsx ../scripts/makeOptimizedMcData.mjs', { stdio: 'inherit', cwd: __dirname }) } fs.copyFileSync(join(__dirname, 'playground.html'), join(__dirname, 'public/index.html')) -fsExtra.copySync(mcDataPath, join(__dirname, 'public/mc-data')) -const availableVersions = fs.readdirSync(mcDataPath).map(ver => ver.replace('.js', '')) /** @type {import('esbuild').BuildOptions} */ const buildOptions = { @@ -37,7 +36,7 @@ const buildOptions = { ], keepNames: true, banner: { - js: `globalThis.global = globalThis;globalThis.includedVersions = ${JSON.stringify(availableVersions)};`, + js: `globalThis.global = globalThis;globalThis.includedVersions = ${JSON.stringify(supportedVersions)};`, }, alias: { events: 'events', @@ -63,13 +62,14 @@ const buildOptions = { }, () => { const defaultVersionsObj = {} return { - contents: `window.mcData ??= ${JSON.stringify(defaultVersionsObj)};module.exports = { pc: window.mcData }`, - loader: 'js', + contents: fs.readFileSync(join(__dirname, '../src/shims/minecraftData.ts'), 'utf8'), + loader: 'ts', + resolveDir: join(__dirname, '../src/shims'), } }) build.onEnd((e) => { if (e.errors.length) return - // fs.writeFileSync(join(__dirname, 'dist/metafile.json'), JSON.stringify(e.metafile), 'utf8') + fs.writeFileSync(join(__dirname, './public/metafile.json'), JSON.stringify(e.metafile), 'utf8') }) } }, diff --git a/prismarine-viewer/examples/playground.ts b/prismarine-viewer/examples/playground.ts index 28dcceef..1726fe86 100644 --- a/prismarine-viewer/examples/playground.ts +++ b/prismarine-viewer/examples/playground.ts @@ -15,6 +15,7 @@ import { loadScript } from '../viewer/lib/utils' import { TWEEN_DURATION } from '../viewer/lib/entities' import { EntityMesh } from '../viewer/lib/entity/EntityMesh' import { WorldDataEmitter, Viewer } from '../viewer' +import '../../src/getCollisionShapes' import { toMajorVersion } from '../../src/utils' window.THREE = THREE @@ -65,21 +66,21 @@ async function main () { let continuousRender = false const { version } = params + await window._LOAD_MC_DATA() // temporary solution until web worker is here, cache data for faster reloads - const globalMcData = window['mcData'] - if (!globalMcData['version']) { - const major = toMajorVersion(version) - const sessionKey = `mcData-${major}` - if (sessionStorage[sessionKey]) { - Object.assign(globalMcData, JSON.parse(sessionStorage[sessionKey])) - } else { - if (sessionStorage.length > 1) sessionStorage.clear() - await loadScript(`./mc-data/${major}.js`) - try { - sessionStorage[sessionKey] = JSON.stringify(Object.fromEntries(Object.entries(globalMcData).filter(([ver]) => ver.startsWith(major)))) - } catch { } - } - } + // const globalMcData = window['mcData'] + // if (!globalMcData['version']) { + // const major = toMajorVersion(version) + // const sessionKey = `mcData-${major}` + // if (sessionStorage[sessionKey]) { + // Object.assign(globalMcData, JSON.parse(sessionStorage[sessionKey])) + // } else { + // if (sessionStorage.length > 1) sessionStorage.clear() + // try { + // sessionStorage[sessionKey] = JSON.stringify(Object.fromEntries(Object.entries(globalMcData).filter(([ver]) => ver.startsWith(major)))) + // } catch { } + // } + // } const mcData: IndexedData = require('minecraft-data')(version) window['loadedData'] = mcData diff --git a/prismarine-viewer/package.json b/prismarine-viewer/package.json index d0ea3826..02b0a304 100644 --- a/prismarine-viewer/package.json +++ b/prismarine-viewer/package.json @@ -38,5 +38,8 @@ "optionalDependencies": { "canvas": "^2.11.2", "node-canvas-webgl": "^0.3.0" + }, + "devDependencies": { + "live-server": "^1.2.2" } } diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index 204ad65f..58e5a6f5 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -2,7 +2,7 @@ import { EventEmitter } from 'events' import { Vec3 } from 'vec3' import * as THREE from 'three' -import mcDataRaw from 'minecraft-data/data.js' // handled correctly in esbuild plugin +import mcDataRaw from 'minecraft-data/data.js' // note: using alias import blocksAtlases from 'mc-assets/dist/blocksAtlases.json' import blocksAtlasLatest from 'mc-assets/dist/blocksAtlasLatest.png' import blocksAtlasLegacy from 'mc-assets/dist/blocksAtlasLegacy.png' @@ -223,8 +223,12 @@ export abstract class WorldRendererCommon sendMesherMcData () { const allMcData = mcDataRaw.pc[this.version] ?? mcDataRaw.pc[toMajorVersion(this.version)] - const mcData = Object.fromEntries(Object.entries(allMcData).filter(([key]) => dynamicMcDataFiles.includes(key))) - mcData.version = JSON.parse(JSON.stringify(mcData.version)) + const mcData = { + version: JSON.parse(JSON.stringify(allMcData.version)) + } + for (const key of dynamicMcDataFiles) { + mcData[key] = allMcData[key] + } for (const worker of this.workers) { worker.postMessage({ type: 'mcData', mcData, config: this.mesherConfig }) diff --git a/rsbuild.config.ts b/rsbuild.config.ts index fd05db8a..588cc35d 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -101,9 +101,10 @@ export default defineConfig({ const prep = async () => { console.time('total-prep') fs.mkdirSync('./generated', { recursive: true }) - if (!fs.existsSync('./generated/minecraft-data-data.js')) { - childProcess.execSync('tsx ./scripts/genShims.ts', { stdio: 'inherit' }) + if (!fs.existsSync('./generated/minecraft-data-optimized.json') || require('./generated/minecraft-data-optimized.json').versionKey !== require('minecraft-data/package.json').version) { + childProcess.execSync('tsx ./scripts/makeOptimizedMcData.mjs', { stdio: 'inherit' }) } + childProcess.execSync('tsx ./scripts/genShims.ts', { stdio: 'inherit' }) if (!fs.existsSync('./generated/latestBlockCollisionsShapes.json')) { childProcess.execSync('tsx ./scripts/optimizeBlockCollisions.ts', { stdio: 'inherit' }) } @@ -117,7 +118,6 @@ export default defineConfig({ configJson.defaultProxy = ':8080' } fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8') - childProcess.execSync('node ./scripts/prepareData.mjs', { stdio: 'inherit' }) // childProcess.execSync('./scripts/prepareSounds.mjs', { stdio: 'inherit' }) // childProcess.execSync('tsx ./scripts/genMcDataTypes.ts', { stdio: 'inherit' }) // childProcess.execSync('tsx ./scripts/genPixelartTypes.ts', { stdio: 'inherit' }) @@ -164,7 +164,7 @@ export default defineConfig({ // throw new Error(`${resource.request} was requested by ${resource.contextInfo.issuer}`) } if (absolute.endsWith('/minecraft-data/data.js')) { - resource.request = path.join(__dirname, './generated/minecraft-data-data.js') + resource.request = path.join(__dirname, './src/shims/minecraftData.ts') } })) addRules([ diff --git a/scripts/build.js b/scripts/build.js index 7d3dc3fc..9b8a85a5 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -45,7 +45,6 @@ exports.getSwAdditionalEntries = () => { // need to be careful with this const filesToCachePatterns = [ 'index.html', - `mc-data/${defaultLocalServerOptions.versionMajor}.js`, 'background/**', // todo-low copy from assets '*.mp3', diff --git a/scripts/genShims.ts b/scripts/genShims.ts index e688e525..0ee8d91e 100644 --- a/scripts/genShims.ts +++ b/scripts/genShims.ts @@ -1,24 +1,7 @@ import fs from 'fs' -import MinecraftData from 'minecraft-data' -import MCProtocol from 'minecraft-protocol' import { appReplacableResources } from '../src/resourcesSource' -const { supportedVersions, defaultVersion } = MCProtocol - -// gen generated/minecraft-data-data.js - -const data = MinecraftData(defaultVersion) -const defaultVersionObj = { - [defaultVersion]: { - version: data.version, - protocol: data.protocol, - } -} - -const mcDataContents = `window.mcData ??= ${JSON.stringify(defaultVersionObj)};module.exports = { pc: window.mcData }` - fs.mkdirSync('./generated', { recursive: true }) -fs.writeFileSync('./generated/minecraft-data-data.js', mcDataContents, 'utf8') // app resources diff --git a/scripts/makeOptimizedMcData.mjs b/scripts/makeOptimizedMcData.mjs new file mode 100644 index 00000000..d0adcae2 --- /dev/null +++ b/scripts/makeOptimizedMcData.mjs @@ -0,0 +1,230 @@ +//@ts-check +import { build } from 'esbuild' +import { existsSync } from 'node:fs' +import Module from "node:module" +import { dirname } from 'node:path' +import supportedVersions from '../src/supportedVersions.mjs' +import { gzipSizeFromFileSync } from 'gzip-size' +import fs from 'fs' +import {default as _JsonOptimizer} from '../src/optimizeJson' +import { gzipSync } from 'zlib'; +import MinecraftData from 'minecraft-data' +import MCProtocol from 'minecraft-protocol' + +/** @type {typeof _JsonOptimizer} */ +//@ts-ignore +const JsonOptimizer = _JsonOptimizer.default + +// console.log(a.diff_main(JSON.stringify({ a: 1 }), JSON.stringify({ a: 1, b: 2 }))) + +const require = Module.createRequire(import.meta.url) + +const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') + +function toMajor (version) { + const [a, b] = (version + '').split('.') + return `${a}.${b}` +} + +const versions = {} +const dataTypes = new Set() + +for (const [version, dataSet] of Object.entries(dataPaths.pc)) { + if (!supportedVersions.includes(version)) continue + for (const type of Object.keys(dataSet)) { + dataTypes.add(type) + } + versions[version] = dataSet +} + +const versionToNumber = (ver) => { + const [x, y = '0', z = '0'] = ver.split('.') + return +`${x.padStart(2, '0')}${y.padStart(2, '0')}${z.padStart(2, '0')}` +} + +// if not included here (even as {}) will not be bundled & accessible! +const compressedOutput = false +// const dataTypeBundling = { +// protocol: { +// // ignoreRemoved: true, +// // ignoreChanges: true +// } +// } +const dataTypeBundling = { + language: { + ignoreRemoved: true, + ignoreChanges: true + }, + blocks: { + arrKey: 'name', + // ignoreRemoved: true, + // genChanges (source, diff) { + // const diffs = {} + // const newItems = {} + // for (const [key, val] of Object.entries(diff)) { + // const src = source[key] + // if (!src) { + // newItems[key] = val + // continue + // } + // const { minStateId, defaultState, maxStateId } = val + // if (defaultState === undefined || minStateId === src.minStateId || maxStateId === src.maxStateId || defaultState === src.defaultState) continue + // diffs[key] = [minStateId, defaultState, maxStateId] + // } + // return { + // stateChanges: diffs + // } + // }, + // ignoreChanges: true + }, + items: { + arrKey: 'name' + }, + attributes: { + arrKey: 'name' + }, + particles: { + arrKey: 'name' + }, + effects: { + arrKey: 'name' + }, + enchantments: { + arrKey: 'name' + }, + instruments: { + arrKey: 'name' + }, + foods: { + arrKey: 'name' + }, + entities: { + arrKey: 'id+type' + }, + materials: {}, + windows: { + arrKey: 'name' + }, + version: { + raw: true + }, + tints: {}, + biomes: { + arrKey: 'name' + }, + entityLoot: { + arrKey: 'entity' + }, + blockLoot: { + arrKey: 'block' + }, + recipes: {}, // todo we can do better + blockCollisionShapes: {}, + loginPacket: {}, + protocol: { + raw: true + }, + sounds: { + arrKey: 'name' + } +} + +const notBundling = [...dataTypes.keys()].filter(x => !Object.keys(dataTypeBundling).includes(x)) +console.log("Not bundling minecraft-data data:", notBundling) + +let previousData = {} +// /** @type {Record} */ +const diffSources = {} +const versionsArr = Object.entries(versions) +const sizePerDataType = {} +const rawDataVersions = {} +// const versionsArr = Object.entries(versions).slice(-1) +for (const [i, [version, dataSet]] of versionsArr.reverse().entries()) { + for (const [dataType, dataPath] of Object.entries(dataSet)) { + const config = dataTypeBundling[dataType] + if (!config) continue + if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { + // contents += ` get ${dataType} () { return window.globalGetCollisionShapes?.("${version}") },\n` + continue + } + const loc = `minecraft-data/data/${dataPath}/` + const dataPathAbsolute = require.resolve(`minecraft-data/${loc}${dataType}`) + // const data = fs.readFileSync(dataPathAbsolute, 'utf8') + const dataRaw = require(dataPathAbsolute) + let injectCode = '' + let rawData = dataRaw + if (config.raw) { + rawDataVersions[dataType] ??= {} + rawDataVersions[dataType][version] = rawData + rawData = dataRaw + } else { + if (!diffSources[dataType]) { + diffSources[dataType] = new JsonOptimizer(config.arrKey, config.ignoreChanges, config.ignoreRemoved) + } + try { + diffSources[dataType].recordDiff(version, dataRaw) + injectCode = `restoreDiff(sources, ${JSON.stringify(dataType)}, ${JSON.stringify(version)})` + } catch (err) { + const error = new Error(`Failed to diff ${dataType} for ${version}: ${err.message}`) + error.stack = err.stack + throw error + } + } + sizePerDataType[dataType] ??= 0 + sizePerDataType[dataType] += Buffer.byteLength(JSON.stringify(injectCode || rawData), 'utf8') + if (config.genChanges && previousData[dataType]) { + const changes = config.genChanges(previousData[dataType], dataRaw) + // Object.assign(data, changes) + } + previousData[dataType] = dataRaw + } +} +const sources = Object.fromEntries(Object.entries(diffSources).map(x => { + const data = x[1].export() + // const data = {} + sizePerDataType[x[0]] += Buffer.byteLength(JSON.stringify(data), 'utf8') + return [x[0], data] +})) +Object.assign(sources, rawDataVersions) +sources.versionKey = require('minecraft-data/package.json').version + +const totalSize = Object.values(sizePerDataType).reduce((acc, val) => acc + val, 0) +console.log('total size (mb)', totalSize / 1024 / 1024) +console.log( + 'size per data type (mb, %)', + Object.fromEntries(Object.entries(sizePerDataType).map(([dataType, size]) => { + return [dataType, [size / 1024 / 1024, Math.round(size / totalSize * 100)]]; + }).sort((a, b) => { + //@ts-ignore + return b[1][1] - a[1][1]; + })) +) + +function compressToBase64(input) { + const buffer = gzipSync(input); + return buffer.toString('base64'); +} + +const filePath = './generated/minecraft-data-optimized.json' +fs.writeFileSync(filePath, JSON.stringify(sources), 'utf8') +if (compressedOutput) { + const minizedCompressed = compressToBase64(fs.readFileSync(filePath)) + console.log('size of compressed', Buffer.byteLength(minizedCompressed, 'utf8') / 1000 / 1000) + const compressedFilePath = './experiments/compressed.js' + fs.writeFileSync(compressedFilePath, minizedCompressed, 'utf8') +} + +console.log('size', fs.lstatSync(filePath).size / 1000 / 1000, gzipSizeFromFileSync(filePath) / 1000 / 1000) + +// always bundled + +const { defaultVersion } = MCProtocol +const data = MinecraftData(defaultVersion) +const initialMcData = { + [defaultVersion]: { + version: data.version, + protocol: data.protocol, + } +} + +fs.writeFileSync('./generated/minecraft-initial-data.json', JSON.stringify(initialMcData), 'utf8') diff --git a/scripts/prepareData.mjs b/scripts/prepareData.mjs deleted file mode 100644 index ab92499e..00000000 --- a/scripts/prepareData.mjs +++ /dev/null @@ -1,72 +0,0 @@ -//@ts-check -import { build } from 'esbuild' -import { existsSync } from 'node:fs' -import Module from "node:module" -import { dirname } from 'node:path' -import supportedVersions from '../src/supportedVersions.mjs' - -if (existsSync('dist/mc-data') && !process.argv.includes('-f')) { - console.log('using cached prepared data') - process.exit(0) -} - -const require = Module.createRequire(import.meta.url) - -const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') - -function toMajor (version) { - const [a, b] = (version + '').split('.') - return `${a}.${b}` -} - -const grouped = {} - -for (const [version, data] of Object.entries(dataPaths.pc)) { - if (!supportedVersions.includes(version)) continue - const major = toMajor(version) - grouped[major] ??= {} - grouped[major][version] = data -} - -const versionToNumber = (ver) => { - const [x, y = '0', z = '0'] = ver.split('.') - return +`${x.padStart(2, '0')}${y.padStart(2, '0')}${z.padStart(2, '0')}` -} - -console.log('preparing data') -console.time('data prepared') -let builds = [] -for (const [major, versions] of Object.entries(grouped)) { - // if (major !== '1.19') continue - let contents = 'Object.assign(window.mcData, {\n' - for (const [version, dataSet] of Object.entries(versions)) { - contents += ` '${version}': {\n` - for (const [dataType, dataPath] of Object.entries(dataSet)) { - if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { - contents += ` get ${dataType} () { return window.globalGetCollisionShapes?.("${version}") },\n` - continue - } - const loc = `minecraft-data/data/${dataPath}/` - contents += ` get ${dataType} () { return require("./${loc}${dataType}.json") },\n` - } - contents += ' },\n' - } - contents += '})' - - const promise = build({ - bundle: true, - outfile: `dist/mc-data/${major}.js`, - stdin: { - contents, - - resolveDir: dirname(require.resolve('minecraft-data')), - sourcefile: `mcData${major}.js`, - loader: 'js', - }, - metafile: true, - }) - // require('fs').writeFileSync('dist/mc-data/metafile.json', JSON.stringify(promise.metafile), 'utf8') - builds.push(promise) -} -await Promise.all(builds) -console.timeEnd('data prepared') diff --git a/scripts/testOptimizedMcdata.ts b/scripts/testOptimizedMcdata.ts new file mode 100644 index 00000000..17b5f7ed --- /dev/null +++ b/scripts/testOptimizedMcdata.ts @@ -0,0 +1,99 @@ +import assert from 'assert' +import JsonOptimizer from '../src/optimizeJson'; +import fs from 'fs' +import minecraftData from 'minecraft-data' + +const json = JSON.parse(fs.readFileSync('./generated/minecraft-data-optimized.json', 'utf8')) + +const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') + +const validateData = (ver, type) => { + const target = JsonOptimizer.restoreData(json[type], ver) + const arrKey = json[type].arrKey + const originalPath = dataPaths.pc[ver][type] + const original = require(`minecraft-data/minecraft-data/data/${originalPath}/${type}.json`) + if (arrKey) { + const originalKeys = original.map(a => JsonOptimizer.getByArrKey(a, arrKey)) as string[] + for (const [i, item] of originalKeys.entries()) { + if (originalKeys.indexOf(item) !== i) { + console.warn(`${type} ${ver} Incorrect source, duplicated arrKey (${arrKey}) ${item}. Ignoring!`) // todo should span instead + const index = originalKeys.indexOf(item); + original.splice(index, 1) + originalKeys.splice(index, 1) + } + } + // if (target.length !== originalKeys.length) { + // throw new Error(`wrong arr length: ${target.length} !== ${original.length}`) + // } + checkKeys(originalKeys, target.map(a => JsonOptimizer.getByArrKey(a, arrKey))) + for (const item of target as any[]) { + const keys = Object.entries(item).map(a => a[0]) + const origItem = original.find(a => JsonOptimizer.getByArrKey(a, arrKey) === JsonOptimizer.getByArrKey(item, arrKey)); + const keysSource = Object.entries(origItem).map(a => a[0]) + checkKeys(keysSource, keys, true, 'prop keys', true) + checkObj(origItem, item) + } + } else { + const keysOriginal = Object.keys(original) + const keysTarget = Object.keys(target) + checkKeys(keysOriginal, keysTarget) + for (const key of keysTarget) { + checkObj(original[key], target[key]) + } + } +} + +const checkObj = (source, diffing) => { + checkKeys(Object.keys(source), Object.keys(diffing)) + for (const [key, val] of Object.entries(source)) { + if (JSON.stringify(val) !== JSON.stringify(diffing[key])) { + throw new Error(`different value of ${key}: ${val} ${diffing[key]}`) + } + } +} + +const checkKeys = (source, diffing, isUniq = true, msg = '', redunantOk = false) => { + if (isUniq) { + for (const [i, item] of diffing.entries()) { + if (diffing.indexOf(item) !== i) { + throw new Error(`Duplicate: ${item}: ${i} ${diffing.indexOf(item)} ${msg}`) + } + } + } + for (const key of source) { + if (!diffing.includes(key)) { + throw new Error(`Diffing does not include "${key}" (${msg})`) + } + } + if (!redunantOk) { + for (const key of diffing) { + if (!source.includes(key)) { + throw new Error(`Source does not include "${key}" (${msg})`) + } + } + } +} + +// const data = minecraftData('1.20.4') +const oldId = JsonOptimizer.restoreData(json['blocks'], '1.20').find(x => x.name === 'brown_stained_glass').id; +const newId = JsonOptimizer.restoreData(json['blocks'], '1.20.4').find(x => x.name === 'brown_stained_glass').id; +assert(oldId !== newId) +// test all types + all versions + +for (const type of Object.keys(json)) { + if (!json[type].__IS_OPTIMIZED__) continue + if (type === 'language') continue // we have loose data for language for size reasons + console.log('validating', type) + const source = json[type] + let checkedVer = 0 + for (const ver of Object.keys(source.diffs)) { + try { + validateData(ver, type) + } catch (err) { + err.message = `Failed to validate ${type} for ${ver}: ${err.message}` + throw err; + } + checkedVer++ + } + console.log('Checked versions:', checkedVer) +} diff --git a/src/getCollisionInteractionShapes.ts b/src/getCollisionInteractionShapes.ts new file mode 100644 index 00000000..9dead22b --- /dev/null +++ b/src/getCollisionInteractionShapes.ts @@ -0,0 +1,17 @@ +import { getRenamedData } from 'flying-squid/dist/blockRenames' +import outputInteractionShapesJson from './interactionShapesGenerated.json' +import './getCollisionShapes' + +export default () => { + customEvents.on('gameLoaded', () => { + // todo also remap block states (e.g. redstone)! + const renamedBlocksInteraction = getRenamedData('blocks', Object.keys(outputInteractionShapesJson), '1.20.2', bot.version) + const interactionShapes = { + ...outputInteractionShapesJson, + ...Object.fromEntries(Object.entries(outputInteractionShapesJson).map(([block, shape], i) => [renamedBlocksInteraction[i], shape])) + } + interactionShapes[''] = interactionShapes['air'] + // todo make earlier + window.interactionShapes = interactionShapes + }) +} diff --git a/src/getCollisionShapes.ts b/src/getCollisionShapes.ts index 4ee0e802..383adc0e 100644 --- a/src/getCollisionShapes.ts +++ b/src/getCollisionShapes.ts @@ -1,6 +1,5 @@ import { getRenamedData } from 'flying-squid/dist/blockRenames' import collisionShapesInit from '../generated/latestBlockCollisionsShapes.json' -import outputInteractionShapesJson from './interactionShapesGenerated.json' // defining globally to be used in loaded data, not sure of better workaround window.globalGetCollisionShapes = (version) => { @@ -13,17 +12,3 @@ window.globalGetCollisionShapes = (version) => { } return collisionShapes } - -export default () => { - customEvents.on('gameLoaded', () => { - // todo also remap block states (e.g. redstone)! - const renamedBlocksInteraction = getRenamedData('blocks', Object.keys(outputInteractionShapesJson), '1.20.2', bot.version) - const interactionShapes = { - ...outputInteractionShapesJson, - ...Object.fromEntries(Object.entries(outputInteractionShapesJson).map(([block, shape], i) => [renamedBlocksInteraction[i], shape])) - } - interactionShapes[''] = interactionShapes['air'] - // todo make earlier - window.interactionShapes = interactionShapes - }) -} diff --git a/src/index.ts b/src/index.ts index c9040e35..39a110a3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import './globals' import './devtools' import './entities' import './globalDomListeners' -import initCollisionShapes from './getCollisionShapes' +import initCollisionShapes from './getCollisionInteractionShapes' import { onGameLoad } from './inventoryWindows' import { supportedVersions } from 'minecraft-protocol' import protocolMicrosoftAuth from 'minecraft-protocol/src/client/microsoftAuth' @@ -380,6 +380,7 @@ async function connect (connectOptions: ConnectOptions) { try { const serverOptions = defaultsDeep({}, connectOptions.serverOverrides ?? {}, options.localServerOptions, defaultServerOptions) Object.assign(serverOptions, connectOptions.serverOverridesFlat ?? {}) + window._LOAD_MC_DATA() // start loading data (if not loaded yet) const downloadMcData = async (version: string) => { if (connectOptions.authenticatedAccount && versionToNumber(version) < versionToNumber('1.19.4')) { // todo support it (just need to fix .export crash) @@ -392,13 +393,13 @@ async function connect (connectOptions: ConnectOptions) { // ignore cache hit versionsByMinecraftVersion.pc[lastVersion]!['dataVersion']!++ } + setLoadingScreenStatus(`Loading data for ${version}`) if (!document.fonts.check('1em mojangles')) { // todo instead re-render signs on load await document.fonts.load('1em mojangles').catch(() => { }) } - setLoadingScreenStatus(`Downloading data for ${version}`) + await window._MC_DATA_RESOLVER.promise // ensure data is loaded await downloadSoundsIfNeeded() - await loadScript(`./mc-data/${toMajorVersion(version)}.js`) miscUiState.loadedDataVersion = version try { await resourcepackReload(version) diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts new file mode 100644 index 00000000..442a72a1 --- /dev/null +++ b/src/optimizeJson.ts @@ -0,0 +1,264 @@ +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' + +type IdMap = Record + +type DiffData = { + removed: number[], + changed: any[], + removedProps: Array<[number, number[]]>, + added +} + +type SourceData = { + keys: IdMap, + properties: IdMap + source: Record + diffs: Record + arrKey? + __IS_OPTIMIZED__: true +} + +export default class JsonOptimizer { + keys = {} as IdMap + idToKey = {} as Record + properties = {} as IdMap + source = {} + previousKeys = [] as number[] + previousValues = {} as Record + diffs = {} as Record + + constructor (public arrKey?: string, public ignoreChanges = false, public ignoreRemoved = false) { } + + export () { + const { keys, properties, source, arrKey, diffs } = this + return { + keys, + properties, + source, + arrKey, + diffs, + '__IS_OPTIMIZED__': true + } satisfies SourceData + } + + diffObj (diffing): DiffData { + const removed = [] as number[] + const changed = [] as any[] + const removedProps = [] as any[] + const { arrKey, ignoreChanges, ignoreRemoved } = this + const added = [] as number[] + + if (!diffing || typeof diffing !== 'object') throw new Error('diffing data is not object') + if (Array.isArray(diffing) && !arrKey) throw new Error('arrKey is required for arrays') + const diffingObj = Array.isArray(diffing) ? Object.fromEntries(diffing.map(x => { + const key = JsonOptimizer.getByArrKey(x, arrKey!) + return [key, x] + })) : diffing + + const possiblyNewKeys = Object.keys(diffingObj) + this.keys ??= {} + this.properties ??= {} + let lastRootKeyId = Object.values(this.keys).length + let lastItemKeyId = Object.values(this.properties).length + for (const key of possiblyNewKeys) { + this.keys[key] ??= lastRootKeyId++ + this.idToKey[this.keys[key]] = key + } + const DEBUG = false + + const addDiff = (key, newVal, prevVal) => { + const valueMapped = [] as any[] + const isItemObj = typeof newVal === 'object' && newVal + const keyId = this.keys[key] + if (isItemObj) { + const removedPropsLocal = [] as any[] + for (const [prop, val] of Object.entries(newVal)) { + // mc-data: why push only changed props? eg for blocks only stateId are different between all versions so we skip a lot of duplicated data like block props + if (!isEqualStructured(newVal[prop], prevVal[prop])) { + let keyMapped = this.properties[prop] + if (keyMapped === undefined) { + this.properties[prop] = lastItemKeyId++ + keyMapped = this.properties[prop] + } + valueMapped.push(DEBUG ? prop : keyMapped, newVal[prop]) + } + } + // also add undefined for removed props + for (const prop of Object.keys(prevVal)) { + if (prop in newVal) continue + let keyMapped = this.properties[prop] + if (keyMapped === undefined) { + this.properties[prop] = lastItemKeyId++ + keyMapped = this.properties[prop] + } + removedPropsLocal.push(DEBUG ? prop : keyMapped) + } + removedProps.push([keyId, removedPropsLocal]) + } + changed.push(DEBUG ? key : keyId, isItemObj ? valueMapped : newVal) + } + for (const [id, sourceVal] of Object.entries(this.source)) { + const key = this.idToKey[id] + const diffVal = diffingObj[key] + if (!ignoreChanges && diffVal !== undefined) { + this.previousValues[id] ??= this.source[id] + const prevVal = this.previousValues[id] + if (!isEqualStructured(prevVal, diffVal)) { + addDiff(key, diffVal, prevVal) + } + this.previousValues[id] = diffVal + } + } + for (const [key, val] of Object.entries(diffingObj)) { + const id = this.keys[key] + if (!this.source[id]) { + this.source[id] = val + } + added.push(id) + } + + for (const previousKey of this.previousKeys) { + const key = this.idToKey[previousKey] + if (diffingObj[key] === undefined && !ignoreRemoved) { + removed.push(previousKey) + } + } + + for (const toRemove of removed) { + this.previousKeys.splice(this.previousKeys.indexOf(toRemove), 1) + } + + for (const previousKey of this.previousKeys) { + const index = added.indexOf(previousKey) + if (index === -1) continue + added.splice(index, 1) + } + + this.previousKeys = [...this.previousKeys, ...added] + + return { + removed, + changed, + added, + removedProps + } + } + + recordDiff (key: string, diffObj: string) { + const diff = this.diffObj(diffObj) + this.diffs[key] = diff + } + + static isOptimizedChangeDiff (changePossiblyArrDiff) { + if (!Array.isArray(changePossiblyArrDiff)) return false + if (changePossiblyArrDiff.length % 2 !== 0) return false + for (let i = 0; i < changePossiblyArrDiff.length; i += 2) { + if (typeof changePossiblyArrDiff[i] !== 'number') return false + } + return true + } + + static restoreData ({ keys, properties, source, arrKey, diffs }: SourceData, targetKey: string) { + // if (!diffs[targetKey]) throw new Error(`The requested data to restore with key ${targetKey} does not exist`) + source = structuredClone(source) + const keysById = Object.fromEntries(Object.entries(keys).map(x => [x[1], x[0]])) + const propertiesById = Object.fromEntries(Object.entries(properties).map(x => [x[1], x[0]])) + const dataByKeys = {} as Record + for (const [versionKey, { added, changed, removed, removedProps }] of Object.entries(diffs)) { + for (const toAdd of added) { + dataByKeys[toAdd] = source[toAdd] + } + for (const toRemove of removed) { + delete dataByKeys[toRemove] + } + for (let i = 0; i < changed.length; i += 2) { + const key = changed[i] + const change = changed[i + 1] + const isOptimizedChange = JsonOptimizer.isOptimizedChangeDiff(change) + if (isOptimizedChange) { + // apply optimized diff + for (let k = 0; k < change.length; k += 2) { + const propId = change[k] + const newVal = change[k + 1] + const prop = propertiesById[propId] + // const prop = propId + if (prop === undefined) throw new Error(`Property id change is undefined: ${propId}`) + dataByKeys[key][prop] = newVal + } + } else { + dataByKeys[key] = change + } + } + for (const [key, removePropsId] of removedProps) { + for (const removePropId of removePropsId) { + const removeProp = propertiesById[removePropId] + delete dataByKeys[key][removeProp] + } + } + if (versionToNumber(versionKey) <= versionToNumber(targetKey)) { + break + } + } + if (arrKey) { + return Object.values(dataByKeys) + } else { + return Object.fromEntries(Object.entries(dataByKeys).map(([key, val]) => [keysById[key], val])) + } + } + + static getByArrKey (item: any, arrKey: string) { + return arrKey.split('+').map(x => item[x]).join('+') + } + + static resolveDefaults (arr) { + if (!Array.isArray(arr)) throw new Error('not an array') + const propsValueCount = {} as { + [key: string]: { + [val: string]: number + } + } + for (const obj of arr) { + if (typeof obj !== 'object' || !obj) continue + for (const [key, val] of Object.entries(obj)) { + const valJson = JSON.stringify(val) + propsValueCount[key] ??= {} + propsValueCount[key][valJson] ??= 0 + propsValueCount[key][valJson] += 1 + } + } + const defaults = Object.fromEntries(Object.entries(propsValueCount).map(([prop, values]) => { + const defaultValue = Object.entries(values).sort(([, count1], [, count2]) => count2 - count1)[0][0] + return [prop, defaultValue] + })) + + const newData = [] as any[] + const noData = {} + for (const [i, obj] of arr.entries()) { + if (typeof obj !== 'object' || !obj) { + newData.push(obj) + continue + } + for (const key of Object.keys(defaults)) { + const val = obj[key] + if (!val) { + noData[key] ??= [] + noData[key].push(key) + continue + } + if (defaults[key] === JSON.stringify(val)) { + delete obj[key] + } + } + newData.push(obj) + } + + return { + data: newData, + defaults + } + } +} + +const isEqualStructured = (val1, val2) => { + return JSON.stringify(val1) === JSON.stringify(val2) +} diff --git a/src/react/CreateWorld.tsx b/src/react/CreateWorld.tsx index 0ff34a39..da710be1 100644 --- a/src/react/CreateWorld.tsx +++ b/src/react/CreateWorld.tsx @@ -45,8 +45,8 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer placeholder='World name' /> { return { value: obj.version, label: obj.version === defaultVersion ? obj.version + ' (available offline)' : obj.version } })} - selected={{ value: defaultVersion, label: defaultVersion + ' (available offline)' }} + versions={versions.map((obj) => { return { value: obj.version, label: obj.version } })} + selected={{ value: defaultVersion, label: defaultVersion }} onChange={(value) => { creatingWorldState.version = value ?? defaultVersion }} diff --git a/src/shims/minecraftData.ts b/src/shims/minecraftData.ts new file mode 100644 index 00000000..6edb5f48 --- /dev/null +++ b/src/shims/minecraftData.ts @@ -0,0 +1,92 @@ +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' +import JsonOptimizer from '../optimizeJson' +import minecraftInitialDataJson from '../../generated/minecraft-initial-data.json' +import { toMajorVersion } from '../utils' + +const customResolver = () => { + const resolver = Promise.withResolvers() + let resolvedData + return { + ...resolver, + get resolvedData () { + return resolvedData + }, + resolve (data) { + resolver.resolve(data) + resolvedData = data + } + } +} + +const optimizedDataResolver = customResolver() +window._MC_DATA_RESOLVER = optimizedDataResolver +window._LOAD_MC_DATA = async () => { + if (optimizedDataResolver.resolvedData) return + optimizedDataResolver.resolve(await import('../../generated/minecraft-data-optimized.json')) +} + +// 30 seconds +const cacheTtl = 30 * 1000 +const cache = new Map() +const cacheTime = new Map() +const possiblyGetFromCache = (version: string) => { + if (minecraftInitialDataJson[version] && !optimizedDataResolver.resolvedData) { + return minecraftInitialDataJson[version] + } + if (cache.has(version)) { + return cache.get(version) + } + const inner = () => { + if (!optimizedDataResolver.resolvedData) { + throw new Error(`Data for ${version} is not ready yet`) + } + const dataTypes = Object.keys(optimizedDataResolver.resolvedData) + const allRestored = {} + for (const dataType of dataTypes) { + if (dataType === 'blockCollisionShapes' && versionToNumber(version) >= versionToNumber('1.13')) { + const shapes = window.globalGetCollisionShapes?.(version) + if (shapes) { + allRestored[dataType] = shapes + continue + } + } + + const data = optimizedDataResolver.resolvedData[dataType] + if (data.__IS_OPTIMIZED__) { + allRestored[dataType] = JsonOptimizer.restoreData(data, version) + } else { + allRestored[dataType] = data[version] ?? data[toMajorVersion(version)] + } + } + return allRestored + } + const data = inner() + cache.set(version, data) + cacheTime.set(version, Date.now()) + return data +} +window.allLoadedMcData = new Proxy({}, { + get (t, version: string) { + // special properties like $typeof + if (version.includes('$')) return + // todo enumerate all props + return new Proxy({}, { + get (target, prop) { + return possiblyGetFromCache(version)[prop] + }, + }) + } +}) + +setInterval(() => { + const now = Date.now() + for (const [version, time] of cacheTime) { + if (now - time > cacheTtl) { + cache.delete(version) + cacheTime.delete(version) + } + } +}, 1000) + +export const pc = window.allLoadedMcData +export default { pc } From bbd01d9682b8b39bfb54a75cb739832b6e14ec5e Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 1 Sep 2024 02:37:22 +0300 Subject: [PATCH 026/851] fix: when left click was pressed down the swing arm packet sending was not limited --- src/worldInteractions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts index 7a9aa508..bc54efcd 100644 --- a/src/worldInteractions.ts +++ b/src/worldInteractions.ts @@ -38,6 +38,7 @@ class WorldInteraction { currentDigTime prevOnGround lastBlockPlaced: number + lastSwing = 0 buttons = [false, false, false] lastButtons = [false, false, false] breakStartTime: number | undefined = 0 @@ -350,8 +351,9 @@ class WorldInteraction { }) customEvents.emit('digStart') this.lastDigged = Date.now() - } else { + } else if (performance.now() - this.lastSwing > 200) { bot.swingArm('right') + this.lastSwing = performance.now() } } this.prevOnGround = onGround From ee966395c6ff78cdb5fe957a3c6ae0751432fb2f Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sun, 1 Sep 2024 03:32:53 +0300 Subject: [PATCH 027/851] feat: Display holding block (experimental setting) (#190) --- experiments/three.html | 1 + experiments/three.ts | 101 +++++++++ prismarine-viewer/viewer/lib/entities.js | 85 +++---- prismarine-viewer/viewer/lib/holdingBlock.ts | 207 ++++++++++++++++++ .../viewer/lib/mesher/standaloneRenderer.ts | 16 ++ prismarine-viewer/viewer/lib/viewer.ts | 30 ++- .../viewer/lib/worldDataEmitter.ts | 35 ++- .../viewer/lib/worldrendererCommon.ts | 6 + .../viewer/lib/worldrendererThree.ts | 35 ++- src/inventoryWindows.ts | 9 +- src/optionsGuiScheme.tsx | 1 + src/optionsStorage.ts | 1 + src/topRightStats.ts | 11 + src/watchOptions.ts | 2 +- src/worldInteractions.ts | 6 + 15 files changed, 489 insertions(+), 57 deletions(-) create mode 100644 experiments/three.html create mode 100644 experiments/three.ts create mode 100644 prismarine-viewer/viewer/lib/holdingBlock.ts diff --git a/experiments/three.html b/experiments/three.html new file mode 100644 index 00000000..8765081b --- /dev/null +++ b/experiments/three.html @@ -0,0 +1 @@ + diff --git a/experiments/three.ts b/experiments/three.ts new file mode 100644 index 00000000..7a629a13 --- /dev/null +++ b/experiments/three.ts @@ -0,0 +1,101 @@ +import * as THREE from 'three' +import * as tweenJs from '@tweenjs/tween.js' +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' +import * as THREE from 'three'; +import Jimp from 'jimp'; + +const scene = new THREE.Scene() +const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000) +camera.position.set(0, 0, 5) +const renderer = new THREE.WebGLRenderer() +renderer.setSize(window.innerWidth, window.innerHeight) +document.body.appendChild(renderer.domElement) + +const controls = new OrbitControls(camera, renderer.domElement) + +const geometry = new THREE.BoxGeometry(1, 1, 1) +const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }) +const cube = new THREE.Mesh(geometry, material) +cube.position.set(0.5, 0.5, 0.5); +const group = new THREE.Group() +group.add(cube) +group.position.set(-0.5, -0.5, -0.5); +const outerGroup = new THREE.Group() +outerGroup.add(group) +outerGroup.scale.set(0.2, 0.2, 0.2) +outerGroup.position.set(1, 1, 0) +scene.add(outerGroup) + +// const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0x00_00_ff, transparent: true, opacity: 0.5 })) +// mesh.position.set(0.5, 1, 0.5) +// const group = new THREE.Group() +// group.add(mesh) +// group.position.set(-0.5, -1, -0.5) +// const outerGroup = new THREE.Group() +// outerGroup.add(group) +// // outerGroup.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z) +// scene.add(outerGroup) + + new tweenJs.Tween(group.rotation).to({ z: THREE.MathUtils.degToRad(90) }, 1000).yoyo(true).repeat(Infinity).start() + +const tweenGroup = new tweenJs.Group() +function animate () { + tweenGroup.update() + requestAnimationFrame(animate) +// cube.rotation.x += 0.01 +// cube.rotation.y += 0.01 + renderer.render(scene, camera) +} +animate() + +// let animation + +window.animate = () => { + // new Tween.Tween(group.position).to({ y: group.position.y - 1}, 1000 * 0.35/2).yoyo(true).repeat(1).start() + new tweenJs.Tween(group.rotation, tweenGroup).to({ z: THREE.MathUtils.degToRad(90) }, 1000 * 0.35 / 2).yoyo(true).repeat(Infinity).start().onRepeat(() => { + console.log('done') + }) +} + +window.stop = () => { + tweenGroup.removeAll() +} + + +function createGeometryFromImage() { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABEElEQVQ4jWNkIAPw2Zv9J0cfXPOSvx/+L/n74T+HqsJ/JlI1T9u3i6H91B7ybdY+vgZuO1majV+fppFmPnuz/+ihy2dv9t/49Wm8mlECkV1FHh5FfPZm/1XXTGX4cechA4eKPMNVq1CGH7cfMBJ0rlxX+X8OVYX/xq9P/5frKifoZ0Z0AwS8HRkYGBgYvt+8xyDXUUbQZgwJPnuz/+wq8gw/7zxk+PXsFUFno0h6mon+l5fgZFhwnYmBTUqMgYGBgaAhLMiaHQyFGOZvf8Lw49FXRgYGhv8MDAwwg/7jMoQFFury/C8Y5m9/wnADohnZVryJhoWBARJ9Cw69gtmMAgiFAcuvZ68Yfj17hU8NXgAATdKfkzbQhBEAAAAASUVORK5CYII=' + console.log('img.complete', img.complete) + img.onload = () => { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const context = canvas.getContext('2d'); + context.drawImage(img, 0, 0, img.width, img.height); + const imgData = context.getImageData(0, 0, img.width, img.height); + + const shape = new THREE.Shape(); + for (let y = 0; y < img.height; y++) { + for (let x = 0; x < img.width; x++) { + const index = (y * img.width + x) * 4; + const alpha = imgData.data[index + 3]; + if (alpha !== 0) { + shape.lineTo(x, y); + } + } + } + + const geometry = new THREE.ShapeGeometry(shape); + resolve(geometry); + }; + img.onerror = reject; + }); +} + +// Usage: +const shapeGeomtry = createGeometryFromImage().then(geometry => { + const material = new THREE.MeshBasicMaterial({ color: 0xffffff }); + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); +}) diff --git a/prismarine-viewer/viewer/lib/entities.js b/prismarine-viewer/viewer/lib/entities.js index 57a45a44..01ce7d6d 100644 --- a/prismarine-viewer/viewer/lib/entities.js +++ b/prismarine-viewer/viewer/lib/entities.js @@ -282,6 +282,46 @@ export class Entities extends EventEmitter { } } + getItemMesh(item) { + const textureUv = this.getItemUv?.(item.itemId ?? item.blockId) + if (textureUv) { + // todo use geometry buffer uv instead! + const { u, v, size, su, sv, texture } = textureUv + const itemsTexture = texture.clone() + itemsTexture.flipY = true + itemsTexture.offset.set(u, 1 - v - (sv ?? size)) + itemsTexture.repeat.set(su ?? size, sv ?? size) + itemsTexture.needsUpdate = true + itemsTexture.magFilter = THREE.NearestFilter + itemsTexture.minFilter = THREE.NearestFilter + const itemsTextureFlipped = itemsTexture.clone() + itemsTextureFlipped.repeat.x *= -1 + itemsTextureFlipped.needsUpdate = true + itemsTextureFlipped.offset.set(u + (su ?? size), 1 - v - (sv ?? size)) + const material = new THREE.MeshStandardMaterial({ + map: itemsTexture, + transparent: true, + alphaTest: 0.1, + }) + const materialFlipped = new THREE.MeshStandardMaterial({ + map: itemsTextureFlipped, + transparent: true, + alphaTest: 0.1, + }) + const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 0), [ + // top left and right bottom are black box materials others are transparent + new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), + new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), + material, materialFlipped, + ]) + return { + mesh, + itemsTexture, + itemsTextureFlipped, + } + } + } + update(/** @type {import('prismarine-entity').Entity & {delete?, pos}} */entity, overrides) { let isPlayerModel = entity.name === 'player' if (entity.name === 'zombie' || entity.name === 'zombie_villager' || entity.name === 'husk') { @@ -296,52 +336,23 @@ export class Entities extends EventEmitter { //@ts-expect-error const item = entity.metadata?.find(m => typeof m === 'object' && m?.itemCount) if (item) { - const textureUv = this.getItemUv?.(item.itemId ?? item.blockId) - if (textureUv) { - // todo use geometry buffer uv instead! - const { u, v, size, su, sv, texture } = textureUv - const itemsTexture = texture.clone() - itemsTexture.flipY = true - itemsTexture.offset.set(u, 1 - v - (sv ?? size)) - itemsTexture.repeat.set(su ?? size, sv ?? size) - itemsTexture.needsUpdate = true - itemsTexture.magFilter = THREE.NearestFilter - itemsTexture.minFilter = THREE.NearestFilter - const itemsTextureFlipped = itemsTexture.clone() - itemsTextureFlipped.repeat.x *= -1 - itemsTextureFlipped.needsUpdate = true - itemsTextureFlipped.offset.set(u + (su ?? size), 1 - v - (sv ?? size)) - const material = new THREE.MeshStandardMaterial({ - map: itemsTexture, - transparent: true, - alphaTest: 0.1, - }) - const materialFlipped = new THREE.MeshStandardMaterial({ - map: itemsTextureFlipped, - transparent: true, - alphaTest: 0.1, - }) - mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 0), [ - // top left and right bottom are black box materials others are transparent - new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), - new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), new THREE.MeshBasicMaterial({ color: 0x00_00_00 }), - material, materialFlipped, - ]) - mesh.scale.set(0.5, 0.5, 0.5) - mesh.position.set(0, 0.2, 0) + const object = this.getItemMesh(item) + if (object) { + object.scale.set(0.5, 0.5, 0.5) + object.position.set(0, 0.2, 0) // set faces // mesh.position.set(targetPos.x + 0.5 + 2, targetPos.y + 0.5, targetPos.z + 0.5) // viewer.scene.add(mesh) const clock = new THREE.Clock() - mesh.onBeforeRender = () => { + object.onBeforeRender = () => { const delta = clock.getDelta() - mesh.rotation.y += delta + object.rotation.y += delta } //@ts-expect-error group.additionalCleanup = () => { // important: avoid texture memory leak and gpu slowdown - itemsTexture.dispose() - itemsTextureFlipped.dispose() + object.itemsTexture.dispose() + object.itemsTextureFlipped.dispose() } } } diff --git a/prismarine-viewer/viewer/lib/holdingBlock.ts b/prismarine-viewer/viewer/lib/holdingBlock.ts new file mode 100644 index 00000000..f5a0ca79 --- /dev/null +++ b/prismarine-viewer/viewer/lib/holdingBlock.ts @@ -0,0 +1,207 @@ +import * as THREE from 'three' +import * as tweenJs from '@tweenjs/tween.js' +import worldBlockProvider from 'mc-assets/dist/worldBlockProvider' +import { getThreeBlockModelGroup, renderBlockThree, setBlockPosition } from './mesher/standaloneRenderer' + +export type HandItemBlock = { + name + properties +} + +export default class HoldingBlock { + holdingBlock: THREE.Object3D | undefined = undefined + swingAnimation: tweenJs.Group | undefined = undefined + blockSwapAnimation: { + tween: tweenJs.Group + hidden: boolean + } | undefined = undefined + cameraGroup = new THREE.Mesh() + objectOuterGroup = new THREE.Group() + objectInnerGroup = new THREE.Group() + camera: THREE.Group | THREE.PerspectiveCamera + stopUpdate = false + lastHeldItem: HandItemBlock | undefined + toBeRenderedItem: HandItemBlock | undefined + isSwinging = false + nextIterStopCallbacks: Array<() => void> | undefined + + constructor (public scene: THREE.Scene) { + this.initCameraGroup() + } + + initCameraGroup () { + this.cameraGroup = new THREE.Mesh() + this.scene.add(this.cameraGroup) + } + + startSwing () { + this.nextIterStopCallbacks = undefined // forget about cancelling + if (this.isSwinging) return + this.swingAnimation = new tweenJs.Group() + this.isSwinging = true + const cube = this.cameraGroup.children[0] + if (cube) { + // const DURATION = 1000 * 0.35 / 2 + const DURATION = 1000 * 0.35 / 3 + // const DURATION = 1000 + const initialPos = { + x: this.objectInnerGroup.position.x, + y: this.objectInnerGroup.position.y, + z: this.objectInnerGroup.position.z + } + const initialRot = { + x: this.objectInnerGroup.rotation.x, + y: this.objectInnerGroup.rotation.y, + z: this.objectInnerGroup.rotation.z + } + const mainAnim = new tweenJs.Tween(this.objectInnerGroup.position, this.swingAnimation).to({ y: this.objectInnerGroup.position.y - this.objectInnerGroup.scale.y / 2 }, DURATION).yoyo(true).repeat(Infinity).start() + let i = 0 + mainAnim.onRepeat(() => { + i++ + if (this.nextIterStopCallbacks && i % 2 === 0) { + for (const callback of this.nextIterStopCallbacks) { + callback() + } + this.nextIterStopCallbacks = undefined + this.isSwinging = false + this.swingAnimation!.removeAll() + this.swingAnimation = undefined + // todo refactor to be more generic for animations + this.objectInnerGroup.position.set(initialPos.x, initialPos.y, initialPos.z) + // this.objectInnerGroup.rotation.set(initialRot.x, initialRot.y, initialRot.z) + Object.assign(this.objectInnerGroup.rotation, initialRot) + } + }) + + new tweenJs.Tween(this.objectInnerGroup.rotation, this.swingAnimation).to({ z: THREE.MathUtils.degToRad(90) }, DURATION).yoyo(true).repeat(Infinity).start() + new tweenJs.Tween(this.objectInnerGroup.rotation, this.swingAnimation).to({ x: -THREE.MathUtils.degToRad(90) }, DURATION).yoyo(true).repeat(Infinity).start() + } + } + + async stopSwing () { + if (!this.isSwinging) return + // might never resolve! + /* return */void new Promise((resolve) => { + this.nextIterStopCallbacks ??= [] + this.nextIterStopCallbacks.push(() => { + resolve() + }) + }) + } + + update (camera: typeof this.camera) { + this.camera = camera + this.swingAnimation?.update() + this.blockSwapAnimation?.tween.update() + this.updateCameraGroup() + } + + // worldTest () { + // const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshPhongMaterial({ color: 0x00_00_ff, transparent: true, opacity: 0.5 })) + // mesh.position.set(0.5, 0.5, 0.5) + // const group = new THREE.Group() + // group.add(mesh) + // group.position.set(-0.5, -0.5, -0.5) + // const outerGroup = new THREE.Group() + // outerGroup.add(group) + // outerGroup.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z) + // this.scene.add(outerGroup) + + // new tweenJs.Tween(group.rotation).to({ z: THREE.MathUtils.degToRad(90) }, 1000).yoyo(true).repeat(Infinity).start() + // } + + async playBlockSwapAnimation () { + // if (this.blockSwapAnimation) return + this.blockSwapAnimation ??= { + tween: new tweenJs.Group(), + hidden: false + } + const DURATION = 1000 * 0.35 / 2 + const tween = new tweenJs.Tween(this.objectInnerGroup.position, this.blockSwapAnimation.tween).to({ + y: this.objectInnerGroup.position.y + (this.objectInnerGroup.scale.y * 1.5 * (this.blockSwapAnimation.hidden ? 1 : -1)) + }, DURATION).start() + return new Promise((resolve) => { + tween.onComplete(() => { + if (this.blockSwapAnimation!.hidden) { + this.blockSwapAnimation = undefined + } else { + this.blockSwapAnimation!.hidden = !this.blockSwapAnimation!.hidden + } + resolve() + }) + }) + } + + isDifferentItem (block: HandItemBlock | undefined) { + return this.lastHeldItem && (this.lastHeldItem.name !== block?.name || JSON.stringify(this.lastHeldItem.properties) !== JSON.stringify(block?.properties ?? '{}')) + } + + updateCameraGroup () { + if (this.stopUpdate) return + const { camera } = this + this.cameraGroup.position.copy(camera.position) + this.cameraGroup.rotation.copy(camera.rotation) + + const viewerSize = viewer.renderer.getSize(new THREE.Vector2()) + // const x = window.x ?? 0.25 * viewerSize.width / viewerSize.height + // const x = 0 * viewerSize.width / viewerSize.height + const x = 0.2 * viewerSize.width / viewerSize.height + this.objectOuterGroup.position.set(x, -0.3, -0.45) + } + + async initHandObject (material: THREE.Material, blockstatesModels: any, blocksAtlases: any, block?: HandItemBlock) { + let animatingCurrent = false + if (!this.swingAnimation && !this.blockSwapAnimation && this.isDifferentItem(block)) { + animatingCurrent = true + await this.playBlockSwapAnimation() + this.holdingBlock?.removeFromParent() + this.holdingBlock = undefined + } + this.lastHeldItem = block + if (!block) { + this.holdingBlock?.removeFromParent() + this.holdingBlock = undefined + this.swingAnimation = undefined + this.blockSwapAnimation = undefined + return + } + const blockProvider = worldBlockProvider(blockstatesModels, blocksAtlases, 'latest') + const models = blockProvider.getAllResolvedModels0_1(block, true) + const blockInner = getThreeBlockModelGroup(material, models, undefined, 'plains', loadedData) + // const { mesh: itemMesh } = viewer.entities.getItemMesh({ + // itemId: 541, + // })! + // itemMesh.position.set(0.5, 0.5, 0.5) + // const blockInner = itemMesh + blockInner.name = 'holdingBlock' + const blockOuterGroup = new THREE.Group() + blockOuterGroup.add(blockInner) + this.holdingBlock = blockInner + this.objectInnerGroup = new THREE.Group() + this.objectInnerGroup.add(blockOuterGroup) + this.objectInnerGroup.position.set(-0.5, -0.5, -0.5) + // todo cleanup + if (animatingCurrent) { + this.objectInnerGroup.position.y -= this.objectInnerGroup.scale.y * 1.5 + } + Object.assign(blockOuterGroup.position, { x: 0.5, y: 0.5, z: 0.5 }) + + this.objectOuterGroup = new THREE.Group() + this.objectOuterGroup.add(this.objectInnerGroup) + + this.cameraGroup.add(this.objectOuterGroup) + const rotation = -45 + -90 + // const rotation = -45 // should be for item + this.holdingBlock.rotation.set(0, THREE.MathUtils.degToRad(rotation), 0, 'ZYX') + + // const scale = window.scale ?? 0.2 + const scale = 0.2 + this.objectOuterGroup.scale.set(scale, scale, scale) + // this.objectOuterGroup.position.set(x, window.y ?? -0.41, window.z ?? -0.45) + // this.objectOuterGroup.position.set(x, 0, -0.45) + + if (animatingCurrent) { + await this.playBlockSwapAnimation() + } + } +} diff --git a/prismarine-viewer/viewer/lib/mesher/standaloneRenderer.ts b/prismarine-viewer/viewer/lib/mesher/standaloneRenderer.ts index 2dc2f599..43369cc2 100644 --- a/prismarine-viewer/viewer/lib/mesher/standaloneRenderer.ts +++ b/prismarine-viewer/viewer/lib/mesher/standaloneRenderer.ts @@ -272,3 +272,19 @@ export const renderBlockThree = (...args: Parameters) => { + const geometry = renderBlockThree(...args) + const mesh = new THREE.Mesh(geometry, material) + mesh.position.set(-0.5, -0.5, -0.5) + const group = new THREE.Group() + group.add(mesh) + group.rotation.set(0, -THREE.MathUtils.degToRad(90), 0, 'ZYX') + globalThis.mesh = group + return group + // return new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshPhongMaterial({ color: 0x00_00_ff, transparent: true, opacity: 0.5 })) +} + +export const setBlockPosition = (object: THREE.Object3D, position: { x: number, y: number, z: number }) => { + object.position.set(position.x + 0.5, position.y + 0.5, position.z + 0.5) +} diff --git a/prismarine-viewer/viewer/lib/viewer.ts b/prismarine-viewer/viewer/lib/viewer.ts index 7893bccc..7cd759e4 100644 --- a/prismarine-viewer/viewer/lib/viewer.ts +++ b/prismarine-viewer/viewer/lib/viewer.ts @@ -7,7 +7,7 @@ import { Entities } from './entities' import { Primitives } from './primitives' import { WorldRendererThree } from './worldrendererThree' import { WorldRendererCommon, WorldRendererConfig, defaultWorldRendererConfig } from './worldrendererCommon' -import { renderBlockThree } from './mesher/standaloneRenderer' +import { getThreeBlockModelGroup, renderBlockThree, setBlockPosition } from './mesher/standaloneRenderer' export class Viewer { scene: THREE.Scene @@ -101,18 +101,32 @@ export class Viewer { } demoModel () { + //@ts-expect-error + const pos = cursorBlockRel(0, 1, 0).position const blockProvider = worldBlockProvider(this.world.blockstatesModels, this.world.blocksAtlases, 'latest') const models = blockProvider.getAllResolvedModels0_1({ - name: 'item_frame', + name: 'furnace', properties: { - map: false + // map: false } - }) - const geometry = renderBlockThree(models, undefined, 'plains', loadedData) + }, true) const { material } = this.world - // block material - const mesh = new THREE.Mesh(geometry, material) - mesh.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z) + const mesh = getThreeBlockModelGroup(material, models, undefined, 'plains', loadedData) + // mesh.rotation.y = THREE.MathUtils.degToRad(90) + setBlockPosition(mesh, pos) + const helper = new THREE.BoxHelper(mesh, 0xff_ff_00) + mesh.add(helper) + this.scene.add(mesh) + } + + demoItem () { + //@ts-expect-error + const pos = cursorBlockRel(0, 1, 0).position + const { mesh } = this.entities.getItemMesh({ + itemId: 541, + })! + mesh.position.set(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5) + // mesh.scale.set(0.5, 0.5, 0.5) const helper = new THREE.BoxHelper(mesh, 0xff_ff_00) mesh.add(helper) this.scene.add(mesh) diff --git a/prismarine-viewer/viewer/lib/worldDataEmitter.ts b/prismarine-viewer/viewer/lib/worldDataEmitter.ts index 381526d9..d832d3db 100644 --- a/prismarine-viewer/viewer/lib/worldDataEmitter.ts +++ b/prismarine-viewer/viewer/lib/worldDataEmitter.ts @@ -5,6 +5,7 @@ import { EventEmitter } from 'events' import { generateSpiralMatrix, ViewRect } from 'flying-squid/dist/utils' import { Vec3 } from 'vec3' import { BotEvents } from 'mineflayer' +import { getItemFromBlock } from '../../../src/botUtils' import { chunkPos } from './simpleUtils' export type ChunkPosKey = string @@ -20,6 +21,14 @@ export class WorldDataEmitter extends EventEmitter { private eventListeners: Record = {} private readonly emitter: WorldDataEmitter keepChunksDistance = 0 + _handDisplay = false + get handDisplay () { + return this._handDisplay + } + set handDisplay (newVal) { + this._handDisplay = newVal + this.eventListeners.heldItemChanged?.() + } constructor (public world: typeof __type_bot['world'], public viewDistance: number, position: Vec3 = new Vec3(0, 0, 0)) { super() @@ -55,7 +64,7 @@ export class WorldDataEmitter extends EventEmitter { }) } - this.eventListeners[bot.username] = { + this.eventListeners = { // 'move': botPosition, entitySpawn (e: any) { emitEntity(e) @@ -70,7 +79,7 @@ export class WorldDataEmitter extends EventEmitter { this.emitter.emit('entity', { id: e.id, delete: true }) }, chunkColumnLoad: (pos: Vec3) => { - this.loadChunk(pos) + void this.loadChunk(pos) }, chunkColumnUnload: (pos: Vec3) => { this.unloadChunk(pos) @@ -82,7 +91,24 @@ export class WorldDataEmitter extends EventEmitter { time: () => { this.emitter.emit('time', bot.time.timeOfDay) }, + heldItemChanged: () => { + if (!this.handDisplay) { + viewer.world.onHandItemSwitch(undefined) + return + } + const newItem = bot.heldItem + if (!newItem) { + viewer.world.onHandItemSwitch(undefined) + return + } + const block = loadedData.blocksByName[newItem.name] + // todo clean types + const blockProperties = block ? new window.PrismarineBlock(block.id, 'void', newItem.metadata).getProperties() : {} + viewer.world.onHandItemSwitch({ name: newItem.name, properties: blockProperties }) + }, } satisfies Partial + this.eventListeners.heldItemChanged() + bot._client.on('update_light', ({ chunkX, chunkZ }) => { const chunkPos = new Vec3(chunkX * 16, 0, chunkZ * 16) @@ -105,7 +131,7 @@ export class WorldDataEmitter extends EventEmitter { this.emitter.emit('listening') } - for (const [evt, listener] of Object.entries(this.eventListeners[bot.username])) { + for (const [evt, listener] of Object.entries(this.eventListeners)) { bot.on(evt as any, listener) } @@ -116,10 +142,9 @@ export class WorldDataEmitter extends EventEmitter { } removeListenersFromBot (bot: import('mineflayer').Bot) { - for (const [evt, listener] of Object.entries(this.eventListeners[bot.username])) { + for (const [evt, listener] of Object.entries(this.eventListeners)) { bot.removeListener(evt as any, listener) } - delete this.eventListeners[bot.username] } async init (pos: Vec3) { diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index 58e5a6f5..482907d4 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -16,6 +16,7 @@ import { toMajorVersion } from '../../../src/utils' import { buildCleanupDecorator } from './cleanupDecorator' import { defaultMesherConfig } from './mesher/shared' import { chunkPos } from './simpleUtils' +import { HandItemBlock } from './holdingBlock' function mod (x, n) { return ((x % n) + n) % n @@ -37,6 +38,7 @@ type CustomTexturesData = { export abstract class WorldRendererCommon { worldConfig = { minY: 0, worldHeight: 256 } + // todo need to cleanup material = new THREE.MeshLambertMaterial({ vertexColors: true, transparent: true, alphaTest: 0.1 }) @worldCleanup() @@ -59,6 +61,7 @@ export abstract class WorldRendererCommon textureDownloaded (): void }> customTexturesDataUrl = undefined as string | undefined + @worldCleanup() currentTextureImage = undefined as any workers: any[] = [] viewerPosition?: Vec3 @@ -157,6 +160,9 @@ export abstract class WorldRendererCommon } } + onHandItemSwitch (item: HandItemBlock | undefined): void { } + changeHandSwingingState (isAnimationPlaying: boolean): void { } + abstract handleWorkerMessage (data: WorkerReceive): void abstract updateCamera (pos: Vec3 | null, yaw: number, pitch: number): void diff --git a/prismarine-viewer/viewer/lib/worldrendererThree.ts b/prismarine-viewer/viewer/lib/worldrendererThree.ts index 6c14e243..b1644ebd 100644 --- a/prismarine-viewer/viewer/lib/worldrendererThree.ts +++ b/prismarine-viewer/viewer/lib/worldrendererThree.ts @@ -9,7 +9,7 @@ import { renderSign } from '../sign-renderer' import { chunkPos, sectionPos } from './simpleUtils' import { WorldRendererCommon, WorldRendererConfig } from './worldrendererCommon' import { disposeObject } from './threeJsUtils' -import { renderBlockThree } from './mesher/standaloneRenderer' +import HoldingBlock, { HandItemBlock } from './holdingBlock' export class WorldRendererThree extends WorldRendererCommon { outputFormat = 'threeJs' as const @@ -19,7 +19,7 @@ export class WorldRendererThree extends WorldRendererCommon { signsCache = new Map() starField: StarField cameraSectionPos: Vec3 = new Vec3(0, 0, 0) - cameraGroup = new THREE.Group() + holdingBlock: HoldingBlock get tilesRendered () { return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).tilesCount, 0) @@ -28,8 +28,34 @@ export class WorldRendererThree extends WorldRendererCommon { constructor (public scene: THREE.Scene, public renderer: THREE.WebGLRenderer, public config: WorldRendererConfig) { super(config) this.starField = new StarField(scene) - // this.initCameraGroup() - // this.initHandObject() + this.holdingBlock = new HoldingBlock(this.scene) + this.onHandItemSwitch({ + name: 'furnace', + properties: {} + }) + + this.renderUpdateEmitter.on('textureDownloaded', () => { + if (this.holdingBlock.toBeRenderedItem) { + this.onHandItemSwitch(this.holdingBlock.toBeRenderedItem) + this.holdingBlock.toBeRenderedItem = undefined + } + }) + } + + onHandItemSwitch (item: HandItemBlock | undefined) { + if (!this.currentTextureImage) { + this.holdingBlock.toBeRenderedItem = item + return + } + void this.holdingBlock.initHandObject(this.material, this.blockstatesModels, this.blocksAtlases, item) + } + + changeHandSwingingState (isAnimationPlaying: boolean) { + if (isAnimationPlaying) { + this.holdingBlock.startSwing() + } else { + void this.holdingBlock.stopSwing() + } } timeUpdated (newTime: number): void { @@ -173,6 +199,7 @@ export class WorldRendererThree extends WorldRendererCommon { render () { tweenJs.update() + this.holdingBlock.update(this.camera) // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style const cam = this.camera instanceof THREE.Group ? this.camera.children.find(child => child instanceof THREE.PerspectiveCamera) as THREE.PerspectiveCamera : this.camera this.renderer.render(this.scene, cam) diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 64e589f9..e5964646 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -87,8 +87,13 @@ export const onGameLoad = (onLoad) => { return } const craftingSlots = bot.inventory.slots.slice(1, 5) - const resultingItem = getResultingRecipe(craftingSlots, 2) - void bot.creative.setInventorySlot(craftingResultSlot, resultingItem ?? null) + try { + const resultingItem = getResultingRecipe(craftingSlots, 2) + void bot.creative.setInventorySlot(craftingResultSlot, resultingItem ?? null) + } catch (err) { + console.error(err) + // todo resolve the error! and why would we ever get here on every update? + } }) as any) bot.on('windowClose', () => { diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index 644ec66c..2082acdb 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -88,6 +88,7 @@ export const guiOptionsScheme: { unit: '', tooltip: 'Additional distance to keep the chunks loading before unloading them by marking them as too far', }, + handDisplay: {}, }, ], main: [ diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index cac8c9f8..b7bf4abe 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -47,6 +47,7 @@ const defaultOptions = { enabledResourcepack: null as string | null, useVersionsTextures: 'latest', serverResourcePacks: 'prompt' as 'prompt' | 'always' | 'never', + handDisplay: false, // antiAliasing: false, diff --git a/src/topRightStats.ts b/src/topRightStats.ts index 71303e81..f0462ae6 100644 --- a/src/topRightStats.ts +++ b/src/topRightStats.ts @@ -75,3 +75,14 @@ export const statsEnd = () => { stats2.end() statsGl.end() } + +window.statsPerSec = {} +let statsPerSec = {} +window.addStatPerSec = (name) => { + statsPerSec[name] ??= 0 + statsPerSec[name]++ +} +setInterval(() => { + window.statsPerSec = statsPerSec + statsPerSec = {} +}, 1000) diff --git a/src/watchOptions.ts b/src/watchOptions.ts index 9deb46a5..d26d2b90 100644 --- a/src/watchOptions.ts +++ b/src/watchOptions.ts @@ -66,11 +66,11 @@ export const watchOptionsAfterViewerInit = () => { let viewWatched = false export const watchOptionsAfterWorldViewInit = () => { - worldView!.keepChunksDistance = options.keepChunksDistance if (viewWatched) return viewWatched = true watchValue(options, o => { if (!worldView) return worldView.keepChunksDistance = o.keepChunksDistance + worldView.handDisplay = o.handDisplay }) } diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts index bc54efcd..cef65b96 100644 --- a/src/worldInteractions.ts +++ b/src/worldInteractions.ts @@ -294,6 +294,8 @@ class WorldInteraction { bot.lookAt = oldLookAt }).catch(console.warn) } + viewer.world.changeHandSwingingState(true) + viewer.world.changeHandSwingingState(false) } else if (!stop) { const offhand = activate ? false : activatableItems(bot.inventory.slots[45]?.name ?? '') bot.activateItem(offhand) // todo offhand @@ -351,11 +353,15 @@ class WorldInteraction { }) customEvents.emit('digStart') this.lastDigged = Date.now() + viewer.world.changeHandSwingingState(true) } else if (performance.now() - this.lastSwing > 200) { bot.swingArm('right') this.lastSwing = performance.now() } } + if (!this.buttons[0] && this.lastButtons[0]) { + viewer.world.changeHandSwingingState(false) + } this.prevOnGround = onGround // Show cursor From 66d26ad2e6a6673e97e6bd08b67d97de052984ba Mon Sep 17 00:00:00 2001 From: Valery-a <83373303+Valery-a@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:53:48 +0300 Subject: [PATCH 028/851] feat: add visuals for entities damaging (#186) --- docs-assets/handled-packets.md | 2 +- prismarine-viewer/viewer/lib/entities.js | 18 ++++++++++++++++++ src/entities.ts | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/docs-assets/handled-packets.md b/docs-assets/handled-packets.md index 0671987c..497ec5ec 100644 --- a/docs-assets/handled-packets.md +++ b/docs-assets/handled-packets.md @@ -32,8 +32,8 @@ ❌ world_border_warning_reach ❌ simulation_distance ❌ chunk_biomes -❌ damage_event ❌ hurt_animation +✅ damage_event ✅ spawn_entity ✅ spawn_entity_experience_orb ✅ named_entity_spawn diff --git a/prismarine-viewer/viewer/lib/entities.js b/prismarine-viewer/viewer/lib/entities.js index 01ce7d6d..59d1164a 100644 --- a/prismarine-viewer/viewer/lib/entities.js +++ b/prismarine-viewer/viewer/lib/entities.js @@ -496,4 +496,22 @@ export class Entities extends EventEmitter { new TWEEN.Tween(e.rotation).to({ y: e.rotation.y + dy }, TWEEN_DURATION).start() } } + + handleDamageEvent(entityId, damageAmount) { + const entityMesh = this.entities[entityId]?.children.find(c => c.name === 'mesh') + if (entityMesh) { + entityMesh.traverse((child) => { + if (child instanceof THREE.Mesh) { + const clonedMaterial = child.material.clone() + clonedMaterial.dispose() + child.material = child.material.clone() + const originalColor = child.material.color.clone() + child.material.color.set(0xff_00_00) + new TWEEN.Tween(child.material.color) + .to(originalColor, 500) + .start() + } + }) + } + } } diff --git a/src/entities.ts b/src/entities.ts index b238d4ea..b5daa220 100644 --- a/src/entities.ts +++ b/src/entities.ts @@ -1,4 +1,5 @@ import { Entity } from 'prismarine-entity' +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' import tracker from '@nxg-org/mineflayer-tracker' import { loader as autoJumpPlugin } from '@nxg-org/mineflayer-auto-jump' import { subscribeKey } from 'valtio/utils' @@ -88,6 +89,21 @@ customEvents.on('gameLoaded', () => { } }) + bot._client.on('damage_event', (data) => { + const { entityId, sourceTypeId: damage } = data + if (viewer.entities.entities[entityId]) { + viewer.entities.handleDamageEvent(entityId, damage) + } + }) + + bot._client.on('entity_status', (data) => { + if (versionToNumber(bot.version) >= versionToNumber('1.19.4')) return + const { entityId, entityStatus } = data + if (entityStatus === 2 && viewer.entities.entities[entityId]) { + viewer.entities.handleDamageEvent(entityId, entityStatus) + } + }) + const loadedSkinEntityIds = new Set() const playerRenderSkin = (e: Entity) => { From 574dbafc282ddac71355e68fed8c1e29f5589c06 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 2 Sep 2024 23:46:22 +0300 Subject: [PATCH 029/851] fix(renderer,important): fix all known rendering issues with starfield by @sa2urami --- prismarine-viewer/viewer/lib/worldrendererThree.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/prismarine-viewer/viewer/lib/worldrendererThree.ts b/prismarine-viewer/viewer/lib/worldrendererThree.ts index b1644ebd..c89bc4cb 100644 --- a/prismarine-viewer/viewer/lib/worldrendererThree.ts +++ b/prismarine-viewer/viewer/lib/worldrendererThree.ts @@ -413,6 +413,7 @@ class StarField { this.points?.position.copy?.(camera.position) material.uniforms.time.value = clock.getElapsedTime() * speed } + this.points.renderOrder = -1 } remove () { @@ -439,7 +440,7 @@ class StarfieldMaterial extends THREE.ShaderMaterial { void main() { vColor = color; vec4 mvPosition = modelViewMatrix * vec4(position, 0.5); - gl_PointSize = size * (30.0 / -mvPosition.z) * (3.0 + sin(time + 100.0)); + gl_PointSize = 0.7 * size * (30.0 / -mvPosition.z) * (3.0 + sin(time + 100.0)); gl_Position = projectionMatrix * mvPosition; }`, fragmentShader: /* glsl */ ` @@ -448,11 +449,7 @@ class StarfieldMaterial extends THREE.ShaderMaterial { varying vec3 vColor; void main() { float opacity = 1.0; - if (fade == 1.0) { - float d = distance(gl_PointCoord, vec2(0.5, 0.5)); - opacity = 1.0 / (1.0 + exp(16.0 * (d - 0.25))); - } - gl_FragColor = vec4(vColor, opacity); + gl_FragColor = vec4(vColor, 1.0); #include #include <${version >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}> From 00dd6060914a4347d6d59b1e1fb9da8c3dc6b6b6 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 2 Sep 2024 23:50:46 +0300 Subject: [PATCH 030/851] [skip ci] cleanup starfield code --- prismarine-viewer/viewer/lib/worldrendererThree.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prismarine-viewer/viewer/lib/worldrendererThree.ts b/prismarine-viewer/viewer/lib/worldrendererThree.ts index c89bc4cb..deec264e 100644 --- a/prismarine-viewer/viewer/lib/worldrendererThree.ts +++ b/prismarine-viewer/viewer/lib/worldrendererThree.ts @@ -62,9 +62,9 @@ export class WorldRendererThree extends WorldRendererCommon { const nightTime = 13_500 const morningStart = 23_000 const displayStars = newTime > nightTime && newTime < morningStart - if (displayStars && !this.starField.points) { + if (displayStars) { this.starField.addToScene() - } else if (!displayStars && this.starField.points) { + } else { this.starField.remove() } } From 0d3a3affd75c72de49a02132adf8ef5bd13210b7 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 01:00:54 +0300 Subject: [PATCH 031/851] fix recently introduced bug with crafting in singleplayer --- scripts/makeOptimizedMcData.mjs | 4 ++- scripts/testOptimizedMcdata.ts | 35 ++++++++++++++----- ...tation_console_controller_gamepad_icon.svg | 1 - src/inventoryWindows.ts | 6 ++-- src/optimizeJson.ts | 7 +++- src/{ => react}/GlobalSearchInput.tsx | 5 ++- src/react/Input.tsx | 12 +++++-- src/reactUi.tsx | 2 +- 8 files changed, 52 insertions(+), 20 deletions(-) delete mode 100644 src/cross_playstation_console_controller_gamepad_icon.svg rename src/{ => react}/GlobalSearchInput.tsx (87%) diff --git a/scripts/makeOptimizedMcData.mjs b/scripts/makeOptimizedMcData.mjs index d0adcae2..9794aeed 100644 --- a/scripts/makeOptimizedMcData.mjs +++ b/scripts/makeOptimizedMcData.mjs @@ -118,7 +118,9 @@ const dataTypeBundling = { blockLoot: { arrKey: 'block' }, - recipes: {}, // todo we can do better + recipes: { + raw: true + }, // todo we can do better blockCollisionShapes: {}, loginPacket: {}, protocol: { diff --git a/scripts/testOptimizedMcdata.ts b/scripts/testOptimizedMcdata.ts index 17b5f7ed..d6c74384 100644 --- a/scripts/testOptimizedMcdata.ts +++ b/scripts/testOptimizedMcdata.ts @@ -8,7 +8,7 @@ const json = JSON.parse(fs.readFileSync('./generated/minecraft-data-optimized.js const dataPaths = require('minecraft-data/minecraft-data/data/dataPaths.json') const validateData = (ver, type) => { - const target = JsonOptimizer.restoreData(json[type], ver) + const target = JsonOptimizer.restoreData(structuredClone(json[type]), ver) const arrKey = json[type].arrKey const originalPath = dataPaths.pc[ver][type] const original = require(`minecraft-data/minecraft-data/data/${originalPath}/${type}.json`) @@ -43,16 +43,33 @@ const validateData = (ver, type) => { } } -const checkObj = (source, diffing) => { - checkKeys(Object.keys(source), Object.keys(diffing)) - for (const [key, val] of Object.entries(source)) { - if (JSON.stringify(val) !== JSON.stringify(diffing[key])) { - throw new Error(`different value of ${key}: ${val} ${diffing[key]}`) - } +const sortObj = (obj) => { + const sorted = {} + for (const key of Object.keys(obj).sort()) { + sorted[key] = obj[key] } + return sorted } -const checkKeys = (source, diffing, isUniq = true, msg = '', redunantOk = false) => { +const checkObj = (source, diffing) => { + if (!Array.isArray(source)) { + source = sortObj(source) + } + if (!Array.isArray(diffing)) { + diffing = sortObj(diffing) + } + if (JSON.stringify(source) !== JSON.stringify(diffing)) { + throw new Error(`different value: ${JSON.stringify(source)} ${JSON.stringify(diffing)}`) + } + // checkKeys(Object.keys(source), Object.keys(diffing)) + // for (const [key, val] of Object.entries(source)) { + // if (JSON.stringify(val) !== JSON.stringify(diffing[key])) { + // throw new Error(`different value of ${key}: ${val} ${diffing[key]}`) + // } + // } +} + +const checkKeys = (source, diffing, isUniq = true, msg = '', redundantIsOk = false) => { if (isUniq) { for (const [i, item] of diffing.entries()) { if (diffing.indexOf(item) !== i) { @@ -65,7 +82,7 @@ const checkKeys = (source, diffing, isUniq = true, msg = '', redunantOk = false) throw new Error(`Diffing does not include "${key}" (${msg})`) } } - if (!redunantOk) { + if (!redundantIsOk) { for (const key of diffing) { if (!source.includes(key)) { throw new Error(`Source does not include "${key}" (${msg})`) diff --git a/src/cross_playstation_console_controller_gamepad_icon.svg b/src/cross_playstation_console_controller_gamepad_icon.svg deleted file mode 100644 index d7d176e2..00000000 --- a/src/cross_playstation_console_controller_gamepad_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index e5964646..b23c88fa 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -69,11 +69,12 @@ export const onGameLoad = (onLoad) => { } }) + // workaround: singleplayer player inventory crafting bot.inventory.on('updateSlot', ((_oldSlot, oldItem, newItem) => { - const oldSlot = _oldSlot as number + const currentSlot = _oldSlot as number if (!miscUiState.singleplayer) return const { craftingResultSlot } = bot.inventory - if (oldSlot === craftingResultSlot && oldItem && !newItem) { + if (currentSlot === craftingResultSlot && oldItem && !newItem) { for (let i = 1; i < 5; i++) { const count = bot.inventory.slots[i]?.count if (count && count > 1) { @@ -86,6 +87,7 @@ export const onGameLoad = (onLoad) => { } return } + if (currentSlot > 4) return const craftingSlots = bot.inventory.slots.slice(1, 5) try { const resultingItem = getResultingRecipe(craftingSlots, 2) diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts index 442a72a1..00547ffe 100644 --- a/src/optimizeJson.ts +++ b/src/optimizeJson.ts @@ -192,7 +192,12 @@ export default class JsonOptimizer { for (const [key, removePropsId] of removedProps) { for (const removePropId of removePropsId) { const removeProp = propertiesById[removePropId] - delete dataByKeys[key][removeProp] + // todo: this is not correct! + if (Array.isArray(dataByKeys[key])) { + dataByKeys[key].splice(removeProp, 1) + } else { + delete dataByKeys[key][removeProp] + } } } if (versionToNumber(versionKey) <= versionToNumber(targetKey)) { diff --git a/src/GlobalSearchInput.tsx b/src/react/GlobalSearchInput.tsx similarity index 87% rename from src/GlobalSearchInput.tsx rename to src/react/GlobalSearchInput.tsx index d9266950..6f2d56d1 100644 --- a/src/GlobalSearchInput.tsx +++ b/src/react/GlobalSearchInput.tsx @@ -1,6 +1,6 @@ import { useSnapshot } from 'valtio' -import { miscUiState } from './globalState' -import Input from './react/Input' +import { miscUiState } from '../globalState' +import Input from './Input' function InnerSearch () { const { currentTouch } = useSnapshot(miscUiState) @@ -19,7 +19,6 @@ function InnerSearch () { autoFocus={currentTouch === false} width={50} placeholder='Search...' - defaultValue="" onChange={({ target: { value } }) => { customEvents.emit('search', value) }} diff --git a/src/react/Input.tsx b/src/react/Input.tsx index c3d40491..41dbc7ba 100644 --- a/src/react/Input.tsx +++ b/src/react/Input.tsx @@ -27,8 +27,16 @@ export default ({ autoFocus, rootStyles, inputRef, validateInput, ...inputProps return
{ setValidationStyle(validateInput?.(e.target.value) ?? {}) setValue(e.target.value) diff --git a/src/reactUi.tsx b/src/reactUi.tsx index b40c47a1..029b2493 100644 --- a/src/reactUi.tsx +++ b/src/reactUi.tsx @@ -28,7 +28,7 @@ import SoundMuffler from './react/SoundMuffler' import TouchControls from './react/TouchControls' import widgets from './react/widgets' import { useIsWidgetActive } from './react/utilsApp' -import GlobalSearchInput from './GlobalSearchInput' +import GlobalSearchInput from './react/GlobalSearchInput' import TouchAreasControlsProvider from './react/TouchAreasControlsProvider' import NotificationProvider, { showNotification } from './react/NotificationProvider' import HotbarRenderApp from './react/HotbarRenderApp' From b2ac80602c8850b4f54ba13e653f56cda66b4190 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 01:10:11 +0300 Subject: [PATCH 032/851] feat(important): redirect to origin website from maps.mcraft.fun which makes testing maps so much easier on preview deploys and locally --- src/react/MainMenuRenderApp.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/react/MainMenuRenderApp.tsx b/src/react/MainMenuRenderApp.tsx index acd61803..37c7966b 100644 --- a/src/react/MainMenuRenderApp.tsx +++ b/src/react/MainMenuRenderApp.tsx @@ -70,6 +70,9 @@ export default () => { } }, []) + let mapsProviderUrl = appConfig?.mapsProvider + if (mapsProviderUrl && location.origin !== 'https://mcraft.fun') mapsProviderUrl = mapsProviderUrl + '?to=' + encodeURIComponent(location.href) + // todo clean, use custom csstransition return {(state) =>
@@ -107,7 +110,7 @@ export default () => { openFilePicker() } }} - mapsProvider={appConfig?.mapsProvider} + mapsProvider={mapsProviderUrl} versionStatus={versionStatus} versionTitle={versionTitle} onVersionClick={async () => { From 559f535207c5dbeab23dbeb82b965a84bf8a377b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 01:11:49 +0300 Subject: [PATCH 033/851] don't lie of resoure pack support --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index b21e8c8a..8ecd7e78 100644 --- a/README.MD +++ b/README.MD @@ -17,7 +17,7 @@ For building the project yourself / contributing, see [Development, Debugging & - Works offline - Play with friends over internet! (P2P is powered by Peer.js discovery servers) - First-class touch (mobile) & controller support -- FULL Resource pack support: Custom GUI, all textures & custom models! Server resource packs are also supported. +- Basic Resource pack support: Custom GUI, all textures. Server resource packs are not supported yet. - Builtin JEI with recipes & guides for every item (also replaces creative inventory) - even even more! From 698fb1d388a0c0dc2805a9ae6d2af7c4cdcc0183 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 01:13:12 +0300 Subject: [PATCH 034/851] fix tsc --- src/optimizeJson.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optimizeJson.ts b/src/optimizeJson.ts index 00547ffe..491c8301 100644 --- a/src/optimizeJson.ts +++ b/src/optimizeJson.ts @@ -194,7 +194,7 @@ export default class JsonOptimizer { const removeProp = propertiesById[removePropId] // todo: this is not correct! if (Array.isArray(dataByKeys[key])) { - dataByKeys[key].splice(removeProp, 1) + dataByKeys[key].splice(removeProp as any, 1) // splice accepts strings as well } else { delete dataByKeys[key][removeProp] } From c2a34ea9f1874ef675e8bccebfc250f2ea4273fa Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 02:48:16 +0300 Subject: [PATCH 035/851] fix(preflat-worlds): improve mesher performance by 2x by syncing the code from webgpu branch fixes #191 --- prismarine-viewer/examples/shared.ts | 11 ++ prismarine-viewer/viewer/lib/mesher/mesher.ts | 12 +- prismarine-viewer/viewer/lib/mesher/models.ts | 158 +++++++++++------- prismarine-viewer/viewer/lib/mesher/shared.ts | 26 ++- prismarine-viewer/viewer/lib/mesher/world.ts | 2 + .../viewer/lib/worldrendererCommon.ts | 19 ++- 6 files changed, 164 insertions(+), 64 deletions(-) create mode 100644 prismarine-viewer/examples/shared.ts diff --git a/prismarine-viewer/examples/shared.ts b/prismarine-viewer/examples/shared.ts new file mode 100644 index 00000000..4ef9b417 --- /dev/null +++ b/prismarine-viewer/examples/shared.ts @@ -0,0 +1,11 @@ +export type BlockFaceType = { + side: number + textureIndex: number + textureName?: string + tint?: [number, number, number] + isTransparent?: boolean +} + +export type BlockType = { + faces: BlockFaceType[] +} diff --git a/prismarine-viewer/viewer/lib/mesher/mesher.ts b/prismarine-viewer/viewer/lib/mesher/mesher.ts index 118f79c7..4813cfc9 100644 --- a/prismarine-viewer/viewer/lib/mesher/mesher.ts +++ b/prismarine-viewer/viewer/lib/mesher/mesher.ts @@ -11,6 +11,7 @@ if (module.require) { global.performance = r('perf_hooks').performance } +let workerIndex = 0 let world: World let dirtySections = new Map() let allDataReady = false @@ -85,8 +86,9 @@ const handleMessage = data => { switch (data.type) { case 'mesherData': { - setMesherData(data.blockstatesModels, data.blocksAtlas) + setMesherData(data.blockstatesModels, data.blocksAtlas, data.config.outputFormat === 'webgpu') allDataReady = true + workerIndex = data.workerIndex break } @@ -148,18 +150,22 @@ setInterval(() => { for (const key of dirtySections.keys()) { const [x, y, z] = key.split(',').map(v => parseInt(v, 10)) const chunk = world.getColumn(x, z) + let processTime = 0 if (chunk?.getSection(new Vec3(x, y, z))) { + const start = performance.now() const geometry = getSectionGeometry(x, y, z, world) - const transferable = [geometry.positions.buffer, geometry.normals.buffer, geometry.colors.buffer, geometry.uvs.buffer] + const transferable = [geometry.positions?.buffer, geometry.normals?.buffer, geometry.colors?.buffer, geometry.uvs?.buffer].filter(Boolean) //@ts-expect-error postMessage({ type: 'geometry', key, geometry }, transferable) + processTime = performance.now() - start } else { // console.info('[mesher] Missing section', x, y, z) } const dirtyTimes = dirtySections.get(key) if (!dirtyTimes) throw new Error('dirtySections.get(key) is falsy') for (let i = 0; i < dirtyTimes; i++) { - postMessage({ type: 'sectionFinished', key }) + postMessage({ type: 'sectionFinished', key, workerIndex, processTime }) + processTime = 0 } dirtySections.delete(key) } diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index c4249ac4..54c879b8 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -1,8 +1,10 @@ import { Vec3 } from 'vec3' import worldBlockProvider, { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider' import legacyJson from '../../../../src/preflatMap.json' +import { BlockType } from '../../../examples/shared' import { World, BlockModelPartsResolved, WorldBlock as Block } from './world' import { BlockElement, buildRotationMatrix, elemFaces, matmul3, matmulmat3, vecadd3, vecsub3 } from './modelsGeometryCommon' +import { MesherGeometryOutput } from './shared' let blockProvider: WorldBlockProvider @@ -19,6 +21,19 @@ for (const key of Object.keys(tintsData)) { tints[key] = prepareTints(tintsData[key]) } +type TestTileData = { + block: string + faces: Array<{ + face: string + neighbor: string + light?: number + }> +} + +type Tiles = { + [blockPos: string]: BlockType & TestTileData +} + function prepareTints (tints) { const map = new Map() const defaultValue = tintToGl(tints.default) @@ -54,19 +69,25 @@ export function preflatBlockCalculation (block: Block, world: World, position: V ] // set needed props to true: east:'false',north:'false',south:'false',west:'false' const props = {} + let changed = false for (const [i, neighbor] of neighbors.entries()) { const isConnectedToSolid = isSolidConnection ? (neighbor && !neighbor.transparent) : false if (isConnectedToSolid || neighbor?.name === block.name) { props[['south', 'north', 'east', 'west'][i]] = 'true' + changed = true } } - return props + return changed ? props : undefined } // case 'gate_in_wall': {} case 'block_snowy': { const aboveIsSnow = world.getBlock(position.offset(0, 1, 0))?.name === 'snow' - return { - snowy: `${aboveIsSnow}` + if (aboveIsSnow) { + return { + snowy: `${aboveIsSnow}` + } + } else { + return } } case 'door': { @@ -139,7 +160,7 @@ function renderLiquid (world: World, cursor: Vec3, texture: any | undefined, typ if (!neighbor) continue if (neighbor.type === type) continue const isGlass = neighbor.name.includes('glass') - if ((isCube(neighbor) && !isUp) || neighbor.getProperties().waterlogged) continue + if ((isCube(neighbor) && !isUp) || neighbor.material === 'plant' || neighbor.getProperties().waterlogged) continue let tint = [1, 1, 1] if (water) { @@ -151,11 +172,12 @@ function renderLiquid (world: World, cursor: Vec3, texture: any | undefined, typ } if (needTiles) { - attr.tiles[`${cursor.x},${cursor.y},${cursor.z}`] ??= { + const tiles = attr.tiles as Tiles + tiles[`${cursor.x},${cursor.y},${cursor.z}`] ??= { block: 'water', faces: [], } - attr.tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({ + tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({ face, neighbor: `${neighborPos.x},${neighborPos.y},${neighborPos.z}`, // texture: eFace.texture.name, @@ -183,7 +205,7 @@ function renderLiquid (world: World, cursor: Vec3, texture: any | undefined, typ let needRecompute = false -function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: boolean, attr: Record, globalMatrix: any, globalShift: any, block: Block, biome: string) { +function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: boolean, attr: MesherGeometryOutput, globalMatrix: any, globalShift: any, block: Block, biome: string) { const position = cursor // const key = `${position.x},${position.y},${position.z}` // if (!globalThis.allowedBlocks.includes(key)) return @@ -192,7 +214,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: // eslint-disable-next-line guard-for-in for (const face in element.faces) { const eFace = element.faces[face] - const { corners, mask1, mask2 } = elemFaces[face] + const { corners, mask1, mask2, side } = elemFaces[face] const dir = matmul3(globalMatrix, elemFaces[face].dir) if (eFace.cullface) { @@ -214,7 +236,10 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: const maxz = element.to[2] const texture = eFace.texture as any - const { u, v, su, sv } = texture + const { u } = texture + const { v } = texture + const { su } = texture + const { sv } = texture const ndx = Math.floor(attr.positions.length / 3) @@ -246,7 +271,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: let localMatrix = null as any let localShift = null as any - if (element.rotation) { + if (element.rotation && !needTiles) { // todo do we support rescale? localMatrix = buildRotationMatrix( element.rotation.axis, @@ -272,21 +297,23 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: (pos[2] ? maxz : minz) ] - vertex = vecadd3(matmul3(localMatrix, vertex), localShift) - vertex = vecadd3(matmul3(globalMatrix, vertex), globalShift) - vertex = vertex.map(v => v / 16) + if (!needTiles) { + vertex = vecadd3(matmul3(localMatrix, vertex), localShift) + vertex = vecadd3(matmul3(globalMatrix, vertex), globalShift) + vertex = vertex.map(v => v / 16) - attr.positions.push( - vertex[0] + (cursor.x & 15) - 8, - vertex[1] + (cursor.y & 15) - 8, - vertex[2] + (cursor.z & 15) - 8 - ) + attr.positions.push( + vertex[0] + (cursor.x & 15) - 8, + vertex[1] + (cursor.y & 15) - 8, + vertex[2] + (cursor.z & 15) - 8 + ) - attr.normals.push(...dir) + attr.normals.push(...dir) - const baseu = (pos[3] - 0.5) * uvcs - (pos[4] - 0.5) * uvsn + 0.5 - const basev = (pos[3] - 0.5) * uvsn + (pos[4] - 0.5) * uvcs + 0.5 - attr.uvs.push(baseu * su + u, basev * sv + v) + const baseu = (pos[3] - 0.5) * uvcs - (pos[4] - 0.5) * uvsn + 0.5 + const basev = (pos[3] - 0.5) * uvsn + (pos[4] - 0.5) * uvcs + 0.5 + attr.uvs.push(baseu * su + u, basev * sv + v) + } let light = 1 if (doAO) { @@ -322,40 +349,49 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: aos.push(ao) } - attr.colors.push(baseLight * tint[0] * light, baseLight * tint[1] * light, baseLight * tint[2] * light) + if (!needTiles) { + attr.colors.push(baseLight * tint[0] * light, baseLight * tint[1] * light, baseLight * tint[2] * light) + } } + const lightWithColor = [baseLight * tint[0], baseLight * tint[1], baseLight * tint[2]] as [number, number, number] + if (needTiles) { - attr.tiles[`${cursor.x},${cursor.y},${cursor.z}`] ??= { + const tiles = attr.tiles as Tiles + tiles[`${cursor.x},${cursor.y},${cursor.z}`] ??= { block: block.name, faces: [], } - attr.tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({ - face, - neighbor: `${neighborPos.x},${neighborPos.y},${neighborPos.z}`, - light: baseLight - // texture: eFace.texture.name, - }) + const needsOnlyOneFace = false + const isTilesEmpty = tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.length < 1 + if (isTilesEmpty || !needsOnlyOneFace) { + tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({ + face, + side, + textureIndex: eFace.texture.tileIndex, + neighbor: `${neighborPos.x},${neighborPos.y},${neighborPos.z}`, + light: baseLight, + tint: lightWithColor, + //@ts-expect-error debug prop + texture: eFace.texture.debugName || block.name, + } satisfies BlockType['faces'][number] & TestTileData['faces'][number] as any) + } } - if (doAO && aos[0] + aos[3] >= aos[1] + aos[2]) { - attr.indices.push( - // eslint-disable-next-line @stylistic/function-call-argument-newline - ndx, ndx + 3, ndx + 2, - ndx, ndx + 1, ndx + 3 - ) - } else { - attr.indices.push( - // eslint-disable-next-line @stylistic/function-call-argument-newline - ndx, ndx + 1, ndx + 2, - ndx + 2, ndx + 1, ndx + 3 - ) + if (!needTiles) { + if (doAO && aos[0] + aos[3] >= aos[1] + aos[2]) { + attr.indices.push( + ndx, ndx + 3, ndx + 2, ndx, ndx + 1, ndx + 3 + ) + } else { + attr.indices.push( + ndx, ndx + 1, ndx + 2, ndx + 2, ndx + 1, ndx + 3 + ) + } } } } -const makeLooseObj = (obj: Record) => obj - const invisibleBlocks = new Set(['air', 'cave_air', 'void_air', 'barrier']) const isBlockWaterlogged = (block: Block) => block.getProperties().waterlogged === true || block.getProperties().waterlogged === 'true' @@ -365,7 +401,7 @@ let erroredBlockModel: BlockModelPartsResolved export function getSectionGeometry (sx, sy, sz, world: World) { let delayedRender = [] as Array<() => void> - const attr = makeLooseObj({ + const attr: MesherGeometryOutput = { sx: sx + 8, sy: sy + 8, sz: sz + 8, @@ -381,9 +417,10 @@ export function getSectionGeometry (sx, sy, sz, world: World) { tiles: {}, // todo this can be removed here signs: {}, + isFull: true, highestBlocks: {}, hadErrors: false - } as Record) + } const cursor = new Vec3(0, 0, 0) for (cursor.y = sy; cursor.y < sy + 16; cursor.y++) { @@ -419,19 +456,17 @@ export function getSectionGeometry (sx, sy, sz, world: World) { } const biome = block.biome.name - let preflatRecomputeVariant = !!(block as any)._originalProperties if (world.preflat) { const patchProperties = preflatBlockCalculation(block, world, cursor) if (patchProperties) { - //@ts-expect-error block._originalProperties ??= block._properties - //@ts-expect-error block._properties = { ...block._originalProperties, ...patchProperties } - preflatRecomputeVariant = true + if (block.models && JSON.stringify(block._originalProperties) !== JSON.stringify(block._properties)) { + // recompute models + block.models = undefined + } } else { - //@ts-expect-error block._properties = block._originalProperties ?? block._properties - //@ts-expect-error block._originalProperties = undefined } } @@ -449,7 +484,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { if (block.name !== 'water' && block.name !== 'lava' && !invisibleBlocks.has(block.name)) { // cache let { models } = block - if (block.models === undefined || preflatRecomputeVariant) { + if (block.models === undefined) { try { models = blockProvider.getAllResolvedModels0_1({ name: block.name, @@ -515,7 +550,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { delayedRender = [] let ndx = attr.positions.length / 3 - for (let i = 0; i < attr.t_positions.length / 12; i++) { + for (let i = 0; i < attr.t_positions!.length / 12; i++) { attr.indices.push( ndx, ndx + 1, ndx + 2, ndx + 2, ndx + 1, ndx + 3, // eslint-disable-next-line @stylistic/function-call-argument-newline @@ -525,10 +560,10 @@ export function getSectionGeometry (sx, sy, sz, world: World) { ndx += 4 } - attr.positions.push(...attr.t_positions) - attr.normals.push(...attr.t_normals) - attr.colors.push(...attr.t_colors) - attr.uvs.push(...attr.t_uvs) + attr.positions.push(...attr.t_positions!) + attr.normals.push(...attr.t_normals!) + attr.colors.push(...attr.t_colors!) + attr.uvs.push(...attr.t_uvs!) delete attr.t_positions delete attr.t_normals @@ -540,6 +575,13 @@ export function getSectionGeometry (sx, sy, sz, world: World) { attr.colors = new Float32Array(attr.colors) as any attr.uvs = new Float32Array(attr.uvs) as any + if (needTiles) { + delete attr.positions + delete attr.normals + delete attr.colors + delete attr.uvs + } + return attr } diff --git a/prismarine-viewer/viewer/lib/mesher/shared.ts b/prismarine-viewer/viewer/lib/mesher/shared.ts index 782a3141..f96c7d4b 100644 --- a/prismarine-viewer/viewer/lib/mesher/shared.ts +++ b/prismarine-viewer/viewer/lib/mesher/shared.ts @@ -1,11 +1,35 @@ +import { BlockType } from '../../../examples/shared' + export const defaultMesherConfig = { version: '', enableLighting: true, skyLight: 15, smoothLighting: true, - outputFormat: 'threeJs' as 'threeJs' | 'webgl', + outputFormat: 'threeJs' as 'threeJs' | 'webgpu', textureSize: 1024, // for testing debugModelVariant: undefined as undefined | number[] } export type MesherConfig = typeof defaultMesherConfig + +export type MesherGeometryOutput = { + sx: number, + sy: number, + sz: number, + // resulting: float32array + positions: any, + normals: any, + colors: any, + uvs: any, + t_positions?: number[], + t_normals?: number[], + t_colors?: number[], + t_uvs?: number[], + + indices: number[], + tiles: Record, + signs: Record, + isFull: boolean + highestBlocks: Record + hadErrors: boolean +} diff --git a/prismarine-viewer/viewer/lib/mesher/world.ts b/prismarine-viewer/viewer/lib/mesher/world.ts index db940f66..ae65118c 100644 --- a/prismarine-viewer/viewer/lib/mesher/world.ts +++ b/prismarine-viewer/viewer/lib/mesher/world.ts @@ -26,6 +26,8 @@ export type WorldBlock = Omit & { isCube: boolean /** cache */ models?: BlockModelPartsResolved | null + _originalProperties?: Record + _properties?: Record } diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index 482907d4..fe5a3ad2 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -91,8 +91,17 @@ export abstract class WorldRendererCommon items?: CustomTexturesData blocks?: CustomTexturesData } = {} + workersProcessAverageTime = 0 + workersProcessAverageTimeCount = 0 + maxWorkersProcessTime = 0 + edgeChunks = {} as Record + lastAddChunk = null as null | { + timeout: any + x: number + z: number + } - abstract outputFormat: 'threeJs' | 'webgl' + abstract outputFormat: 'threeJs' | 'webgpu' constructor (public config: WorldRendererConfig) { // this.initWorkers(1) // preload script on page load @@ -145,6 +154,11 @@ export abstract class WorldRendererCommon } this.renderUpdateEmitter.emit('update') + if (data.processTime) { + this.workersProcessAverageTimeCount++ + this.workersProcessAverageTime = ((this.workersProcessAverageTime * (this.workersProcessAverageTimeCount - 1)) + data.processTime) / this.workersProcessAverageTimeCount + this.maxWorkersProcessTime = Math.max(this.maxWorkersProcessTime, data.processTime) + } } } worker.onmessage = ({ data }) => { @@ -265,7 +279,7 @@ export abstract class WorldRendererCommon this.currentTextureImage = this.material.map.image this.mesherConfig.textureSize = this.material.map.image.width - for (const worker of this.workers) { + for (const [i, worker] of this.workers.entries()) { const { blockstatesModels } = this if (this.customBlockStates) { // TODO! remove from other versions as well @@ -282,6 +296,7 @@ export abstract class WorldRendererCommon } worker.postMessage({ type: 'mesherData', + workerIndex: i, blocksAtlas: { latest: blocksAtlas }, From 684261e5157a8891b8e2ec65283c314a517f7328 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 03:08:19 +0300 Subject: [PATCH 036/851] fix building, update test types --- prismarine-viewer/examples/shared.ts | 19 +++++++++++++------ prismarine-viewer/viewer/lib/mesher/models.ts | 15 ++++----------- .../viewer/lib/worldrendererCommon.ts | 7 ++++--- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/prismarine-viewer/examples/shared.ts b/prismarine-viewer/examples/shared.ts index 4ef9b417..d7a402dd 100644 --- a/prismarine-viewer/examples/shared.ts +++ b/prismarine-viewer/examples/shared.ts @@ -1,11 +1,18 @@ export type BlockFaceType = { - side: number - textureIndex: number - textureName?: string - tint?: [number, number, number] - isTransparent?: boolean + side: number + textureIndex: number + tint?: [number, number, number] + isTransparent?: boolean + + // for testing + face: string + neighbor: string + light?: number } export type BlockType = { - faces: BlockFaceType[] + faces: BlockFaceType[] + + // for testing + block: string } diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index 54c879b8..1f19fc71 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -21,17 +21,8 @@ for (const key of Object.keys(tintsData)) { tints[key] = prepareTints(tintsData[key]) } -type TestTileData = { - block: string - faces: Array<{ - face: string - neighbor: string - light?: number - }> -} - type Tiles = { - [blockPos: string]: BlockType & TestTileData + [blockPos: string]: BlockType } function prepareTints (tints) { @@ -180,6 +171,8 @@ function renderLiquid (world: World, cursor: Vec3, texture: any | undefined, typ tiles[`${cursor.x},${cursor.y},${cursor.z}`].faces.push({ face, neighbor: `${neighborPos.x},${neighborPos.y},${neighborPos.z}`, + side: 0, // todo + textureIndex: 0, // texture: eFace.texture.name, }) } @@ -374,7 +367,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: tint: lightWithColor, //@ts-expect-error debug prop texture: eFace.texture.debugName || block.name, - } satisfies BlockType['faces'][number] & TestTileData['faces'][number] as any) + } satisfies BlockType['faces'][number]) } } diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index fe5a3ad2..49de2ca0 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -14,7 +14,7 @@ import TypedEmitter from 'typed-emitter' import { dynamicMcDataFiles } from '../../buildMesherConfig.mjs' import { toMajorVersion } from '../../../src/utils' import { buildCleanupDecorator } from './cleanupDecorator' -import { defaultMesherConfig } from './mesher/shared' +import { MesherGeometryOutput, defaultMesherConfig } from './mesher/shared' import { chunkPos } from './simpleUtils' import { HandItemBlock } from './holdingBlock' @@ -123,8 +123,9 @@ export abstract class WorldRendererCommon if (!this.active) return this.handleWorkerMessage(data) if (data.type === 'geometry') { - for (const key in data.geometry.highestBlocks) { - const highest = data.geometry.highestBlocks[key] + const geometry = data.geometry as MesherGeometryOutput + for (const key in geometry.highestBlocks) { + const highest = geometry.highestBlocks[key] if (!this.highestBlocks[key] || this.highestBlocks[key].y < highest.y) { this.highestBlocks[key] = highest } From 9e7711e386ef3764f824b5e7d19e93ed79fdc5f4 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 3 Sep 2024 03:15:17 +0300 Subject: [PATCH 037/851] add eaglercraft as alternative, fix types again --- README.MD | 1 + prismarine-viewer/viewer/lib/mesher/models.ts | 2 +- prismarine-viewer/viewer/lib/mesher/shared.ts | 2 +- prismarine-viewer/viewer/lib/mesher/test/mesherTester.ts | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.MD b/README.MD index 8ecd7e78..e875873b 100644 --- a/README.MD +++ b/README.MD @@ -188,3 +188,4 @@ General: ### Alternatives - [https://github.com/ClassiCube/ClassiCube](ClassiCube - Better C# Rewrite) [DEMO](https://www.classicube.net/server/play/?warned=true) +- [https://m.eaglercraft.com/](EaglerCraft) - Eaglercraft runnable on mobile (real Minecraft in the browser) diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index 1f19fc71..88505c06 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -410,7 +410,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { tiles: {}, // todo this can be removed here signs: {}, - isFull: true, + // isFull: true, highestBlocks: {}, hadErrors: false } diff --git a/prismarine-viewer/viewer/lib/mesher/shared.ts b/prismarine-viewer/viewer/lib/mesher/shared.ts index f96c7d4b..30b62c45 100644 --- a/prismarine-viewer/viewer/lib/mesher/shared.ts +++ b/prismarine-viewer/viewer/lib/mesher/shared.ts @@ -29,7 +29,7 @@ export type MesherGeometryOutput = { indices: number[], tiles: Record, signs: Record, - isFull: boolean + // isFull: boolean highestBlocks: Record hadErrors: boolean } diff --git a/prismarine-viewer/viewer/lib/mesher/test/mesherTester.ts b/prismarine-viewer/viewer/lib/mesher/test/mesherTester.ts index 986aa079..2885c2a7 100644 --- a/prismarine-viewer/viewer/lib/mesher/test/mesherTester.ts +++ b/prismarine-viewer/viewer/lib/mesher/test/mesherTester.ts @@ -41,7 +41,7 @@ export const setup = (version, initialBlocks: Array<[number[], string]>) => { reload() const getLights = () => { - return Object.fromEntries(getGeometry().faces.map(({ face, light }) => ([face, light * 15 - 2]))) + return Object.fromEntries(getGeometry().faces.map(({ face, light }) => ([face, (light ?? 0) * 15 - 2]))) } const setLight = (x: number, y: number, z: number, val = 0) => { From 79394d72b6006e720f7ac3845fe0a44245d61c74 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 4 Sep 2024 00:39:35 +0300 Subject: [PATCH 038/851] fix(important,singleplayer): stop loading chunks in previous position when teleported to a new pos and always start loading chunks in new pos --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 695aff89..71dfde67 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "esbuild-plugin-polyfill-node": "^0.3.0", "express": "^4.18.2", "filesize": "^10.0.12", - "flying-squid": "npm:@zardoy/flying-squid@^0.0.35", + "flying-squid": "npm:@zardoy/flying-squid@^0.0.36", "fs-extra": "^11.1.1", "google-drive-browserfs": "github:zardoy/browserfs#google-drive", "jszip": "^3.10.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d9621973..869e5573 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,8 +117,8 @@ importers: specifier: ^10.0.12 version: 10.0.12 flying-squid: - specifier: npm:@zardoy/flying-squid@^0.0.35 - version: '@zardoy/flying-squid@0.0.35(encoding@0.1.13)' + specifier: npm:@zardoy/flying-squid@^0.0.36 + version: '@zardoy/flying-squid@0.0.36(encoding@0.1.13)' fs-extra: specifier: ^11.1.1 version: 11.1.1 @@ -3390,8 +3390,8 @@ packages: resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==} engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'} - '@zardoy/flying-squid@0.0.35': - resolution: {integrity: sha512-6cZdDi7yaqxh6KbOPhDueipcr9DBgJ3mJY+/QwAjaSzhP//5n1BLjyVGlx2Ncs/6Vns2grTOmeuDhJjMbVgjQg==} + '@zardoy/flying-squid@0.0.36': + resolution: {integrity: sha512-d4clMPDpw723SDF5P2mMVNfbthUFLX6OT+vTCECAMshX8/M7CyMq/q9BfBQoeJcBL0H9nplhwtFbnx3Edb2fzA==} engines: {node: '>=8'} hasBin: true @@ -13366,7 +13366,7 @@ snapshots: '@types/emscripten': 1.39.8 tslib: 1.14.1 - '@zardoy/flying-squid@0.0.35(encoding@0.1.13)': + '@zardoy/flying-squid@0.0.36(encoding@0.1.13)': dependencies: '@tootallnate/once': 2.0.0 change-case: 4.1.2 From 306f894d8cc5c6314bb0495ddaaaa4a19be2cb4c Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 4 Sep 2024 03:18:56 +0300 Subject: [PATCH 039/851] fix(important,singleplayer): stop loading chunks in previous position when teleported to a new pos and always start loading chunks in new pos --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5337cebf..bd18bc03 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "esbuild-plugin-polyfill-node": "^0.3.0", "express": "^4.18.2", "filesize": "^10.0.12", - "flying-squid": "npm:@zardoy/flying-squid@^0.0.36", + "flying-squid": "npm:@zardoy/flying-squid@^0.0.38", "fs-extra": "^11.1.1", "google-drive-browserfs": "github:zardoy/browserfs#google-drive", "jszip": "^3.10.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8075be60..77810ab0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,8 +117,8 @@ importers: specifier: ^10.0.12 version: 10.0.12 flying-squid: - specifier: npm:@zardoy/flying-squid@^0.0.36 - version: '@zardoy/flying-squid@0.0.36(encoding@0.1.13)' + specifier: npm:@zardoy/flying-squid@^0.0.38 + version: '@zardoy/flying-squid@0.0.38(encoding@0.1.13)' fs-extra: specifier: ^11.1.1 version: 11.1.1 @@ -3390,8 +3390,8 @@ packages: resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==} engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'} - '@zardoy/flying-squid@0.0.36': - resolution: {integrity: sha512-d4clMPDpw723SDF5P2mMVNfbthUFLX6OT+vTCECAMshX8/M7CyMq/q9BfBQoeJcBL0H9nplhwtFbnx3Edb2fzA==} + '@zardoy/flying-squid@0.0.38': + resolution: {integrity: sha512-xz/ZuWmva3mlT1cigOudOMqa5iQF2sWsUUVeBNUoqfHscXoXl0TIOXnRScBeEGZjY2fD7meJ24nlcInewgNfZg==} engines: {node: '>=8'} hasBin: true @@ -13366,7 +13366,7 @@ snapshots: '@types/emscripten': 1.39.8 tslib: 1.14.1 - '@zardoy/flying-squid@0.0.36(encoding@0.1.13)': + '@zardoy/flying-squid@0.0.38(encoding@0.1.13)': dependencies: '@tootallnate/once': 2.0.0 change-case: 4.1.2 From 5aaa687392cef67e153270463461ce3a46704813 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Wed, 4 Sep 2024 05:03:17 +0300 Subject: [PATCH 040/851] feat: display progress of downloading chunks visually (#195) --- prismarine-viewer/viewer/lib/mesher/models.ts | 10 +- src/react/AppStatus.tsx | 13 ++- src/react/AppStatusProvider.tsx | 33 ++++-- src/react/DiveTransition.tsx | 12 +- src/react/LoadingChunks.css | 12 ++ src/react/LoadingChunks.stories.tsx | 43 +++++++ src/react/LoadingChunks.tsx | 60 ++++++++++ src/react/PauseScreen.tsx | 108 ++++++++++++++---- src/utils.ts | 9 +- 9 files changed, 263 insertions(+), 37 deletions(-) create mode 100644 src/react/LoadingChunks.css create mode 100644 src/react/LoadingChunks.stories.tsx create mode 100644 src/react/LoadingChunks.tsx diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index 88505c06..8b9115e7 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -282,6 +282,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: const aos: number[] = [] const neighborPos = position.plus(new Vec3(...dir)) + // 10% const baseLight = world.getLight(neighborPos, undefined, undefined, block.name) / 15 for (const pos of corners) { let vertex = [ @@ -290,7 +291,7 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: (pos[2] ? maxz : minz) ] - if (!needTiles) { + if (!needTiles) { // 10% vertex = vecadd3(matmul3(localMatrix, vertex), localShift) vertex = vecadd3(matmul3(globalMatrix, vertex), globalShift) vertex = vertex.map(v => v / 16) @@ -411,7 +412,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { // todo this can be removed here signs: {}, // isFull: true, - highestBlocks: {}, + highestBlocks: {}, // todo migrate to map for 2% boost perf hadErrors: false } @@ -449,7 +450,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { } const biome = block.biome.name - if (world.preflat) { + if (world.preflat) { // 10% perf const patchProperties = preflatBlockCalculation(block, world, cursor) if (patchProperties) { block._originalProperties ??= block._properties @@ -505,6 +506,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { const model = modelVars[useVariant] ?? modelVars[0] if (!model) continue + // #region 10% let globalMatrix = null as any let globalShift = null as any for (const axis of ['x', 'y', 'z'] as const) { @@ -518,6 +520,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { globalShift = [8, 8, 8] globalShift = vecsub3(globalShift, matmul3(globalMatrix, globalShift)) } + // #endregion for (const element of model.elements ?? []) { const ao = model.ao ?? true @@ -527,6 +530,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { renderElement(world, pos, element, ao, attr, globalMatrix, globalShift, block, biome) }) } else { + // 60% renderElement(world, cursor, element, ao, attr, globalMatrix, globalShift, block, biome) } } diff --git a/src/react/AppStatus.tsx b/src/react/AppStatus.tsx index ca83c29a..31f08641 100644 --- a/src/react/AppStatus.tsx +++ b/src/react/AppStatus.tsx @@ -2,8 +2,18 @@ import { useEffect, useState } from 'react' import styles from './appStatus.module.css' import Button from './Button' import Screen from './Screen' +import LoadingChunks from './LoadingChunks' -export default ({ status, isError, hideDots = false, lastStatus = '', backAction = undefined as undefined | (() => void), description = '', actionsSlot = null as React.ReactNode | null }) => { +export default ({ + status, + isError, + hideDots = false, + lastStatus = '', + backAction = undefined as undefined | (() => void), + description = '', + actionsSlot = null as React.ReactNode | null, + children +}) => { const [loadingDots, setLoadingDots] = useState('') useEffect(() => { @@ -52,6 +62,7 @@ export default ({ status, isError, hideDots = false, lastStatus = '', backAction +} + export const tryFindOptionConfig = (option: keyof AppOptions) => { for (const group of Object.values(guiOptionsScheme)) { for (const optionConfig of group) { diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index b7bf4abe..936d5bfc 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -82,6 +82,8 @@ const defaultOptions = { /** Wether to popup sign editor on server action */ autoSignEditor: true, wysiwygSignEditor: 'auto' as 'auto' | 'always' | 'never', + displayBossBars: false, // boss bar overlay was removed for some reason, enable safely + disabledUiParts: [] as string[], } function getDefaultTouchControlsPositions () { diff --git a/src/react/BossBarOverlayProvider.tsx b/src/react/BossBarOverlayProvider.tsx index 9bb7d948..5cac3c8a 100644 --- a/src/react/BossBarOverlayProvider.tsx +++ b/src/react/BossBarOverlayProvider.tsx @@ -1,5 +1,4 @@ import { useState, useEffect } from 'react' -import { BotEvents } from 'mineflayer' import BossBar, { BossBarType } from './BossBarOverlay' import './BossBarOverlay.css' @@ -8,9 +7,8 @@ export default () => { const [bossBars, setBossBars] = useState(new Map()) useEffect(() => { - // typescript error: no bossBarCreated in BotEvents. Why?? - bot.on('bossBarCreated' as keyof BotEvents, (bossBar) => { - setBossBars(prevBossBars => new Map(prevBossBars.set(bossBar.entityUUID, bossBar))) + bot.on('bossBarCreated', (bossBar) => { + setBossBars(prevBossBars => new Map(prevBossBars.set(bossBar.entityUUID, bossBar as any))) }) bot.on('bossBarUpdated', (bossBar) => { setBossBars(prevBossBars => new Map(prevBossBars.set(bossBar.entityUUID, bossBar as BossBarType))) diff --git a/src/react/DebugOverlay.tsx b/src/react/DebugOverlay.tsx index 9315d8a4..23f0d7db 100644 --- a/src/react/DebugOverlay.tsx +++ b/src/react/DebugOverlay.tsx @@ -14,10 +14,17 @@ export default () => { received: {} as { [key: string]: number }, sent: {} as { [key: string]: number } }) + window.packetsCountByNamePerSec = packetsCountByNamePerSec + const packetsCountByNamePer10Sec = useRef({ + received: {} as { [key: string]: number }, + sent: {} as { [key: string]: number } + }) + window.packetsCountByNamePer10Sec = packetsCountByNamePer10Sec const packetsCountByName = useRef({ received: {} as { [key: string]: number }, sent: {} as { [key: string]: number } }) + window.packetsCountByName = packetsCountByName const ignoredPackets = useRef(new Set([] as any[])) const [packetsString, setPacketsString] = useState('') const [showDebug, setShowDebug] = useState(false) @@ -62,9 +69,11 @@ export default () => { const managePackets = (type, name, data) => { packetsCountByName.current[type][name] ??= 0 packetsCountByName.current[type][name]++ + packetsCountByNamePerSec.current[type][name] ??= 0 + packetsCountByNamePerSec.current[type][name]++ + packetsCountByNamePer10Sec.current[type][name] ??= 0 + packetsCountByNamePer10Sec.current[type][name]++ if (options.debugLogNotFrequentPackets && !ignoredPackets.current.has(name) && !hardcodedListOfDebugPacketsToIgnore[type].includes(name)) { - packetsCountByNamePerSec.current[type][name] ??= 0 - packetsCountByNamePerSec.current[type][name]++ if (packetsCountByNamePerSec.current[type][name] > 5 || packetsCountByName.current[type][name] > 100) { // todo think of tracking the count within 10s console.info(`[packet ${name} was ${type} too frequent] Ignoring...`) ignoredPackets.current.add(name) @@ -76,12 +85,17 @@ export default () => { useEffect(() => { document.addEventListener('keydown', handleF3) + let update = 0 const packetsUpdateInterval = setInterval(() => { setPacketsString(`↓ ${received.current.count} (${(received.current.size / 1024).toFixed(2)} KB/s, ${getFixedFilesize(receivedTotal.current)}) ↑ ${sent.current.count}`) received.current = { ...defaultPacketsCount } sent.current = { ...defaultPacketsCount } packetsCountByNamePerSec.current.received = {} packetsCountByNamePerSec.current.sent = {} + if (update++ % 10 === 0) { + packetsCountByNamePer10Sec.current.received = {} + packetsCountByNamePer10Sec.current.sent = {} + } }, 1000) const freqUpdateInterval = setInterval(() => { diff --git a/src/react/IndicatorEffectsProvider.tsx b/src/react/IndicatorEffectsProvider.tsx index 00b78d8a..b82782e6 100644 --- a/src/react/IndicatorEffectsProvider.tsx +++ b/src/react/IndicatorEffectsProvider.tsx @@ -3,6 +3,7 @@ import { useEffect, useMemo } from 'react' import { inGameError } from '../utils' import { fsState } from '../loadSave' import { miscUiState } from '../globalState' +import { options } from '../optionsStorage' import IndicatorEffects, { EffectType, defaultIndicatorsState } from './IndicatorEffects' import { images } from './effectsImages' @@ -52,6 +53,7 @@ const getEffectIndex = (newEffect: EffectType) => { export default () => { const stateIndicators = useSnapshot(state.indicators) const { hasErrors } = useSnapshot(miscUiState) + const { disabledUiParts } = useSnapshot(options) const { isReadonly, openReadOperations, openWriteOperations } = useSnapshot(fsState) const allIndicators: typeof defaultIndicatorsState = { readonlyFiles: isReadonly, diff --git a/src/react/ScoreboardProvider.tsx b/src/react/ScoreboardProvider.tsx index bcae9907..cb67b941 100644 --- a/src/react/ScoreboardProvider.tsx +++ b/src/react/ScoreboardProvider.tsx @@ -10,6 +10,7 @@ export default function ScoreboardProvider () { useMemo(() => { // useMemo instead of useEffect to register them asap and not after the initial dom render const updateSidebarScoreboard = () => { + addStatPerSec('scoreboard') if (bot.scoreboard.sidebar) { setTitle(bot.scoreboard.sidebar.title) setItems([...bot.scoreboard.sidebar.items]) diff --git a/src/reactUi.tsx b/src/reactUi.tsx index 029b2493..599a18f7 100644 --- a/src/reactUi.tsx +++ b/src/reactUi.tsx @@ -42,6 +42,8 @@ import BedTime from './react/BedTime' import NoModalFoundProvider from './react/NoModalFoundProvider' import SignInMessageProvider from './react/SignInMessageProvider' import BookProvider from './react/BookProvider' +import { options } from './optionsStorage' +import BossBarOverlayProvider from './react/BossBarOverlayProvider' const RobustPortal = ({ children, to }) => { return createPortal({children}, to) @@ -99,6 +101,7 @@ const InGameComponent = ({ children }) => { const InGameUi = () => { const { gameLoaded, showUI: showUIRaw } = useSnapshot(miscUiState) + const { disabledUiParts, displayBossBars } = useSnapshot(options) const hasModals = useSnapshot(activeModalStack).length > 0 const showUI = showUIRaw || hasModals if (!gameLoaded || !bot) return @@ -107,26 +110,27 @@ const InGameUi = () => { {/* apply scaling */}
- - - - - + {!disabledUiParts.includes('death-screen') && } + {!disabledUiParts.includes('debug-overlay') && } + {!disabledUiParts.includes('mobile-top-buttons') && } + {!disabledUiParts.includes('players-list') && } + {!disabledUiParts.includes('chat') && } - - - - - + {!disabledUiParts.includes('title') && } + {!disabledUiParts.includes('scoreboard') && } + {!disabledUiParts.includes('effects-indicators') && } + {!disabledUiParts.includes('crosshair') && } + {!disabledUiParts.includes('books') && } + {!disabledUiParts.includes('bossbars') && displayBossBars && }
- - + {!disabledUiParts.includes('xp-bar') && } + {!disabledUiParts.includes('hud-bars') && }
- {showUI && } + {showUI && !disabledUiParts.includes('hotbar') && }
From 1c7fdc21a6b3c997a2cece632bf40184a02e57d7 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 7 Sep 2024 19:33:16 +0300 Subject: [PATCH 053/851] add a way to to disable neighbor chunk updates and all the UI (needed for perf testing) --- .../viewer/lib/worldrendererCommon.ts | 17 ++++++++++------- src/optionsGuiScheme.tsx | 7 ++++--- src/optionsStorage.ts | 1 + src/reactUi.tsx | 2 +- src/watchOptions.ts | 4 ++++ 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index 49de2ca0..92fc9681 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -100,6 +100,7 @@ export abstract class WorldRendererCommon x: number z: number } + neighborChunkUpdates = true abstract outputFormat: 'threeJs' | 'webgpu' @@ -321,7 +322,7 @@ export abstract class WorldRendererCommon for (let y = this.worldConfig.minY; y < this.worldConfig.worldHeight; y += 16) { const loc = new Vec3(x, y, z) this.setSectionDirty(loc) - if (!isLightUpdate || this.mesherConfig.smoothLighting) { + if (this.neighborChunkUpdates && (!isLightUpdate || this.mesherConfig.smoothLighting)) { this.setSectionDirty(loc.offset(-16, 0, 0)) this.setSectionDirty(loc.offset(16, 0, 0)) this.setSectionDirty(loc.offset(0, 0, -16)) @@ -357,12 +358,14 @@ export abstract class WorldRendererCommon worker.postMessage({ type: 'blockUpdate', pos, stateId }) } this.setSectionDirty(pos) - if ((pos.x & 15) === 0) this.setSectionDirty(pos.offset(-16, 0, 0)) - if ((pos.x & 15) === 15) this.setSectionDirty(pos.offset(16, 0, 0)) - if ((pos.y & 15) === 0) this.setSectionDirty(pos.offset(0, -16, 0)) - if ((pos.y & 15) === 15) this.setSectionDirty(pos.offset(0, 16, 0)) - if ((pos.z & 15) === 0) this.setSectionDirty(pos.offset(0, 0, -16)) - if ((pos.z & 15) === 15) this.setSectionDirty(pos.offset(0, 0, 16)) + if (this.neighborChunkUpdates) { + if ((pos.x & 15) === 0) this.setSectionDirty(pos.offset(-16, 0, 0)) + if ((pos.x & 15) === 15) this.setSectionDirty(pos.offset(16, 0, 0)) + if ((pos.y & 15) === 0) this.setSectionDirty(pos.offset(0, -16, 0)) + if ((pos.y & 15) === 15) this.setSectionDirty(pos.offset(0, 16, 0)) + if ((pos.z & 15) === 0) this.setSectionDirty(pos.offset(0, 0, -16)) + if ((pos.z & 15) === 15) this.setSectionDirty(pos.offset(0, 0, 16)) + } } queueAwaited = false diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index fc9e347e..015013d1 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -91,6 +91,7 @@ export const guiOptionsScheme: { tooltip: 'Additional distance to keep the chunks loading before unloading them by marking them as too far', }, handDisplay: {}, + neighborChunkUpdates: {}, }, ], main: [ @@ -429,15 +430,15 @@ const Category = ({ children }) =>
{ const { disabledUiParts } = useSnapshot(options) - const currentlyEnabled = disabledUiParts.includes(name) + const currentlyDisabled = disabledUiParts.includes(name) if (addUiText) label = `${label} UI` return + >{currentlyDisabled ? 'Enable' : 'Disable'} {label} } export const tryFindOptionConfig = (option: keyof AppOptions) => { diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index 936d5bfc..d5a9e5aa 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -84,6 +84,7 @@ const defaultOptions = { wysiwygSignEditor: 'auto' as 'auto' | 'always' | 'never', displayBossBars: false, // boss bar overlay was removed for some reason, enable safely disabledUiParts: [] as string[], + neighborChunkUpdates: true } function getDefaultTouchControlsPositions () { diff --git a/src/reactUi.tsx b/src/reactUi.tsx index 599a18f7..2fb63c19 100644 --- a/src/reactUi.tsx +++ b/src/reactUi.tsx @@ -104,7 +104,7 @@ const InGameUi = () => { const { disabledUiParts, displayBossBars } = useSnapshot(options) const hasModals = useSnapshot(activeModalStack).length > 0 const showUI = showUIRaw || hasModals - if (!gameLoaded || !bot) return + if (!gameLoaded || !bot || disabledUiParts.includes('*')) return return <> diff --git a/src/watchOptions.ts b/src/watchOptions.ts index d26d2b90..926c6be1 100644 --- a/src/watchOptions.ts +++ b/src/watchOptions.ts @@ -62,6 +62,10 @@ export const watchOptionsAfterViewerInit = () => { if (!(viewer.world instanceof WorldRendererThree)) return viewer.world.starField.enabled = o.starfieldRendering }) + + watchValue(options, o => { + viewer.world.neighborChunkUpdates = o.neighborChunkUpdates + }) } let viewWatched = false From a063a0d75b58ecca6d692581aaf47bbc7f051966 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 7 Sep 2024 19:42:50 +0300 Subject: [PATCH 054/851] fix: fix cobblestone_wall and player head (skull) rendering in preflat versions --- prismarine-viewer/viewer/lib/mesher/models.ts | 23 +++++++++++++++--- src/preflatMap.json | 24 +++++++++---------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index 8b9115e7..c7bce0c0 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -479,15 +479,32 @@ export function getSectionGeometry (sx, sy, sz, world: World) { // cache let { models } = block if (block.models === undefined) { + const props = block.getProperties() try { + // fixme + if (world.preflat) { + if (block.name === 'cobblestone_wall') { + props.up = 'true' + for (const key of ['north', 'south', 'east', 'west']) { + const val = props[key] + if (val === 'false' || val === 'true') { + props[key] = val === 'true' ? 'low' : 'none' + } + } + } + } + models = blockProvider.getAllResolvedModels0_1({ name: block.name, - properties: block.getProperties(), + properties: props, })! - if (!models.length) models = null + if (!models.length) { + console.debug('[mesher] block to render not found', block.name, props) + models = null + } } catch (err) { models ??= erroredBlockModel - console.error(`Critical assets error. Unable to get block model for ${block.name}[${JSON.stringify(block.getProperties())}]: ` + err.message, err.stack) + console.error(`Critical assets error. Unable to get block model for ${block.name}[${JSON.stringify(props)}]: ` + err.message, err.stack) attr.hadErrors = true } } diff --git a/src/preflatMap.json b/src/preflatMap.json index fdf2640f..81c2a20a 100644 --- a/src/preflatMap.json +++ b/src/preflatMap.json @@ -925,18 +925,18 @@ "143:11": "oak_button[face=wall,facing=south,powered=true]", "143:12": "oak_button[face=wall,facing=north,powered=true]", "143:13": "oak_button[face=floor,facing=north,powered=true]", - "144:0": "undefined[facing=down,nodrop=false]", - "144:1": "undefined[facing=up,nodrop=false]", - "144:2": "undefined[facing=north,nodrop=false]", - "144:3": "undefined[facing=south,nodrop=false]", - "144:4": "undefined[facing=west,nodrop=false]", - "144:5": "undefined[facing=east,nodrop=false]", - "144:8": "undefined[facing=down,nodrop=true]", - "144:9": "undefined[facing=up,nodrop=true]", - "144:10": "undefined[facing=north,nodrop=true]", - "144:11": "undefined[facing=south,nodrop=true]", - "144:12": "undefined[facing=west,nodrop=true]", - "144:13": "undefined[facing=east,nodrop=true]", + "144:0": "player_head[facing=down]", + "144:1": "player_head[facing=up]", + "144:2": "player_head[facing=north]", + "144:3": "player_head[facing=south]", + "144:4": "player_head[facing=west]", + "144:5": "player_head[facing=east]", + "144:8": "player_head[facing=down]", + "144:9": "player_head[facing=up]", + "144:10": "player_head[facing=north]", + "144:11": "player_head[facing=south]", + "144:12": "player_head[facing=west]", + "144:13": "player_head[facing=east]", "145:0": "anvil[facing=south]", "145:1": "anvil[facing=west]", "145:2": "anvil[facing=north]", From fad9fd6e3a6052e8fe8b291d187e103f5e0b205d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 7 Sep 2024 19:48:08 +0300 Subject: [PATCH 055/851] fix: provide a hack to just render blocks all the blocks even with unknown states for preflat versions --- prismarine-viewer/viewer/lib/mesher/models.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prismarine-viewer/viewer/lib/mesher/models.ts b/prismarine-viewer/viewer/lib/mesher/models.ts index c7bce0c0..e63a92ea 100644 --- a/prismarine-viewer/viewer/lib/mesher/models.ts +++ b/prismarine-viewer/viewer/lib/mesher/models.ts @@ -497,7 +497,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) { models = blockProvider.getAllResolvedModels0_1({ name: block.name, properties: props, - })! + }, world.preflat)! // fixme! this is a hack (also need a setting for all versions) if (!models.length) { console.debug('[mesher] block to render not found', block.name, props) models = null From d743981fc24f8d6f451c940f2b896b9ddb9e2ae9 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 18:20:34 +0300 Subject: [PATCH 056/851] up workflow files: new commands --- .github/workflows/ci.yml | 2 +- .github/workflows/fix-lint.yml | 23 +++++++++++++++++++++++ .github/workflows/merge-next.yml | 24 ++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/fix-lint.yml create mode 100644 .github/workflows/merge-next.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1882914..3649e89e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,6 @@ jobs: name: cypress-images path: cypress/integration/__image_snapshots__/ - run: node scripts/outdatedGitPackages.mjs - if: github.ref == 'refs/heads/next' + if: ${{ github.ref == 'refs/heads/next' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/fix-lint.yml b/.github/workflows/fix-lint.yml new file mode 100644 index 00000000..990de3fa --- /dev/null +++ b/.github/workflows/fix-lint.yml @@ -0,0 +1,23 @@ +name: Fix Lint Command +on: + issue_comment: + types: [created] +jobs: + deploy: + runs-on: ubuntu-latest + if: >- + github.event.issue.pull_request != '' && + ( + contains(github.event.comment.body, '/fix') + ) + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v2 + with: + ref: refs/pull/${{ github.event.issue.number }}/head + - run: pnpm lint --fix + - name: Push Changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml new file mode 100644 index 00000000..c7a66454 --- /dev/null +++ b/.github/workflows/merge-next.yml @@ -0,0 +1,24 @@ +name: Update Base Branch Command +on: + issue_comment: + types: [created] +jobs: + deploy: + runs-on: ubuntu-latest + if: >- + github.event.issue.pull_request != '' && + ( + contains(github.event.comment.body, '/update') + ) + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v2 + with: + ref: refs/pull/${{ github.event.issue.number }}/head + - name: Merge From Next + run: git merge next --strategy-option=theirs + - name: Push Changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} From f9a4960c3182e523e5492172b699691c128e1898 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 18:28:55 +0300 Subject: [PATCH 057/851] ci: try to fix the commands --- .github/workflows/ci.yml | 2 ++ .github/workflows/merge-next.yml | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3649e89e..a57a1646 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,8 @@ jobs: with: name: cypress-images path: cypress/integration/__image_snapshots__/ + - name: print current ref + run: echo ${{ github.ref }} - run: node scripts/outdatedGitPackages.mjs if: ${{ github.ref == 'refs/heads/next' }} env: diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index c7a66454..8bc34607 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -15,9 +15,11 @@ jobs: steps: - uses: actions/checkout@v2 with: - ref: refs/pull/${{ github.event.issue.number }}/head + fetch-depth: 0 # Fetch all history so we can merge branches + - name: Fetch All Branches + run: git fetch --all - name: Merge From Next - run: git merge next --strategy-option=theirs + run: git merge origin/next --strategy-option=theirs - name: Push Changes uses: ad-m/github-push-action@master with: From e89196041e6eb9d7e998de603fc100619f47811f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 18:37:06 +0300 Subject: [PATCH 058/851] ci: update commands --- .github/workflows/ci.yml | 4 ++-- .github/workflows/merge-next.yml | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a57a1646..29b2f2d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,8 +37,8 @@ jobs: name: cypress-images path: cypress/integration/__image_snapshots__/ - name: print current ref - run: echo ${{ github.ref }} + run: echo ${{ github.event.pull_request.base.ref }} - run: node scripts/outdatedGitPackages.mjs - if: ${{ github.ref == 'refs/heads/next' }} + if: ${{ github.event.pull_request.base.ref == 'next' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index 8bc34607..c2e5ddf3 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -21,6 +21,4 @@ jobs: - name: Merge From Next run: git merge origin/next --strategy-option=theirs - name: Push Changes - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} + run: git push From f518dce04d0588d7fc580a37846f810a53bdb2eb Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 18:39:13 +0300 Subject: [PATCH 059/851] ci: checkout pr before merge --- .github/workflows/merge-next.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index c2e5ddf3..0b95e51e 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -18,6 +18,8 @@ jobs: fetch-depth: 0 # Fetch all history so we can merge branches - name: Fetch All Branches run: git fetch --all + - name: Checkout PR + run: git checkout ${{ refs/pull/${{ github.event.issue.number }}/head }} - name: Merge From Next run: git merge origin/next --strategy-option=theirs - name: Push Changes From a3ef16a81a5b385135ddd98ea458773547125c3f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 18:40:37 +0300 Subject: [PATCH 060/851] ci: checkout pr before merge --- .github/workflows/merge-next.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index 0b95e51e..bd84fb02 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -19,7 +19,7 @@ jobs: - name: Fetch All Branches run: git fetch --all - name: Checkout PR - run: git checkout ${{ refs/pull/${{ github.event.issue.number }}/head }} + run: git checkout ${{ github.event.issue.pull_request.head.ref }} - name: Merge From Next run: git merge origin/next --strategy-option=theirs - name: Push Changes From ad8dc1a21a64880d6f6900d4201a460d53d129aa Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 8 Sep 2024 21:03:11 +0300 Subject: [PATCH 061/851] fix: fix compatibility with some versions of new region format files --- pnpm-lock.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 84ea8fa1..bb94a569 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -160,7 +160,7 @@ importers: version: 6.1.1 prismarine-provider-anvil: specifier: github:zardoy/prismarine-provider-anvil#everything - version: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/0228b5252f48a0d6ad7f36d7189851c427fbe8c4(minecraft-data@3.65.0) + version: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699(minecraft-data@3.65.0) prosemirror-example-setup: specifier: ^1.2.2 version: 1.2.2 @@ -7514,8 +7514,8 @@ packages: prismarine-physics@1.8.0: resolution: {integrity: sha512-gbM+S+bmVtOKVv+Z0WGaHMeEeBHISIDsRDRlv8sr0dex3ZJRhuq8djA02CBreguXtI18ZKh6q3TSj2qDr45NHA==} - prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/0228b5252f48a0d6ad7f36d7189851c427fbe8c4: - resolution: {tarball: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/0228b5252f48a0d6ad7f36d7189851c427fbe8c4} + prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699: + resolution: {tarball: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699} version: 2.8.0 prismarine-realms@1.3.2: @@ -13385,7 +13385,7 @@ snapshots: prismarine-entity: 2.3.1 prismarine-item: 1.14.0 prismarine-nbt: 2.5.0 - prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/0228b5252f48a0d6ad7f36d7189851c427fbe8c4(minecraft-data@3.65.0) + prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699(minecraft-data@3.65.0) prismarine-windows: 2.9.0 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/187a87f6d71cba12881a7bbaa510ed9085bf6da7 rambda: 9.2.0 @@ -18623,7 +18623,7 @@ snapshots: prismarine-nbt: 2.5.0 vec3: 0.1.8 - prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/0228b5252f48a0d6ad7f36d7189851c427fbe8c4(minecraft-data@3.65.0): + prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/a3f462dc81ded5b46e88e3442f99aadf35f7f699(minecraft-data@3.65.0): dependencies: prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/a69b66ab1e4be6b67f25a5a6db15e0ad39e11819 prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.65.0) From 3fb872129ed98b677cc4190e332237309f53f771 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 10 Sep 2024 00:29:30 +0300 Subject: [PATCH 062/851] fix: update autojump module --- .github/workflows/merge-next.yml | 1 + package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index bd84fb02..9bed1b3d 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -12,6 +12,7 @@ jobs: ) permissions: pull-requests: write + contents: write steps: - uses: actions/checkout@v2 with: diff --git a/package.json b/package.json index 80c6fdac..0f72fefc 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@dimaka/interface": "0.0.3-alpha.0", "@floating-ui/react": "^0.26.1", "@mui/base": "5.0.0-beta.40", - "@nxg-org/mineflayer-auto-jump": "^0.7.7", + "@nxg-org/mineflayer-auto-jump": "^0.7.8", "@nxg-org/mineflayer-tracker": "^1.2.1", "@react-oauth/google": "^0.12.1", "@stylistic/eslint-plugin": "^2.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bb94a569..987a9dd3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,8 +45,8 @@ importers: specifier: 5.0.0-beta.40 version: 5.0.0-beta.40(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@nxg-org/mineflayer-auto-jump': - specifier: ^0.7.7 - version: 0.7.7 + specifier: ^0.7.8 + version: 0.7.8 '@nxg-org/mineflayer-tracker': specifier: ^1.2.1 version: 1.2.1 @@ -2110,8 +2110,8 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This functionality has been moved to @npmcli/fs - '@nxg-org/mineflayer-auto-jump@0.7.7': - resolution: {integrity: sha512-50FYsz5rxBuLzOh7wqmg9iN9zdVGD+QjuaPcw/mD7q8Bq6Bq+o1/DfXfpoNGIHaDag80q6FJSpc73MI3Scid8g==} + '@nxg-org/mineflayer-auto-jump@0.7.8': + resolution: {integrity: sha512-o3XVruz2siApRvJKMe9EjQYTMANTMhStM3mRUKpZ1ar/2QqJ6sgyqEZTD9sE/zNA7bcjr49sZfuB8bX4t07Hww==} '@nxg-org/mineflayer-physics-util@1.5.8': resolution: {integrity: sha512-KmCkAqpUo8BbuRdIBs6+V2hWHehz++PRz3lRwIsb47CuG0u4sgLYh37RY3ifAznC6uWvmPK+q3B4ZXwJzPy1MQ==} @@ -11514,7 +11514,7 @@ snapshots: rimraf: 3.0.2 optional: true - '@nxg-org/mineflayer-auto-jump@0.7.7': + '@nxg-org/mineflayer-auto-jump@0.7.8': dependencies: '@nxg-org/mineflayer-physics-util': 1.5.8 strict-event-emitter-types: 2.0.0 From c6c25a7bb94d9c20e48f9852c77831a860c1fc15 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 10 Sep 2024 00:52:39 +0300 Subject: [PATCH 063/851] up again --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 0f72fefc..dce99824 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@dimaka/interface": "0.0.3-alpha.0", "@floating-ui/react": "^0.26.1", "@mui/base": "5.0.0-beta.40", - "@nxg-org/mineflayer-auto-jump": "^0.7.8", + "@nxg-org/mineflayer-auto-jump": "^0.7.11", "@nxg-org/mineflayer-tracker": "^1.2.1", "@react-oauth/google": "^0.12.1", "@stylistic/eslint-plugin": "^2.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 987a9dd3..df10d754 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,8 +45,8 @@ importers: specifier: 5.0.0-beta.40 version: 5.0.0-beta.40(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@nxg-org/mineflayer-auto-jump': - specifier: ^0.7.8 - version: 0.7.8 + specifier: ^0.7.11 + version: 0.7.11 '@nxg-org/mineflayer-tracker': specifier: ^1.2.1 version: 1.2.1 @@ -2110,8 +2110,8 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This functionality has been moved to @npmcli/fs - '@nxg-org/mineflayer-auto-jump@0.7.8': - resolution: {integrity: sha512-o3XVruz2siApRvJKMe9EjQYTMANTMhStM3mRUKpZ1ar/2QqJ6sgyqEZTD9sE/zNA7bcjr49sZfuB8bX4t07Hww==} + '@nxg-org/mineflayer-auto-jump@0.7.11': + resolution: {integrity: sha512-ex6lYch+YXXZKs/TGIMkspZqWTZ3pkteX4ZZHnrx1D3Yw8xfLaeU/lZ4O/8lH2uInuZbsx5pLKtwChOwKmJTlg==} '@nxg-org/mineflayer-physics-util@1.5.8': resolution: {integrity: sha512-KmCkAqpUo8BbuRdIBs6+V2hWHehz++PRz3lRwIsb47CuG0u4sgLYh37RY3ifAznC6uWvmPK+q3B4ZXwJzPy1MQ==} @@ -11514,7 +11514,7 @@ snapshots: rimraf: 3.0.2 optional: true - '@nxg-org/mineflayer-auto-jump@0.7.8': + '@nxg-org/mineflayer-auto-jump@0.7.11': dependencies: '@nxg-org/mineflayer-physics-util': 1.5.8 strict-event-emitter-types: 2.0.0 From a5dddfaad53ee94769a102ae662e1d2449254768 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 10 Sep 2024 01:20:39 +0300 Subject: [PATCH 064/851] up mineflayer-auto-jump --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index dce99824..0eb733fb 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@dimaka/interface": "0.0.3-alpha.0", "@floating-ui/react": "^0.26.1", "@mui/base": "5.0.0-beta.40", - "@nxg-org/mineflayer-auto-jump": "^0.7.11", + "@nxg-org/mineflayer-auto-jump": "^0.7.12", "@nxg-org/mineflayer-tracker": "^1.2.1", "@react-oauth/google": "^0.12.1", "@stylistic/eslint-plugin": "^2.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df10d754..31adde17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,8 +45,8 @@ importers: specifier: 5.0.0-beta.40 version: 5.0.0-beta.40(@types/react@18.2.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@nxg-org/mineflayer-auto-jump': - specifier: ^0.7.11 - version: 0.7.11 + specifier: ^0.7.12 + version: 0.7.12 '@nxg-org/mineflayer-tracker': specifier: ^1.2.1 version: 1.2.1 @@ -2110,8 +2110,8 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This functionality has been moved to @npmcli/fs - '@nxg-org/mineflayer-auto-jump@0.7.11': - resolution: {integrity: sha512-ex6lYch+YXXZKs/TGIMkspZqWTZ3pkteX4ZZHnrx1D3Yw8xfLaeU/lZ4O/8lH2uInuZbsx5pLKtwChOwKmJTlg==} + '@nxg-org/mineflayer-auto-jump@0.7.12': + resolution: {integrity: sha512-F5vX/lerlWx/5HVlkDNbvrtQ19PL6iG8i4ItPTIRtjGiFzusDefP7DI226zSFR8Wlaw45qHv0jn814p/4/qVdQ==} '@nxg-org/mineflayer-physics-util@1.5.8': resolution: {integrity: sha512-KmCkAqpUo8BbuRdIBs6+V2hWHehz++PRz3lRwIsb47CuG0u4sgLYh37RY3ifAznC6uWvmPK+q3B4ZXwJzPy1MQ==} @@ -11514,7 +11514,7 @@ snapshots: rimraf: 3.0.2 optional: true - '@nxg-org/mineflayer-auto-jump@0.7.11': + '@nxg-org/mineflayer-auto-jump@0.7.12': dependencies: '@nxg-org/mineflayer-physics-util': 1.5.8 strict-event-emitter-types: 2.0.0 From 2c971f331ed61c194ec6adf3b3c76025e63d62ab Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 10 Sep 2024 01:34:26 +0300 Subject: [PATCH 065/851] fix: update entities tracker which should fix playing walking animations when players are standing still --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 0eb733fb..c0ac0cc1 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@floating-ui/react": "^0.26.1", "@mui/base": "5.0.0-beta.40", "@nxg-org/mineflayer-auto-jump": "^0.7.12", - "@nxg-org/mineflayer-tracker": "^1.2.1", + "@nxg-org/mineflayer-tracker": "^1.2.3", "@react-oauth/google": "^0.12.1", "@stylistic/eslint-plugin": "^2.6.1", "@types/gapi": "^0.0.47", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31adde17..05b83f3c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,8 +48,8 @@ importers: specifier: ^0.7.12 version: 0.7.12 '@nxg-org/mineflayer-tracker': - specifier: ^1.2.1 - version: 1.2.1 + specifier: ^1.2.3 + version: 1.2.3 '@react-oauth/google': specifier: ^0.12.1 version: 0.12.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -2116,8 +2116,8 @@ packages: '@nxg-org/mineflayer-physics-util@1.5.8': resolution: {integrity: sha512-KmCkAqpUo8BbuRdIBs6+V2hWHehz++PRz3lRwIsb47CuG0u4sgLYh37RY3ifAznC6uWvmPK+q3B4ZXwJzPy1MQ==} - '@nxg-org/mineflayer-tracker@1.2.1': - resolution: {integrity: sha512-SI1ffF8zvg3/ZNE021Ja2W0FZPN+WbQDZf8yFqOcXtPRXAtM9W6HvoACdzXep8BZid7WYgYLIgjKpB+9RqvCNQ==} + '@nxg-org/mineflayer-tracker@1.2.3': + resolution: {integrity: sha512-E7Ik/scU117Rr6kQUHHMBk8qOGh63YlTCGN33jMfeP7L8xmLeSHN3JtV/fbog8Y+R+HgO99yfZiRAaV7z1T6gQ==} '@nxg-org/mineflayer-trajectories@1.1.1': resolution: {integrity: sha512-X103KXlX8+L3uMeK4jQxMUdTizv01sQRSfBizAF/iOAdfQZehRLXr3CYKeJzfwPYGLN0X0JCl++cMEcZVn4vbg==} @@ -11523,7 +11523,7 @@ snapshots: dependencies: '@nxg-org/mineflayer-util-plugin': 1.8.3 - '@nxg-org/mineflayer-tracker@1.2.1': + '@nxg-org/mineflayer-tracker@1.2.3': dependencies: '@nxg-org/mineflayer-trajectories': 1.1.1 '@nxg-org/mineflayer-util-plugin': 1.8.3 From 18bf1aa80a8c8bcbe472f5a6ae803bccdff31adf Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 10 Sep 2024 20:00:09 +0300 Subject: [PATCH 066/851] feat: The commit also adds a new keybind action for the 'F4' key, allowing the user to cycle through different game modes. Depending on the current game mode, the bot's chat command is updated accordingly. --- src/controls.ts | 70 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/src/controls.ts b/src/controls.ts index 39e4c8c7..d882e967 100644 --- a/src/controls.ts +++ b/src/controls.ts @@ -291,6 +291,27 @@ const alwaysPressedHandledCommand = (command: Command) => { hideCurrentModal() } } + if (command === 'advanced.lockUrl') { + lockUrl() + } +} + +function lockUrl () { + let newQs = '' + if (fsState.saveLoaded) { + const save = localServer!.options.worldFolder.split('/').at(-1) + newQs = `loadSave=${save}` + } else if (process.env.NODE_ENV === 'development') { + newQs = `reconnect=1` + } else { + const qs = new URLSearchParams() + const { server, version } = localStorage + qs.set('server', server) + if (version) qs.set('version', version) + newQs = String(qs.toString()) + } + + window.history.replaceState({}, '', `${window.location.pathname}?${newQs}`) } function cycleHotbarSlot (dir: 1 | -1) { @@ -390,24 +411,6 @@ contro.on('trigger', ({ command }) => { break } } - if (command === 'advanced.lockUrl') { - let newQs = '' - if (fsState.saveLoaded) { - const save = localServer!.options.worldFolder.split('/').at(-1) - newQs = `loadSave=${save}` - } else if (process.env.NODE_ENV === 'development') { - newQs = `reconnect=1` - } else { - const qs = new URLSearchParams() - const { server, version } = localStorage - qs.set('server', server) - if (version) qs.set('version', version) - newQs = String(qs.toString()) - } - - window.history.replaceState({}, '', `${window.location.pathname}?${newQs}`) - // return - } if (command === 'ui.pauseMenu') { showModal({ reactType: 'pause-screen' }) @@ -472,7 +475,36 @@ export const f3Keybinds = [ await completeTexturePackInstall('default', 'default') } }, - mobileTitle: 'Open Widget' + mobileTitle: 'Reload Textures' + }, + { + key: 'F4', + async action () { + switch (bot.game.gameMode) { + case 'creative': { + bot.chat('/gamemode survival') + + break + } + case 'survival': { + bot.chat('/gamemode adventure') + + break + } + case 'adventure': { + bot.chat('/gamemode spectator') + + break + } + case 'spectator': { + bot.chat('/gamemode creative') + + break + } + // No default + } + }, + mobileTitle: 'Cycle Game Mode' } ] From f9b87d5087629363c94008976b6a372429d9d2c9 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 01:07:46 +0300 Subject: [PATCH 067/851] move playground to rsbuild! now fast reloads! reload on workers change fix cd --- .github/workflows/next-deploy.yml | 5 +- .github/workflows/preview.yml | 5 +- .github/workflows/publish.yml | 5 +- package.json | 13 ++- prismarine-viewer/buildMesherWorker.mjs | 8 +- prismarine-viewer/esbuild.mjs | 96 ---------------------- prismarine-viewer/playground.html | 2 +- prismarine-viewer/rsbuild.config.ts | 37 +++++++++ prismarine-viewer/rsbuildSharedConfig.ts | 100 +++++++++++++++++++++++ rsbuild.config.ts | 84 +++---------------- 10 files changed, 171 insertions(+), 184 deletions(-) delete mode 100644 prismarine-viewer/esbuild.mjs create mode 100644 prismarine-viewer/rsbuild.config.ts create mode 100644 prismarine-viewer/rsbuildSharedConfig.ts diff --git a/.github/workflows/next-deploy.yml b/.github/workflows/next-deploy.yml index f6915839..a01e0bb8 100644 --- a/.github/workflows/next-deploy.yml +++ b/.github/workflows/next-deploy.yml @@ -24,7 +24,10 @@ jobs: run: vercel build --token=${{ secrets.VERCEL_TOKEN }} - run: pnpm build-storybook - name: Copy playground files - run: pnpm build-playground && cp prismarine-viewer/public/index.html .vercel/output/static/playground.html && cp prismarine-viewer/public/playground.js .vercel/output/static/playground.js + run: | + mkdir -p .vercel/output/static/playground + pnpm build-playground + cp -r prismarine-viewer/dist/* .vercel/output/static/playground/ - name: Download Generated Sounds map run: node scripts/downloadSoundsMap.mjs - name: Deploy Project Artifacts to Vercel diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index e0f9736a..c074d2e5 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -35,7 +35,10 @@ jobs: run: vercel build --token=${{ secrets.VERCEL_TOKEN }} - run: pnpm build-storybook - name: Copy playground files - run: pnpm build-playground && cp prismarine-viewer/public/index.html .vercel/output/static/playground.html && cp prismarine-viewer/public/playground.js .vercel/output/static/playground.js + run: | + mkdir -p .vercel/output/static/playground + pnpm build-playground + cp -r prismarine-viewer/dist/* .vercel/output/static/playground/ - name: Download Generated Sounds map run: node scripts/downloadSoundsMap.mjs - name: Deploy Project Artifacts to Vercel diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9472118b..fa1f0281 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,10 @@ jobs: - run: vercel build --token=${{ secrets.VERCEL_TOKEN }} --prod - run: pnpm build-storybook - name: Copy playground files - run: pnpm build-playground && cp prismarine-viewer/public/index.html .vercel/output/static/playground.html && cp prismarine-viewer/public/playground.js .vercel/output/static/playground.js + run: | + mkdir -p .vercel/output/static/playground + pnpm build-playground + cp -r prismarine-viewer/dist/* .vercel/output/static/playground/ - name: Download Generated Sounds map run: node scripts/downloadSoundsMap.mjs - name: Deploy Project to Vercel diff --git a/package.json b/package.json index c0ac0cc1..d715016a 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,8 @@ "dev-rsbuild": "rsbuild dev", "dev-proxy": "node server.js", "start": "run-p dev-rsbuild dev-proxy watch-mesher", - "start-watch-script": "nodemon -w rsbuild.config.ts --watch", - "build": "rsbuild build", - "build-analyze": "BUNDLE_ANALYZE=true rsbuild build", + "build": "pnpm build-other-workers && rsbuild build", + "build-analyze": "BUNDLE_ANALYZE=true rsbuild build && pnpm build-other-workers", "check-build": "tsx scripts/genShims.ts && tsc && pnpm build", "test:cypress": "cypress run", "test-unit": "vitest", @@ -20,13 +19,13 @@ "build-storybook": "storybook build && node scripts/build.js moveStorybookFiles", "start-experiments": "vite --config experiments/vite.config.ts --host", "watch-other-workers": "echo NOT IMPLEMENTED", + "build-other-workers": "echo NOT IMPLEMENTED", "build-mesher": "node prismarine-viewer/buildMesherWorker.mjs", "watch-mesher": "pnpm build-mesher -w", - "run-playground": "run-p watch-mesher watch-other-workers playground-server watch-playground", + "run-playground": "run-p watch-mesher watch-other-workers watch-playground", "run-all": "run-p start run-playground", - "playground-server": "live-server --port=9090 prismarine-viewer/public", - "build-playground": "node prismarine-viewer/esbuild.mjs", - "watch-playground": "node prismarine-viewer/esbuild.mjs -w" + "build-playground": "rsbuild build --root ./prismarine-viewer", + "watch-playground": "rsbuild dev --root ./prismarine-viewer" }, "keywords": [ "prismarine", diff --git a/prismarine-viewer/buildMesherWorker.mjs b/prismarine-viewer/buildMesherWorker.mjs index f70b8128..03b952b4 100644 --- a/prismarine-viewer/buildMesherWorker.mjs +++ b/prismarine-viewer/buildMesherWorker.mjs @@ -30,7 +30,7 @@ const buildOptions = { sourcemap: 'linked', write: false, metafile: true, - outdir: path.join(__dirname, './public'), + outdir: path.join(__dirname, './dist'), define: { 'process.env.BROWSER': '"true"', }, @@ -108,9 +108,9 @@ const buildOptions = { }) build.onEnd(({ metafile, outputFiles }) => { if (!metafile) return - fs.mkdirSync(path.join(__dirname, './public'), { recursive: true }) - fs.writeFileSync(path.join(__dirname, './public/metafile.json'), JSON.stringify(metafile)) - for (const outDir of ['../dist/', './public/']) { + fs.mkdirSync(path.join(__dirname, './dist'), { recursive: true }) + fs.writeFileSync(path.join(__dirname, './dist/metafile.json'), JSON.stringify(metafile)) + for (const outDir of ['../dist/', './dist/']) { for (const outputFile of outputFiles) { if (outDir === '../dist/' && outputFile.path.endsWith('.map')) { // skip writing & browser loading sourcemap there, worker debugging should be done in playground diff --git a/prismarine-viewer/esbuild.mjs b/prismarine-viewer/esbuild.mjs deleted file mode 100644 index e410c8b6..00000000 --- a/prismarine-viewer/esbuild.mjs +++ /dev/null @@ -1,96 +0,0 @@ -//@ts-check -import * as fs from 'fs' -import fsExtra from 'fs-extra' - -import * as esbuild from 'esbuild' -import { polyfillNode } from 'esbuild-plugin-polyfill-node' -import path, { dirname, join } from 'path' -import { fileURLToPath } from 'url' -import childProcess from 'child_process' -import supportedVersions from '../src/supportedVersions.mjs' - -const dev = process.argv.includes('-w') - -const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url))) - -const mcDataPath = join(__dirname, '../generated/minecraft-data-optimized.json') -if (!fs.existsSync(mcDataPath)) { - childProcess.execSync('tsx ../scripts/makeOptimizedMcData.mjs', { stdio: 'inherit', cwd: __dirname }) -} - -fs.copyFileSync(join(__dirname, 'playground.html'), join(__dirname, 'public/index.html')) - -/** @type {import('esbuild').BuildOptions} */ -const buildOptions = { - bundle: true, - entryPoints: [join(__dirname, './examples/playground.ts')], - // target: ['es2020'], - // logLevel: 'debug', - logLevel: 'info', - platform: 'browser', - sourcemap: dev ? 'inline' : false, - minify: !dev, - outfile: join(__dirname, 'public/playground.js'), - mainFields: [ - 'browser', 'module', 'main' - ], - keepNames: true, - banner: { - js: `globalThis.global = globalThis;globalThis.includedVersions = ${JSON.stringify(supportedVersions)};`, - }, - alias: { - events: 'events', - buffer: 'buffer', - 'fs': 'browserfs/dist/shims/fs.js', - http: 'http-browserify', - stream: 'stream-browserify', - net: 'net-browserify', - // 'mc-assets': '/Users/vitaly/Documents/mc-assets', - }, - inject: [], - metafile: true, - loader: { - '.png': 'dataurl', - '.obj': 'text', - }, - plugins: [ - { - name: 'minecraft-data', - setup(build) { - build.onLoad({ - filter: /minecraft-data[\/\\]data.js$/, - }, () => { - const defaultVersionsObj = {} - return { - contents: fs.readFileSync(join(__dirname, '../src/shims/minecraftData.ts'), 'utf8'), - loader: 'ts', - resolveDir: join(__dirname, '../src/shims'), - } - }) - build.onEnd((e) => { - if (e.errors.length) return - fs.writeFileSync(join(__dirname, './public/metafile.json'), JSON.stringify(e.metafile), 'utf8') - }) - } - }, - polyfillNode({ - polyfills: { - fs: false, - crypto: false, - events: false, - http: false, - stream: false, - buffer: false, - perf_hooks: false, - net: false, - }, - }) - ], -} -if (dev) { - (await esbuild.context(buildOptions)).watch() -} else { - await esbuild.build(buildOptions) -} - -// await ctx.rebuild() diff --git a/prismarine-viewer/playground.html b/prismarine-viewer/playground.html index f1b36015..92713c1c 100644 --- a/prismarine-viewer/playground.html +++ b/prismarine-viewer/playground.html @@ -31,6 +31,6 @@ - +
diff --git a/prismarine-viewer/rsbuild.config.ts b/prismarine-viewer/rsbuild.config.ts new file mode 100644 index 00000000..1299670d --- /dev/null +++ b/prismarine-viewer/rsbuild.config.ts @@ -0,0 +1,37 @@ +import { defineConfig, mergeRsbuildConfig } from '@rsbuild/core'; +import supportedVersions from '../src/supportedVersions.mjs' +import childProcess from 'child_process' +import path, { dirname, join } from 'path' +import { pluginReact } from '@rsbuild/plugin-react'; +import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill'; +import fs from 'fs' +import { appAndRendererSharedConfig, rspackViewerConfig } from './rsbuildSharedConfig'; + +const mcDataPath = join(__dirname, '../generated/minecraft-data-optimized.json') + +if (!fs.existsSync(mcDataPath)) { + childProcess.execSync('tsx ./scripts/makeOptimizedMcData.mjs', { stdio: 'inherit', cwd: path.join(__dirname, '..') }) +} + +export default mergeRsbuildConfig( + appAndRendererSharedConfig(), + defineConfig({ + html: { + template: './playground.html', + }, + output: { + cleanDistPath: false, + }, + server: { + port: 9090, + }, + source: { + entry: { + index: join(__dirname, './examples/playground.ts') + }, + define: { + 'globalThis.includedVersions': JSON.stringify(supportedVersions), + }, + }, + }) +) diff --git a/prismarine-viewer/rsbuildSharedConfig.ts b/prismarine-viewer/rsbuildSharedConfig.ts new file mode 100644 index 00000000..3fba1ef1 --- /dev/null +++ b/prismarine-viewer/rsbuildSharedConfig.ts @@ -0,0 +1,100 @@ +import { defineConfig, ModifyRspackConfigUtils } from '@rsbuild/core'; +import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill'; +import { pluginReact } from '@rsbuild/plugin-react'; +import path from 'path' + +export const appAndRendererSharedConfig = () => defineConfig({ + dev: { + progressBar: true, + writeToDisk: true, + watchFiles: { + paths: [ + path.join(__dirname, './dist/webgpuRendererWorker.js'), + path.join(__dirname, './dist/mesher.js'), + ] + }, + }, + output: { + polyfill: 'usage', + // 50kb limit for data uri + dataUriLimit: 50 * 1024 + }, + source: { + alias: { + fs: path.join(__dirname, `../src/shims/fs.js`), + http: 'http-browserify', + stream: 'stream-browserify', + net: 'net-browserify', + 'minecraft-protocol$': 'minecraft-protocol/src/index.js', + 'buffer$': 'buffer', + // avoid bundling, not used on client side + 'prismarine-auth': path.join(__dirname, `../src/shims/prismarineAuthReplacement.ts`), + perf_hooks: path.join(__dirname, `../src/shims/perf_hooks_replacement.js`), + crypto: path.join(__dirname, `../src/shims/crypto.js`), + dns: path.join(__dirname, `../src/shims/dns.js`), + yggdrasil: path.join(__dirname, `../src/shims/yggdrasilReplacement.ts`), + 'three$': 'three/src/Three.js', + 'stats.js$': 'stats.js/src/Stats.js', + }, + define: { + 'process.platform': '"browser"', + }, + decorators: { + version: 'legacy', // default is a lie + }, + }, + server: { + htmlFallback: false, + publicDir: false, + headers: { + // enable shared array buffer + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'require-corp', + }, + open: process.env.OPEN_BROWSER === 'true', + }, + plugins: [ + pluginReact(), + pluginNodePolyfill() + ], + tools: { + rspack (config, helpers) { + rspackViewerConfig(config, helpers) + } + }, +}) + +export const rspackViewerConfig = (config, { appendPlugins, addRules, rspack }: ModifyRspackConfigUtils) => { + appendPlugins(new rspack.NormalModuleReplacementPlugin(/data/, (resource) => { + let absolute: string + const request = resource.request.replaceAll('\\', '/') + absolute = path.join(resource.context, request).replaceAll('\\', '/') + if (request.includes('minecraft-data/data/pc/1.')) { + console.log('Error: incompatible resource', request, resource.contextInfo.issuer) + process.exit(1) + // throw new Error(`${resource.request} was requested by ${resource.contextInfo.issuer}`) + } + if (absolute.endsWith('/minecraft-data/data.js')) { + resource.request = path.join(__dirname, `../src/shims/minecraftData.ts`) + } + })) + addRules([ + { + test: /\.obj$/, + type: 'asset/source', + }, + { + test: /\.wgsl$/, + type: 'asset/source', + }, + { + test: /\.mp3$/, + type: 'asset/source', + } + ]) + config.ignoreWarnings = [ + /the request of a dependency is an expression/, + /Unsupported pseudo class or element: xr-overlay/ + ] + +} diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 588cc35d..f8a965dc 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, RsbuildPluginAPI } from '@rsbuild/core' +import { defineConfig, mergeRsbuildConfig, RsbuildPluginAPI } from '@rsbuild/core' import { pluginReact } from '@rsbuild/plugin-react' import { pluginTypedCSSModules } from '@rsbuild/plugin-typed-css-modules' import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill' @@ -10,6 +10,7 @@ import fsExtra from 'fs-extra' import { promisify } from 'util' import { generateSW } from 'workbox-build' import { getSwAdditionalEntries } from './scripts/build' +import { appAndRendererSharedConfig } from './prismarine-viewer/rsbuildSharedConfig' //@ts-ignore try { require('./localSettings.js') } catch { } @@ -20,16 +21,12 @@ const buildingVersion = new Date().toISOString().split(':')[0] const dev = process.env.NODE_ENV === 'development' -export default defineConfig({ - dev: { - progressBar: true, - writeToDisk: true - }, +// base options are in ./prismarine-viewer/rsbuildSharedConfig.ts +const appConfig = defineConfig({ html: { template: './index.html', }, output: { - polyfill: 'usage', externals: [ 'sharp' ], @@ -37,25 +34,8 @@ export default defineConfig({ js: 'source-map', css: true, }, - // 50kb limit for data uri - dataUriLimit: 50 * 1024 }, source: { - alias: { - fs: './src/shims/fs.js', - http: 'http-browserify', - stream: 'stream-browserify', - net: 'net-browserify', - 'minecraft-protocol$': 'minecraft-protocol/src/index.js', - 'buffer$': 'buffer', - // avoid bundling, not used on client side - 'prismarine-auth': './src/shims/prismarineAuthReplacement.ts', - perf_hooks: './src/shims/perf_hooks_replacement.js', - crypto: './src/shims/crypto.js', - dns: './src/shims/dns.js', - yggdrasil: './src/shims/yggdrasilReplacement.ts', - 'three$': 'three/src/Three.js' - }, entry: { index: './src/index.ts', }, @@ -65,36 +45,22 @@ export default defineConfig({ define: { 'process.env.BUILD_VERSION': JSON.stringify(!dev ? buildingVersion : 'undefined'), 'process.env.MAIN_MENU_LINKS': JSON.stringify(process.env.MAIN_MENU_LINKS), - 'process.platform': '"browser"', 'process.env.GITHUB_URL': JSON.stringify(`https://github.com/${process.env.GITHUB_REPOSITORY || `${process.env.VERCEL_GIT_REPO_OWNER}/${process.env.VERCEL_GIT_REPO_SLUG}`}`), 'process.env.DEPS_VERSIONS': JSON.stringify({}) }, - decorators: { - version: 'legacy', // default is a lie - }, }, server: { // strictPort: true, - htmlFallback: false, - publicDir: false, // publicDir: { // name: 'assets', // }, - headers: { - // enable shared array buffer - 'Cross-Origin-Opener-Policy': 'same-origin', - 'Cross-Origin-Embedder-Policy': 'require-corp', - }, - open: process.env.OPEN_BROWSER === 'true', proxy: { '/api': 'http://localhost:8080', }, }, plugins: [ - pluginReact(), pluginTypedCSSModules(), - pluginNodePolyfill(), { name: 'test', setup (build: RsbuildPluginAPI) { @@ -121,9 +87,9 @@ export default defineConfig({ // childProcess.execSync('./scripts/prepareSounds.mjs', { stdio: 'inherit' }) // childProcess.execSync('tsx ./scripts/genMcDataTypes.ts', { stdio: 'inherit' }) // childProcess.execSync('tsx ./scripts/genPixelartTypes.ts', { stdio: 'inherit' }) - if (fs.existsSync('./prismarine-viewer/public/mesher.js') && dev) { + if (fs.existsSync('./prismarine-viewer/dist/mesher.js') && dev) { // copy mesher - fs.copyFileSync('./prismarine-viewer/public/mesher.js', './dist/mesher.js') + fs.copyFileSync('./prismarine-viewer/dist/mesher.js', './dist/mesher.js') } else if (!dev) { await execAsync('pnpm run build-mesher') } @@ -150,39 +116,6 @@ export default defineConfig({ }, }, ], - tools: { - bundlerChain (chain, { CHAIN_ID }) { - }, - rspack (config, { addRules, appendPlugins, rspack }) { - appendPlugins(new rspack.NormalModuleReplacementPlugin(/data/, (resource) => { - let absolute: string - const request = resource.request.replaceAll('\\', '/') - absolute = path.join(resource.context, request).replaceAll('\\', '/') - if (request.includes('minecraft-data/data/pc/1.')) { - console.log('Error: incompatible resource', request, resource.contextInfo.issuer) - process.exit(1) - // throw new Error(`${resource.request} was requested by ${resource.contextInfo.issuer}`) - } - if (absolute.endsWith('/minecraft-data/data.js')) { - resource.request = path.join(__dirname, './src/shims/minecraftData.ts') - } - })) - addRules([ - { - test: /\.obj$/, - type: 'asset/source', - }, - { - test: /\.mp3$/, - type: 'asset/source', - } - ]) - config.ignoreWarnings = [ - /the request of a dependency is an expression/, - /Unsupported pseudo class or element: xr-overlay/ - ] - } - }, // performance: { // bundleAnalyze: { // analyzerMode: 'json', @@ -190,3 +123,8 @@ export default defineConfig({ // }, // }, }) + +export default mergeRsbuildConfig( + appAndRendererSharedConfig(), + appConfig +) From 1446ccc0a7a16b72facfe2c18f4229a8ca71e014 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 02:35:29 +0300 Subject: [PATCH 068/851] should fix playground build --- package.json | 2 +- prismarine-viewer/rsbuild.config.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d715016a..84c3951a 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "watch-mesher": "pnpm build-mesher -w", "run-playground": "run-p watch-mesher watch-other-workers watch-playground", "run-all": "run-p start run-playground", - "build-playground": "rsbuild build --root ./prismarine-viewer", + "build-playground": "rsbuild build --config prismarine-viewer/rsbuild.config.ts", "watch-playground": "rsbuild dev --root ./prismarine-viewer" }, "keywords": [ diff --git a/prismarine-viewer/rsbuild.config.ts b/prismarine-viewer/rsbuild.config.ts index 1299670d..cea001db 100644 --- a/prismarine-viewer/rsbuild.config.ts +++ b/prismarine-viewer/rsbuild.config.ts @@ -17,10 +17,13 @@ export default mergeRsbuildConfig( appAndRendererSharedConfig(), defineConfig({ html: { - template: './playground.html', + template: join(__dirname, './playground.html'), }, output: { cleanDistPath: false, + distPath: { + root: join(__dirname, './dist'), + }, }, server: { port: 9090, From 4d3c92f6111b78af9d91813250aedda5329b6bcb Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 03:19:24 +0300 Subject: [PATCH 069/851] some playground fixes --- README.MD | 2 +- assets/playground.html | 4 ++++ prismarine-viewer/rsbuild.config.ts | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 assets/playground.html diff --git a/README.MD b/README.MD index ae23b60b..6a9df6db 100644 --- a/README.MD +++ b/README.MD @@ -87,7 +87,7 @@ To open the console, press `F12`, or if you are on mobile, you can type `#dev` i It should be easy to build/start the project locally. See [CONTRIBUTING.MD](./CONTRIBUTING.md) for more info. Also you can look at Dockerfile for reference. -There is world renderer playground ([link](https://mcon.vercel.app/playground.html)). +There is world renderer playground ([link](https://mcon.vercel.app/playground)). However, there are many things that can be done in online production version (like debugging actual source code). Also you can access some global variables in the console and there are a few useful examples: diff --git a/assets/playground.html b/assets/playground.html new file mode 100644 index 00000000..e4bb88ec --- /dev/null +++ b/assets/playground.html @@ -0,0 +1,4 @@ + + diff --git a/prismarine-viewer/rsbuild.config.ts b/prismarine-viewer/rsbuild.config.ts index cea001db..fa666d99 100644 --- a/prismarine-viewer/rsbuild.config.ts +++ b/prismarine-viewer/rsbuild.config.ts @@ -24,6 +24,7 @@ export default mergeRsbuildConfig( distPath: { root: join(__dirname, './dist'), }, + assetPrefix: './', }, server: { port: 9090, From 76bed4d496c1b28744e182a7f308ddf8ea8c4e11 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 03:34:55 +0300 Subject: [PATCH 070/851] eslint: ignore dist linting! --- .eslintignore | 1 + assets/playground.html | 2 +- rsbuild.config.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.eslintignore b/.eslintignore index ce8d8d37..bf3201cf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,4 +3,5 @@ rsbuild.config.ts *.module.css.d.ts *.generated.ts generated +dist public diff --git a/assets/playground.html b/assets/playground.html index e4bb88ec..d4179621 100644 --- a/assets/playground.html +++ b/assets/playground.html @@ -1,4 +1,4 @@ diff --git a/rsbuild.config.ts b/rsbuild.config.ts index f8a965dc..88280acf 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -77,6 +77,7 @@ const appConfig = defineConfig({ fsExtra.copySync('./node_modules/mc-assets/dist/other-textures/latest/entity', './dist/textures/entity') fsExtra.copySync('./assets/background', './dist/background') fs.copyFileSync('./assets/favicon.png', './dist/favicon.png') + fs.copyFileSync('./assets/playground.html', './dist/playground.html') fs.copyFileSync('./assets/manifest.json', './dist/manifest.json') fs.copyFileSync('./assets/loading-bg.jpg', './dist/loading-bg.jpg') const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8')) From 74fe84e10d9d3684b1eacdeca9247c4bd6804551 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Wed, 11 Sep 2024 19:27:17 +0300 Subject: [PATCH 071/851] fix playground on windows: rsbuild does not implement --root option for win --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84c3951a..4b41660a 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "run-playground": "run-p watch-mesher watch-other-workers watch-playground", "run-all": "run-p start run-playground", "build-playground": "rsbuild build --config prismarine-viewer/rsbuild.config.ts", - "watch-playground": "rsbuild dev --root ./prismarine-viewer" + "watch-playground": "rsbuild dev --config prismarine-viewer/rsbuild.config.ts" }, "keywords": [ "prismarine", From 25db002bc3ede0620b938e2d41f28938b3d8f7da Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 21:39:15 +0300 Subject: [PATCH 072/851] redirect to correct playground url --- .github/workflows/preview.yml | 2 +- README.MD | 2 +- assets/playground.html | 4 ++-- prismarine-viewer/playground.html | 6 ++++++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index c074d2e5..526705e9 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -51,7 +51,7 @@ jobs: allow-repeats: true message: | Deployed to Vercel Preview: ${{ steps.deploy.outputs.stdout }} - [Playground](${{ steps.deploy.outputs.stdout }}/playground.html) + [Playground](${{ steps.deploy.outputs.stdout }}/playground/) [Storybook](${{ steps.deploy.outputs.stdout }}/storybook/) # - run: git checkout next scripts/githubActions.mjs - name: Get deployment alias diff --git a/README.MD b/README.MD index 6a9df6db..9b07bdb5 100644 --- a/README.MD +++ b/README.MD @@ -87,7 +87,7 @@ To open the console, press `F12`, or if you are on mobile, you can type `#dev` i It should be easy to build/start the project locally. See [CONTRIBUTING.MD](./CONTRIBUTING.md) for more info. Also you can look at Dockerfile for reference. -There is world renderer playground ([link](https://mcon.vercel.app/playground)). +There is world renderer playground ([link](https://mcon.vercel.app/playground/)). However, there are many things that can be done in online production version (like debugging actual source code). Also you can access some global variables in the console and there are a few useful examples: diff --git a/assets/playground.html b/assets/playground.html index d4179621..8c394f91 100644 --- a/assets/playground.html +++ b/assets/playground.html @@ -1,4 +1,4 @@ - + diff --git a/prismarine-viewer/playground.html b/prismarine-viewer/playground.html index 92713c1c..c8ea00d2 100644 --- a/prismarine-viewer/playground.html +++ b/prismarine-viewer/playground.html @@ -29,6 +29,12 @@ src: url(../../../assets/mojangles.ttf); } +
From 755eead9764c5b0c9140cf38847f5b04b0f7b9b8 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 21:51:12 +0300 Subject: [PATCH 073/851] correctly capture screenshots of cypress --- .eslintignore | 1 + .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintignore b/.eslintignore index bf3201cf..4c431739 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,3 +5,4 @@ rsbuild.config.ts generated dist public +**/*/rsbuildSharedConfig.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 29b2f2d5..e2117da3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: if: failure() with: name: cypress-images - path: cypress/integration/__image_snapshots__/ + path: cypress/screenshots/ - name: print current ref run: echo ${{ github.event.pull_request.base.ref }} - run: node scripts/outdatedGitPackages.mjs From 5a3fb6f2253fa54c227e5e06b84bdf4db85e489f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 22:06:14 +0300 Subject: [PATCH 074/851] disable Java integration test because of issues with downloading server --- cypress.config.ts | 1 + cypress/e2e/index.spec.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cypress.config.ts b/cypress.config.ts index f9bd9478..861931e3 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -3,6 +3,7 @@ import { defineConfig } from 'cypress' export default defineConfig({ video: false, chromeWebSecurity: false, + screenshotOnRunFailure: true, // Enable screenshots on test failures e2e: { // We've imported your old cypress plugins here. // You may want to clean this up later by importing these. diff --git a/cypress/e2e/index.spec.ts b/cypress/e2e/index.spec.ts index cb1b6880..fc67ad21 100644 --- a/cypress/e2e/index.spec.ts +++ b/cypress/e2e/index.spec.ts @@ -49,7 +49,7 @@ it('Joins to local flying-squid server', () => { testWorldLoad() }) -it('Joins to local latest Java vanilla server', () => { +it.skip('Joins to local latest Java vanilla server', () => { const version = supportedVersions.at(-1)! cy.task('startServer', [version, 25_590]).then(() => { visit('/?ip=localhost:25590&username=bot') From 16bb43c7d929a211b15b474f50b1316eece2e323 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 22:16:26 +0300 Subject: [PATCH 075/851] update current ref --- .github/workflows/ci.yml | 2 +- .github/workflows/merge-next.yml | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2117da3..d5d77dc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,6 @@ jobs: - name: print current ref run: echo ${{ github.event.pull_request.base.ref }} - run: node scripts/outdatedGitPackages.mjs - if: ${{ github.event.pull_request.base.ref == 'next' }} + if: ${{ github.event.pull_request.base.ref == 'release' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/merge-next.yml b/.github/workflows/merge-next.yml index 9bed1b3d..ee02789b 100644 --- a/.github/workflows/merge-next.yml +++ b/.github/workflows/merge-next.yml @@ -17,10 +17,11 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # Fetch all history so we can merge branches + ref: refs/pull/${{ github.event.issue.number }}/head - name: Fetch All Branches run: git fetch --all - - name: Checkout PR - run: git checkout ${{ github.event.issue.pull_request.head.ref }} + # - name: Checkout PR + # run: git checkout ${{ github.event.issue.pull_request.head.ref }} - name: Merge From Next run: git merge origin/next --strategy-option=theirs - name: Push Changes From d0b921a48e50fea48dd2046c23b802e69a3b71ff Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 22:39:51 +0300 Subject: [PATCH 076/851] revert update current ref --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5d77dc7..e2117da3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,6 @@ jobs: - name: print current ref run: echo ${{ github.event.pull_request.base.ref }} - run: node scripts/outdatedGitPackages.mjs - if: ${{ github.event.pull_request.base.ref == 'release' }} + if: ${{ github.event.pull_request.base.ref == 'next' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 33437823f3d408e2c4b1beece255e9a26a3ce102 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 11 Sep 2024 22:40:41 +0300 Subject: [PATCH 077/851] disable outdated packages check for now --- .github/workflows/ci.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2117da3..2208e460 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,9 +36,7 @@ jobs: with: name: cypress-images path: cypress/screenshots/ - - name: print current ref - run: echo ${{ github.event.pull_request.base.ref }} - - run: node scripts/outdatedGitPackages.mjs - if: ${{ github.event.pull_request.base.ref == 'next' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # - run: node scripts/outdatedGitPackages.mjs + # if: ${{ github.event.pull_request.base.ref == 'release' }} + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 18a6f2c1f5a41bbf67d53436bbc9f5490862fc01 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 12 Sep 2024 04:32:37 +0300 Subject: [PATCH 078/851] fix: rare case where digging animation was not cancelled after actual dig cancel after respawn --- src/worldInteractions.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts index cef65b96..dd5f0aec 100644 --- a/src/worldInteractions.ts +++ b/src/worldInteractions.ts @@ -48,7 +48,7 @@ class WorldInteraction { breakTextures: THREE.Texture[] lastDigged: number lineMaterial: LineMaterial - debugStatus: string + debugDigStatus: string oneTimeInit () { const loader = new THREE.TextureLoader() @@ -129,11 +129,11 @@ class WorldInteraction { // TODO: If the tool and enchantments immediately exceed the hardness times 30, the block breaks with no delay; SO WE NEED TO CHECK THAT // TODO: Any blocks with a breaking time of 0.05 this.lastDigged = Date.now() - this.debugStatus = 'done' + this.debugDigStatus = 'done' }) bot.on('diggingAborted', (block) => { if (!this.cursorBlock?.position.equals(block.position)) return - this.debugStatus = 'aborted' + this.debugDigStatus = 'aborted' // if (this.lastDugBlock) this.breakStartTime = undefined if (this.buttons[0]) { @@ -326,7 +326,8 @@ class WorldInteraction { // We stopped breaking if ((!this.buttons[0] && this.lastButtons[0])) { this.lastDugBlock = null - this.debugStatus = 'cancelled' + this.breakStartTime = undefined + this.debugDigStatus = 'cancelled' } const onGround = bot.entity.onGround || bot.game.gameMode === 'creative' @@ -340,7 +341,7 @@ class WorldInteraction { && (!this.lastButtons[0] || ((cursorChanged || (this.lastDugBlock && !this.lastDugBlock.equals(cursorBlock!.position))) && Date.now() - (this.lastDigged ?? 0) > 300) || onGround !== this.prevOnGround) && onGround) { this.lastDugBlock = null - this.debugStatus = 'breaking' + this.debugDigStatus = 'breaking' this.currentDigTime = bot.digTime(cursorBlockDiggable) this.breakStartTime = performance.now() const vecArray = [new Vec3(0, -1, 0), new Vec3(0, 1, 0), new Vec3(0, 0, -1), new Vec3(0, 0, 1), new Vec3(-1, 0, 0), new Vec3(1, 0, 0)] From 7da41b02c95b61873fff8fd13f45712eb545c7aa Mon Sep 17 00:00:00 2001 From: Valery-a <83373303+Valery-a@users.noreply.github.com> Date: Thu, 12 Sep 2024 23:00:58 +0300 Subject: [PATCH 079/851] fix: fixed zombies and husks not having texture (#203) --- prismarine-viewer/viewer/lib/entities.ts | 3 +- .../viewer/lib/entity/EntityMesh.js | 10 + .../viewer/lib/entity/exportedModels.js | 1 + .../viewer/lib/entity/models/zombie.obj | 472 +++++++++--------- 4 files changed, 248 insertions(+), 238 deletions(-) diff --git a/prismarine-viewer/viewer/lib/entities.ts b/prismarine-viewer/viewer/lib/entities.ts index 281a442e..ee659263 100644 --- a/prismarine-viewer/viewer/lib/entities.ts +++ b/prismarine-viewer/viewer/lib/entities.ts @@ -340,9 +340,8 @@ export class Entities extends EventEmitter { } update (entity: import('prismarine-entity').Entity & { delete?; pos }, overrides) { - let isPlayerModel = entity.name === 'player' + const isPlayerModel = entity.name === 'player' if (entity.name === 'zombie' || entity.name === 'zombie_villager' || entity.name === 'husk') { - isPlayerModel = true overrides.texture = `textures/1.16.4/entity/${entity.name === 'zombie_villager' ? 'zombie_villager/zombie_villager.png' : `zombie/${entity.name}.png`}` } if (!this.entities[entity.id] && !entity.delete) { diff --git a/prismarine-viewer/viewer/lib/entity/EntityMesh.js b/prismarine-viewer/viewer/lib/entity/EntityMesh.js index 39b2d62d..1387d2cc 100644 --- a/prismarine-viewer/viewer/lib/entity/EntityMesh.js +++ b/prismarine-viewer/viewer/lib/entity/EntityMesh.js @@ -1,6 +1,7 @@ //@ts-check import * as THREE from 'three' import { OBJLoader } from 'three-stdlib' +import huskPng from 'mc-assets/dist/other-textures/latest/entity/zombie/husk.png' import entities from './entities.json' import { externalModels } from './objModels' import externalTexturesJson from './externalTextures.json' @@ -290,6 +291,10 @@ const getEntity = (name) => { // zombie_villager: 'zombie_villager/zombie_villager' // } +const scaleEntity = { + zombie: 1.9, + husk: 1.9 +} // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class EntityMesh { constructor(version, type, scene, /** @type {{textures?, rotation?: Record}} */overrides = {}) { @@ -303,6 +308,9 @@ export class EntityMesh { if (originalType === 'zombie_horse') { texturePath = `textures/${version}/entity/horse/horse_zombie.png` } + if (originalType === 'husk') { + texturePath = huskPng + } if (originalType === 'skeleton_horse') { texturePath = `textures/${version}/entity/horse/horse_skeleton.png` } @@ -325,6 +333,8 @@ export class EntityMesh { alphaTest: 0.1 }) const obj = objLoader.parse(externalModels[type]) + const scale = scaleEntity[originalType] + if (scale) obj.scale.set(scale, scale, scale) if (type === 'boat') obj.position.y = -1 // todo, should not be hardcoded obj.traverse((child) => { if (child instanceof THREE.Mesh) { diff --git a/prismarine-viewer/viewer/lib/entity/exportedModels.js b/prismarine-viewer/viewer/lib/entity/exportedModels.js index b43658ec..53d55d5e 100644 --- a/prismarine-viewer/viewer/lib/entity/exportedModels.js +++ b/prismarine-viewer/viewer/lib/entity/exportedModels.js @@ -35,4 +35,5 @@ export { default as witch } from './models/witch.obj' export { default as wolf } from './models/wolf.obj' export { default as zombie_villager } from './models/zombie_villager.obj' export { default as zombie } from './models/zombie.obj' +export { default as husk } from './models/zombie.obj' export { default as boat } from './models/boat.obj' diff --git a/prismarine-viewer/viewer/lib/entity/models/zombie.obj b/prismarine-viewer/viewer/lib/entity/models/zombie.obj index bcd7444c..42f1e722 100644 --- a/prismarine-viewer/viewer/lib/entity/models/zombie.obj +++ b/prismarine-viewer/viewer/lib/entity/models/zombie.obj @@ -1,322 +1,322 @@ -# Made in Blockbench 4.9.4 +# Made in Blockbench 4.10.4 mtllib materials.mtl -o Body -v 0.25 1.5 0.125 -v 0.25 1.5 -0.125 -v 0.25 0.75 0.125 -v 0.25 0.75 -0.125 -v -0.25 1.5 -0.125 -v -0.25 1.5 0.125 -v -0.25 0.75 -0.125 -v -0.25 0.75 0.125 -vt 0.3125 0.375 -vt 0.4375 0.375 -vt 0.4375 0 -vt 0.3125 0 -vt 0.25 0.375 -vt 0.3125 0.375 -vt 0.3125 0 -vt 0.25 0 -vt 0.5 0.375 -vt 0.625 0.375 -vt 0.625 0 -vt 0.5 0 -vt 0.4375 0.375 -vt 0.5 0.375 -vt 0.5 0 -vt 0.4375 0 -vt 0.4375 0.375 -vt 0.3125 0.375 -vt 0.3125 0.5 -vt 0.4375 0.5 -vt 0.5625 0.5 -vt 0.4375 0.5 -vt 0.4375 0.375 -vt 0.5625 0.375 +o /head +v 0.125 1 0.125 +v 0.125 1 -0.125 +v 0.125 0.75 0.125 +v 0.125 0.75 -0.125 +v -0.125 1 -0.125 +v -0.125 1 0.125 +v -0.125 0.75 -0.125 +v -0.125 0.75 0.125 +vt 0.125 0.875 +vt 0.25 0.875 +vt 0.25 0.75 +vt 0.125 0.75 +vt 0 0.875 +vt 0.125 0.875 +vt 0.125 0.75 +vt 0 0.75 +vt 0.375 0.875 +vt 0.5 0.875 +vt 0.5 0.75 +vt 0.375 0.75 +vt 0.25 0.875 +vt 0.375 0.875 +vt 0.375 0.75 +vt 0.25 0.75 +vt 0.25 0.875 +vt 0.125 0.875 +vt 0.125 1 +vt 0.25 1 +vt 0.25 1 +vt 0.375 1 +vt 0.375 0.875 +vt 0.25 0.875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 4/4/1 7/3/1 5/2/1 2/1/1 f 3/8/2 4/7/2 2/6/2 1/5/2 f 8/12/3 3/11/3 1/10/3 6/9/3 f 7/16/4 8/15/4 6/14/4 5/13/4 f 6/20/5 1/19/5 2/18/5 5/17/5 f 7/24/6 4/23/6 3/22/6 8/21/6 -o Head -v 0.25 2 0.25 -v 0.25 2 -0.25 -v 0.25 1.5 0.25 -v 0.25 1.5 -0.25 -v -0.25 2 -0.25 -v -0.25 2 0.25 -v -0.25 1.5 -0.25 -v -0.25 1.5 0.25 -vt 0.125 0.75 -vt 0.25 0.75 -vt 0.25 0.5 -vt 0.125 0.5 -vt 0 0.75 -vt 0.125 0.75 -vt 0.125 0.5 -vt 0 0.5 -vt 0.375 0.75 -vt 0.5 0.75 -vt 0.5 0.5 -vt 0.375 0.5 -vt 0.25 0.75 -vt 0.375 0.75 -vt 0.375 0.5 -vt 0.25 0.5 -vt 0.25 0.75 -vt 0.125 0.75 -vt 0.125 1 -vt 0.25 1 -vt 0.375 1 -vt 0.25 1 -vt 0.25 0.75 -vt 0.375 0.75 +o /right_arm +v -0.125 0.75 0.0625 +v -0.125 0.75 -0.0625 +v -0.125 0.375 0.0625 +v -0.125 0.375 -0.0625 +v -0.25 0.75 -0.0625 +v -0.25 0.75 0.0625 +v -0.25 0.375 -0.0625 +v -0.25 0.375 0.0625 +vt 0.6875 0.6875 +vt 0.75 0.6875 +vt 0.75 0.5 +vt 0.6875 0.5 +vt 0.625 0.6875 +vt 0.6875 0.6875 +vt 0.6875 0.5 +vt 0.625 0.5 +vt 0.8125 0.6875 +vt 0.875 0.6875 +vt 0.875 0.5 +vt 0.8125 0.5 +vt 0.75 0.6875 +vt 0.8125 0.6875 +vt 0.8125 0.5 +vt 0.75 0.5 +vt 0.75 0.6875 +vt 0.6875 0.6875 +vt 0.6875 0.75 +vt 0.75 0.75 +vt 0.75 0.75 +vt 0.8125 0.75 +vt 0.8125 0.6875 +vt 0.75 0.6875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 12/28/7 15/27/7 13/26/7 10/25/7 f 11/32/8 12/31/8 10/30/8 9/29/8 f 16/36/9 11/35/9 9/34/9 14/33/9 f 15/40/10 16/39/10 14/38/10 13/37/10 f 14/44/11 9/43/11 10/42/11 13/41/11 f 15/48/12 12/47/12 11/46/12 16/45/12 -o Hat Layer -v 0.28125 2.03125 0.28125 -v 0.28125 2.03125 -0.28125 -v 0.28125 1.46875 0.28125 -v 0.28125 1.46875 -0.28125 -v -0.28125 2.03125 -0.28125 -v -0.28125 2.03125 0.28125 -v -0.28125 1.46875 -0.28125 -v -0.28125 1.46875 0.28125 -vt 0.625 0.75 -vt 0.75 0.75 -vt 0.75 0.5 -vt 0.625 0.5 -vt 0.5 0.75 -vt 0.625 0.75 -vt 0.625 0.5 -vt 0.5 0.5 -vt 0.875 0.75 -vt 1 0.75 -vt 1 0.5 -vt 0.875 0.5 -vt 0.75 0.75 -vt 0.875 0.75 -vt 0.875 0.5 -vt 0.75 0.5 -vt 0.75 0.75 -vt 0.625 0.75 -vt 0.625 1 -vt 0.75 1 -vt 0.875 1 -vt 0.75 1 -vt 0.75 0.75 -vt 0.875 0.75 +o /left_leg +v 0.12187499999999996 0.375 0.0625 +v 0.12187499999999996 0.375 -0.0625 +v 0.12187499999999996 0 0.0625 +v 0.12187499999999996 0 -0.0625 +v -0.003124999999999989 0.375 -0.0625 +v -0.003124999999999989 0.375 0.0625 +v -0.003124999999999989 0 -0.0625 +v -0.003124999999999989 0 0.0625 +vt 0.0625 0.6875 +vt 0.125 0.6875 +vt 0.125 0.5 +vt 0.0625 0.5 +vt 0 0.6875 +vt 0.0625 0.6875 +vt 0.0625 0.5 +vt 0 0.5 +vt 0.1875 0.6875 +vt 0.25 0.6875 +vt 0.25 0.5 +vt 0.1875 0.5 +vt 0.125 0.6875 +vt 0.1875 0.6875 +vt 0.1875 0.5 +vt 0.125 0.5 +vt 0.125 0.6875 +vt 0.0625 0.6875 +vt 0.0625 0.75 +vt 0.125 0.75 +vt 0.125 0.75 +vt 0.1875 0.75 +vt 0.1875 0.6875 +vt 0.125 0.6875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 20/52/13 23/51/13 21/50/13 18/49/13 f 19/56/14 20/55/14 18/54/14 17/53/14 f 24/60/15 19/59/15 17/58/15 22/57/15 f 23/64/16 24/63/16 22/62/16 21/61/16 f 22/68/17 17/67/17 18/66/17 21/65/17 f 23/72/18 20/71/18 19/70/18 24/69/18 -o RightArm -v 0.5 1.5 0.125 -v 0.5 1.5 -0.125 -v 0.5 0.75 0.125 -v 0.5 0.75 -0.125 -v 0.25 1.5 -0.125 -v 0.25 1.5 0.125 -v 0.25 0.75 -0.125 -v 0.25 0.75 0.125 -vt 0.6875 0.375 -vt 0.75 0.375 -vt 0.75 0 -vt 0.6875 0 -vt 0.625 0.375 -vt 0.6875 0.375 -vt 0.6875 0 -vt 0.625 0 -vt 0.8125 0.375 -vt 0.875 0.375 -vt 0.875 0 -vt 0.8125 0 -vt 0.75 0.375 -vt 0.8125 0.375 -vt 0.8125 0 -vt 0.75 0 -vt 0.75 0.375 -vt 0.6875 0.375 -vt 0.6875 0.5 +o /left_arm +v 0.25 0.75 0.0625 +v 0.25 0.75 -0.0625 +v 0.25 0.375 0.0625 +v 0.25 0.375 -0.0625 +v 0.125 0.75 -0.0625 +v 0.125 0.75 0.0625 +v 0.125 0.375 -0.0625 +v 0.125 0.375 0.0625 +vt 0.6875 0.6875 +vt 0.75 0.6875 vt 0.75 0.5 +vt 0.6875 0.5 +vt 0.625 0.6875 +vt 0.6875 0.6875 +vt 0.6875 0.5 +vt 0.625 0.5 +vt 0.8125 0.6875 +vt 0.875 0.6875 +vt 0.875 0.5 +vt 0.8125 0.5 +vt 0.75 0.6875 +vt 0.8125 0.6875 vt 0.8125 0.5 vt 0.75 0.5 -vt 0.75 0.375 -vt 0.8125 0.375 +vt 0.75 0.6875 +vt 0.6875 0.6875 +vt 0.6875 0.75 +vt 0.75 0.75 +vt 0.75 0.75 +vt 0.8125 0.75 +vt 0.8125 0.6875 +vt 0.75 0.6875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 28/76/19 31/75/19 29/74/19 26/73/19 f 27/80/20 28/79/20 26/78/20 25/77/20 f 32/84/21 27/83/21 25/82/21 30/81/21 f 31/88/22 32/87/22 30/86/22 29/85/22 f 30/92/23 25/91/23 26/90/23 29/89/23 f 31/96/24 28/95/24 27/94/24 32/93/24 -o LeftArm -v -0.25 1.5 0.125 -v -0.25 1.5 -0.125 -v -0.25 0.75 0.125 -v -0.25 0.75 -0.125 -v -0.5 1.5 -0.125 -v -0.5 1.5 0.125 -v -0.5 0.75 -0.125 -v -0.5 0.75 0.125 -vt 0.75 0.375 -vt 0.6875 0.375 -vt 0.6875 0 -vt 0.75 0 -vt 0.8125 0.375 -vt 0.75 0.375 -vt 0.75 0 -vt 0.8125 0 -vt 0.875 0.375 -vt 0.8125 0.375 -vt 0.8125 0 -vt 0.875 0 -vt 0.6875 0.375 -vt 0.625 0.375 -vt 0.625 0 -vt 0.6875 0 -vt 0.6875 0.375 -vt 0.75 0.375 -vt 0.75 0.5 -vt 0.6875 0.5 -vt 0.75 0.5 -vt 0.8125 0.5 -vt 0.8125 0.375 -vt 0.75 0.375 +o /right_leg +v 0.0031250000000000444 0.375 0.0625 +v 0.0031250000000000444 0.375 -0.0625 +v 0.0031250000000000444 0 0.0625 +v 0.0031250000000000444 0 -0.0625 +v -0.12187500000000001 0.375 -0.0625 +v -0.12187500000000001 0.375 0.0625 +v -0.12187500000000001 0 -0.0625 +v -0.12187500000000001 0 0.0625 +vt 0.0625 0.6875 +vt 0.125 0.6875 +vt 0.125 0.5 +vt 0.0625 0.5 +vt 0 0.6875 +vt 0.0625 0.6875 +vt 0.0625 0.5 +vt 0 0.5 +vt 0.1875 0.6875 +vt 0.25 0.6875 +vt 0.25 0.5 +vt 0.1875 0.5 +vt 0.125 0.6875 +vt 0.1875 0.6875 +vt 0.1875 0.5 +vt 0.125 0.5 +vt 0.125 0.6875 +vt 0.0625 0.6875 +vt 0.0625 0.75 +vt 0.125 0.75 +vt 0.125 0.75 +vt 0.1875 0.75 +vt 0.1875 0.6875 +vt 0.125 0.6875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 36/100/25 39/99/25 37/98/25 34/97/25 f 35/104/26 36/103/26 34/102/26 33/101/26 f 40/108/27 35/107/27 33/106/27 38/105/27 f 39/112/28 40/111/28 38/110/28 37/109/28 f 38/116/29 33/115/29 34/114/29 37/113/29 f 39/120/30 36/119/30 35/118/30 40/117/30 -o RightLeg -v 0.24375000000000002 0.75 0.125 -v 0.24375000000000002 0.75 -0.125 -v 0.24375000000000002 0 0.125 -v 0.24375000000000002 0 -0.125 -v -0.006249999999999978 0.75 -0.125 -v -0.006249999999999978 0.75 0.125 -v -0.006249999999999978 0 -0.125 -v -0.006249999999999978 0 0.125 -vt 0.0625 0.375 -vt 0.125 0.375 -vt 0.125 0 -vt 0.0625 0 -vt 0 0.375 -vt 0.0625 0.375 -vt 0.0625 0 -vt 0 0 -vt 0.1875 0.375 -vt 0.25 0.375 -vt 0.25 0 -vt 0.1875 0 -vt 0.125 0.375 -vt 0.1875 0.375 -vt 0.1875 0 -vt 0.125 0 -vt 0.125 0.375 -vt 0.0625 0.375 -vt 0.0625 0.5 -vt 0.125 0.5 -vt 0.1875 0.5 -vt 0.125 0.5 -vt 0.125 0.375 -vt 0.1875 0.375 +o /hat +v 0.140625 1.015625 0.140625 +v 0.140625 1.015625 -0.140625 +v 0.140625 0.734375 0.140625 +v 0.140625 0.734375 -0.140625 +v -0.140625 1.015625 -0.140625 +v -0.140625 1.015625 0.140625 +v -0.140625 0.734375 -0.140625 +v -0.140625 0.734375 0.140625 +vt 0.625 0.875 +vt 0.75 0.875 +vt 0.75 0.75 +vt 0.625 0.75 +vt 0.5 0.875 +vt 0.625 0.875 +vt 0.625 0.75 +vt 0.5 0.75 +vt 0.875 0.875 +vt 1 0.875 +vt 1 0.75 +vt 0.875 0.75 +vt 0.75 0.875 +vt 0.875 0.875 +vt 0.875 0.75 +vt 0.75 0.75 +vt 0.75 0.875 +vt 0.625 0.875 +vt 0.625 1 +vt 0.75 1 +vt 0.75 1 +vt 0.875 1 +vt 0.875 0.875 +vt 0.75 0.875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 44/124/31 47/123/31 45/122/31 42/121/31 f 43/128/32 44/127/32 42/126/32 41/125/32 f 48/132/33 43/131/33 41/130/33 46/129/33 f 47/136/34 48/135/34 46/134/34 45/133/34 f 46/140/35 41/139/35 42/138/35 45/137/35 f 47/144/36 44/143/36 43/142/36 48/141/36 -o LeftLeg -v 0.006249999999999978 0.75 0.125 -v 0.006249999999999978 0.75 -0.125 -v 0.006249999999999978 0 0.125 -v 0.006249999999999978 0 -0.125 -v -0.24375000000000002 0.75 -0.125 -v -0.24375000000000002 0.75 0.125 -v -0.24375000000000002 0 -0.125 -v -0.24375000000000002 0 0.125 -vt 0.125 0.375 -vt 0.0625 0.375 -vt 0.0625 0 -vt 0.125 0 -vt 0.1875 0.375 -vt 0.125 0.375 -vt 0.125 0 -vt 0.1875 0 -vt 0.25 0.375 -vt 0.1875 0.375 -vt 0.1875 0 -vt 0.25 0 -vt 0.0625 0.375 -vt 0 0.375 -vt 0 0 -vt 0.0625 0 -vt 0.0625 0.375 -vt 0.125 0.375 -vt 0.125 0.5 -vt 0.0625 0.5 -vt 0.125 0.5 -vt 0.1875 0.5 -vt 0.1875 0.375 -vt 0.125 0.375 +o /body +v 0.125 0.75 0.0625 +v 0.125 0.75 -0.0625 +v 0.125 0.375 0.0625 +v 0.125 0.375 -0.0625 +v -0.125 0.75 -0.0625 +v -0.125 0.75 0.0625 +v -0.125 0.375 -0.0625 +v -0.125 0.375 0.0625 +vt 0.3125 0.6875 +vt 0.4375 0.6875 +vt 0.4375 0.5 +vt 0.3125 0.5 +vt 0.25 0.6875 +vt 0.3125 0.6875 +vt 0.3125 0.5 +vt 0.25 0.5 +vt 0.5 0.6875 +vt 0.625 0.6875 +vt 0.625 0.5 +vt 0.5 0.5 +vt 0.4375 0.6875 +vt 0.5 0.6875 +vt 0.5 0.5 +vt 0.4375 0.5 +vt 0.4375 0.6875 +vt 0.3125 0.6875 +vt 0.3125 0.75 +vt 0.4375 0.75 +vt 0.4375 0.75 +vt 0.5625 0.75 +vt 0.5625 0.6875 +vt 0.4375 0.6875 vn 0 0 -1 vn 1 0 0 vn 0 0 1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 -usemtl m_9eb5cf2e-0212-52a4-6070-8cb3b67f2e24 +usemtl none f 52/148/37 55/147/37 53/146/37 50/145/37 f 51/152/38 52/151/38 50/150/38 49/149/38 f 56/156/39 51/155/39 49/154/39 54/153/39 From 9bac681c293da03e364a6c49eab7fed92ed1fbf7 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 12 Sep 2024 23:38:45 +0300 Subject: [PATCH 080/851] use correct zombie model --- .../viewer/lib/entity/EntityMesh.js | 14 +- .../viewer/lib/entity/models/zombie.obj | 174 +++++++++--------- 2 files changed, 98 insertions(+), 90 deletions(-) diff --git a/prismarine-viewer/viewer/lib/entity/EntityMesh.js b/prismarine-viewer/viewer/lib/entity/EntityMesh.js index 1387d2cc..a518171f 100644 --- a/prismarine-viewer/viewer/lib/entity/EntityMesh.js +++ b/prismarine-viewer/viewer/lib/entity/EntityMesh.js @@ -2,6 +2,7 @@ import * as THREE from 'three' import { OBJLoader } from 'three-stdlib' import huskPng from 'mc-assets/dist/other-textures/latest/entity/zombie/husk.png' +import { Vec3 } from 'vec3' import entities from './entities.json' import { externalModels } from './objModels' import externalTexturesJson from './externalTextures.json' @@ -292,9 +293,15 @@ const getEntity = (name) => { // } const scaleEntity = { - zombie: 1.9, - husk: 1.9 + zombie: 1.85, + husk: 1.85 } +const offsetEntity = { + zombie: new Vec3(0, 1, 0), + husk: new Vec3(0, 1, 0), + boat: new Vec3(0, -1, 0), +} + // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class EntityMesh { constructor(version, type, scene, /** @type {{textures?, rotation?: Record}} */overrides = {}) { @@ -335,7 +342,8 @@ export class EntityMesh { const obj = objLoader.parse(externalModels[type]) const scale = scaleEntity[originalType] if (scale) obj.scale.set(scale, scale, scale) - if (type === 'boat') obj.position.y = -1 // todo, should not be hardcoded + const offset = offsetEntity[originalType] + if (offset) obj.position.set(offset.x, offset.y, offset.z) obj.traverse((child) => { if (child instanceof THREE.Mesh) { child.material = material diff --git a/prismarine-viewer/viewer/lib/entity/models/zombie.obj b/prismarine-viewer/viewer/lib/entity/models/zombie.obj index 42f1e722..fa5d8f4d 100644 --- a/prismarine-viewer/viewer/lib/entity/models/zombie.obj +++ b/prismarine-viewer/viewer/lib/entity/models/zombie.obj @@ -2,14 +2,14 @@ mtllib materials.mtl o /head -v 0.125 1 0.125 -v 0.125 1 -0.125 -v 0.125 0.75 0.125 -v 0.125 0.75 -0.125 -v -0.125 1 -0.125 -v -0.125 1 0.125 -v -0.125 0.75 -0.125 -v -0.125 0.75 0.125 +v 0.125 0.5 0.125 +v 0.125 0.5 -0.125 +v 0.125 0.25 0.125 +v 0.125 0.25 -0.125 +v -0.125 0.5 -0.125 +v -0.125 0.5 0.125 +v -0.125 0.25 -0.125 +v -0.125 0.25 0.125 vt 0.125 0.875 vt 0.25 0.875 vt 0.25 0.75 @@ -48,38 +48,38 @@ f 7/16/4 8/15/4 6/14/4 5/13/4 f 6/20/5 1/19/5 2/18/5 5/17/5 f 7/24/6 4/23/6 3/22/6 8/21/6 o /right_arm -v -0.125 0.75 0.0625 -v -0.125 0.75 -0.0625 -v -0.125 0.375 0.0625 -v -0.125 0.375 -0.0625 -v -0.25 0.75 -0.0625 -v -0.25 0.75 0.0625 -v -0.25 0.375 -0.0625 -v -0.25 0.375 0.0625 -vt 0.6875 0.6875 +v -0.125 0.25 0.0625 +v -0.125 0.25 -0.3125 +v -0.125 0.125 0.0625 +v -0.125 0.125 -0.3125 +v -0.25 0.25 -0.3125 +v -0.25 0.25 0.0625 +v -0.25 0.125 -0.3125 +v -0.25 0.125 0.0625 +vt 0.8125 0.6875 vt 0.75 0.6875 -vt 0.75 0.5 -vt 0.6875 0.5 -vt 0.625 0.6875 +vt 0.75 0.75 +vt 0.8125 0.75 vt 0.6875 0.6875 vt 0.6875 0.5 vt 0.625 0.5 -vt 0.8125 0.6875 -vt 0.875 0.6875 -vt 0.875 0.5 -vt 0.8125 0.5 -vt 0.75 0.6875 -vt 0.8125 0.6875 -vt 0.8125 0.5 -vt 0.75 0.5 +vt 0.625 0.6875 vt 0.75 0.6875 vt 0.6875 0.6875 vt 0.6875 0.75 vt 0.75 0.75 -vt 0.75 0.75 -vt 0.8125 0.75 -vt 0.8125 0.6875 +vt 0.75 0.5 vt 0.75 0.6875 +vt 0.8125 0.6875 +vt 0.8125 0.5 +vt 0.75 0.5 +vt 0.6875 0.5 +vt 0.6875 0.6875 +vt 0.75 0.6875 +vt 0.8125 0.6875 +vt 0.875 0.6875 +vt 0.875 0.5 +vt 0.8125 0.5 vn 0 0 -1 vn 1 0 0 vn 0 0 1 @@ -94,14 +94,14 @@ f 15/40/10 16/39/10 14/38/10 13/37/10 f 14/44/11 9/43/11 10/42/11 13/41/11 f 15/48/12 12/47/12 11/46/12 16/45/12 o /left_leg -v 0.12187499999999996 0.375 0.0625 -v 0.12187499999999996 0.375 -0.0625 -v 0.12187499999999996 0 0.0625 -v 0.12187499999999996 0 -0.0625 -v -0.003124999999999989 0.375 -0.0625 -v -0.003124999999999989 0.375 0.0625 -v -0.003124999999999989 0 -0.0625 -v -0.003124999999999989 0 0.0625 +v 0.12187499999999996 -0.125 0.0625 +v 0.12187499999999996 -0.125 -0.0625 +v 0.12187499999999996 -0.5 0.0625 +v 0.12187499999999996 -0.5 -0.0625 +v -0.0031250000000000444 -0.125 -0.0625 +v -0.0031250000000000444 -0.125 0.0625 +v -0.0031250000000000444 -0.5 -0.0625 +v -0.0031250000000000444 -0.5 0.0625 vt 0.0625 0.6875 vt 0.125 0.6875 vt 0.125 0.5 @@ -140,38 +140,38 @@ f 23/64/16 24/63/16 22/62/16 21/61/16 f 22/68/17 17/67/17 18/66/17 21/65/17 f 23/72/18 20/71/18 19/70/18 24/69/18 o /left_arm -v 0.25 0.75 0.0625 -v 0.25 0.75 -0.0625 -v 0.25 0.375 0.0625 -v 0.25 0.375 -0.0625 -v 0.125 0.75 -0.0625 -v 0.125 0.75 0.0625 -v 0.125 0.375 -0.0625 -v 0.125 0.375 0.0625 -vt 0.6875 0.6875 +v 0.25 0.25 0.0625 +v 0.25 0.25 -0.3125 +v 0.25 0.125 0.0625 +v 0.25 0.125 -0.3125 +v 0.125 0.25 -0.3125 +v 0.125 0.25 0.0625 +v 0.125 0.125 -0.3125 +v 0.125 0.125 0.0625 +vt 0.8125 0.6875 vt 0.75 0.6875 -vt 0.75 0.5 -vt 0.6875 0.5 -vt 0.625 0.6875 +vt 0.75 0.75 +vt 0.8125 0.75 vt 0.6875 0.6875 vt 0.6875 0.5 vt 0.625 0.5 -vt 0.8125 0.6875 -vt 0.875 0.6875 -vt 0.875 0.5 -vt 0.8125 0.5 -vt 0.75 0.6875 -vt 0.8125 0.6875 -vt 0.8125 0.5 -vt 0.75 0.5 +vt 0.625 0.6875 vt 0.75 0.6875 vt 0.6875 0.6875 vt 0.6875 0.75 vt 0.75 0.75 -vt 0.75 0.75 -vt 0.8125 0.75 -vt 0.8125 0.6875 +vt 0.75 0.5 vt 0.75 0.6875 +vt 0.8125 0.6875 +vt 0.8125 0.5 +vt 0.75 0.5 +vt 0.6875 0.5 +vt 0.6875 0.6875 +vt 0.75 0.6875 +vt 0.8125 0.6875 +vt 0.875 0.6875 +vt 0.875 0.5 +vt 0.8125 0.5 vn 0 0 -1 vn 1 0 0 vn 0 0 1 @@ -186,14 +186,14 @@ f 31/88/22 32/87/22 30/86/22 29/85/22 f 30/92/23 25/91/23 26/90/23 29/89/23 f 31/96/24 28/95/24 27/94/24 32/93/24 o /right_leg -v 0.0031250000000000444 0.375 0.0625 -v 0.0031250000000000444 0.375 -0.0625 -v 0.0031250000000000444 0 0.0625 -v 0.0031250000000000444 0 -0.0625 -v -0.12187500000000001 0.375 -0.0625 -v -0.12187500000000001 0.375 0.0625 -v -0.12187500000000001 0 -0.0625 -v -0.12187500000000001 0 0.0625 +v 0.0031250000000000444 -0.125 0.0625 +v 0.0031250000000000444 -0.125 -0.0625 +v 0.0031250000000000444 -0.5 0.0625 +v 0.0031250000000000444 -0.5 -0.0625 +v -0.12187499999999996 -0.125 -0.0625 +v -0.12187499999999996 -0.125 0.0625 +v -0.12187499999999996 -0.5 -0.0625 +v -0.12187499999999996 -0.5 0.0625 vt 0.0625 0.6875 vt 0.125 0.6875 vt 0.125 0.5 @@ -232,14 +232,14 @@ f 39/112/28 40/111/28 38/110/28 37/109/28 f 38/116/29 33/115/29 34/114/29 37/113/29 f 39/120/30 36/119/30 35/118/30 40/117/30 o /hat -v 0.140625 1.015625 0.140625 -v 0.140625 1.015625 -0.140625 -v 0.140625 0.734375 0.140625 -v 0.140625 0.734375 -0.140625 -v -0.140625 1.015625 -0.140625 -v -0.140625 1.015625 0.140625 -v -0.140625 0.734375 -0.140625 -v -0.140625 0.734375 0.140625 +v 0.140625 0.515625 0.140625 +v 0.140625 0.515625 -0.140625 +v 0.140625 0.234375 0.140625 +v 0.140625 0.234375 -0.140625 +v -0.140625 0.515625 -0.140625 +v -0.140625 0.515625 0.140625 +v -0.140625 0.234375 -0.140625 +v -0.140625 0.234375 0.140625 vt 0.625 0.875 vt 0.75 0.875 vt 0.75 0.75 @@ -278,14 +278,14 @@ f 47/136/34 48/135/34 46/134/34 45/133/34 f 46/140/35 41/139/35 42/138/35 45/137/35 f 47/144/36 44/143/36 43/142/36 48/141/36 o /body -v 0.125 0.75 0.0625 -v 0.125 0.75 -0.0625 -v 0.125 0.375 0.0625 -v 0.125 0.375 -0.0625 -v -0.125 0.75 -0.0625 -v -0.125 0.75 0.0625 -v -0.125 0.375 -0.0625 -v -0.125 0.375 0.0625 +v 0.125 0.25 0.0625 +v 0.125 0.25 -0.0625 +v 0.125 -0.125 0.0625 +v 0.125 -0.125 -0.0625 +v -0.125 0.25 -0.0625 +v -0.125 0.25 0.0625 +v -0.125 -0.125 -0.0625 +v -0.125 -0.125 0.0625 vt 0.3125 0.6875 vt 0.4375 0.6875 vt 0.4375 0.5 @@ -322,4 +322,4 @@ f 51/152/38 52/151/38 50/150/38 49/149/38 f 56/156/39 51/155/39 49/154/39 54/153/39 f 55/160/40 56/159/40 54/158/40 53/157/40 f 54/164/41 49/163/41 50/162/41 53/161/41 -f 55/168/42 52/167/42 51/166/42 56/165/42 \ No newline at end of file +f 55/168/42 52/167/42 51/166/42 56/165/42 From 3ea95d509ac436236dc9113292396b1791512dbf Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 19 Sep 2024 02:34:45 +0300 Subject: [PATCH 081/851] fix: fix github pages main deploy --- prismarine-viewer/rsbuild.config.ts | 1 - prismarine-viewer/rsbuildSharedConfig.ts | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prismarine-viewer/rsbuild.config.ts b/prismarine-viewer/rsbuild.config.ts index fa666d99..cea001db 100644 --- a/prismarine-viewer/rsbuild.config.ts +++ b/prismarine-viewer/rsbuild.config.ts @@ -24,7 +24,6 @@ export default mergeRsbuildConfig( distPath: { root: join(__dirname, './dist'), }, - assetPrefix: './', }, server: { port: 9090, diff --git a/prismarine-viewer/rsbuildSharedConfig.ts b/prismarine-viewer/rsbuildSharedConfig.ts index 3fba1ef1..0c248f6c 100644 --- a/prismarine-viewer/rsbuildSharedConfig.ts +++ b/prismarine-viewer/rsbuildSharedConfig.ts @@ -17,7 +17,8 @@ export const appAndRendererSharedConfig = () => defineConfig({ output: { polyfill: 'usage', // 50kb limit for data uri - dataUriLimit: 50 * 1024 + dataUriLimit: 50 * 1024, + assetPrefix: './', }, source: { alias: { From 40f81d84cd7c337bd661b15e74358a7c80c6bf59 Mon Sep 17 00:00:00 2001 From: Valery-a <83373303+Valery-a@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:43:47 -0700 Subject: [PATCH 082/851] server change (#207) --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index e4f86060..33095707 100644 --- a/config.json +++ b/config.json @@ -10,9 +10,9 @@ "description": "Chaos and destruction server. Free for everyone." }, { - "ip": "go.mineberry.org", + "ip": "play.applemc.fun", "version": "1.18.2", - "description": "One of the best servers here. Join now!" + "description": "Very nice server. Try it now!" }, { "ip": "sus.shhnowisnottheti.me", From 00150dda1ddb9cb27426e68cebdc0f4b44529554 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 28 Sep 2024 02:57:18 +0300 Subject: [PATCH 083/851] fix: inventory UI crash in some cases with some specific window titles fix: client messages were not displayed on the latest version --- src/botUtils.ts | 20 ++++++++++++++++++++ src/builtinCommands.ts | 5 ++--- src/inventoryWindows.ts | 23 ++++++++++++++++------- src/worldInteractions.ts | 7 ++----- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/botUtils.ts b/src/botUtils.ts index 79b10118..e98d1e84 100644 --- a/src/botUtils.ts +++ b/src/botUtils.ts @@ -2,6 +2,7 @@ import { fromFormattedString, TextComponent } from '@xmcl/text-component' import type { IndexedData } from 'minecraft-data' +import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' export type MessageFormatPart = Pick & { text: string @@ -120,3 +121,22 @@ export const getItemFromBlock = (block: import('prismarine-block').Block) => { const item = global.loadedData.itemsByName[blockToItemRemaps[block.name] ?? block.name] return item } + +export const displayClientChat = (text: string) => { + const message = { + text + } + if (versionToNumber(bot.version) >= versionToNumber('1.19')) { + bot._client.emit('systemChat', { + formattedMessage: JSON.stringify(message), + position: 0, + sender: 'minecraft:chat' + }) + return + } + bot._client.write('chat', { + message: JSON.stringify(message), + position: 0, + sender: 'minecraft:chat' + }) +} diff --git a/src/builtinCommands.ts b/src/builtinCommands.ts index 21b06ca6..ede5480e 100644 --- a/src/builtinCommands.ts +++ b/src/builtinCommands.ts @@ -6,6 +6,7 @@ import { closeWan, openToWanAndCopyJoinLink } from './localServerMultiplayer' import { copyFilesAsync, uniqueFileNameFromWorldName } from './browserfs' import { saveServer } from './flyingSquidUtils' import { setLoadingScreenStatus } from './utils' +import { displayClientChat } from './botUtils' const notImplemented = () => { return 'Not implemented yet' @@ -75,9 +76,7 @@ const exportLoadedWorld = async () => { window.exportWorld = exportLoadedWorld const writeText = (text) => { - bot._client.emit('chat', { - message: JSON.stringify({ text }) - }) + displayClientChat(text) } const commands: Array<{ diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 23e898a5..0665b7a6 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -11,12 +11,13 @@ import PItem, { Item } from 'prismarine-item' import { ItemsRenderer } from 'mc-assets/dist/itemsRenderer' import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils' import { getRenamedData } from 'flying-squid/dist/blockRenames' +import PrismarineChatLoader from 'prismarine-chat' import Generic95 from '../assets/generic_95.png' import { appReplacableResources } from './generated/resources' import { activeModalStack, hideCurrentModal, hideModal, miscUiState, showModal } from './globalState' import { options } from './optionsStorage' import { assertDefined, inGameError } from './utils' -import { MessageFormatPart } from './botUtils' +import { displayClientChat, MessageFormatPart } from './botUtils' import { currentScaling } from './scaleInterface' import { getItemDescription } from './itemsDescriptions' @@ -59,11 +60,7 @@ export const onGameLoad = (onLoad) => { openWindow('ChestWin') } else { // todo format - bot._client.emit('chat', { - message: JSON.stringify({ - text: `[client error] cannot open unimplemented window ${win.id} (${win.type}). Slots: ${win.slots.map(item => getItemName(item)).filter(Boolean).join(', ')}` - }) - }) + displayClientChat(`[client error] cannot open unimplemented window ${win.id} (${win.type}). Slots: ${win.slots.map(item => getItemName(item)).filter(Boolean).join(', ')}`) bot.currentWindow?.['close']() } }) @@ -288,6 +285,7 @@ const implementedContainersGuiMap = { 'minecraft:furnace': 'FurnaceWin', 'minecraft:smoker': 'FurnaceWin', 'minecraft:crafting': 'CraftingWin', + 'minecraft:crafting3x3': 'CraftingWin', // todo different result slot 'minecraft:anvil': 'AnvilWin', // enchant 'minecraft:enchanting_table': 'EnchantingWin', @@ -365,7 +363,18 @@ const openWindow = (type: string | undefined) => { cleanLoadedImagesCache() const inv = openItemsCanvas(type) inv.canvasManager.children[0].mobileHelpers = miscUiState.currentTouch - inv.canvasManager.children[0].customTitleText = bot.currentWindow?.title ? fromFormattedString(bot.currentWindow.title).text : undefined + const title = bot.currentWindow?.title + const PrismarineChat = PrismarineChatLoader(bot.version) + try { + inv.canvasManager.children[0].customTitleText = title ? + typeof title === 'string' ? + fromFormattedString(title).text : + new PrismarineChat(title).toString() : + undefined + } catch (err) { + reportError?.(err) + inv.canvasManager.children[0].customTitleText = undefined + } // todo inv.canvasManager.setScale(currentScaling.scale === 1 ? 1.5 : currentScaling.scale) inv.canvas.style.zIndex = '10' diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts index dd5f0aec..dcd5dc3d 100644 --- a/src/worldInteractions.ts +++ b/src/worldInteractions.ts @@ -22,6 +22,7 @@ import { assertDefined } from './utils' import { options } from './optionsStorage' import { itemBeingUsed } from './react/Crosshair' import { isCypress } from './standaloneUtils' +import { displayClientChat } from './botUtils' function getViewDirection (pitch, yaw) { const csPitch = Math.cos(pitch) @@ -263,11 +264,7 @@ class WorldInteraction { hideCurrentModal() } // if (e.message === 'bot is not sleeping') return - bot._client.emit('chat', { - message: JSON.stringify({ - text: e.message, - }) - }) + displayClientChat(e.message) }) setTimeout(() => { cancelSleep = false From 2953554c5302dce09922352cc48c3fb3cdbd3772 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 28 Sep 2024 03:28:27 +0300 Subject: [PATCH 084/851] fix(regression): player walking animation was broken --- package.json | 2 +- pnpm-lock.yaml | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 4b41660a..f8bd1557 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@floating-ui/react": "^0.26.1", "@mui/base": "5.0.0-beta.40", "@nxg-org/mineflayer-auto-jump": "^0.7.12", - "@nxg-org/mineflayer-tracker": "^1.2.3", + "@nxg-org/mineflayer-tracker": "1.2.1", "@react-oauth/google": "^0.12.1", "@stylistic/eslint-plugin": "^2.6.1", "@types/gapi": "^0.0.47", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05b83f3c..6aca0d12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,8 +48,8 @@ importers: specifier: ^0.7.12 version: 0.7.12 '@nxg-org/mineflayer-tracker': - specifier: ^1.2.3 - version: 1.2.3 + specifier: 1.2.1 + version: 1.2.1 '@react-oauth/google': specifier: ^0.12.1 version: 0.12.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -2116,8 +2116,8 @@ packages: '@nxg-org/mineflayer-physics-util@1.5.8': resolution: {integrity: sha512-KmCkAqpUo8BbuRdIBs6+V2hWHehz++PRz3lRwIsb47CuG0u4sgLYh37RY3ifAznC6uWvmPK+q3B4ZXwJzPy1MQ==} - '@nxg-org/mineflayer-tracker@1.2.3': - resolution: {integrity: sha512-E7Ik/scU117Rr6kQUHHMBk8qOGh63YlTCGN33jMfeP7L8xmLeSHN3JtV/fbog8Y+R+HgO99yfZiRAaV7z1T6gQ==} + '@nxg-org/mineflayer-tracker@1.2.1': + resolution: {integrity: sha512-SI1ffF8zvg3/ZNE021Ja2W0FZPN+WbQDZf8yFqOcXtPRXAtM9W6HvoACdzXep8BZid7WYgYLIgjKpB+9RqvCNQ==} '@nxg-org/mineflayer-trajectories@1.1.1': resolution: {integrity: sha512-X103KXlX8+L3uMeK4jQxMUdTizv01sQRSfBizAF/iOAdfQZehRLXr3CYKeJzfwPYGLN0X0JCl++cMEcZVn4vbg==} @@ -5372,7 +5372,7 @@ packages: resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} engines: {node: '>= 4.0'} os: [darwin] - deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2 + deprecated: Upgrade to fsevents v2 to mitigate potential security issues fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -7747,6 +7747,7 @@ packages: range@0.0.3: resolution: {integrity: sha512-OxK2nY2bmeEB4NxoBraQIBOOeOIxoBvm6yt8MA1kLappgkG3SyLf173iOtT5woWycrtESDD2g0Nl2yt8YPoUnw==} engines: {node: '>=0.8'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. raw-body@2.5.1: resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} @@ -8217,6 +8218,10 @@ packages: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + sentence-case@3.0.4: resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} @@ -11523,7 +11528,7 @@ snapshots: dependencies: '@nxg-org/mineflayer-util-plugin': 1.8.3 - '@nxg-org/mineflayer-tracker@1.2.3': + '@nxg-org/mineflayer-tracker@1.2.1': dependencies: '@nxg-org/mineflayer-trajectories': 1.1.1 '@nxg-org/mineflayer-util-plugin': 1.8.3 @@ -17188,7 +17193,7 @@ snapshots: object-assign: 4.1.1 opn: 6.0.0 proxy-middleware: 0.15.0 - send: 0.18.0 + send: 0.19.0 serve-index: 1.9.1 transitivePeerDependencies: - supports-color @@ -19502,6 +19507,24 @@ snapshots: transitivePeerDependencies: - supports-color + send@0.19.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + sentence-case@3.0.4: dependencies: no-case: 3.0.4 From ab5f6ab448377cf9cf01ee0b000dc53e83e68578 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 1 Oct 2024 01:34:42 +0300 Subject: [PATCH 085/851] fix: add fallback peerjs discovery server to bypass geo restrictions and because sometimes official server is down feat: allow to use custom peerjs server via config --- config.json | 2 + prismarine-viewer/viewer/lib/entities.ts | 1 + src/connect.ts | 1 + src/entities.ts | 7 +- src/globalState.ts | 3 + src/index.ts | 11 ++- src/localServerMultiplayer.ts | 86 +++++++++++++++++++++--- src/react/PauseScreen.tsx | 9 ++- 8 files changed, 101 insertions(+), 19 deletions(-) diff --git a/config.json b/config.json index 33095707..7813b591 100644 --- a/config.json +++ b/config.json @@ -3,6 +3,8 @@ "defaultHost": "", "defaultProxy": "proxy.mcraft.fun", "mapsProvider": "https://maps.mcraft.fun/", + "peerJsServer": "", + "peerJsServerFallback": "https://p2p.mcraft.fun", "promoteServers": [ { "ip": "kaboom.pw", diff --git a/prismarine-viewer/viewer/lib/entities.ts b/prismarine-viewer/viewer/lib/entities.ts index ee659263..c7476f6c 100644 --- a/prismarine-viewer/viewer/lib/entities.ts +++ b/prismarine-viewer/viewer/lib/entities.ts @@ -340,6 +340,7 @@ export class Entities extends EventEmitter { } update (entity: import('prismarine-entity').Entity & { delete?; pos }, overrides) { + console.log('entity', entity) const isPlayerModel = entity.name === 'player' if (entity.name === 'zombie' || entity.name === 'zombie_villager' || entity.name === 'husk') { overrides.texture = `textures/1.16.4/entity/${entity.name === 'zombie_villager' ? 'zombie_villager/zombie_villager.png' : `zombie/${entity.name}.png`}` diff --git a/src/connect.ts b/src/connect.ts index 12a1fc5b..40a47669 100644 --- a/src/connect.ts +++ b/src/connect.ts @@ -15,4 +15,5 @@ export type ConnectOptions = { serverIndex?: string /** If true, will show a UI to authenticate with a new account */ authenticatedAccount?: AuthenticatedAccount | true + peerOptions?: any } diff --git a/src/entities.ts b/src/entities.ts index de97fb57..26641f5a 100644 --- a/src/entities.ts +++ b/src/entities.ts @@ -75,10 +75,9 @@ customEvents.on('gameLoaded', () => { const isWalking = Math.abs(speed.x) > WALKING_SPEED || Math.abs(speed.z) > WALKING_SPEED const isSprinting = Math.abs(speed.x) > SPRINTING_SPEED || Math.abs(speed.z) > SPRINTING_SPEED const newAnimation = isWalking ? (isSprinting ? 'running' : 'walking') : 'idle' - const username = e.username! - if (newAnimation !== playerPerAnimation[username]) { + if (newAnimation !== playerPerAnimation[id]) { viewer.entities.playAnimation(e.id, newAnimation) - playerPerAnimation[username] = newAnimation + playerPerAnimation[id] = newAnimation } } }) @@ -122,7 +121,7 @@ customEvents.on('gameLoaded', () => { } viewer.entities.addListener('remove', (e) => { loadedSkinEntityIds.delete(e.id) - playerPerAnimation[e.username] = '' + playerPerAnimation[e.id] = '' bot.tracker.stopTrackingEntity(e, true) }) diff --git a/src/globalState.ts b/src/globalState.ts index b0a447f2..cefa7810 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -109,6 +109,8 @@ export type AppConfig = { defaultProxy?: string // defaultProxySave?: string // defaultVersion?: string + peerJsServer?: string + peerJsServerFallback?: string promoteServers?: Array<{ ip, description, version? }> mapsProvider?: string } @@ -120,6 +122,7 @@ export const miscUiState = proxy({ singleplayer: false, flyingSquid: false, wanOpened: false, + wanOpening: false, /** wether game hud is shown (in playing state) */ gameLoaded: false, showUI: true, diff --git a/src/index.ts b/src/index.ts index db855960..b26619c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -72,7 +72,7 @@ import defaultServerOptions from './defaultLocalServerOptions' import dayCycle from './dayCycle' import { onAppLoad, resourcepackReload } from './resourcePack' -import { connectToPeer } from './localServerMultiplayer' +import { ConnectPeerOptions, connectToPeer } from './localServerMultiplayer' import CustomChannelClient from './customClient' import { loadScript } from 'prismarine-viewer/viewer/lib/utils' import { registerServiceWorker } from './serviceWorker' @@ -486,7 +486,7 @@ async function connect (connectOptions: ConnectOptions) { port: server.port ? +server.port : undefined, version: connectOptions.botVersion || false, ...p2pMultiplayer ? { - stream: await connectToPeer(connectOptions.peerId!), + stream: await connectToPeer(connectOptions.peerId!, connectOptions.peerOptions), } : {}, ...singleplayer || p2pMultiplayer ? { keepAlive: false, @@ -1022,6 +1022,10 @@ downloadAndOpenFile().then((downloadAction) => { void Promise.resolve().then(() => { // try to connect to peer const peerId = qs.get('connectPeer') + const peerOptions = {} as ConnectPeerOptions + if (qs.get('server')) { + peerOptions.server = qs.get('server')! + } const version = qs.get('peerVersion') if (peerId) { let username: string | null = options.guestUsername @@ -1031,7 +1035,8 @@ downloadAndOpenFile().then((downloadAction) => { void connect({ username, botVersion: version || undefined, - peerId + peerId, + peerOptions }) } }) diff --git a/src/localServerMultiplayer.ts b/src/localServerMultiplayer.ts index c8e4bc5e..7d147d0d 100644 --- a/src/localServerMultiplayer.ts +++ b/src/localServerMultiplayer.ts @@ -19,6 +19,8 @@ class CustomDuplex extends Duplex { let peerInstance: Peer | undefined +let overridePeerJsServer = null as string | null + export const getJoinLink = () => { if (!peerInstance) return const url = new URL(window.location.href) @@ -27,6 +29,11 @@ export const getJoinLink = () => { } url.searchParams.set('connectPeer', peerInstance.id) url.searchParams.set('peerVersion', localServer!.options.version) + const host = (overridePeerJsServer ?? miscUiState.appConfig?.peerJsServer) ?? undefined + if (host) { + // TODO! use miscUiState.appConfig.peerJsServer + url.searchParams.set('server', host) + } return url.toString() } @@ -46,8 +53,12 @@ export const openToWanAndCopyJoinLink = async (writeText: (text) => void, doCopy if (doCopy) await copyJoinLink() return 'Already opened to wan. Join link copied' } + miscUiState.wanOpening = true + const host = (overridePeerJsServer ?? miscUiState.appConfig?.peerJsServer) || undefined + const params = host ? parseUrl(host) : undefined const peer = new Peer({ debug: 3, + ...params }) peerInstance = peer peer.on('connection', (connection) => { @@ -83,34 +94,91 @@ export const openToWanAndCopyJoinLink = async (writeText: (text) => void, doCopy connection.on('close', disconnected) connection.on('error', disconnected) }) + const fallbackServer = miscUiState.appConfig?.peerJsServerFallback + const hasFallback = fallbackServer && peer.options.host !== fallbackServer + let hadErrorReported = false peer.on('error', (error) => { - console.error(error) - writeText(error.message) + console.error('peerJS error', error) + if (error.type === 'server-error' && hasFallback) { + return + } + hadErrorReported = true + writeText(error.message || JSON.stringify(error)) }) - return new Promise(resolve => { + let timeout + const destroy = () => { + clearTimeout(timeout) + timeout = undefined + peer.destroy() + peerInstance = undefined + } + + const result = await new Promise(resolve => { peer.on('open', async () => { await copyJoinLink() resolve('Copied join link to clipboard') }) - setTimeout(() => { + timeout = setTimeout(() => { + if (!hadErrorReported && timeout !== undefined) { + writeText('timeout') + } resolve('Failed to open to wan (timeout)') - }, 5000) + }, 6000) + + // fallback + peer.on('error', async (error) => { + if (!peer.open) { + if (hasFallback) { + destroy() + + overridePeerJsServer = fallbackServer + console.log('Trying fallback server', fallbackServer) + resolve((await openToWanAndCopyJoinLink(writeText, doCopy))!) + } + } + }) }) + if (!peerInstance.open) { + destroy() + } + miscUiState.wanOpening = false + return result +} + +const parseUrl = (url: string) => { + // peerJS does this internally for some reason: const url = new URL(`${protocol}://${host}:${port}${path}${key}/${method}`) + if (!url.startsWith('http')) url = `${location.protocol}//${url}` + const urlObj = new URL(url) + const key = urlObj.searchParams.get('key') + return { + host: urlObj.hostname, + path: urlObj.pathname, + protocol: urlObj.protocol.slice(0, -1), + ...urlObj.port ? { port: +urlObj.port } : {}, + ...key ? { key } : {}, + } } export const closeWan = () => { - if (!peerInstance) return - peerInstance.destroy() + peerInstance?.destroy() peerInstance = undefined miscUiState.wanOpened = false - return 'Closed to wan' + return 'Closed WAN' } -export const connectToPeer = async (peerId: string) => { +export type ConnectPeerOptions = { + server?: string +} + +export const connectToPeer = async (peerId: string, options: ConnectPeerOptions = {}) => { setLoadingScreenStatus('Connecting to peer server') // todo destroy connection on error + // TODO! use miscUiState.appConfig.peerJsServer + const host = options.server + const params = host ? parseUrl(host) : undefined const peer = new Peer({ debug: 3, + ...params }) await resolveTimeout(new Promise(resolve => { peer.once('open', resolve) diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index 1c4fdcd4..19f1385c 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -151,7 +151,7 @@ export default () => { const isModalActive = useIsModalActive('pause-screen') const fsStateSnap = useSnapshot(fsState) const activeModalStackSnap = useSnapshot(activeModalStack) - const { singleplayer, wanOpened } = useSnapshot(miscUiState) + const { singleplayer, wanOpened, wanOpening } = useSnapshot(miscUiState) const handlePointerLockChange = () => { if (!pointerLock.hasPointerLock && activeModalStack.length === 0) { @@ -188,7 +188,10 @@ export default () => { return } if (!wanOpened || !qr) { - await openToWanAndCopyJoinLink(() => { }, !qr) + await openToWanAndCopyJoinLink((err) => { + if (!miscUiState.wanOpening) return + alert(`Something went wrong: ${err}`) + }, !qr) } if (qr) { const joinLink = getJoinLink() @@ -230,7 +233,7 @@ export default () => { {singleplayer ? (
{(navigator.share as typeof navigator.share | undefined) ? ( {(navigator.share as typeof navigator.share | undefined) ? ( ) + } else if (button.type === 'url' && button.text) { + rowButtons.push() + } + } + pauseLinks.push(
{rowButtons}
) + } + } + return -
- - -
+ {pauseLinks} {singleplayer ? (
From 52ae41a78d24b1bb95e26eb24d9b4b3b5cb977f0 Mon Sep 17 00:00:00 2001 From: Max Lee Date: Wed, 26 Feb 2025 20:33:50 +0100 Subject: [PATCH 387/851] Add better chat link prompt screen (#290) --- src/react/MessageFormatted.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/react/MessageFormatted.tsx b/src/react/MessageFormatted.tsx index 95204b26..554d5a9b 100644 --- a/src/react/MessageFormatted.tsx +++ b/src/react/MessageFormatted.tsx @@ -6,6 +6,7 @@ import { openURL } from 'renderer/viewer/lib/simpleUtils' import { MessageFormatPart } from '../chatUtils' import { chatInputValueGlobal } from './Chat' import './MessageFormatted.css' +import { showOptionsModal } from './SelectOption' const hoverItemToText = (hoverEvent: MessageFormatPart['hoverEvent']) => { try { @@ -42,17 +43,21 @@ const clickEventToProps = (clickEvent: MessageFormatPart['clickEvent']) => { } } } - if (clickEvent.action === 'open_url') { + if (clickEvent.action === 'open_url' || clickEvent.action === 'open_file') { return { - onClick () { - const confirm = window.confirm(`Open ${clickEvent.value}?`) - if (confirm) { + async onClick () { + const promptMessageText = `Open "${clickEvent.value}"?` + const confirm = await showOptionsModal(promptMessageText, ['Open', 'Copy'], { + cancel: true + }) + if (confirm === 'Open') { openURL(clickEvent.value) + } else if (confirm === 'Copy') { + void navigator.clipboard.writeText(clickEvent.value) } } } } - //@ts-expect-error todo if (clickEvent.action === 'copy_to_clipboard') { return { onClick () { @@ -71,6 +76,7 @@ export const MessagePart = ({ part, ...props }: { part: MessageFormatPart } & Co const hoverItemText = hoverMessageRaw && typeof hoverMessageRaw !== 'string' ? render(hoverMessageRaw).children.map(child => child.component.text).join('') : hoverMessageRaw const applyStyles = [ + clickProps && messageFormatStylesMap.clickEvent, color ? colorF(color.toLowerCase()) + `; text-shadow: 1px 1px 0px ${getColorShadow(colorF(color.toLowerCase()).replace('color:', ''))}` : messageFormatStylesMap.white, italic && messageFormatStylesMap.italic, bold && messageFormatStylesMap.bold, @@ -80,7 +86,7 @@ export const MessagePart = ({ part, ...props }: { part: MessageFormatPart } & Co obfuscated && messageFormatStylesMap.obfuscated ].filter(a => a !== false && a !== undefined).filter(Boolean) - return {text} + return {text} } export default ({ parts, className }: { parts: readonly MessageFormatPart[], className?: string }) => { @@ -138,4 +144,5 @@ export const messageFormatStylesMap = { underlined: 'text-decoration:underline', italic: 'font-style:italic', obfuscated: 'filter:blur(2px)', + clickEvent: 'cursor:pointer', } From edad57a225c98809c7877039ffee01fafa1df516 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 22:56:02 +0300 Subject: [PATCH 388/851] feat: allow to load client without free space on device (or no write permissions) --- src/browserfs.ts | 25 ++++++++++++++++++++++--- src/dragndrop.ts | 2 +- src/globalState.ts | 1 + src/react/MainMenu.tsx | 7 +++++-- src/react/MainMenuRenderApp.tsx | 3 ++- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/browserfs.ts b/src/browserfs.ts index d38b13a2..41608e30 100644 --- a/src/browserfs.ts +++ b/src/browserfs.ts @@ -14,22 +14,41 @@ import { VALID_REPLAY_EXTENSIONS, openFile } from './packetsReplay/replayPackets import { getFixedFilesize } from './downloadAndOpenFile' import { packetsReplayState } from './react/state/packetsReplayState' import { createFullScreenProgressReporter } from './core/progressReporter' +import { showNotification } from './react/NotificationProvider' const { GoogleDriveFileSystem } = require('google-drive-browserfs/src/backends/GoogleDrive') browserfs.install(window) const defaultMountablePoints = { - '/world': { fs: 'LocalStorage' }, // will be removed in future '/data': { fs: 'IndexedDB' }, '/resourcepack': { fs: 'InMemory' }, // temporary storage for currently loaded resource pack + '/temp': { fs: 'InMemory' } +} +const fallbackMountablePoints = { + '/resourcepack': { fs: 'InMemory' }, // temporary storage for downloaded server resource pack + '/temp': { fs: 'InMemory' } } browserfs.configure({ fs: 'MountableFileSystem', options: defaultMountablePoints, }, async (e) => { - // todo disable singleplayer button - if (e) throw e + if (e) { + browserfs.configure({ + fs: 'MountableFileSystem', + options: fallbackMountablePoints, + }, async (e2) => { + if (e2) { + showNotification('Unknown FS error, cannot continue', e2.message, true) + throw e2 + } + showNotification('Failed to access device storage', `Check you have free space. ${e.message}`, true) + miscUiState.appLoaded = true + miscUiState.singleplayerAvailable = false + }) + return + } await updateTexturePackInstalledState() miscUiState.appLoaded = true + miscUiState.singleplayerAvailable = true }) export const forceCachedDataPaths = {} diff --git a/src/dragndrop.ts b/src/dragndrop.ts index 6c8af856..6be90551 100644 --- a/src/dragndrop.ts +++ b/src/dragndrop.ts @@ -64,7 +64,7 @@ async function handleDroppedFile (file: File) { return } if (file.name.endsWith('.mca')) { - const tempPath = '/data/temp.mca' + const tempPath = '/temp/temp.mca' try { await fs.promises.writeFile(tempPath, Buffer.from(await file.arrayBuffer()) as any) const region = new RegionFile(tempPath) diff --git a/src/globalState.ts b/src/globalState.ts index cc9cd127..74e6c3ff 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -145,6 +145,7 @@ export const miscUiState = proxy({ /** currently trying to load or loaded mc version, after all data is loaded */ loadedDataVersion: null as string | null, appLoaded: false, + singleplayerAvailable: false, usingGamepadInput: false, appConfig: null as AppConfig | null, displaySearchInput: false, diff --git a/src/react/MainMenu.tsx b/src/react/MainMenu.tsx index fc770ad1..09214af2 100644 --- a/src/react/MainMenu.tsx +++ b/src/react/MainMenu.tsx @@ -24,6 +24,7 @@ interface Props { bottomRightLinks?: string versionText?: string onVersionTextClick?: () => void + singleplayerAvailable?: boolean } const httpsRegex = /^https?:\/\// @@ -41,7 +42,8 @@ export default ({ versionStatus, versionTitle, onVersionStatusClick, - bottomRightLinks + bottomRightLinks, + singleplayerAvailable = true }: Props) => { if (!bottomRightLinks?.trim()) bottomRightLinks = undefined // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion @@ -107,6 +109,7 @@ export default ({ style={{ width: 150 }} {...singleplayerLongPress} data-test-id='singleplayer-button' + disabled={!singleplayerAvailable} initialTooltip={{ content: 'Create worlds and play offline', placement: 'left', @@ -183,7 +186,7 @@ export default ({
})}
- A Minecraft client in the browser! + A Minecraft client clone in the browser!
diff --git a/src/react/MainMenuRenderApp.tsx b/src/react/MainMenuRenderApp.tsx index b12d9ba8..e06ced52 100644 --- a/src/react/MainMenuRenderApp.tsx +++ b/src/react/MainMenuRenderApp.tsx @@ -75,7 +75,7 @@ export const mainMenuState = proxy({ let disableAnimation = false export default () => { const haveModals = useSnapshot(activeModalStack).length - const { gameLoaded, appLoaded, appConfig } = useSnapshot(miscUiState) + const { gameLoaded, appLoaded, appConfig, singleplayerAvailable } = useSnapshot(miscUiState) const noDisplay = haveModals || gameLoaded || !appLoaded @@ -118,6 +118,7 @@ export default () => { return {(state) =>
showModal({ reactType: 'serversList' })} singleplayerAction={async () => { const oldFormatSave = fs.existsSync('./world/level.dat') From 2414111b9cf24ebbe75948234ed33c20cc4c6e50 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 23:29:18 +0300 Subject: [PATCH 389/851] feat: add packets recording control to pause menu, display packets view after recording was started for in real time server packets debug, fix auto captured packets display --- package.json | 2 +- pnpm-lock.yaml | 12 ++--- src/mineflayer/plugins/localRelay.ts | 59 +++++++++++++++++++++--- src/packetsReplay/packetsReplayLegacy.ts | 10 ++-- src/react/AppStatusProvider.tsx | 2 +- src/react/PauseScreen.tsx | 25 ++++++++-- 6 files changed, 86 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index f3f2f644..b67a2b03 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "mojangson": "^2.0.4", "net-browserify": "github:zardoy/prismarinejs-net-browserify", "node-gzip": "^1.1.2", - "mcraft-fun-mineflayer": "^0.1.6", + "mcraft-fun-mineflayer": "^0.1.7", "peerjs": "^1.5.0", "pixelarticons": "^1.8.1", "pretty-bytes": "^6.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7060521d..3386ac03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,8 +135,8 @@ importers: specifier: ^4.17.21 version: 4.17.21 mcraft-fun-mineflayer: - specifier: ^0.1.6 - version: 0.1.6(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) + specifier: ^0.1.7 + version: 0.1.7(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) minecraft-data: specifier: 3.83.1 version: 3.83.1 @@ -6252,9 +6252,9 @@ packages: resolution: {integrity: sha512-49tk3shwxsDoV0PrrbORZEKg613vUQPULgusWjXNl8JEma5y41LEo57D6q4aHliXsV3Gb9ThrkFf6hIb0WlY1Q==} engines: {node: '>=18.0.0'} - mcraft-fun-mineflayer@0.1.6: - resolution: {integrity: sha512-ifaIL//NJtkGcYasEULy0KcJjwUA8BwcmC/KoIpTTj6Xmk5o8AYEVdUnR9jrir4kpiLBOIbHgG1QhY1Wbofl1g==} - version: 0.1.6 + mcraft-fun-mineflayer@0.1.7: + resolution: {integrity: sha512-DJPpp1YFwztoscdIOwfqBV9lbotva621F9GEep3BlqG3l06UdTzX2ElkvwyTR0IurFFBX/YKsNfxwL5WtLytgw==} + version: 0.1.7 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: '@roamhq/wrtc': '*' @@ -16691,7 +16691,7 @@ snapshots: apl-image-packer: 1.1.0 zod: 3.24.1 - mcraft-fun-mineflayer@0.1.6(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): + mcraft-fun-mineflayer@0.1.7(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): dependencies: '@zardoy/flying-squid': 0.0.49(encoding@0.1.13) exit-hook: 2.2.1 diff --git a/src/mineflayer/plugins/localRelay.ts b/src/mineflayer/plugins/localRelay.ts index e6c8cfe5..ab8527e5 100644 --- a/src/mineflayer/plugins/localRelay.ts +++ b/src/mineflayer/plugins/localRelay.ts @@ -3,9 +3,22 @@ import { PACKETS_REPLAY_FILE_EXTENSION, WORLD_STATE_FILE_EXTENSION } from 'mcraf import { Bot } from 'mineflayer' import CircularBuffer from 'flying-squid/dist/circularBuffer' import { PacketsLogger } from 'mcraft-fun-mineflayer/build/packetsLogger' +import { subscribe } from 'valtio' import { lastConnectOptions } from '../../react/AppStatusProvider' +import { packetsReplaceSessionState } from '../../packetsReplay/packetsReplayLegacy' +import { packetsReplayState } from '../../react/state/packetsReplayState' + +const AUTO_CAPTURE_PACKETS_COUNT = 30 +let circularBuffer: CircularBuffer | undefined +let lastConnectVersion = '' export const localRelayServerPlugin = (bot: Bot) => { + lastConnectVersion = bot.version + let ended = false + bot.on('end', () => { + ended = true + }) + bot.loadPlugin( viewerConnector({ tcpEnabled: false, @@ -28,28 +41,60 @@ export const localRelayServerPlugin = (bot: Bot) => { } circularBuffer = new CircularBuffer(AUTO_CAPTURE_PACKETS_COUNT) + let position = 0 bot._client.on('writePacket' as any, (name, params) => { - circularBuffer!.add({ name, params, isFromServer: false }) + circularBuffer!.add({ name, state: bot._client.state, params, isFromServer: false, timestamp: Date.now() }) + if (packetsReplaceSessionState.active) { + packetsReplayState.packetsPlayback.push({ + name, + data: params, + isFromClient: true, + isUpcoming: false, + position: position++, + timestamp: Date.now(), + }) + } }) bot._client.on('packet', (data, { name }) => { - circularBuffer!.add({ name, params: data, isFromServer: true }) + if (name === 'map_chunk') data = { x: data.x, z: data.z } + circularBuffer!.add({ name, state: bot._client.state, params: data, isFromServer: true, timestamp: Date.now() }) + if (packetsReplaceSessionState.active) { + packetsReplayState.packetsPlayback.push({ + name, + data, + isFromClient: false, + isUpcoming: false, + position: position++, + timestamp: Date.now(), + }) + } }) + + upPacketsReplayPanel() } +const upPacketsReplayPanel = () => { + if (packetsReplaceSessionState.active && bot) { + packetsReplayState.isOpen = true + packetsReplayState.replayName = 'Recording all packets for ' + bot.username + } +} + +subscribe(packetsReplaceSessionState, () => { + upPacketsReplayPanel() +}) + declare module 'mineflayer' { interface Bot { downloadCurrentWorldState: () => void } } -const AUTO_CAPTURE_PACKETS_COUNT = 30 -let circularBuffer: CircularBuffer | undefined - export const getLastAutoCapturedPackets = () => circularBuffer?.size export const downloadAutoCapturedPackets = () => { - const logger = new PacketsLogger() + const logger = new PacketsLogger({ minecraftVersion: lastConnectVersion }) for (const packet of circularBuffer?.getLastElements() ?? []) { - logger.log(packet.isFromServer, packet.name, packet.params) + logger.log(packet.isFromServer, { name: packet.name, state: packet.state, time: packet.timestamp }, packet.params) } const textContents = logger.contents const blob = new Blob([textContents], { type: 'text/plain' }) diff --git a/src/packetsReplay/packetsReplayLegacy.ts b/src/packetsReplay/packetsReplayLegacy.ts index 0db97012..478bbc5d 100644 --- a/src/packetsReplay/packetsReplayLegacy.ts +++ b/src/packetsReplay/packetsReplayLegacy.ts @@ -7,7 +7,8 @@ export const packetsReplaceSessionState = proxy({ hasRecordedPackets: false }) -export const replayLogger = new PacketsLogger() +// eslint-disable-next-line import/no-mutable-exports +export let replayLogger: PacketsLogger | undefined const isBufferData = (data: any): boolean => { if (Buffer.isBuffer(data) || data instanceof Uint8Array) return true @@ -35,13 +36,14 @@ const processPacketData = (data: any): any => { export default () => { customEvents.on('mineflayerBotCreated', () => { + replayLogger = new PacketsLogger({ minecraftVersion: bot.version }) replayLogger.contents = '' packetsReplaceSessionState.hasRecordedPackets = false const handleServerPacket = (data, { name, state = bot._client.state }) => { if (!packetsReplaceSessionState.active) { return } - replayLogger.log(true, { name, state }, processPacketData(data)) + replayLogger!.log(true, { name, state }, processPacketData(data)) packetsReplaceSessionState.hasRecordedPackets = true } bot._client.on('packet', handleServerPacket) @@ -53,7 +55,7 @@ export default () => { if (!packetsReplaceSessionState.active) { return } - replayLogger.log(false, { name, state: bot._client.state }, processPacketData(data)) + replayLogger!.log(false, { name, state: bot._client.state }, processPacketData(data)) packetsReplaceSessionState.hasRecordedPackets = true }) }) @@ -61,7 +63,7 @@ export default () => { export const downloadPacketsReplay = async () => { const a = document.createElement('a') - a.href = `data:text/plain;charset=utf-8,${encodeURIComponent(replayLogger.contents)}` + a.href = `data:text/plain;charset=utf-8,${encodeURIComponent(replayLogger!.contents)}` a.download = `packets-replay-${new Date().toISOString()}.txt` a.click() } diff --git a/src/react/AppStatusProvider.tsx b/src/react/AppStatusProvider.tsx index a3ef4c9d..e83416c1 100644 --- a/src/react/AppStatusProvider.tsx +++ b/src/react/AppStatusProvider.tsx @@ -147,7 +147,7 @@ export default () => { <> {displayAuthButton &&
error
}>
From dffadbb06c56ef9288d0ed61758f63724dd3d7e6 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 26 Feb 2025 23:33:13 +0300 Subject: [PATCH 390/851] wip jei channel --- src/customChannels.ts | 36 +++++++++++++++++++++++++++++- src/packetsReplay/replayPackets.ts | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/customChannels.ts b/src/customChannels.ts index 71dc7067..35922101 100644 --- a/src/customChannels.ts +++ b/src/customChannels.ts @@ -8,7 +8,10 @@ customEvents.on('mineflayerBotCreated', async () => { resolve(true) }) }) + registerBlockModelsChannel() +}) +const registerBlockModelsChannel = () => { const CHANNEL_NAME = 'minecraft-web-client:blockmodels' const packetStructure = [ @@ -72,4 +75,35 @@ customEvents.on('mineflayerBotCreated', async () => { }) console.debug(`registered custom channel ${CHANNEL_NAME} channel`) -}) +} + +const registeredJeiChannel = () => { + const CHANNEL_NAME = 'minecraft-web-client:jei' + // id - string, categoryTitle - string, items - string (json array) + const packetStructure = [ + 'container', + [ + { + name: 'id', + type: 'pstring', + }, + { + name: 'categoryTitle', + type: 'pstring', + }, + { + name: 'items', + type: 'pstring', + }, + ] + ] + + bot._client.registerChannel(CHANNEL_NAME, packetStructure, true) + + bot._client.on(CHANNEL_NAME as any, (data) => { + const { id, categoryTitle, items } = data + // ... + }) + + console.debug(`registered custom channel ${CHANNEL_NAME} channel`) +} diff --git a/src/packetsReplay/replayPackets.ts b/src/packetsReplay/replayPackets.ts index e3935258..9e777b38 100644 --- a/src/packetsReplay/replayPackets.ts +++ b/src/packetsReplay/replayPackets.ts @@ -1,7 +1,7 @@ /* eslint-disable no-await-in-loop */ import { createServer, ServerClient } from 'minecraft-protocol' import { ParsedReplayPacket, parseReplayContents } from 'mcraft-fun-mineflayer/build/packetsLogger' -import { WorldStateHeader, PACKETS_REPLAY_FILE_EXTENSION, WORLD_STATE_FILE_EXTENSION } from 'mcraft-fun-mineflayer/build/worldState' +import { PACKETS_REPLAY_FILE_EXTENSION, WORLD_STATE_FILE_EXTENSION } from 'mcraft-fun-mineflayer/build/worldState' import MinecraftData from 'minecraft-data' import { LocalServer } from '../customServer' import { UserError } from '../mineflayer/userError' From d348a44bb86cb1678f7727b92c2018acbc0eaf51 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Feb 2025 04:48:03 +0300 Subject: [PATCH 391/851] add a way to disable recording button on pause menu, refactor --- src/index.ts | 2 +- .../{localRelay.ts => packetsRecording.ts} | 10 +++--- src/optionsGuiScheme.tsx | 8 ++--- src/optionsStorage.ts | 2 +- src/packetsReplay/packetsReplayLegacy.ts | 12 +++---- src/react/AppStatusProvider.tsx | 6 ++-- src/react/Chat.tsx | 1 + src/react/PauseScreen.tsx | 34 +++++++++++-------- 8 files changed, 41 insertions(+), 34 deletions(-) rename src/mineflayer/plugins/{localRelay.ts => packetsRecording.ts} (92%) diff --git a/src/index.ts b/src/index.ts index 01912e94..249fbe9d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -115,7 +115,7 @@ import { UserError } from './mineflayer/userError' import ping from './mineflayer/plugins/ping' import { LocalServer } from './customServer' import { startLocalReplayServer } from './packetsReplay/replayPackets' -import { localRelayServerPlugin } from './mineflayer/plugins/localRelay' +import { localRelayServerPlugin } from './mineflayer/plugins/packetsRecording' import { createFullScreenProgressReporter } from './core/progressReporter' window.debug = debug diff --git a/src/mineflayer/plugins/localRelay.ts b/src/mineflayer/plugins/packetsRecording.ts similarity index 92% rename from src/mineflayer/plugins/localRelay.ts rename to src/mineflayer/plugins/packetsRecording.ts index ab8527e5..f1ff18cf 100644 --- a/src/mineflayer/plugins/localRelay.ts +++ b/src/mineflayer/plugins/packetsRecording.ts @@ -5,7 +5,7 @@ import CircularBuffer from 'flying-squid/dist/circularBuffer' import { PacketsLogger } from 'mcraft-fun-mineflayer/build/packetsLogger' import { subscribe } from 'valtio' import { lastConnectOptions } from '../../react/AppStatusProvider' -import { packetsReplaceSessionState } from '../../packetsReplay/packetsReplayLegacy' +import { packetsRecordingState } from '../../packetsReplay/packetsReplayLegacy' import { packetsReplayState } from '../../react/state/packetsReplayState' const AUTO_CAPTURE_PACKETS_COUNT = 30 @@ -44,7 +44,7 @@ export const localRelayServerPlugin = (bot: Bot) => { let position = 0 bot._client.on('writePacket' as any, (name, params) => { circularBuffer!.add({ name, state: bot._client.state, params, isFromServer: false, timestamp: Date.now() }) - if (packetsReplaceSessionState.active) { + if (packetsRecordingState.active) { packetsReplayState.packetsPlayback.push({ name, data: params, @@ -58,7 +58,7 @@ export const localRelayServerPlugin = (bot: Bot) => { bot._client.on('packet', (data, { name }) => { if (name === 'map_chunk') data = { x: data.x, z: data.z } circularBuffer!.add({ name, state: bot._client.state, params: data, isFromServer: true, timestamp: Date.now() }) - if (packetsReplaceSessionState.active) { + if (packetsRecordingState.active) { packetsReplayState.packetsPlayback.push({ name, data, @@ -74,13 +74,13 @@ export const localRelayServerPlugin = (bot: Bot) => { } const upPacketsReplayPanel = () => { - if (packetsReplaceSessionState.active && bot) { + if (packetsRecordingState.active && bot) { packetsReplayState.isOpen = true packetsReplayState.replayName = 'Recording all packets for ' + bot.username } } -subscribe(packetsReplaceSessionState, () => { +subscribe(packetsRecordingState, () => { upPacketsReplayPanel() }) diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index 6f4d4fea..7032e644 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -11,7 +11,7 @@ import { getScreenRefreshRate } from './utils' import { setLoadingScreenStatus } from './appStatus' import { openFilePicker, resetLocalStorageWithoutWorld } from './browserfs' import { completeResourcepackPackInstall, getResourcePackNames, resourcePackState, uninstallResourcePack } from './resourcePack' -import { downloadPacketsReplay, packetsReplaceSessionState } from './packetsReplay/packetsReplayLegacy' +import { downloadPacketsReplay, packetsRecordingState } from './packetsReplay/packetsReplayLegacy' import { showOptionsModal } from './react/SelectOption' import supportedVersions from './supportedVersions.mjs' import { getVersionAutoSelect } from './connect' @@ -462,18 +462,18 @@ export const guiOptionsScheme: { }, { custom () { - const { active } = useSnapshot(packetsReplaceSessionState) + const { active } = useSnapshot(packetsRecordingState) return }, }, { custom () { - const { active, hasRecordedPackets } = useSnapshot(packetsReplaceSessionState) + const { active, hasRecordedPackets } = useSnapshot(packetsRecordingState) return
error
}> From dec93c2b64c185b251ebb092c3b3519d206fb890 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Feb 2025 04:48:16 +0300 Subject: [PATCH 392/851] fix react warning --- src/react/PauseScreen.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index d7aa2201..4c4dec03 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -227,19 +227,19 @@ export default () => { if (!isModalActive) return null - const pauseLinks: any[] = [] + const pauseLinks: React.ReactNode[] = [] const pauseLinksConfig = miscUiState.appConfig?.pauseLinks if (pauseLinksConfig) { - for (const row of pauseLinksConfig) { - const rowButtons: any[] = [] + for (const [i, row] of pauseLinksConfig.entries()) { + const rowButtons: React.ReactNode[] = [] for (const button of row) { const style = { width: (204 / row.length - (row.length > 1 ? 4 : 0)) + 'px' } if (button.type === 'discord') { - rowButtons.push() + rowButtons.push() } else if (button.type === 'github') { - rowButtons.push() + rowButtons.push() } else if (button.type === 'url' && button.text) { - rowButtons.push() + rowButtons.push() } } pauseLinks.push(
{rowButtons}
) From fa9c0813c3214cceea51de13ce844b6b86006260 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Feb 2025 05:17:18 +0300 Subject: [PATCH 393/851] fix: seagrass and kelp are always waterlogged --- renderer/viewer/lib/mesher/models.ts | 15 ++++++++++----- renderer/viewer/lib/mesher/world.ts | 13 ------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/renderer/viewer/lib/mesher/models.ts b/renderer/viewer/lib/mesher/models.ts index 0dec4015..dd2952fb 100644 --- a/renderer/viewer/lib/mesher/models.ts +++ b/renderer/viewer/lib/mesher/models.ts @@ -43,10 +43,6 @@ function prepareTints (tints) { }) } -function mod (x: number, n: number) { - return ((x % n) + n) % n -} - const calculatedBlocksEntries = Object.entries(legacyJson.clientCalculatedBlocks) export function preflatBlockCalculation (block: Block, world: World, position: Vec3) { const type = calculatedBlocksEntries.find(([name, blocks]) => blocks.includes(block.name))?.[0] @@ -439,7 +435,16 @@ function renderElement (world: World, cursor: Vec3, element: BlockElement, doAO: } } -const isBlockWaterlogged = (block: Block) => block.getProperties().waterlogged === true || block.getProperties().waterlogged === 'true' +const ALWAYS_WATERLOGGED = new Set([ + 'seagrass', + 'tall_seagrass', + 'kelp', + 'kelp_plant', + 'bubble_column' +]) +const isBlockWaterlogged = (block: Block) => { + return block.getProperties().waterlogged === true || block.getProperties().waterlogged === 'true' || ALWAYS_WATERLOGGED.has(block.name) +} let unknownBlockModel: BlockModelPartsResolved export function getSectionGeometry (sx, sy, sz, world: World) { diff --git a/renderer/viewer/lib/mesher/world.ts b/renderer/viewer/lib/mesher/world.ts index e13bf760..f2757ae6 100644 --- a/renderer/viewer/lib/mesher/world.ts +++ b/renderer/viewer/lib/mesher/world.ts @@ -10,14 +10,6 @@ import { INVISIBLE_BLOCKS } from './worldConstants' const ignoreAoBlocks = Object.keys(moreBlockDataGeneratedJson.noOcclusions) -const ALWAYS_WATERLOGGED = new Set([ - 'seagrass', - 'tall_seagrass', - 'kelp', - 'kelp_plant', - 'bubble_column' -]) - function columnKey (x, z) { return `${x},${z}` } @@ -178,11 +170,6 @@ export class World { if (!attr) throw new Error('attr is required') const props = block.getProperties() - // Patch waterlogged property for ocean plants - if (ALWAYS_WATERLOGGED.has(block.name)) { - props.waterlogged = 'true' - } - try { // fixme if (this.preflat) { From ceb4cb0b66644962da2afbbf31d4faf6ec35cd76 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Thu, 27 Feb 2025 15:26:38 +0300 Subject: [PATCH 394/851] feat: Refactor mouse controls, fixing all false entity/item interaction issues (#286) --- package.json | 1 + pnpm-lock.yaml | 858 ++++++++++++++++++++++++++- src/cameraRotationControls.ts | 14 +- src/devtools.ts | 5 +- src/globals.d.ts | 2 +- src/index.ts | 11 +- src/mineflayer/plugins/mouse.ts | 204 +++++++ src/optionsStorage.ts | 1 + src/react/GameInteractionOverlay.tsx | 3 +- src/react/TouchAreasControls.tsx | 10 +- src/worldInteractions.ts | 551 ----------------- 11 files changed, 1066 insertions(+), 594 deletions(-) create mode 100644 src/mineflayer/plugins/mouse.ts delete mode 100644 src/worldInteractions.ts diff --git a/package.json b/package.json index b67a2b03..b0c5ee20 100644 --- a/package.json +++ b/package.json @@ -145,6 +145,7 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", + "mineflayer-mouse": "^0.0.4", "mc-assets": "^0.2.37", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3386ac03..be7ce2e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -282,7 +282,7 @@ importers: version: 7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) '@storybook/react-vite': specifier: ^7.4.6 - version: 7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@2.79.1)(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3)) + version: 7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@2.79.1)(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1)) '@types/diff-match-patch': specifier: ^1.0.36 version: 1.0.36 @@ -358,6 +358,9 @@ importers: mineflayer: specifier: github:zardoy/mineflayer version: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13) + mineflayer-mouse: + specifier: ^0.0.4 + version: 0.0.4(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -1265,6 +1268,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.18.20': resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} engines: {node: '>=12'} @@ -1277,6 +1286,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.18.20': resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} engines: {node: '>=12'} @@ -1289,6 +1304,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.18.20': resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} engines: {node: '>=12'} @@ -1301,6 +1322,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.18.20': resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} engines: {node: '>=12'} @@ -1313,6 +1340,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.18.20': resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} engines: {node: '>=12'} @@ -1325,6 +1358,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.18.20': resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} engines: {node: '>=12'} @@ -1337,6 +1376,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.18.20': resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} engines: {node: '>=12'} @@ -1349,6 +1394,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.18.20': resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} engines: {node: '>=12'} @@ -1361,6 +1412,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.18.20': resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} engines: {node: '>=12'} @@ -1373,6 +1430,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.18.20': resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} engines: {node: '>=12'} @@ -1385,6 +1448,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.18.20': resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} engines: {node: '>=12'} @@ -1397,6 +1466,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.18.20': resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} engines: {node: '>=12'} @@ -1409,6 +1484,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.18.20': resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} engines: {node: '>=12'} @@ -1421,6 +1502,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.18.20': resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} engines: {node: '>=12'} @@ -1433,6 +1520,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.18.20': resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} engines: {node: '>=12'} @@ -1445,6 +1538,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.18.20': resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} engines: {node: '>=12'} @@ -1457,6 +1556,18 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.18.20': resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} engines: {node: '>=12'} @@ -1469,6 +1580,18 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.18.20': resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} engines: {node: '>=12'} @@ -1481,6 +1604,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.18.20': resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} engines: {node: '>=12'} @@ -1493,6 +1622,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.18.20': resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} engines: {node: '>=12'} @@ -1505,6 +1640,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.18.20': resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} engines: {node: '>=12'} @@ -1517,6 +1658,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.18.20': resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} engines: {node: '>=12'} @@ -1529,6 +1676,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1813,6 +1966,9 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} @@ -2244,6 +2400,101 @@ packages: rollup: optional: true + '@rollup/rollup-android-arm-eabi@4.34.8': + resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.34.8': + resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.34.8': + resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.34.8': + resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.34.8': + resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.34.8': + resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.34.8': + resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.34.8': + resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.34.8': + resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.34.8': + resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.34.8': + resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.34.8': + resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.34.8': + resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.34.8': + resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.34.8': + resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.34.8': + resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} + cpu: [x64] + os: [win32] + '@rsbuild/core@1.0.1-beta.9': resolution: {integrity: sha512-F9npL47TFmNVhPBqoE6jBvKGxXEKNszBA7skhbi3opskmX7Ako9vfXvtgi2W2jQjq837/WUL8gG/ua9zRqKFEQ==} engines: {node: '>=16.7.0'} @@ -2722,6 +2973,9 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/express-serve-static-core@4.17.37': resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==} @@ -3023,18 +3277,47 @@ packages: '@vitest/expect@0.34.6': resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + '@vitest/expect@3.0.7': + resolution: {integrity: sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==} + + '@vitest/mocker@3.0.7': + resolution: {integrity: sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.0.7': + resolution: {integrity: sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==} + '@vitest/runner@0.34.6': resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + '@vitest/runner@3.0.7': + resolution: {integrity: sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==} + '@vitest/snapshot@0.34.6': resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + '@vitest/snapshot@3.0.7': + resolution: {integrity: sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==} + '@vitest/spy@0.34.6': resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + '@vitest/spy@3.0.7': + resolution: {integrity: sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==} + '@vitest/utils@0.34.6': resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + '@vitest/utils@3.0.7': + resolution: {integrity: sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==} + '@webassemblyjs/ast@1.12.1': resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} @@ -3418,6 +3701,10 @@ packages: assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + assign-symbols@1.0.0: resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} engines: {node: '>=0.10.0'} @@ -3777,6 +4064,10 @@ packages: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3795,6 +4086,9 @@ packages: change-case@5.1.2: resolution: {integrity: sha512-CAtbGEDulyjzs05RXy3uKcwqeztz/dMEuAc1Xu9NQBsbrhuGMneL0u9Dj5SoutLKBFYun8txxYIwhjtLNfUmCA==} + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} @@ -3804,6 +4098,10 @@ packages: check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + check-more-types@2.24.0: resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} engines: {node: '>= 0.8.0'} @@ -4220,6 +4518,10 @@ packages: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -4541,6 +4843,9 @@ packages: es-module-lexer@1.5.4: resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -4586,6 +4891,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -4794,6 +5104,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -4849,6 +5162,10 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} + exponential-backoff@3.1.1: resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} @@ -6161,6 +6478,9 @@ packages: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} deprecated: Please upgrade to 2.3.7 which fixes GHSA-4q6p-r6v2-jvc5 + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -6189,6 +6509,9 @@ packages: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.4: resolution: {integrity: sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==} engines: {node: '>=12'} @@ -6478,6 +6801,10 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824} version: 1.2.0 + mineflayer-mouse@0.0.4: + resolution: {integrity: sha512-55GqQhJWCXnOnm30uOjtI7nsawPb0kA3cAv6a5n1NJjTWFR6hzMkiRT6xGLYrvYhdf6Er3nsE2Ok/Aysa/jtFQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + mineflayer-pathfinder@2.4.4: resolution: {integrity: sha512-HAXakZrJRb1UC+5dv8EaDrqjW3ZnBnBk3nkb6x/YWyhHCUKn/E7VU0FO+UN9whuqPlkSaVumEdXJdydE6lSYxQ==} @@ -6615,6 +6942,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanomatch@1.2.13: resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} engines: {node: '>=0.10.0'} @@ -7006,9 +7338,16 @@ packages: pathe@1.1.1: resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} @@ -7122,6 +7461,10 @@ packages: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + potpack@1.0.2: resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} @@ -7810,6 +8153,11 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true + rollup@4.34.8: + resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rope-sequence@1.3.4: resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} @@ -8199,6 +8547,9 @@ packages: std-env@3.4.3: resolution: {integrity: sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==} + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + store2@2.14.2: resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==} @@ -8441,17 +8792,35 @@ packages: tinybench@2.5.1: resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinypool@0.7.0: resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} engines: {node: '>=14.0.0'} + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + tinyspy@2.2.0: resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} engines: {node: '>=14.0.0'} + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + title-case@3.0.3: resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} @@ -8906,6 +9275,11 @@ packages: engines: {node: '>=v14.18.0'} hasBin: true + vite-node@3.0.7: + resolution: {integrity: sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + vite@4.5.3: resolution: {integrity: sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -8934,6 +9308,46 @@ packages: terser: optional: true + vite@6.2.0: + resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitest@0.34.6: resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} engines: {node: '>=v14.18.0'} @@ -8965,6 +9379,34 @@ packages: webdriverio: optional: true + vitest@3.0.7: + resolution: {integrity: sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.0.7 + '@vitest/ui': 3.0.7 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} @@ -9067,6 +9509,11 @@ packages: engines: {node: '>=8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} @@ -10255,138 +10702,213 @@ snapshots: '@esbuild/aix-ppc64@0.19.11': optional: true + '@esbuild/aix-ppc64@0.25.0': + optional: true + '@esbuild/android-arm64@0.18.20': optional: true '@esbuild/android-arm64@0.19.11': optional: true + '@esbuild/android-arm64@0.25.0': + optional: true + '@esbuild/android-arm@0.18.20': optional: true '@esbuild/android-arm@0.19.11': optional: true + '@esbuild/android-arm@0.25.0': + optional: true + '@esbuild/android-x64@0.18.20': optional: true '@esbuild/android-x64@0.19.11': optional: true + '@esbuild/android-x64@0.25.0': + optional: true + '@esbuild/darwin-arm64@0.18.20': optional: true '@esbuild/darwin-arm64@0.19.11': optional: true + '@esbuild/darwin-arm64@0.25.0': + optional: true + '@esbuild/darwin-x64@0.18.20': optional: true '@esbuild/darwin-x64@0.19.11': optional: true + '@esbuild/darwin-x64@0.25.0': + optional: true + '@esbuild/freebsd-arm64@0.18.20': optional: true '@esbuild/freebsd-arm64@0.19.11': optional: true + '@esbuild/freebsd-arm64@0.25.0': + optional: true + '@esbuild/freebsd-x64@0.18.20': optional: true '@esbuild/freebsd-x64@0.19.11': optional: true + '@esbuild/freebsd-x64@0.25.0': + optional: true + '@esbuild/linux-arm64@0.18.20': optional: true '@esbuild/linux-arm64@0.19.11': optional: true + '@esbuild/linux-arm64@0.25.0': + optional: true + '@esbuild/linux-arm@0.18.20': optional: true '@esbuild/linux-arm@0.19.11': optional: true + '@esbuild/linux-arm@0.25.0': + optional: true + '@esbuild/linux-ia32@0.18.20': optional: true '@esbuild/linux-ia32@0.19.11': optional: true + '@esbuild/linux-ia32@0.25.0': + optional: true + '@esbuild/linux-loong64@0.18.20': optional: true '@esbuild/linux-loong64@0.19.11': optional: true + '@esbuild/linux-loong64@0.25.0': + optional: true + '@esbuild/linux-mips64el@0.18.20': optional: true '@esbuild/linux-mips64el@0.19.11': optional: true + '@esbuild/linux-mips64el@0.25.0': + optional: true + '@esbuild/linux-ppc64@0.18.20': optional: true '@esbuild/linux-ppc64@0.19.11': optional: true + '@esbuild/linux-ppc64@0.25.0': + optional: true + '@esbuild/linux-riscv64@0.18.20': optional: true '@esbuild/linux-riscv64@0.19.11': optional: true + '@esbuild/linux-riscv64@0.25.0': + optional: true + '@esbuild/linux-s390x@0.18.20': optional: true '@esbuild/linux-s390x@0.19.11': optional: true + '@esbuild/linux-s390x@0.25.0': + optional: true + '@esbuild/linux-x64@0.18.20': optional: true '@esbuild/linux-x64@0.19.11': optional: true + '@esbuild/linux-x64@0.25.0': + optional: true + + '@esbuild/netbsd-arm64@0.25.0': + optional: true + '@esbuild/netbsd-x64@0.18.20': optional: true '@esbuild/netbsd-x64@0.19.11': optional: true + '@esbuild/netbsd-x64@0.25.0': + optional: true + + '@esbuild/openbsd-arm64@0.25.0': + optional: true + '@esbuild/openbsd-x64@0.18.20': optional: true '@esbuild/openbsd-x64@0.19.11': optional: true + '@esbuild/openbsd-x64@0.25.0': + optional: true + '@esbuild/sunos-x64@0.18.20': optional: true '@esbuild/sunos-x64@0.19.11': optional: true + '@esbuild/sunos-x64@0.25.0': + optional: true + '@esbuild/win32-arm64@0.18.20': optional: true '@esbuild/win32-arm64@0.19.11': optional: true + '@esbuild/win32-arm64@0.25.0': + optional: true + '@esbuild/win32-ia32@0.18.20': optional: true '@esbuild/win32-ia32@0.19.11': optional: true + '@esbuild/win32-ia32@0.25.0': + optional: true + '@esbuild/win32-x64@0.18.20': optional: true '@esbuild/win32-x64@0.19.11': optional: true + '@esbuild/win32-x64@0.25.0': + optional: true + '@eslint-community/eslint-utils@4.4.0(eslint@8.50.0)': dependencies: eslint: 8.50.0 @@ -10814,13 +11336,13 @@ snapshots: regenerator-runtime: 0.13.11 optional: true - '@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1))': dependencies: glob: 7.2.3 glob-promise: 4.2.2(glob@7.2.3) magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.5.4) - vite: 4.5.3(@types/node@22.8.1)(terser@5.31.3) + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) optionalDependencies: typescript: 5.5.4 @@ -10841,6 +11363,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.1 @@ -11299,6 +11823,63 @@ snapshots: optionalDependencies: rollup: 2.79.1 + '@rollup/rollup-android-arm-eabi@4.34.8': + optional: true + + '@rollup/rollup-android-arm64@4.34.8': + optional: true + + '@rollup/rollup-darwin-arm64@4.34.8': + optional: true + + '@rollup/rollup-darwin-x64@4.34.8': + optional: true + + '@rollup/rollup-freebsd-arm64@4.34.8': + optional: true + + '@rollup/rollup-freebsd-x64@4.34.8': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.34.8': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.34.8': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-x64-musl@4.34.8': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.34.8': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.34.8': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.34.8': + optional: true + '@rsbuild/core@1.0.1-beta.9': dependencies: '@rspack/core': 1.0.0-beta.1(@swc/helpers@0.5.11) @@ -11696,7 +12277,7 @@ snapshots: - encoding - supports-color - '@storybook/builder-vite@7.4.6(encoding@0.1.13)(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3))': + '@storybook/builder-vite@7.4.6(encoding@0.1.13)(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1))': dependencies: '@storybook/channels': 7.4.6 '@storybook/client-logger': 7.4.6 @@ -11717,7 +12298,7 @@ snapshots: remark-external-links: 8.0.0 remark-slug: 6.1.0 rollup: 3.29.4 - vite: 4.5.3(@types/node@22.8.1)(terser@5.31.3) + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -12003,19 +12584,19 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@storybook/react-vite@7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@2.79.1)(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3))': + '@storybook/react-vite@7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@2.79.1)(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1)) '@rollup/pluginutils': 5.0.5(rollup@2.79.1) - '@storybook/builder-vite': 7.4.6(encoding@0.1.13)(typescript@5.5.4)(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3)) + '@storybook/builder-vite': 7.4.6(encoding@0.1.13)(typescript@5.5.4)(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1)) '@storybook/react': 7.4.6(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) - '@vitejs/plugin-react': 3.1.0(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3)) + '@vitejs/plugin-react': 3.1.0(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1)) ast-types: 0.14.2 magic-string: 0.30.4 react: 18.2.0 react-docgen: 6.0.0-alpha.3 react-dom: 18.2.0(react@18.2.0) - vite: 4.5.3(@types/node@22.8.1)(terser@5.31.3) + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -12237,6 +12818,8 @@ snapshots: '@types/estree@1.0.5': {} + '@types/estree@1.0.6': {} + '@types/express-serve-static-core@4.17.37': dependencies: '@types/node': 22.8.1 @@ -12582,14 +13165,14 @@ snapshots: '@typescript-eslint/types': 8.0.0 eslint-visitor-keys: 3.4.3 - '@vitejs/plugin-react@3.1.0(vite@4.5.3(@types/node@22.8.1)(terser@5.31.3))': + '@vitejs/plugin-react@3.1.0(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1))': dependencies: '@babel/core': 7.22.11 '@babel/plugin-transform-react-jsx-self': 7.22.5(@babel/core@7.22.11) '@babel/plugin-transform-react-jsx-source': 7.22.5(@babel/core@7.22.11) magic-string: 0.27.0 react-refresh: 0.14.2 - vite: 4.5.3(@types/node@22.8.1)(terser@5.31.3) + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) transitivePeerDependencies: - supports-color @@ -12599,28 +13182,68 @@ snapshots: '@vitest/utils': 0.34.6 chai: 4.3.10 + '@vitest/expect@3.0.7': + dependencies: + '@vitest/spy': 3.0.7 + '@vitest/utils': 3.0.7 + chai: 5.2.0 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.0.7(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1))': + dependencies: + '@vitest/spy': 3.0.7 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + + '@vitest/pretty-format@3.0.7': + dependencies: + tinyrainbow: 2.0.0 + '@vitest/runner@0.34.6': dependencies: '@vitest/utils': 0.34.6 p-limit: 4.0.0 pathe: 1.1.1 + '@vitest/runner@3.0.7': + dependencies: + '@vitest/utils': 3.0.7 + pathe: 2.0.3 + '@vitest/snapshot@0.34.6': dependencies: magic-string: 0.30.4 pathe: 1.1.1 pretty-format: 29.7.0 + '@vitest/snapshot@3.0.7': + dependencies: + '@vitest/pretty-format': 3.0.7 + magic-string: 0.30.17 + pathe: 2.0.3 + '@vitest/spy@0.34.6': dependencies: tinyspy: 2.2.0 + '@vitest/spy@3.0.7': + dependencies: + tinyspy: 3.0.2 + '@vitest/utils@0.34.6': dependencies: diff-sequences: 29.6.3 loupe: 2.3.6 pretty-format: 29.7.0 + '@vitest/utils@3.0.7': + dependencies: + '@vitest/pretty-format': 3.0.7 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + '@webassemblyjs/ast@1.12.1': dependencies: '@webassemblyjs/helper-numbers': 1.11.6 @@ -13123,6 +13746,8 @@ snapshots: assertion-error@1.1.0: {} + assertion-error@2.0.1: {} + assign-symbols@1.0.0: {} ast-types@0.14.2: @@ -13603,6 +14228,14 @@ snapshots: pathval: 1.1.1 type-detect: 4.0.8 + chai@5.2.0: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.3 + pathval: 2.0.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -13633,6 +14266,8 @@ snapshots: change-case@5.1.2: {} + change-case@5.4.4: {} + character-entities@2.0.2: {} charenc@0.0.2: {} @@ -13641,6 +14276,8 @@ snapshots: dependencies: get-func-name: 2.0.2 + check-error@2.1.1: {} + check-more-types@2.24.0: optional: true @@ -14154,6 +14791,8 @@ snapshots: dependencies: type-detect: 4.0.8 + deep-eql@5.0.2: {} + deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -14600,6 +15239,8 @@ snapshots: es-module-lexer@1.5.4: {} + es-module-lexer@1.6.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -14693,6 +15334,34 @@ snapshots: '@esbuild/win32-ia32': 0.19.11 '@esbuild/win32-x64': 0.19.11 + esbuild@0.25.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 + escalade@3.1.2: {} escape-html@1.0.3: {} @@ -14968,6 +15637,10 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + esutils@2.0.3: {} etag@1.8.1: {} @@ -15045,6 +15718,8 @@ snapshots: expand-template@2.0.3: {} + expect-type@1.1.0: {} + exponential-backoff@3.1.1: optional: true @@ -16586,6 +17261,8 @@ snapshots: dependencies: get-func-name: 2.0.2 + loupe@3.1.3: {} + lower-case@2.0.2: dependencies: tslib: 2.6.2 @@ -16613,6 +17290,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + magic-string@0.30.4: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -17061,6 +17742,33 @@ snapshots: - encoding - supports-color + mineflayer-mouse@0.0.4(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + dependencies: + change-case: 5.4.4 + debug: 4.4.0(supports-color@8.1.1) + prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/ab2146c9933eef3247c3f64446de4ccc2c484c7c + vitest: 3.0.7(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + transitivePeerDependencies: + - '@edge-runtime/vm' + - '@types/debug' + - '@types/node' + - '@vitest/browser' + - '@vitest/ui' + - happy-dom + - jiti + - jsdom + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + mineflayer-pathfinder@2.4.4: dependencies: minecraft-data: 3.83.1 @@ -17254,6 +17962,8 @@ snapshots: nanoid@3.3.7: {} + nanoid@3.3.8: {} + nanomatch@1.2.13: dependencies: arr-diff: 4.0.0 @@ -17712,8 +18422,12 @@ snapshots: pathe@1.1.1: {} + pathe@2.0.3: {} + pathval@1.1.1: {} + pathval@2.0.0: {} + pause-stream@0.0.11: dependencies: through: 2.3.8 @@ -17821,6 +18535,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.3: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + potpack@1.0.2: {} prebuild-install@7.1.1: @@ -18703,6 +19423,31 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + rollup@4.34.8: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.34.8 + '@rollup/rollup-android-arm64': 4.34.8 + '@rollup/rollup-darwin-arm64': 4.34.8 + '@rollup/rollup-darwin-x64': 4.34.8 + '@rollup/rollup-freebsd-arm64': 4.34.8 + '@rollup/rollup-freebsd-x64': 4.34.8 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 + '@rollup/rollup-linux-arm-musleabihf': 4.34.8 + '@rollup/rollup-linux-arm64-gnu': 4.34.8 + '@rollup/rollup-linux-arm64-musl': 4.34.8 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 + '@rollup/rollup-linux-riscv64-gnu': 4.34.8 + '@rollup/rollup-linux-s390x-gnu': 4.34.8 + '@rollup/rollup-linux-x64-gnu': 4.34.8 + '@rollup/rollup-linux-x64-musl': 4.34.8 + '@rollup/rollup-win32-arm64-msvc': 4.34.8 + '@rollup/rollup-win32-ia32-msvc': 4.34.8 + '@rollup/rollup-win32-x64-msvc': 4.34.8 + fsevents: 2.3.3 + rope-sequence@1.3.4: {} rtl-css-js@1.16.1: @@ -19251,6 +19996,8 @@ snapshots: std-env@3.4.3: {} + std-env@3.8.0: {} + store2@2.14.2: {} storybook@7.4.6(encoding@0.1.13): @@ -19537,13 +20284,23 @@ snapshots: tinybench@2.5.1: {} + tinybench@2.9.0: {} + tinycolor2@1.6.0: optional: true + tinyexec@0.3.2: {} + tinypool@0.7.0: {} + tinypool@1.0.2: {} + + tinyrainbow@2.0.0: {} + tinyspy@2.2.0: {} + tinyspy@3.0.2: {} + title-case@3.0.3: dependencies: tslib: 2.6.2 @@ -20014,6 +20771,27 @@ snapshots: - supports-color - terser + vite-node@3.0.7(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + dependencies: + cac: 6.7.14 + debug: 4.4.0(supports-color@8.1.1) + es-module-lexer: 1.6.0 + pathe: 2.0.3 + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vite@4.5.3(@types/node@22.8.1)(terser@5.31.3): dependencies: esbuild: 0.18.20 @@ -20024,6 +20802,18 @@ snapshots: fsevents: 2.3.3 terser: 5.31.3 + vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + dependencies: + esbuild: 0.25.0 + postcss: 8.5.3 + rollup: 4.34.8 + optionalDependencies: + '@types/node': 22.8.1 + fsevents: 2.3.3 + terser: 5.31.3 + tsx: 4.7.0 + yaml: 2.4.1 + vitest@0.34.6(terser@5.31.3): dependencies: '@types/chai': 4.3.6 @@ -20059,6 +20849,45 @@ snapshots: - supports-color - terser + vitest@3.0.7(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + dependencies: + '@vitest/expect': 3.0.7 + '@vitest/mocker': 3.0.7(vite@6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1)) + '@vitest/pretty-format': 3.0.7 + '@vitest/runner': 3.0.7 + '@vitest/snapshot': 3.0.7 + '@vitest/spy': 3.0.7 + '@vitest/utils': 3.0.7 + chai: 5.2.0 + debug: 4.4.0(supports-color@8.1.1) + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 2.0.3 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 6.2.0(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + vite-node: 3.0.7(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.8.1 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vm-browserify@1.1.2: {} w3c-keyname@2.2.8: {} @@ -20212,6 +21041,11 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + wide-align@1.1.5: dependencies: string-width: 4.2.3 diff --git a/src/cameraRotationControls.ts b/src/cameraRotationControls.ts index b9bc5fe9..0c222dc6 100644 --- a/src/cameraRotationControls.ts +++ b/src/cameraRotationControls.ts @@ -3,20 +3,10 @@ import { activeModalStack, isGameActive, miscUiState, showModal } from './global import { options } from './optionsStorage' import { hideNotification, notificationProxy } from './react/NotificationProvider' import { pointerLock } from './utils' -import worldInteractions from './worldInteractions' import { updateMotion, initMotionTracking } from './react/uiMotion' let lastMouseMove: number -const MOTION_DAMPING = 0.92 -const MAX_MOTION_OFFSET = 30 -const motionVelocity = { x: 0, y: 0 } -const lastUpdate = performance.now() - -export const updateCursor = () => { - worldInteractions.update() -} - export type CameraMoveEvent = { movementX: number movementY: number @@ -30,7 +20,7 @@ export function onCameraMove (e: MouseEvent | CameraMoveEvent) { e.stopPropagation?.() const now = performance.now() // todo: limit camera movement for now to avoid unexpected jumps - if (now - lastMouseMove < 4) return + if (now - lastMouseMove < 4 && !options.preciseMouseInput) return lastMouseMove = now let { mouseSensX, mouseSensY } = options if (mouseSensY === -1) mouseSensY = mouseSensX @@ -38,7 +28,7 @@ export function onCameraMove (e: MouseEvent | CameraMoveEvent) { x: e.movementX * mouseSensX * 0.0001, y: e.movementY * mouseSensY * 0.0001 }) - updateCursor() + bot.mouse.update() updateMotion() } diff --git a/src/devtools.ts b/src/devtools.ts index 5d0be20b..617a4440 100644 --- a/src/devtools.ts +++ b/src/devtools.ts @@ -3,7 +3,6 @@ import fs from 'fs' import { WorldRendererThree } from 'renderer/viewer/lib/worldrendererThree' import { enable, disable, enabled } from 'debug' -import { getEntityCursor } from './worldInteractions' window.cursorBlockRel = (x = 0, y = 0, z = 0) => { const newPos = bot.blockAtCursor(5)?.position.offset(x, y, z) @@ -11,8 +10,8 @@ window.cursorBlockRel = (x = 0, y = 0, z = 0) => { return bot.world.getBlock(newPos) } -window.cursorEntity = () => { - return getEntityCursor() +window.entityCursor = () => { + return bot.mouse.getCursorState().entity } // wanderer diff --git a/src/globals.d.ts b/src/globals.d.ts index 96b32916..6b2c6640 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -21,7 +21,7 @@ declare const loadedData: import('minecraft-data').IndexedData & { sounds: Recor declare const customEvents: import('typed-emitter').default<{ /** Singleplayer load requested */ singleplayer (): void - digStart () + digStart (): void gameLoaded (): void mineflayerBotCreated (): void search (q: string): void diff --git a/src/index.ts b/src/index.ts index 249fbe9d..beb86aea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,8 +40,6 @@ import { WorldDataEmitter, Viewer } from 'renderer/viewer' import pathfinder from 'mineflayer-pathfinder' import { Vec3 } from 'vec3' -import worldInteractions from './worldInteractions' - import * as THREE from 'three' import MinecraftData from 'minecraft-data' import debug from 'debug' @@ -106,13 +104,12 @@ import { parseFormattedMessagePacket } from './botUtils' import { getViewerVersionData, getWsProtocolStream, handleCustomChannel } from './viewerConnector' import { getWebsocketStream } from './mineflayer/websocket-core' import { appQueryParams, appQueryParamsArray } from './appParams' -import { updateCursor } from './cameraRotationControls' -import { pingServerVersion } from './mineflayer/minecraft-protocol-extra' import { playerState, PlayerStateManager } from './mineflayer/playerState' import { states } from 'minecraft-protocol' import { initMotionTracking } from './react/uiMotion' import { UserError } from './mineflayer/userError' import ping from './mineflayer/plugins/ping' +import mouse from './mineflayer/plugins/mouse' import { LocalServer } from './customServer' import { startLocalReplayServer } from './packetsReplay/replayPackets' import { localRelayServerPlugin } from './mineflayer/plugins/packetsRecording' @@ -120,7 +117,6 @@ import { createFullScreenProgressReporter } from './core/progressReporter' window.debug = debug window.THREE = THREE -window.worldInteractions = worldInteractions window.beforeRenderFrame = [] // ACTUAL CODE @@ -705,6 +701,7 @@ export async function connect (connectOptions: ConnectOptions) { if (connectOptions.server) { bot.loadPlugin(ping) } + bot.loadPlugin(mouse) if (!localReplaySession) { bot.loadPlugin(localRelayServerPlugin) } @@ -754,8 +751,6 @@ export async function connect (connectOptions: ConnectOptions) { onBotCreate() bot.once('login', () => { - worldInteractions.initBot() - setLoadingScreenStatus('Loading world') const mcData = MinecraftData(bot.version) @@ -815,8 +810,6 @@ export async function connect (connectOptions: ConnectOptions) { const worldView = window.worldView = new WorldDataEmitter(bot.world, renderDistance, center) watchOptionsAfterWorldViewInit() - bot.on('physicsTick', () => updateCursor()) - void initVR() initMotionTracking() diff --git a/src/mineflayer/plugins/mouse.ts b/src/mineflayer/plugins/mouse.ts new file mode 100644 index 00000000..e5b5e283 --- /dev/null +++ b/src/mineflayer/plugins/mouse.ts @@ -0,0 +1,204 @@ +import { createMouse } from 'mineflayer-mouse' +import * as THREE from 'three' +import { Bot } from 'mineflayer' +import { Block } from 'prismarine-block' +import { Vec3 } from 'vec3' +import { LineMaterial } from 'three-stdlib' +import { subscribeKey } from 'valtio/utils' +import { disposeObject } from 'renderer/viewer/lib/threeJsUtils' +import { isGameActive, showModal } from '../../globalState' + +// wouldn't better to create atlas instead? +import destroyStage0 from '../../../assets/destroy_stage_0.png' +import destroyStage1 from '../../../assets/destroy_stage_1.png' +import destroyStage2 from '../../../assets/destroy_stage_2.png' +import destroyStage3 from '../../../assets/destroy_stage_3.png' +import destroyStage4 from '../../../assets/destroy_stage_4.png' +import destroyStage5 from '../../../assets/destroy_stage_5.png' +import destroyStage6 from '../../../assets/destroy_stage_6.png' +import destroyStage7 from '../../../assets/destroy_stage_7.png' +import destroyStage8 from '../../../assets/destroy_stage_8.png' +import destroyStage9 from '../../../assets/destroy_stage_9.png' +import { options } from '../../optionsStorage' +import { isCypress } from '../../standaloneUtils' +import { playerState } from '../playerState' + +function createDisplayManager (bot: Bot, scene: THREE.Scene, renderer: THREE.WebGLRenderer) { + // State + const state = { + blockBreakMesh: null as THREE.Mesh | null, + breakTextures: [] as THREE.Texture[], + } + + // Initialize break mesh and textures + const loader = new THREE.TextureLoader() + const destroyStagesImages = [ + destroyStage0, destroyStage1, destroyStage2, destroyStage3, destroyStage4, + destroyStage5, destroyStage6, destroyStage7, destroyStage8, destroyStage9 + ] + + for (let i = 0; i < 10; i++) { + const texture = loader.load(destroyStagesImages[i]) + texture.magFilter = THREE.NearestFilter + texture.minFilter = THREE.NearestFilter + state.breakTextures.push(texture) + } + + const breakMaterial = new THREE.MeshBasicMaterial({ + transparent: true, + blending: THREE.MultiplyBlending, + alphaTest: 0.5, + }) + state.blockBreakMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), breakMaterial) + state.blockBreakMesh.visible = false + state.blockBreakMesh.renderOrder = 999 + state.blockBreakMesh.name = 'blockBreakMesh' + scene.add(state.blockBreakMesh) + + // Update functions + function updateLineMaterial () { + const inCreative = bot.game.gameMode === 'creative' + const pixelRatio = viewer.renderer.getPixelRatio() + + viewer.world.threejsCursorLineMaterial = new LineMaterial({ + color: (() => { + switch (options.highlightBlockColor) { + case 'blue': + return 0x40_80_ff + case 'classic': + return 0x00_00_00 + default: + return inCreative ? 0x40_80_ff : 0x00_00_00 + } + })(), + linewidth: Math.max(pixelRatio * 0.7, 1) * 2, + // dashed: true, + // dashSize: 5, + }) + } + + function updateDisplay () { + if (viewer.world.threejsCursorLineMaterial) { + const { renderer } = viewer + viewer.world.threejsCursorLineMaterial.resolution.set(renderer.domElement.width, renderer.domElement.height) + viewer.world.threejsCursorLineMaterial.dashOffset = performance.now() / 750 + } + } + beforeRenderFrame.push(updateDisplay) + + // Update cursor line material on game mode change + bot.on('game', updateLineMaterial) + // Update material when highlight color setting changes + subscribeKey(options, 'highlightBlockColor', updateLineMaterial) + + function updateBreakAnimation (block: Block | undefined, stage: number | null) { + hideBreakAnimation() + if (!state.blockBreakMesh) return // todo + if (stage === null || !block) return + + const mergedShape = bot.mouse.getMergedCursorShape(block) + if (!mergedShape) return + const { position, width, height, depth } = bot.mouse.getDataFromShape(mergedShape) + state.blockBreakMesh.scale.set(width * 1.001, height * 1.001, depth * 1.001) + position.add(block.position) + state.blockBreakMesh.position.set(position.x, position.y, position.z) + state.blockBreakMesh.visible = true + + //@ts-expect-error + state.blockBreakMesh.material.map = state.breakTextures[stage] ?? state.breakTextures.at(-1) + //@ts-expect-error + state.blockBreakMesh.material.needsUpdate = true + } + + function hideBreakAnimation () { + if (state.blockBreakMesh) { + state.blockBreakMesh.visible = false + } + } + + function updateCursorBlock (data?: { block: Block }) { + if (!data?.block) { + viewer.world.setHighlightCursorBlock(null) + return + } + + const { block } = data + viewer.world.setHighlightCursorBlock(block.position, bot.mouse.getBlockCursorShapes(block).map(shape => { + return bot.mouse.getDataFromShape(shape) + })) + } + + bot.on('highlightCursorBlock', updateCursorBlock) + + bot.on('blockBreakProgressStage', updateBreakAnimation) + + bot.on('end', () => { + disposeObject(state.blockBreakMesh!, true) + scene.remove(state.blockBreakMesh!) + viewer.world.setHighlightCursorBlock(null) + }) +} + +export default (bot: Bot) => { + bot.loadPlugin(createMouse({})) + + domListeners(bot) + createDisplayManager(bot, viewer.scene, viewer.renderer) + + otherListeners() +} + +const otherListeners = () => { + bot.on('startDigging', (block) => { + customEvents.emit('digStart') + }) + + bot.on('goingToSleep', () => { + showModal({ reactType: 'bed' }) + }) + + bot.on('botArmSwingStart', (hand) => { + viewer.world.changeHandSwingingState(true, hand === 'left') + }) + + bot.on('botArmSwingEnd', (hand) => { + viewer.world.changeHandSwingingState(false, hand === 'left') + }) + + bot.on('startUsingItem', (item, slot, isOffhand, duration) => { + customEvents.emit('activateItem', item, isOffhand ? 45 : bot.quickBarSlot, isOffhand) + playerState.startUsingItem() + }) + + bot.on('stopUsingItem', () => { + playerState.stopUsingItem() + }) +} + +const domListeners = (bot: Bot) => { + document.addEventListener('mousedown', (e) => { + if (e.isTrusted && !document.pointerLockElement && !isCypress()) return + if (!isGameActive(true)) return + + if (e.button === 0) { + bot.leftClickStart() + } else if (e.button === 2) { + bot.rightClickStart() + } + }) + + document.addEventListener('mouseup', (e) => { + if (e.button === 0) { + bot.leftClickEnd() + } else if (e.button === 2) { + bot.rightClickEnd() + } + }) + + bot.mouse.beforeUpdateChecks = () => { + if (!document.hasFocus()) { + // deactive all buttons + bot.mouse.buttons.fill(false) + } + } +} diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index 47109979..b88b71c4 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -60,6 +60,7 @@ const defaultOptions = { serversAutoVersionSelect: 'auto' as 'auto' | 'latest' | '1.20.4' | string, customChannels: false, packetsReplayAutoStart: false, + preciseMouseInput: false, // todo ui setting, maybe enable by default? waitForChunksRender: 'sp-only' as 'sp-only' | boolean, diff --git a/src/react/GameInteractionOverlay.tsx b/src/react/GameInteractionOverlay.tsx index 503ca8b4..cb7a39f8 100644 --- a/src/react/GameInteractionOverlay.tsx +++ b/src/react/GameInteractionOverlay.tsx @@ -3,7 +3,6 @@ import { subscribe, useSnapshot } from 'valtio' import { useUtilsEffect } from '@zardoy/react-util' import { options } from '../optionsStorage' import { activeModalStack, isGameActive, miscUiState } from '../globalState' -import worldInteractions from '../worldInteractions' import { onCameraMove, CameraMoveEvent } from '../cameraRotationControls' import { pointerLock, isInRealGameSession } from '../utils' import { handleMovementStickDelta, joystickPointer } from './TouchAreasControls' @@ -152,7 +151,7 @@ function GameInteractionOverlayInner ({ virtualClickActive = false } else if (!capturedPointer.active.activateCameraMove && (Date.now() - capturedPointer.active.time < touchStartBreakingBlockMs)) { document.dispatchEvent(new MouseEvent('mousedown', { button: 2 })) - worldInteractions.update() + bot.mouse.update() document.dispatchEvent(new MouseEvent('mouseup', { button: 2 })) } diff --git a/src/react/TouchAreasControls.tsx b/src/react/TouchAreasControls.tsx index 981ebebb..6b34f9bc 100644 --- a/src/react/TouchAreasControls.tsx +++ b/src/react/TouchAreasControls.tsx @@ -1,7 +1,6 @@ import { CSSProperties, PointerEvent, useEffect, useRef } from 'react' import { proxy, ref, useSnapshot } from 'valtio' import { contro } from '../controls' -import worldInteractions from '../worldInteractions' import { options } from '../optionsStorage' import PixelartIcon from './PixelartIcon' import Button from './Button' @@ -73,8 +72,9 @@ export default ({ setupActive, closeButtonsSetup, foregroundGameActive }: Props) }[name] const holdDown = { action () { + if (!bot) return document.dispatchEvent(new MouseEvent('mousedown', { button: 2 })) - worldInteractions.update() + bot.mouse.update() }, sneak () { void contro.emit('trigger', { @@ -84,8 +84,9 @@ export default ({ setupActive, closeButtonsSetup, foregroundGameActive }: Props) active = bot?.getControlState('sneak') }, break () { + if (!bot) return document.dispatchEvent(new MouseEvent('mousedown', { button: 0 })) - worldInteractions.update() + bot.mouse.update() active = true }, jump () { @@ -108,8 +109,9 @@ export default ({ setupActive, closeButtonsSetup, foregroundGameActive }: Props) active = bot?.getControlState('sneak') }, break () { + if (!bot) return document.dispatchEvent(new MouseEvent('mouseup', { button: 0 })) - worldInteractions.update() + bot.mouse.update() active = false }, jump () { diff --git a/src/worldInteractions.ts b/src/worldInteractions.ts deleted file mode 100644 index 5b03184c..00000000 --- a/src/worldInteractions.ts +++ /dev/null @@ -1,551 +0,0 @@ -//@ts-check - -import * as THREE from 'three' - -// wouldn't better to create atlas instead? -import { Vec3 } from 'vec3' -import { LineMaterial } from 'three-stdlib' -import { Entity } from 'prismarine-entity' -import { Block } from 'prismarine-block' -import { subscribeKey } from 'valtio/utils' -import destroyStage0 from '../assets/destroy_stage_0.png' -import destroyStage1 from '../assets/destroy_stage_1.png' -import destroyStage2 from '../assets/destroy_stage_2.png' -import destroyStage3 from '../assets/destroy_stage_3.png' -import destroyStage4 from '../assets/destroy_stage_4.png' -import destroyStage5 from '../assets/destroy_stage_5.png' -import destroyStage6 from '../assets/destroy_stage_6.png' -import destroyStage7 from '../assets/destroy_stage_7.png' -import destroyStage8 from '../assets/destroy_stage_8.png' -import destroyStage9 from '../assets/destroy_stage_9.png' - -import { hideCurrentModal, isGameActive, showModal } from './globalState' -import { assertDefined } from './utils' -import { options } from './optionsStorage' -import { itemBeingUsed } from './react/Crosshair' -import { isCypress } from './standaloneUtils' -import { displayClientChat } from './botUtils' -import { playerState } from './mineflayer/playerState' - -function getViewDirection (pitch, yaw) { - const csPitch = Math.cos(pitch) - const snPitch = Math.sin(pitch) - const csYaw = Math.cos(yaw) - const snYaw = Math.sin(yaw) - return new Vec3(-snYaw * csPitch, snPitch, -csYaw * csPitch) -} - -class WorldInteraction { - ready = false - cursorBlock: Block | null = null - prevBreakState: number | null = null - currentDigTime: number | null = null - prevOnGround: boolean | null = null - lastBlockPlaced: number - lastSwing = 0 - buttons = [false, false, false] - lastButtons = [false, false, false] - breakStartTime: number | undefined = 0 - lastDugBlock: Vec3 | null = null - blockBreakMesh: THREE.Mesh - breakTextures: THREE.Texture[] - lastDigged: number - debugDigStatus: string - currentBreakBlock: { block: any, stage: number } | null = null - swingTimeout: any = null - - oneTimeInit () { - const loader = new THREE.TextureLoader() - this.breakTextures = [] - const destroyStagesImages = [ - destroyStage0, - destroyStage1, - destroyStage2, - destroyStage3, - destroyStage4, - destroyStage5, - destroyStage6, - destroyStage7, - destroyStage8, - destroyStage9 - ] - for (let i = 0; i < 10; i++) { - const texture = loader.load(destroyStagesImages[i]) - texture.magFilter = THREE.NearestFilter - texture.minFilter = THREE.NearestFilter - this.breakTextures.push(texture) - } - const breakMaterial = new THREE.MeshBasicMaterial({ - transparent: true, - blending: THREE.MultiplyBlending, - alphaTest: 0.5, - }) - this.blockBreakMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), breakMaterial) - this.blockBreakMesh.visible = false - this.blockBreakMesh.renderOrder = 999 - this.blockBreakMesh.name = 'blockBreakMesh' - viewer.scene.add(this.blockBreakMesh) - - // Setup events - document.addEventListener('mouseup', (e) => { - this.buttons[e.button] = false - }) - - this.lastBlockPlaced = 4 // ticks since last placed - document.addEventListener('mousedown', (e) => { - if (e.isTrusted && !document.pointerLockElement && !isCypress()) return - if (!isGameActive(true)) return - this.buttons[e.button] = true - - const entity = getEntityCursor() - - if (entity) { - if (e.button === 0) { // left click - bot.attack(entity) - } else if (e.button === 2) { // right click - this.activateEntity(entity) - } - } - }) - document.addEventListener('blur', (e) => { - this.buttons = [false, false, false] - }) - - beforeRenderFrame.push(() => { - if (viewer.world.threejsCursorLineMaterial) { - const { renderer } = viewer - viewer.world.threejsCursorLineMaterial.resolution.set(renderer.domElement.width, renderer.domElement.height) - viewer.world.threejsCursorLineMaterial.dashOffset = performance.now() / 750 - } - }) - } - - initBot () { - if (!this.ready) { - this.ready = true - this.oneTimeInit() - } - assertDefined(viewer) - bot.on('physicsTick', () => { if (this.lastBlockPlaced < 4) this.lastBlockPlaced++ }) - bot.on('diggingCompleted', (block) => { - this.breakStartTime = undefined - this.lastDugBlock = block.position - // TODO: If the tool and enchantments immediately exceed the hardness times 30, the block breaks with no delay; SO WE NEED TO CHECK THAT - // TODO: Any blocks with a breaking time of 0.05 - this.lastDigged = Date.now() - this.debugDigStatus = 'done' - this.stopBreakAnimation() - }) - bot.on('diggingAborted', (block) => { - if (!viewer.world.cursorBlock?.equals(block.position)) return - this.debugDigStatus = 'aborted' - this.breakStartTime = undefined - if (this.buttons[0]) { - this.buttons[0] = false - this.update() - this.buttons[0] = true // trigger again - } - this.lastDugBlock = null - this.stopBreakAnimation() - }) - bot.on('heldItemChanged' as any, () => { - itemBeingUsed.name = null - }) - - // Add new event listeners for block breaking and swinging - bot.on('entitySwingArm', (entity: Entity) => { - if (entity.id === bot.entity.id) { - if (this.swingTimeout) { - clearTimeout(this.swingTimeout) - } - bot.swingArm('right') - viewer.world.changeHandSwingingState(true, false) - this.swingTimeout = setTimeout(() => { - viewer.world.changeHandSwingingState(false, false) - this.swingTimeout = null - }, 250) - } - }) - - //@ts-expect-error mineflayer types are wrong - bot.on('blockBreakProgressObserved', (block: Block, destroyStage: number, entity: Entity) => { - if (this.cursorBlock?.position.equals(block.position) && entity.id === bot.entity.id) { - if (!this.buttons[0]) { - // Simulate left mouse button press - this.buttons[0] = true - this.update() - } - // this.setBreakState(block, destroyStage) - } - }) - - //@ts-expect-error mineflayer types are wrong - bot.on('blockBreakProgressEnd', (block: Block, entity: Entity) => { - if (this.currentBreakBlock?.block.position.equals(block.position) && entity.id === bot.entity.id) { - if (!this.buttons[0]) { - // Simulate left mouse button press - this.buttons[0] = false - this.update() - } - // this.stopBreakAnimation() - } - }) - - // Handle acknowledge_player_digging packet - bot._client.on('acknowledge_player_digging', (data: { location: { x: number, y: number, z: number }, block: number, status: number, successful: boolean } | { sequenceId: number }) => { - if ('location' in data && !data.successful) { - const packetPos = new Vec3(data.location.x, data.location.y, data.location.z) - if (this.cursorBlock?.position.equals(packetPos)) { - this.buttons[0] = false - this.update() - this.stopBreakAnimation() - } - } - }) - - const upLineMaterial = () => { - const inCreative = bot.game.gameMode === 'creative' - const pixelRatio = viewer.renderer.getPixelRatio() - viewer.world.threejsCursorLineMaterial = new LineMaterial({ - color: (() => { - switch (options.highlightBlockColor) { - case 'blue': - return 0x40_80_ff - case 'classic': - return 0x00_00_00 - default: - return inCreative ? 0x40_80_ff : 0x00_00_00 - } - })(), - linewidth: Math.max(pixelRatio * 0.7, 1) * 2, - // dashed: true, - // dashSize: 5, - }) - } - upLineMaterial() - // todo use gamemode update only - bot.on('game', upLineMaterial) - // Update material when highlight color setting changes - subscribeKey(options, 'highlightBlockColor', upLineMaterial) - } - - activateEntity (entity: Entity) { - // mineflayer has completely wrong implementation of this action - if (bot.supportFeature('armAnimationBeforeUse')) { - bot.swingArm('right') - } - bot._client.write('use_entity', { - target: entity.id, - mouse: 2, - // todo do not fake - x: 0.581_012_585_759_162_9, - y: 0.581_012_585_759_162_9, - z: 0.581_012_585_759_162_9, - // x: raycastPosition.x - entity.position.x, - // y: raycastPosition.y - entity.position.y, - // z: raycastPosition.z - entity.position.z - sneaking: bot.getControlState('sneak'), - hand: 0 - }) - bot._client.write('use_entity', { - target: entity.id, - mouse: 0, - sneaking: bot.getControlState('sneak'), - hand: 0 - }) - if (!bot.supportFeature('armAnimationBeforeUse')) { - bot.swingArm('right') - } - } - - beforeUpdateChecks () { - if (!document.hasFocus()) { - // deactive all buttson - this.buttons.fill(false) - } - } - - // todo this shouldnt be done in the render loop, migrate the code to dom events to avoid delays on lags - update () { - this.beforeUpdateChecks() - const inSpectator = bot.game.gameMode === 'spectator' - const inAdventure = bot.game.gameMode === 'adventure' - const entity = getEntityCursor() - let _cursorBlock = inSpectator && !options.showCursorBlockInSpectator ? null : bot.blockAtCursor(5) - if (entity) { - _cursorBlock = null - } - this.cursorBlock = _cursorBlock - const { cursorBlock } = this - - let cursorBlockDiggable = cursorBlock - if (cursorBlock && (!bot.canDigBlock(cursorBlock) || inAdventure) && bot.game.gameMode !== 'creative') cursorBlockDiggable = null - - const cursorChanged = cursorBlock && viewer.world.cursorBlock ? !viewer.world.cursorBlock.equals(cursorBlock.position) : viewer.world.cursorBlock !== cursorBlock - - // Place / interact / activate - if (this.buttons[2] && this.lastBlockPlaced >= 4) { - const activatableItems = (itemName: string) => { - return ['egg', 'fishing_rod', 'firework_rocket', - 'fire_charge', 'snowball', 'ender_pearl', 'experience_bottle', 'potion', - 'glass_bottle', 'bucket', 'water_bucket', 'lava_bucket', 'milk_bucket', - 'minecart', 'boat', 'tnt_minecart', 'chest_minecart', 'hopper_minecart', - 'command_block_minecart', 'armor_stand', 'lead', 'name_tag', - // - 'writable_book', 'written_book', 'compass', 'clock', 'filled_map', 'empty_map', 'map', - 'shears', 'carrot_on_a_stick', 'warped_fungus_on_a_stick', - 'spawn_egg', 'trident', 'crossbow', 'elytra', 'shield', 'turtle_helmet', 'bow', 'crossbow', 'bucket_of_cod', - ...loadedData.foodsArray.map((f) => f.name), - ].includes(itemName) - } - const activate = bot.heldItem && activatableItems(bot.heldItem.name) - let stop = false - if (!bot.controlState.sneak) { - if (cursorBlock?.name === 'bed' || cursorBlock?.name.endsWith('_bed')) { - stop = true - showModal({ reactType: 'bed' }) - let cancelSleep = true - void bot.sleep(cursorBlock).catch((e) => { - if (cancelSleep) { - hideCurrentModal() - } - // if (e.message === 'bot is not sleeping') return - displayClientChat(e.message) - }) - setTimeout(() => { - cancelSleep = false - }) - } - } - // todo placing with offhand - if (cursorBlock && !activate && !stop) { - const vecArray = [new Vec3(0, -1, 0), new Vec3(0, 1, 0), new Vec3(0, 0, -1), new Vec3(0, 0, 1), new Vec3(-1, 0, 0), new Vec3(1, 0, 0)] - //@ts-expect-error - const delta = cursorBlock.intersect.minus(cursorBlock.position) - - if (bot.heldItem) { - //@ts-expect-error todo - bot._placeBlockWithOptions(cursorBlock, vecArray[cursorBlock.face], { delta, forceLook: 'ignore' }).catch(console.warn) - } else { - // https://discord.com/channels/413438066984747026/413438150594265099/1198724637572477098 - const oldLookAt = bot.lookAt - //@ts-expect-error - bot.lookAt = (pos) => { } - //@ts-expect-error - // TODO it still must 1. fire block place 2. swing arm (right) - bot.activateBlock(cursorBlock, vecArray[cursorBlock.face], delta).finally(() => { - bot.lookAt = oldLookAt - }).catch(console.warn) - } - viewer.world.changeHandSwingingState(true, false) - viewer.world.changeHandSwingingState(false, false) - } else if (!stop) { - const offhand = activate ? false : activatableItems(bot.inventory.slots[45]?.name ?? '') - bot.activateItem(offhand) // todo offhand - const item = offhand ? bot.inventory.slots[45] : bot.heldItem - if (item) { - customEvents.emit('activateItem', item, offhand ? 45 : bot.quickBarSlot, offhand) - } - playerState.startUsingItem() - itemBeingUsed.name = (offhand ? bot.inventory.slots[45]?.name : bot.heldItem?.name) ?? null - itemBeingUsed.hand = offhand ? 1 : 0 - } - this.lastBlockPlaced = 0 - } - // stop using activated item (cancel) - if (itemBeingUsed.name && !this.buttons[2]) { - itemBeingUsed.name = null - // "only foods and bow can be deactivated" - not true, shields also can be deactivated and client always sends this - // if (bot.heldItem && (loadedData.foodsArray.map((f) => f.name).includes(bot.heldItem.name) || bot.heldItem.name === 'bow')) { - bot.deactivateItem() - playerState.stopUsingItem() - // } - } - - // Stop break - if ((!this.buttons[0] && this.lastButtons[0]) || cursorChanged) { - try { - bot.stopDigging() // this shouldnt throw anything... - } catch (e) { } // to be reworked in mineflayer, then remove the try here - } - // We stopped breaking - if ((!this.buttons[0] && this.lastButtons[0])) { - this.lastDugBlock = null - this.breakStartTime = undefined - this.debugDigStatus = 'cancelled' - this.stopBreakAnimation() - } - - const onGround = bot.entity.onGround || bot.game.gameMode === 'creative' - this.prevOnGround ??= onGround // todo this should be fixed in mineflayer to involve correct calculations when this changes as this is very important when mining straight down // todo this should be fixed in mineflayer to involve correct calculations when this changes as this is very important when mining straight down // todo this should be fixed in mineflayer to involve correct calculations when this changes as this is very important when mining straight down - // Start break - // todo last check doesnt work as cursorChanged happens once (after that check is false) - if ( - this.buttons[0] - ) { - if (cursorBlockDiggable - && (!this.lastButtons[0] || ((cursorChanged || (this.lastDugBlock && !this.lastDugBlock.equals(cursorBlock!.position))) && Date.now() - (this.lastDigged ?? 0) > 300) || onGround !== this.prevOnGround) - && onGround) { - this.lastDugBlock = null - this.debugDigStatus = 'breaking' - this.currentDigTime = bot.digTime(cursorBlockDiggable) - this.breakStartTime = performance.now() - const vecArray = [new Vec3(0, -1, 0), new Vec3(0, 1, 0), new Vec3(0, 0, -1), new Vec3(0, 0, 1), new Vec3(-1, 0, 0), new Vec3(1, 0, 0)] - bot.dig( - //@ts-expect-error - cursorBlockDiggable, 'ignore', vecArray[cursorBlockDiggable.face] - ).catch((err) => { - if (err.message === 'Digging aborted') return - throw err - }) - customEvents.emit('digStart') - this.lastDigged = Date.now() - viewer.world.changeHandSwingingState(true, false) - } else if (performance.now() - this.lastSwing > 200) { - bot.swingArm('right') - this.lastSwing = performance.now() - } - } - if (!this.buttons[0] && this.lastButtons[0]) { - viewer.world.changeHandSwingingState(false, false) - } - this.prevOnGround = onGround - - // Show cursor - const allShapes = [...cursorBlock?.shapes ?? [], ...cursorBlock?.['interactionShapes'] ?? []] - if (cursorBlock) { - // BREAK MESH - // union of all values - const breakShape = allShapes.reduce((acc, cur) => { - return [ - Math.min(acc[0], cur[0]), - Math.min(acc[1], cur[1]), - Math.min(acc[2], cur[2]), - Math.max(acc[3], cur[3]), - Math.max(acc[4], cur[4]), - Math.max(acc[5], cur[5]) - ] - }) - const { position, width, height, depth } = getDataFromShape(breakShape) - this.blockBreakMesh.scale.set(width * 1.001, height * 1.001, depth * 1.001) - position.add(cursorBlock.position) - this.blockBreakMesh.position.set(position.x, position.y, position.z) - } - - // Show break animation - if (cursorBlockDiggable && this.breakStartTime && bot.game.gameMode !== 'creative') { - const elapsed = performance.now() - this.breakStartTime - const time = bot.digTime(cursorBlockDiggable) - if (time !== this.currentDigTime) { - console.warn('dig time changed! cancelling!', time, 'from', this.currentDigTime) // todo - try { bot.stopDigging() } catch { } - } - const state = Math.floor((elapsed / time) * 10) - if (state !== this.prevBreakState) { - this.setBreakState(cursorBlockDiggable, Math.min(state, 9)) - } - this.prevBreakState = state - } else { - this.blockBreakMesh.visible = false - } - - // Update state - if (cursorChanged) { - viewer.world.setHighlightCursorBlock(cursorBlock?.position ?? null, allShapes.map(shape => { - return getDataFromShape(shape) - })) - } - this.lastButtons[0] = this.buttons[0] - this.lastButtons[1] = this.buttons[1] - this.lastButtons[2] = this.buttons[2] - } - - setBreakState (block: Block, stage: number) { - this.currentBreakBlock = { block, stage } - this.blockBreakMesh.visible = true - //@ts-expect-error - this.blockBreakMesh.material.map = this.breakTextures[stage] ?? this.breakTextures.at(-1) - //@ts-expect-error - this.blockBreakMesh.material.needsUpdate = true - } - - stopBreakAnimation () { - this.currentBreakBlock = null - this.blockBreakMesh.visible = false - } -} - -const getDataFromShape = (shape) => { - const width = shape[3] - shape[0] - const height = shape[4] - shape[1] - const depth = shape[5] - shape[2] - const centerX = (shape[3] + shape[0]) / 2 - const centerY = (shape[4] + shape[1]) / 2 - const centerZ = (shape[5] + shape[2]) / 2 - const position = new Vec3(centerX, centerY, centerZ) - return { position, width, height, depth } -} - -// Blocks that can be interacted with in adventure mode -const activatableBlockPatterns = [ - // Containers - /^(chest|barrel|hopper|dispenser|dropper)$/, - /^.*shulker_box$/, - /^.*(furnace|smoker)$/, - /^(brewing_stand|beacon)$/, - // Crafting - /^.*table$/, - /^(grindstone|stonecutter|loom)$/, - /^.*anvil$/, - // Redstone - /^(lever|repeater|comparator|daylight_detector|observer|note_block|jukebox|bell)$/, - // Buttons - /^.*button$/, - // Doors and trapdoors - /^.*door$/, - /^.*trapdoor$/, - // Functional blocks - /^(enchanting_table|lectern|composter|respawn_anchor|lodestone|conduit)$/, - /^.*bee.*$/, - // Beds - /^.*bed$/, - // Misc - /^(cake|decorated_pot|crafter|trial_spawner|vault)$/ -] - -function isBlockActivatable (blockName: string) { - return activatableBlockPatterns.some(pattern => pattern.test(blockName)) -} - -function isLookingAtActivatableBlock (block: Block) { - return isBlockActivatable(block.name) -} - -export const getEntityCursor = () => { - const entity = bot.nearestEntity((e) => { - if (e.position.distanceTo(bot.entity.position) <= (bot.game.gameMode === 'creative' ? 5 : 3)) { - const dir = getViewDirection(bot.entity.pitch, bot.entity.yaw) - const { width, height } = e - const { x: eX, y: eY, z: eZ } = e.position - const { x: bX, y: bY, z: bZ } = bot.entity.position - const box = new THREE.Box3( - new THREE.Vector3(eX - width / 2, eY, eZ - width / 2), - new THREE.Vector3(eX + width / 2, eY + height, eZ + width / 2) - ) - - const r = new THREE.Raycaster( - new THREE.Vector3(bX, bY + 1.52, bZ), - new THREE.Vector3(dir.x, dir.y, dir.z) - ) - const int = r.ray.intersectBox(box, new THREE.Vector3(eX, eY, eZ)) - return int !== null - } - - return false - }) - return entity -} - -const worldInteraction = new WorldInteraction() -globalThis.worldInteraction = worldInteraction -export default worldInteraction From 10f17063c06a5c45106c1cec01af9b0a4727ac51 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 14:19:38 +0300 Subject: [PATCH 395/851] fix: fix whole pipeline of rendering custom items from rp: add them to atlas and update texture propertly. align behavior blocks vs items and gui vs hand/floor --- renderer/viewer/lib/viewer.ts | 7 +---- renderer/viewer/lib/worldrendererCommon.ts | 12 ++++++-- src/index.ts | 17 ++++-------- src/inventoryWindows.ts | 32 +++++++++------------- src/mineflayer/items.ts | 1 + src/resourcePack.ts | 23 +++++++++++----- src/resourcesManager.ts | 23 ++++++++++++++++ 7 files changed, 70 insertions(+), 45 deletions(-) create mode 100644 src/resourcesManager.ts diff --git a/renderer/viewer/lib/viewer.ts b/renderer/viewer/lib/viewer.ts index 7b951438..941f2182 100644 --- a/renderer/viewer/lib/viewer.ts +++ b/renderer/viewer/lib/viewer.ts @@ -86,12 +86,7 @@ export class Viewer { console.log('[viewer] Using version:', userVersion, 'textures:', texturesVersion) this.entities.clear() // this.primitives.clear() - return this.world.setVersion(userVersion, texturesVersion).then(async () => { - return new THREE.TextureLoader().loadAsync(this.world.itemsAtlasParser!.latestImage) - }).then((texture) => { - this.entities.itemsTexture = texture - this.world.renderUpdateEmitter.emit('itemsTextureDownloaded') - }) + return this.world.setVersion(userVersion, texturesVersion) } addColumn (x, z, chunk, isLightUpdate = false) { diff --git a/renderer/viewer/lib/worldrendererCommon.ts b/renderer/viewer/lib/worldrendererCommon.ts index 3ca100a2..a54f9fe8 100644 --- a/renderer/viewer/lib/worldrendererCommon.ts +++ b/renderer/viewer/lib/worldrendererCommon.ts @@ -365,6 +365,7 @@ export abstract class WorldRendererCommon } const customBlockTextures = Object.keys(this.customTextures.blocks?.textures ?? {}) + const customItemTextures = Object.keys(this.customTextures.items?.textures ?? {}) console.time('createBlocksAtlas') const { atlas: blocksAtlas, canvas: blocksCanvas } = await blocksAssetsParser.makeNewAtlas(this.texturesVersion ?? this.version ?? 'latest', (textureName) => { const texture = this.customTextures?.blocks?.textures[textureName] @@ -376,7 +377,7 @@ export abstract class WorldRendererCommon const texture = this.customTextures?.items?.textures[textureName] if (!texture) return return texture - }, this.customTextures?.items?.tileSize) + }, this.customTextures?.items?.tileSize, undefined, customItemTextures) console.timeEnd('createItemsAtlas') this.blocksAtlasParser = new AtlasParser({ latest: blocksAtlas }, blocksCanvas.toDataURL()) this.itemsAtlasParser = new AtlasParser({ latest: itemsAtlas }, itemsCanvas.toDataURL()) @@ -417,8 +418,15 @@ export abstract class WorldRendererCommon config: this.mesherConfig, }) } + const itemsTexture = await new THREE.TextureLoader().loadAsync(this.itemsAtlasParser.latestImage) + itemsTexture.magFilter = THREE.NearestFilter + itemsTexture.minFilter = THREE.NearestFilter + itemsTexture.flipY = false + viewer.entities.itemsTexture = itemsTexture + this.renderUpdateEmitter.emit('textureDownloaded') - console.log('texture loaded') + this.renderUpdateEmitter.emit('itemsTextureDownloaded') + console.log('textures loaded') } async downloadDebugAtlas (isItems = false) { diff --git a/src/index.ts b/src/index.ts index beb86aea..8e586a8f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,7 @@ import './mineflayer/java-tester/index' import './external' import { getServerInfo } from './mineflayer/mc-protocol' import { onGameLoad, renderSlot } from './inventoryWindows' -import { RenderItem } from './mineflayer/items' +import { GeneralInputItem, RenderItem } from './mineflayer/items' import initCollisionShapes from './getCollisionInteractionShapes' import protocolMicrosoftAuth from 'minecraft-protocol/src/client/microsoftAuth' import microsoftAuthflow from './microsoftAuthflow' @@ -114,6 +114,7 @@ import { LocalServer } from './customServer' import { startLocalReplayServer } from './packetsReplay/replayPackets' import { localRelayServerPlugin } from './mineflayer/plugins/packetsRecording' import { createFullScreenProgressReporter } from './core/progressReporter' +import { getItemModelName } from './resourcesManager' window.debug = debug window.THREE = THREE @@ -181,19 +182,13 @@ viewer.entities.getItemUv = (item, specificProps) => { const name = typeof idOrName === 'number' ? loadedData.items[idOrName]?.name : idOrName if (!name) throw new Error(`Item not found: ${idOrName}`) - const itemSelector = playerState.getItemSelector({ - ...specificProps - }) - const model = getItemDefinition(viewer.world.itemsDefinitionsStore, { + const model = getItemModelName({ + ...item, name, - version: viewer.world.texturesVersion!, - properties: itemSelector - })?.model ?? name + } as GeneralInputItem, specificProps) const renderInfo = renderSlot({ - ...item, - nbt: null, - name: model, + modelName: model, }, false, true) if (!renderInfo) throw new Error(`Failed to get render info for item ${name}`) diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 471f5be4..d473f5be 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -20,6 +20,7 @@ import { currentScaling } from './scaleInterface' import { getItemDescription } from './itemsDescriptions' import { MessageFormatPart } from './chatUtils' import { GeneralInputItem, getItemMetadata, getItemNameRaw, RenderItem } from './mineflayer/items' +import { getItemModelName } from './resourcesManager' const loadedImagesCache = new Map() const cleanLoadedImagesCache = () => { @@ -174,14 +175,18 @@ const getImage = ({ path = undefined as string | undefined, texture = undefined return loadedImagesCache.get(loadPath) } -export const renderSlot = (slot: GeneralInputItem, debugIsQuickbar = false, fullBlockModelSupport = false): { +export type ResolvedItemModelRender = { + modelName: string, +} + +export const renderSlot = (model: ResolvedItemModelRender, debugIsQuickbar = false, fullBlockModelSupport = false): { texture: string, blockData?: Record & { resolvedModel: BlockModel }, scale?: number, slice?: number[], modelName?: string } | undefined => { - let itemModelName = slot.name + let itemModelName = model.modelName const originalItemName = itemModelName const isItem = loadedData.itemsByName[itemModelName] @@ -190,15 +195,12 @@ export const renderSlot = (slot: GeneralInputItem, debugIsQuickbar = false, full // #endregion - const { customModel } = getItemMetadata(slot) - if (customModel) { - itemModelName = customModel - } - let itemTexture try { assertDefined(viewer.world.itemsRenderer) - itemTexture = viewer.world.itemsRenderer.getItemTexture(itemModelName, {}, false, fullBlockModelSupport) ?? viewer.world.itemsRenderer.getItemTexture('item/missing_texture')! + itemTexture = + viewer.world.itemsRenderer.getItemTexture(itemModelName, {}, false, fullBlockModelSupport) + ?? viewer.world.itemsRenderer.getItemTexture('item/missing_texture')! } catch (err) { inGameError(`Failed to render item ${itemModelName} (original: ${originalItemName}) on ${bot.version} (resourcepack: ${options.enabledResourcepack}): ${err.stack}`) itemTexture = viewer.world.itemsRenderer!.getItemTexture('block/errored')! @@ -228,23 +230,15 @@ const getItemName = (slot: Item | RenderItem | null) => { return text.join('') } -export const renderSlotExternal = (slot) => { - const data = renderSlot(slot) - if (!data) return - return { - imageDataUrl: data.texture === 'invsprite' ? undefined : getImage({ path: data.texture })?.src, - sprite: data.slice && data.texture !== 'invsprite' ? data.slice.map(x => x * 2) : data.slice, - displayName: getItemName(slot) ?? slot.displayName, - } -} - const mapSlots = (slots: Array) => { return slots.map((slot, i) => { // todo stateid if (!slot) return try { - const slotCustomProps = renderSlot(slot, i === bot.inventory.hotbarStart + bot.quickBarSlot) + const debugIsQuickbar = i === bot.inventory.hotbarStart + bot.quickBarSlot + const modelName = getItemModelName(slot, { 'minecraft:display_context': 'gui', }) + const slotCustomProps = renderSlot({ modelName }, debugIsQuickbar) const itemCustomName = getItemName(slot) Object.assign(slot, { ...slotCustomProps, displayName: itemCustomName ?? slot.displayName }) //@ts-expect-error diff --git a/src/mineflayer/items.ts b/src/mineflayer/items.ts index 2f1b2f8d..bb437ef4 100644 --- a/src/mineflayer/items.ts +++ b/src/mineflayer/items.ts @@ -19,6 +19,7 @@ export type RenderItem = Pick & { components?: RenderSlotComponent[], displayName?: string + modelResolved?: boolean } type JsonString = string diff --git a/src/resourcePack.ts b/src/resourcePack.ts index aef23e8f..6e9c28a4 100644 --- a/src/resourcePack.ts +++ b/src/resourcePack.ts @@ -314,7 +314,8 @@ export const getResourcepackTiles = async (type: 'blocks' | 'items' | 'armor', e const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) => { viewer.world.customBlockStates = {} viewer.world.customModels = {} - const usedTextures = new Set() + const usedBlockTextures = new Set() + const usedItemTextures = new Set() const basePath = await getActiveResourcepackBasePath() if (!basePath) return progressReporter.beginStage('read-resource-pack-blockstates-and-models', 'Reading resource pack blockstates and models') @@ -328,8 +329,9 @@ const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) = if (file.endsWith('.json')) { const contents = await fs.promises.readFile(filePath, 'utf8') let name = file.replace('.json', '') + const isBlock = path.endsWith('block') if (type === 'models') { - name = `${path.endsWith('block') ? 'block' : 'item'}/${name}` + name = `${isBlock ? 'block' : 'item'}/${name}` } const parsed = JSON.parse(contents) if (namespaceDir === 'minecraft') { @@ -341,7 +343,11 @@ const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) = if (typeof texturePath !== 'string') continue if (texturePath.startsWith('#')) continue if (!texturePath.includes(':')) texturePath = `minecraft:${texturePath}` - usedTextures.add(texturePath as string) + if (isBlock) { + usedBlockTextures.add(texturePath as string) + } else { + usedItemTextures.add(texturePath as string) + } } } } @@ -369,7 +375,10 @@ const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) = viewer.world.customBlockStates = undefined viewer.world.customModels = undefined } - return { usedTextures } + return { + usedBlockTextures, + usedItemTextures + } } const downloadAndUseResourcePack = async (url: string, progressReporter: ProgressReporter): Promise => { @@ -517,9 +526,9 @@ const updateTextures = async (progressReporter = createConsoleLogProgressReporte const origBlocksFiles = Object.keys(viewer.world.sourceData.blocksAtlases.latest.textures) const origItemsFiles = Object.keys(viewer.world.sourceData.itemsAtlases.latest.textures) const origArmorFiles = Object.keys(armorTextures) - const { usedTextures: extraBlockTextures = new Set() } = await prepareBlockstatesAndModels(progressReporter) ?? {} - const blocksData = await getResourcepackTiles('blocks', [...origBlocksFiles, ...extraBlockTextures], progressReporter) - const itemsData = await getResourcepackTiles('items', origItemsFiles, progressReporter) + const { usedBlockTextures, usedItemTextures } = await prepareBlockstatesAndModels(progressReporter) ?? {} + const blocksData = await getResourcepackTiles('blocks', [...origBlocksFiles, ...usedBlockTextures ?? []], progressReporter) + const itemsData = await getResourcepackTiles('items', [...origItemsFiles, ...usedItemTextures ?? []], progressReporter) const armorData = await getResourcepackTiles('armor', origArmorFiles, progressReporter) await updateAllReplacableTextures() viewer.world.customTextures = {} diff --git a/src/resourcesManager.ts b/src/resourcesManager.ts new file mode 100644 index 00000000..fd92e58b --- /dev/null +++ b/src/resourcesManager.ts @@ -0,0 +1,23 @@ +import { Item } from 'prismarine-item' +import { ItemSpecificContextProperties } from 'renderer/viewer/lib/basePlayerState' +import { getItemDefinition } from 'mc-assets/dist/itemDefinitions' +import { playerState } from './mineflayer/playerState' +import { GeneralInputItem, getItemMetadata } from './mineflayer/items' + +export const getItemModelName = (item: GeneralInputItem, specificProps: ItemSpecificContextProperties) => { + let itemModelName = item.name + const { customModel } = getItemMetadata(item) + if (customModel) { + itemModelName = customModel + } + + const itemSelector = playerState.getItemSelector({ + ...specificProps + }) + const model = getItemDefinition(viewer.world.itemsDefinitionsStore, { + name: itemModelName, + version: viewer.world.texturesVersion!, + properties: itemSelector + })?.model ?? itemModelName + return model +} From b0da1e41d690d081636d320b8d29a1a80da58991 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 15:31:25 +0300 Subject: [PATCH 396/851] fix: fix crashes on packets logging recording fix: make replay panel minmizable --- package.json | 4 +- pnpm-lock.yaml | 22 ++-- src/appParams.ts | 3 +- src/core/progressReporter.ts | 1 + src/index.ts | 3 +- src/optionsStorage.ts | 2 +- src/react/PauseScreen.tsx | 9 +- src/react/ReplayPanel.tsx | 119 +++++++++++++++------ src/react/components/replay/PacketList.tsx | 8 +- 9 files changed, 114 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index b0c5ee20..7cacae56 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "mojangson": "^2.0.4", "net-browserify": "github:zardoy/prismarinejs-net-browserify", "node-gzip": "^1.1.2", - "mcraft-fun-mineflayer": "^0.1.7", + "mcraft-fun-mineflayer": "^0.1.8", "peerjs": "^1.5.0", "pixelarticons": "^1.8.1", "pretty-bytes": "^6.1.1", @@ -145,7 +145,7 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", - "mineflayer-mouse": "^0.0.4", + "mineflayer-mouse": "^0.0.5", "mc-assets": "^0.2.37", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be7ce2e5..dbbac60e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,8 +135,8 @@ importers: specifier: ^4.17.21 version: 4.17.21 mcraft-fun-mineflayer: - specifier: ^0.1.7 - version: 0.1.7(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) + specifier: ^0.1.8 + version: 0.1.8(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) minecraft-data: specifier: 3.83.1 version: 3.83.1 @@ -359,8 +359,8 @@ importers: specifier: github:zardoy/mineflayer version: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13) mineflayer-mouse: - specifier: ^0.0.4 - version: 0.0.4(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + specifier: ^0.0.5 + version: 0.0.5(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -6575,9 +6575,9 @@ packages: resolution: {integrity: sha512-49tk3shwxsDoV0PrrbORZEKg613vUQPULgusWjXNl8JEma5y41LEo57D6q4aHliXsV3Gb9ThrkFf6hIb0WlY1Q==} engines: {node: '>=18.0.0'} - mcraft-fun-mineflayer@0.1.7: - resolution: {integrity: sha512-DJPpp1YFwztoscdIOwfqBV9lbotva621F9GEep3BlqG3l06UdTzX2ElkvwyTR0IurFFBX/YKsNfxwL5WtLytgw==} - version: 0.1.7 + mcraft-fun-mineflayer@0.1.8: + resolution: {integrity: sha512-jyJTihNHfeToBPwVs3QMKBlVcaCABJ25YN2eoIBQEVTRVFzaXh13XRpElphLzTMj1Q5XFYqufHtMoR4tsb08qQ==} + version: 0.1.8 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: '@roamhq/wrtc': '*' @@ -6801,8 +6801,8 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824} version: 1.2.0 - mineflayer-mouse@0.0.4: - resolution: {integrity: sha512-55GqQhJWCXnOnm30uOjtI7nsawPb0kA3cAv6a5n1NJjTWFR6hzMkiRT6xGLYrvYhdf6Er3nsE2Ok/Aysa/jtFQ==} + mineflayer-mouse@0.0.5: + resolution: {integrity: sha512-0r/AOGTq+wZH9vrBcW93jH2dGRSlwlO6xc1Z67VJUFlZZ8oBefAOpiZq7LIGc7ROVbpcKEKjROdNv/iCFmzXYA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} mineflayer-pathfinder@2.4.4: @@ -17372,7 +17372,7 @@ snapshots: apl-image-packer: 1.1.0 zod: 3.24.1 - mcraft-fun-mineflayer@0.1.7(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): + mcraft-fun-mineflayer@0.1.8(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): dependencies: '@zardoy/flying-squid': 0.0.49(encoding@0.1.13) exit-hook: 2.2.1 @@ -17742,7 +17742,7 @@ snapshots: - encoding - supports-color - mineflayer-mouse@0.0.4(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + mineflayer-mouse@0.0.5(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): dependencies: change-case: 5.4.4 debug: 4.4.0(supports-color@8.1.1) diff --git a/src/appParams.ts b/src/appParams.ts index 50fbf055..962eb168 100644 --- a/src/appParams.ts +++ b/src/appParams.ts @@ -63,7 +63,8 @@ type AppQsParamsArrayTransformed = { [k in keyof AppQsParamsArray]: string[] } -const initialAppConfig = process.env.INLINED_APP_CONFIG as AppConfig ?? {} +globalThis.process ??= {} as any +const initialAppConfig = process?.env?.INLINED_APP_CONFIG as AppConfig ?? {} export const appQueryParams = new Proxy({} as AppQsParams, { get (target, property) { diff --git a/src/core/progressReporter.ts b/src/core/progressReporter.ts index db1958da..e2bdf5bc 100644 --- a/src/core/progressReporter.ts +++ b/src/core/progressReporter.ts @@ -138,6 +138,7 @@ export const createFullScreenProgressReporter = (): ProgressReporter => { setLoadingScreenStatus(message) }, end () { + if (appStatusState.isError) return fullScreenReporters.splice(fullScreenReporters.indexOf(reporter), 1) if (fullScreenReporters.length === 0) { setLoadingScreenStatus(undefined) diff --git a/src/index.ts b/src/index.ts index 8e586a8f..7269781d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -322,6 +322,7 @@ export async function connect (connectOptions: ConnectOptions) { if (ended) return ended = true viewer.resetAll() + progress.end() localServer = window.localServer = window.server = undefined gameAdditionalState.viewerConnection = false @@ -692,6 +693,7 @@ export async function connect (connectOptions: ConnectOptions) { } catch (err) { handleError(err) } + if (!bot) return if (connectOptions.server) { bot.loadPlugin(ping) @@ -700,7 +702,6 @@ export async function connect (connectOptions: ConnectOptions) { if (!localReplaySession) { bot.loadPlugin(localRelayServerPlugin) } - if (!bot) return const p2pConnectTimeout = p2pMultiplayer ? setTimeout(() => { throw new UserError('Spawn timeout. There might be error on the other side, check console.') }, 20_000) : undefined diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index b88b71c4..904b0a29 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -8,7 +8,7 @@ import { appQueryParamsArray } from './appParams' import type { AppConfig } from './globalState' const isDev = process.env.NODE_ENV === 'development' -const initialAppConfig = process.env.INLINED_APP_CONFIG as AppConfig ?? {} +const initialAppConfig = process.env?.INLINED_APP_CONFIG as AppConfig ?? {} const defaultOptions = { renderDistance: 3, keepChunksDistance: 1, diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index 4c4dec03..35d873ea 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -232,14 +232,15 @@ export default () => { if (pauseLinksConfig) { for (const [i, row] of pauseLinksConfig.entries()) { const rowButtons: React.ReactNode[] = [] - for (const button of row) { + for (const [k, button] of row.entries()) { + const key = `${i}-${k}` const style = { width: (204 / row.length - (row.length > 1 ? 4 : 0)) + 'px' } if (button.type === 'discord') { - rowButtons.push() + rowButtons.push() } else if (button.type === 'github') { - rowButtons.push() + rowButtons.push() } else if (button.type === 'url' && button.text) { - rowButtons.push() + rowButtons.push() } } pauseLinks.push(
{rowButtons}
) diff --git a/src/react/ReplayPanel.tsx b/src/react/ReplayPanel.tsx index 59950555..fd4082af 100644 --- a/src/react/ReplayPanel.tsx +++ b/src/react/ReplayPanel.tsx @@ -41,31 +41,100 @@ export default function ReplayPanel ({ style }: Props) { const [filter, setFilter] = useState(defaultFilter) + const [isMinimized, setIsMinimized] = useState(false) const { filtered: filteredPackets, hiddenCount } = filterPackets(packets.slice(-500), filter) useEffect(() => { onFilterChange(filter) }, [filter, onFilterChange]) + const handlePlayPauseClick = () => { + if (isMinimized) { + setIsMinimized(false) + } else { + onPlayPause?.(!isPlaying) + } + } + + const playPauseButton = ( + + ) + + const baseContainerStyle = { + position: 'fixed', + top: 18, + right: 0, + zIndex: 1000, + background: DARK_COLORS.bg, + padding: '16px', + borderRadius: '0 0 8px 0', + boxShadow: '0 2px 8px rgba(0,0,0,0.3)', + display: 'flex', + flexDirection: 'column', + gap: '12px', + color: DARK_COLORS.text, + ...style + } as const + + if (isMinimized) { + return ( +
+ {playPauseButton} +
+ ) + } + return (
-
{replayName || 'Unnamed Replay'}
+
+
{replayName || 'Unnamed Replay'}
+ +
+
Integrated server emulation. Testing client...
- - + {playPauseButton}
diff --git a/src/react/components/replay/PacketList.tsx b/src/react/components/replay/PacketList.tsx index 484c42c9..5183bf3f 100644 --- a/src/react/components/replay/PacketList.tsx +++ b/src/react/components/replay/PacketList.tsx @@ -1,4 +1,5 @@ import { useRef, useState } from 'react' +import { processPacketDataForLogging } from 'mcraft-fun-mineflayer/build/packetsLogger' import { PacketData } from '../../ReplayPanel' import { useScrollBehavior } from '../../hooks/useScrollBehavior' import { ClientOnMap } from '../../../generatedServerPackets' @@ -12,6 +13,7 @@ const formatters: Record string> = { const blockEntitiesCount = data.blockEntities?.length return `x:${data.x} z:${data.z} C:${sizeOfChunk} E:${blockEntitiesCount}` }, + default: (data) => processPacketDataForLogging(data) } const getPacketIcon = (name: string): string => { @@ -115,7 +117,7 @@ export default function PacketList ({ packets, filter, maxHeight = 300 }: Props) {packet.name} - {formatters[packet.name]?.(packet.data) ?? JSON.stringify(packet.data)} + {formatters[packet.name]?.(packet.data) ?? formatters.default(packet.data)}
{expandedPacket === packet.position && ( @@ -123,14 +125,14 @@ export default function PacketList ({ packets, filter, maxHeight = 300 }: Props)
Data:
-                        {JSON.stringify(packet.data, null, 2)}
+                        {JSON.stringify(JSON.parse(formatters.default(packet.data)), null, 2)}
                       
{packet.actualVersion && (
Actual Version:
-                          {JSON.stringify(packet.actualVersion, null, 2)}
+                          {JSON.stringify(JSON.parse(formatters.default(packet.actualVersion)), null, 2)}
                         
)} From 2619e5da89fc270297d3cc379758a95e13e01e0a Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 17:42:01 +0300 Subject: [PATCH 397/851] fix: was not possible to click notification, make error routing more strict & obvious --- src/core/progressReporter.ts | 25 ++++++++++++++++++++++++- src/react/NotificationProvider.tsx | 11 +++++++---- src/reactUi.tsx | 2 +- src/resourcePack.ts | 4 +++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/core/progressReporter.ts b/src/core/progressReporter.ts index e2bdf5bc..6ef6044f 100644 --- a/src/core/progressReporter.ts +++ b/src/core/progressReporter.ts @@ -13,12 +13,14 @@ export interface ProgressReporter { setMessage (message: string): void - end (): void + end(): void + error(message: string): void } interface ReporterDisplayImplementation { setMessage (message: string): void end (): void + error(message: string): void } interface StageInfo { @@ -124,6 +126,10 @@ const createProgressReporter = (implementation: ReporterDisplayImplementation): get currentMessage () { return currentMessage + }, + + error (message: string): void { + implementation.error(message) } } @@ -145,6 +151,11 @@ export const createFullScreenProgressReporter = (): ProgressReporter => { } else { setLoadingScreenStatus(fullScreenReporters.at(-1)!.currentMessage) } + }, + + error (message: string): void { + if (appStatusState.isError) return + setLoadingScreenStatus(message, true) } }) fullScreenReporters.push(reporter) @@ -162,6 +173,10 @@ export const createNotificationProgressReporter = (endMessage?: string): Progres } else { hideNotification() } + }, + + error (message: string): void { + showNotification(message, '', true, '', undefined, true) } }) } @@ -173,6 +188,10 @@ export const createConsoleLogProgressReporter = (): ProgressReporter => { }, end () { console.log('done') + }, + + error (message: string): void { + console.error(message) } }) } @@ -191,6 +210,10 @@ export const createWrappedProgressReporter = (reporter: ProgressReporter, messag if (message) { reporter.endStage(stage) } + }, + + error (message: string): void { + reporter.error(message) } }) } diff --git a/src/react/NotificationProvider.tsx b/src/react/NotificationProvider.tsx index 4c9661e0..6460c0e9 100644 --- a/src/react/NotificationProvider.tsx +++ b/src/react/NotificationProvider.tsx @@ -60,10 +60,13 @@ export default () => { // }, []) const scale = useAppScale() - return
+ return
{ +
@@ -214,7 +215,6 @@ const App = () => {
-
diff --git a/src/resourcePack.ts b/src/resourcePack.ts index 6e9c28a4..fd01168a 100644 --- a/src/resourcePack.ts +++ b/src/resourcePack.ts @@ -392,7 +392,7 @@ const downloadAndUseResourcePack = async (url: string, progressReporter: Progres const response = await fetch(url).catch((err) => { console.log(`Ensure server on ${url} support CORS which is not required for regular client, but is required for the web client`) console.error(err) - showNotification('Failed to download resource pack: ' + err.message) + progressReporter.error('Failed to download resource pack: ' + err.message) }) console.timeEnd('downloadServerResourcePack') if (!response) return @@ -425,6 +425,8 @@ const downloadAndUseResourcePack = async (url: string, progressReporter: Progres console.error(err) showNotification('Failed to install resource pack: ' + err.message) }) + } catch (err) { + progressReporter.error('Could not install resource pack: ' + err.message) } finally { progressReporter.endStage('download-resource-pack') resourcePackState.isServerInstalling = false From 2a8f5140957be1e832a8b480c262cec5963f514e Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 18:24:06 +0300 Subject: [PATCH 398/851] add build zip workflow --- .github/workflows/build-zip.yml | 43 +++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/build-zip.yml diff --git a/.github/workflows/build-zip.yml b/.github/workflows/build-zip.yml new file mode 100644 index 00000000..9f8a6292 --- /dev/null +++ b/.github/workflows/build-zip.yml @@ -0,0 +1,43 @@ +name: Build and Bundle + +on: + workflow_dispatch: + +jobs: + build-and-bundle: + runs-on: ubuntu-latest + permissions: write-all + steps: + - name: Checkout repository + uses: actions/checkout@master + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install dependencies + run: pnpm install + + - name: Build project + run: pnpm build + + - name: Bundle server.js + run: | + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js + + - name: Create distribution package + run: | + mkdir -p package + cp -r dist package/ + cp bundled-server.js package/server.js + cd package + zip -r ../dist-package.zip . + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: dist-package + path: dist-package.zip From 874cafc75e605688e9a7d745eed09859ff820154 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 18:42:08 +0300 Subject: [PATCH 399/851] add self host zip publishing with release --- .github/workflows/build-zip.yml | 10 +++++----- .github/workflows/publish.yml | 31 ++++++++++++++++++++++++------- package.json | 3 +++ server.js | 2 +- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-zip.yml b/.github/workflows/build-zip.yml index 9f8a6292..e6ed334c 100644 --- a/.github/workflows/build-zip.yml +++ b/.github/workflows/build-zip.yml @@ -1,4 +1,4 @@ -name: Build and Bundle +name: Make Self Host Zip on: workflow_dispatch: @@ -26,7 +26,7 @@ jobs: - name: Bundle server.js run: | - pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define=process.env.NODE_ENV="production" - name: Create distribution package run: | @@ -34,10 +34,10 @@ jobs: cp -r dist package/ cp bundled-server.js package/server.js cd package - zip -r ../dist-package.zip . + zip -r ../self-host.zip . - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: dist-package - path: dist-package.zip + name: self-host + path: self-host.zip diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index eb765648..958aa50e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -43,19 +43,36 @@ jobs: with: run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} --prod id: deploy - - run: | - pnpx zardoy-release node --footer "This release URL: ${{ steps.deploy.outputs.stdout }}" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # has possible output: tag - id: release - # has output + # publish to github - run: cp vercel.json .vercel/output/static/vercel.json - uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: .vercel/output/static force_orphan: true + + - name: Build self-host version + run: pnpm build + - name: Bundle server.js + run: | + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define=process.env.NODE_ENV="production" + + - name: Create zip package + run: | + mkdir -p package + cp -r dist package/ + cp bundled-server.js package/server.js + cd package + zip -r ../self-host.zip . + + - run: | + pnpx zardoy-release node --footer "This release URL: ${{ steps.deploy.outputs.stdout }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # has possible output: tag + id: release + + # has output - name: Set publishing config run: pnpm config set '//registry.npmjs.org/:_authToken' "${NODE_AUTH_TOKEN}" env: diff --git a/package.json b/package.json index 7cacae56..0e0aa6a8 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,9 @@ "web", "client" ], + "release": { + "attachReleaseFiles": "self-host.zip" + }, "publish": { "preset": { "publishOnlyIfChanged": true, diff --git a/server.js b/server.js index 2dbb05b3..20e66051 100644 --- a/server.js +++ b/server.js @@ -15,7 +15,7 @@ try { // Create our app const app = express() -const isProd = process.argv.includes('--prod') +const isProd = process.argv.includes('--prod') || process.env.NODE_ENV === 'production' app.use(compression()) app.use(cors()) app.use(netApi({ allowOrigin: '*' })) From 1d4dc0ddaa8fe203197c6c819f0223b551bcaeb9 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 18:45:25 +0300 Subject: [PATCH 400/851] fix define in arg build --- .github/workflows/build-zip.yml | 2 +- .github/workflows/publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-zip.yml b/.github/workflows/build-zip.yml index e6ed334c..2e7e886d 100644 --- a/.github/workflows/build-zip.yml +++ b/.github/workflows/build-zip.yml @@ -26,7 +26,7 @@ jobs: - name: Bundle server.js run: | - pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define=process.env.NODE_ENV="production" + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="production" - name: Create distribution package run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 958aa50e..7e0af8d2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -55,7 +55,7 @@ jobs: run: pnpm build - name: Bundle server.js run: | - pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define=process.env.NODE_ENV="production" + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="production" - name: Create zip package run: | From 4b54be637d1917b87e51f8c8c03938141fbc3bfe Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 3 Mar 2025 18:48:27 +0300 Subject: [PATCH 401/851] ci: adjust esbuild build arg syntax for prod --- .github/workflows/build-zip.yml | 2 +- .github/workflows/publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-zip.yml b/.github/workflows/build-zip.yml index 2e7e886d..cc472476 100644 --- a/.github/workflows/build-zip.yml +++ b/.github/workflows/build-zip.yml @@ -26,7 +26,7 @@ jobs: - name: Bundle server.js run: | - pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="production" + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="'production'" - name: Create distribution package run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7e0af8d2..4fbff15d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -55,7 +55,7 @@ jobs: run: pnpm build - name: Bundle server.js run: | - pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="production" + pnpm esbuild server.js --bundle --platform=node --outfile=bundled-server.js --define:process.env.NODE_ENV="'production'" - name: Create zip package run: | From 1c700aac1e3b7d8592c3a0a60e1f27ad5ad80756 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Tue, 4 Mar 2025 19:00:20 +0300 Subject: [PATCH 402/851] feat(config-json): Only either bundle or load from remote (#291) --- .github/workflows/next-deploy.yml | 2 ++ .github/workflows/preview.yml | 2 ++ .github/workflows/publish.yml | 2 ++ Dockerfile | 5 +-- rsbuild.config.ts | 12 +++++-- scripts/dockerPrepare.mjs | 22 ++++++++++-- src/appConfig.ts | 59 +++++++++++++++++++++++++++++++ src/appParams.ts | 2 +- src/browserfs.ts | 4 +-- src/customChannels.ts | 6 ++-- src/globalState.ts | 41 ++------------------- src/index.ts | 17 +++------ src/optionsStorage.ts | 19 +++++++--- src/panorama.ts | 2 +- src/react/MainMenuRenderApp.tsx | 6 ++-- 15 files changed, 127 insertions(+), 74 deletions(-) create mode 100644 src/appConfig.ts diff --git a/.github/workflows/next-deploy.yml b/.github/workflows/next-deploy.yml index 665abb30..042302a4 100644 --- a/.github/workflows/next-deploy.yml +++ b/.github/workflows/next-deploy.yml @@ -32,6 +32,8 @@ jobs: echo "{\"latestTag\": \"$(git rev-parse --short $GITHUB_SHA)\", \"isCommit\": true}" > assets/release.json - name: Build Project Artifacts run: vercel build --token=${{ secrets.VERCEL_TOKEN }} + env: + CONFIG_JSON_SOURCE: BUNDLED - run: pnpm build-storybook - name: Copy playground files run: | diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 18c80e8c..6408c86a 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -61,6 +61,8 @@ jobs: echo "{\"latestTag\": \"$(git rev-parse --short ${{ github.event.pull_request.head.sha }})\", \"isCommit\": true}" > assets/release.json - name: Build Project Artifacts run: vercel build --token=${{ secrets.VERCEL_TOKEN }} + env: + CONFIG_JSON_SOURCE: BUNDLED - run: pnpm build-storybook - name: Copy playground files run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4fbff15d..5af8abab 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -30,6 +30,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: vercel build --token=${{ secrets.VERCEL_TOKEN }} --prod + env: + CONFIG_JSON_SOURCE: BUNDLED - run: pnpm build-storybook - name: Copy playground files run: | diff --git a/Dockerfile b/Dockerfile index 4769141f..34641353 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,7 @@ RUN npm i -g pnpm@9.0.4 # Build arguments ARG DOWNLOAD_SOUNDS=false ARG DISABLE_SERVICE_WORKER=false +ARG CONFIG_JSON_SOURCE=REMOTE # TODO need flat --no-root-optional RUN node ./scripts/dockerPrepare.mjs RUN pnpm i @@ -22,8 +23,8 @@ RUN if [ "$DOWNLOAD_SOUNDS" = "true" ] ; then node scripts/downloadSoundsMap.mjs # ENTRYPOINT ["pnpm", "run", "run-all"] # only for prod -RUN GITHUB_REPOSITORY=zardoy/minecraft-web-client \ - DISABLE_SERVICE_WORKER=$DISABLE_SERVICE_WORKER \ +RUN DISABLE_SERVICE_WORKER=$DISABLE_SERVICE_WORKER \ + CONFIG_JSON_SOURCE=$CONFIG_JSON_SOURCE \ pnpm run build # ---- Run Stage ---- diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 4147ce7d..875d0e0c 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -25,12 +25,14 @@ const disableServiceWorker = process.env.DISABLE_SERVICE_WORKER === 'true' let releaseTag let releaseLink let releaseChangelog +let githubRepositoryFallback if (fs.existsSync('./assets/release.json')) { const releaseJson = JSON.parse(fs.readFileSync('./assets/release.json', 'utf8')) releaseTag = releaseJson.latestTag releaseLink = releaseJson.isCommit ? `/commit/${releaseJson.latestTag}` : `/releases/${releaseJson.latestTag}` releaseChangelog = releaseJson.changelog?.replace(//, '') + githubRepositoryFallback = releaseJson.repository } const configJson = JSON.parse(fs.readFileSync('./config.json', 'utf8')) @@ -41,6 +43,8 @@ if (dev) { configJson.defaultProxy = ':8080' } +const configSource = process.env.CONFIG_JSON_SOURCE || 'REMOTE' + // base options are in ./renderer/rsbuildSharedConfig.ts const appConfig = defineConfig({ html: { @@ -66,13 +70,13 @@ const appConfig = defineConfig({ 'process.env.BUILD_VERSION': JSON.stringify(!dev ? buildingVersion : 'undefined'), 'process.env.MAIN_MENU_LINKS': JSON.stringify(process.env.MAIN_MENU_LINKS), 'process.env.GITHUB_URL': - JSON.stringify(`https://github.com/${process.env.GITHUB_REPOSITORY || `${process.env.VERCEL_GIT_REPO_OWNER}/${process.env.VERCEL_GIT_REPO_SLUG}`}`), + JSON.stringify(`https://github.com/${process.env.GITHUB_REPOSITORY || `${process.env.VERCEL_GIT_REPO_OWNER}/${process.env.VERCEL_GIT_REPO_SLUG}` || githubRepositoryFallback}`), 'process.env.DEPS_VERSIONS': JSON.stringify({}), 'process.env.RELEASE_TAG': JSON.stringify(releaseTag), 'process.env.RELEASE_LINK': JSON.stringify(releaseLink), 'process.env.RELEASE_CHANGELOG': JSON.stringify(releaseChangelog), 'process.env.DISABLE_SERVICE_WORKER': JSON.stringify(disableServiceWorker), - 'process.env.INLINED_APP_CONFIG': JSON.stringify(configJson), + 'process.env.INLINED_APP_CONFIG': JSON.stringify(configSource === 'BUNDLED' ? configJson : null), }, }, server: { @@ -109,7 +113,9 @@ const appConfig = defineConfig({ fs.copyFileSync('./assets/release.json', './dist/release.json') } - fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8') + if (configSource === 'REMOTE') { + fs.writeFileSync('./dist/config.json', JSON.stringify(configJson), 'utf8') + } if (fs.existsSync('./generated/sounds.js')) { fs.copyFileSync('./generated/sounds.js', './dist/sounds.js') } diff --git a/scripts/dockerPrepare.mjs b/scripts/dockerPrepare.mjs index 37e57d01..62a4f5e4 100644 --- a/scripts/dockerPrepare.mjs +++ b/scripts/dockerPrepare.mjs @@ -4,9 +4,27 @@ import path from 'path' import { fileURLToPath } from 'url' import { execSync } from 'child_process' -// write release tag +// Get repository from git config +const getGitRepository = () => { + try { + const gitConfig = fs.readFileSync('.git/config', 'utf8') + const originUrlMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url = .*?github\.com[:/](.*?)(\.git)?\n/m) + if (originUrlMatch) { + return originUrlMatch[1] + } + } catch (err) { + console.warn('Failed to read git repository from config:', err) + } + return null +} + +// write release tag and repository info const commitShort = execSync('git rev-parse --short HEAD').toString().trim() -fs.writeFileSync('./assets/release.json', JSON.stringify({ latestTag: `${commitShort} (docker)` }), 'utf8') +const repository = getGitRepository() +fs.writeFileSync('./assets/release.json', JSON.stringify({ + latestTag: `${commitShort} (docker)`, + repository +}), 'utf8') const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8')) delete packageJson.optionalDependencies diff --git a/src/appConfig.ts b/src/appConfig.ts new file mode 100644 index 00000000..3d6d8f93 --- /dev/null +++ b/src/appConfig.ts @@ -0,0 +1,59 @@ +import { disabledSettings, options, qsOptions } from './optionsStorage' +import { miscUiState } from './globalState' +import { setLoadingScreenStatus } from './appStatus' + +export type AppConfig = { + // defaultHost?: string + // defaultHostSave?: string + defaultProxy?: string + // defaultProxySave?: string + // defaultVersion?: string + peerJsServer?: string + peerJsServerFallback?: string + promoteServers?: Array<{ ip, description, version? }> + mapsProvider?: string + + appParams?: Record // query string params + + defaultSettings?: Record + forceSettings?: Record + // hideSettings?: Record + allowAutoConnect?: boolean + pauseLinks?: Array>> +} + +export const loadAppConfig = (appConfig: AppConfig) => { + if (miscUiState.appConfig) { + Object.assign(miscUiState.appConfig, appConfig) + } else { + miscUiState.appConfig = appConfig + } + + if (appConfig.forceSettings) { + for (const [key, value] of Object.entries(appConfig.forceSettings)) { + if (value) { + disabledSettings.value.add(key) + // since the setting is forced, we need to set it to that value + if (appConfig.defaultSettings?.[key] && !qsOptions[key]) { + options[key] = appConfig.defaultSettings[key] + } + } else { + disabledSettings.value.delete(key) + } + } + } +} + +export const isBundledConfigUsed = !!process.env.INLINED_APP_CONFIG + +if (isBundledConfigUsed) { + loadAppConfig(process.env.INLINED_APP_CONFIG as AppConfig ?? {}) +} else { + void window.fetch('config.json').then(async res => res.json()).then(c => c, (error) => { + // console.warn('Failed to load optional app config.json', error) + // return {} + setLoadingScreenStatus('Failed to load app config.json', true) + }).then((config: AppConfig) => { + loadAppConfig(config) + }) +} diff --git a/src/appParams.ts b/src/appParams.ts index 962eb168..98d6ff62 100644 --- a/src/appParams.ts +++ b/src/appParams.ts @@ -1,4 +1,4 @@ -import type { AppConfig } from './globalState' +import type { AppConfig } from './appConfig' const qsParams = new URLSearchParams(window.location?.search ?? '') diff --git a/src/browserfs.ts b/src/browserfs.ts index 41608e30..0f4579b8 100644 --- a/src/browserfs.ts +++ b/src/browserfs.ts @@ -41,13 +41,13 @@ browserfs.configure({ throw e2 } showNotification('Failed to access device storage', `Check you have free space. ${e.message}`, true) - miscUiState.appLoaded = true + miscUiState.fsReady = true miscUiState.singleplayerAvailable = false }) return } await updateTexturePackInstalledState() - miscUiState.appLoaded = true + miscUiState.fsReady = true miscUiState.singleplayerAvailable = true }) diff --git a/src/customChannels.ts b/src/customChannels.ts index 35922101..ff0f8a32 100644 --- a/src/customChannels.ts +++ b/src/customChannels.ts @@ -85,15 +85,15 @@ const registeredJeiChannel = () => { [ { name: 'id', - type: 'pstring', + type: ['pstring', { countType: 'i16' }] }, { name: 'categoryTitle', - type: 'pstring', + type: ['pstring', { countType: 'i16' }] }, { name: 'items', - type: 'pstring', + type: ['pstring', { countType: 'i16' }] }, ] ] diff --git a/src/globalState.ts b/src/globalState.ts index 74e6c3ff..0ee8671d 100644 --- a/src/globalState.ts +++ b/src/globalState.ts @@ -5,6 +5,7 @@ import type { WorldWarp } from 'flying-squid/dist/lib/modules/warps' import type { OptionsGroupType } from './optionsGuiScheme' import { appQueryParams } from './appParams' import { options, disabledSettings } from './optionsStorage' +import { AppConfig } from './appConfig' // todo: refactor structure with support of hideNext=false @@ -110,26 +111,6 @@ export const showContextmenu = (items: ContextMenuItem[], { clientX, clientY }) // --- -export type AppConfig = { - // defaultHost?: string - // defaultHostSave?: string - defaultProxy?: string - // defaultProxySave?: string - // defaultVersion?: string - peerJsServer?: string - peerJsServerFallback?: string - promoteServers?: Array<{ ip, description, version? }> - mapsProvider?: string - - appParams?: Record // query string params - - defaultSettings?: Record - forceSettings?: Record - // hideSettings?: Record - allowAutoConnect?: boolean - pauseLinks?: Array>> -} - export const miscUiState = proxy({ currentDisplayQr: null as string | null, currentTouch: null as boolean | null, @@ -144,7 +125,7 @@ export const miscUiState = proxy({ loadedServerIndex: '', /** currently trying to load or loaded mc version, after all data is loaded */ loadedDataVersion: null as string | null, - appLoaded: false, + fsReady: false, singleplayerAvailable: false, usingGamepadInput: false, appConfig: null as AppConfig | null, @@ -152,24 +133,6 @@ export const miscUiState = proxy({ displayFullmap: false }) -export const loadAppConfig = (appConfig: AppConfig) => { - if (miscUiState.appConfig) { - Object.assign(miscUiState.appConfig, appConfig) - } else { - miscUiState.appConfig = appConfig - } - - if (appConfig.forceSettings) { - for (const [key, value] of Object.entries(appConfig.forceSettings)) { - if (value) { - disabledSettings.value.delete(key) - } else { - disabledSettings.value.add(key) - } - } - } -} - export const isGameActive = (foregroundCheck: boolean) => { if (foregroundCheck && activeModalStack.length) return false return miscUiState.gameLoaded diff --git a/src/index.ts b/src/index.ts index 7269781d..b8727395 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import './mineflayer/cameraShake' import './shims/patchShims' import './mineflayer/java-tester/index' import './external' +import './appConfig' import { getServerInfo } from './mineflayer/mc-protocol' import { onGameLoad, renderSlot } from './inventoryWindows' import { GeneralInputItem, RenderItem } from './mineflayer/items' @@ -48,7 +49,6 @@ import initializePacketsReplay from './packetsReplay/packetsReplayLegacy' import { initVR } from './vr' import { - AppConfig, activeModalStack, activeModalStacks, hideModal, @@ -57,7 +57,6 @@ import { miscUiState, showModal, gameAdditionalState, - loadAppConfig } from './globalState' import { parseServerAddress } from './parseServerAddress' @@ -904,8 +903,9 @@ export async function connect (connectOptions: ConnectOptions) { const reconnectOptions = sessionStorage.getItem('reconnectOptions') ? JSON.parse(sessionStorage.getItem('reconnectOptions')!) : undefined listenGlobalEvents() -watchValue(miscUiState, async s => { - if (s.appLoaded) { // fs ready +const unsubscribe = watchValue(miscUiState, async s => { + if (s.fsReady && s.appConfig) { + unsubscribe() if (reconnectOptions) { sessionStorage.removeItem('reconnectOptions') if (Date.now() - reconnectOptions.timestamp < 1000 * 60 * 2) { @@ -966,15 +966,6 @@ document.body.addEventListener('touchstart', (e) => { }, { passive: false }) // #endregion -loadAppConfig(process.env.INLINED_APP_CONFIG as AppConfig ?? {}) -// load maybe updated config on the server with updated params (just in case) -void window.fetch('config.json').then(async res => res.json()).then(c => c, (error) => { - console.warn('Failed to load optional app config.json', error) - return {} -}).then((config: AppConfig | {}) => { - loadAppConfig(config) -}) - // qs open actions if (!reconnectOptions) { downloadAndOpenFile().then((downloadAction) => { diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index 904b0a29..79748679 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -5,7 +5,7 @@ import { proxy, subscribe } from 'valtio/vanilla' import { subscribeKey } from 'valtio/utils' import { omitObj } from '@zardoy/utils' import { appQueryParamsArray } from './appParams' -import type { AppConfig } from './globalState' +import type { AppConfig } from './appConfig' const isDev = process.env.NODE_ENV === 'development' const initialAppConfig = process.env?.INLINED_APP_CONFIG as AppConfig ?? {} @@ -188,7 +188,7 @@ subscribe(options, () => { localStorage.options = JSON.stringify(saveOptions) }) -type WatchValue = >(proxy: T, callback: (p: T, isChanged: boolean) => void) => void +type WatchValue = >(proxy: T, callback: (p: T, isChanged: boolean) => void) => () => void export const watchValue: WatchValue = (proxy, callback) => { const watchedProps = new Set() @@ -198,10 +198,19 @@ export const watchValue: WatchValue = (proxy, callback) => { return Reflect.get(target, p, receiver) }, }), false) + const unsubscribes = [] as Array<() => void> for (const prop of watchedProps) { - subscribeKey(proxy, prop, () => { - callback(proxy, true) - }) + unsubscribes.push( + subscribeKey(proxy, prop, () => { + callback(proxy, true) + }) + ) + } + + return () => { + for (const unsubscribe of unsubscribes) { + unsubscribe() + } } } diff --git a/src/panorama.ts b/src/panorama.ts index efc06e16..3c888246 100644 --- a/src/panorama.ts +++ b/src/panorama.ts @@ -48,7 +48,7 @@ const updateResourcePackSupportPanorama = async () => { } watchValue(miscUiState, m => { - if (m.appLoaded) { + if (m.fsReady) { // Also adds panorama on app load here watchValue(resourcePackState, async (s) => { const oldState = panoramaUsesResourcePack diff --git a/src/react/MainMenuRenderApp.tsx b/src/react/MainMenuRenderApp.tsx index e06ced52..30fd3047 100644 --- a/src/react/MainMenuRenderApp.tsx +++ b/src/react/MainMenuRenderApp.tsx @@ -75,12 +75,12 @@ export const mainMenuState = proxy({ let disableAnimation = false export default () => { const haveModals = useSnapshot(activeModalStack).length - const { gameLoaded, appLoaded, appConfig, singleplayerAvailable } = useSnapshot(miscUiState) + const { gameLoaded, fsReady, appConfig, singleplayerAvailable } = useSnapshot(miscUiState) - const noDisplay = haveModals || gameLoaded || !appLoaded + const noDisplay = haveModals || gameLoaded || !fsReady useEffect(() => { - if (noDisplay && appLoaded) disableAnimation = true + if (noDisplay && fsReady) disableAnimation = true }, [noDisplay]) const [versionStatus, setVersionStatus] = useState('') From 465ce35e8314a0ddda93a39e733243b7763c49f3 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 13:02:55 +0300 Subject: [PATCH 403/851] feat: display motd/players info for ws servers (still no icon sadly) add new server --- config.json | 4 ++++ src/mineflayer/minecraft-protocol-extra.ts | 2 +- src/react/ServersListProvider.tsx | 6 +++++- src/react/Singleplayer.tsx | 4 +++- src/react/singleplayer.module.css | 3 +++ 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/config.json b/config.json index 6aa86397..e48d758b 100644 --- a/config.json +++ b/config.json @@ -6,6 +6,10 @@ "peerJsServer": "", "peerJsServerFallback": "https://p2p.mcraft.fun", "promoteServers": [ + { + "ip": "ws://mcraft.ryzyn.xyz", + "version": "1.19.4" + }, { "ip": "ws://play.mcraft.fun" }, diff --git a/src/mineflayer/minecraft-protocol-extra.ts b/src/mineflayer/minecraft-protocol-extra.ts index ef1bfbf2..e8216a00 100644 --- a/src/mineflayer/minecraft-protocol-extra.ts +++ b/src/mineflayer/minecraft-protocol-extra.ts @@ -13,7 +13,7 @@ export const pingServerVersion = async (ip: string, port?: number, mergeOptions: ...mergeOptions, } let latency = 0 - let fullInfo = null + let fullInfo: any = null fakeClient.autoVersionHooks = [(res) => { latency = res.latency fullInfo = res diff --git a/src/react/ServersListProvider.tsx b/src/react/ServersListProvider.tsx index 16c5ae8b..56ec75b6 100644 --- a/src/react/ServersListProvider.tsx +++ b/src/react/ServersListProvider.tsx @@ -17,6 +17,7 @@ import { useCopyKeybinding } from './simpleHooks' import { AuthenticatedAccount, getInitialServersList, getServerConnectionHistory, setNewServersList, StoreServerItem } from './serversStorage' type AdditionalDisplayData = { + textNameRightGrayed: string formattedText: string textNameRight: string icon?: string @@ -143,9 +144,11 @@ const Inner = ({ hidden, customServersList }: { hidden?: boolean, customServersL let data if (isWebSocket) { const pingResult = await getServerInfo(server.ip, undefined, undefined, true) + console.log('pingResult.fullInfo.description', pingResult.fullInfo.description) data = { - formattedText: `${pingResult.version} server with a direct websocket connection`, + formattedText: pingResult.fullInfo.description, textNameRight: `ws ${pingResult.latency}ms`, + textNameRightGrayed: `${pingResult.fullInfo.players?.online ?? '??'}/${pingResult.fullInfo.players?.max ?? '??'}`, offline: false } } else { @@ -364,6 +367,7 @@ const Inner = ({ hidden, customServersList }: { hidden?: boolean, customServersL detail: (server.versionOverride ?? '') + ' ' + (server.usernameOverride ?? ''), formattedTextOverride: additional?.formattedText, worldNameRight: additional?.textNameRight ?? '', + worldNameRightGrayed: additional?.textNameRightGrayed ?? '', iconSrc: additional?.icon, offline: additional?.offline } diff --git a/src/react/Singleplayer.tsx b/src/react/Singleplayer.tsx index 5effc269..6d7d6b0e 100644 --- a/src/react/Singleplayer.tsx +++ b/src/react/Singleplayer.tsx @@ -24,13 +24,14 @@ export interface WorldProps { detail?: string formattedTextOverride?: string worldNameRight?: string + worldNameRightGrayed?: string onFocus?: (name: string) => void onInteraction?(interaction: 'enter' | 'space') elemRef?: React.Ref offline?: boolean } -const World = ({ name, isFocused, title, lastPlayed, size, detail = '', onFocus, onInteraction, iconSrc, formattedTextOverride, worldNameRight, elemRef, offline }: WorldProps & { ref?: React.Ref }) => { +const World = ({ name, isFocused, title, lastPlayed, size, detail = '', onFocus, onInteraction, iconSrc, formattedTextOverride, worldNameRight, worldNameRightGrayed, elemRef, offline }: WorldProps & { ref?: React.Ref }) => { const timeRelativeFormatted = useMemo(() => { if (!lastPlayed) return '' const formatter = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }) @@ -63,6 +64,7 @@ const World = ({ name, isFocused, title, lastPlayed, size, detail = '', onFocus,
{title}
+ {worldNameRightGrayed && {worldNameRightGrayed}} {offline ? ( diff --git a/src/react/singleplayer.module.css b/src/react/singleplayer.module.css index 0e69ea46..0de91735 100644 --- a/src/react/singleplayer.module.css +++ b/src/react/singleplayer.module.css @@ -36,6 +36,9 @@ .world_title_right { color: #999; font-size: 9px; + display: flex; + align-items: end; + gap: 1px; } .world_info { margin-left: 3px; From 998f0f0a85cd82cd3455b45d9866279000504298 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 13:07:21 +0300 Subject: [PATCH 404/851] fix: fix sentry #6092213276 DataCloneError: Cannot decode detached ArrayBuffer --- src/basicSounds.ts | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/basicSounds.ts b/src/basicSounds.ts index 6c2b5f4f..40428c6b 100644 --- a/src/basicSounds.ts +++ b/src/basicSounds.ts @@ -17,17 +17,30 @@ const convertedSounds = [] as string[] export async function loadSound (path: string, contents = path) { if (loadingSounds.includes(path)) return true loadingSounds.push(path) - const res = await window.fetch(contents) - if (!res.ok) { - const error = `Failed to load sound ${path}` - if (isCypress()) throw new Error(error) - else console.warn(error) - return - } - const data = await res.arrayBuffer() - sounds[path] = data - loadingSounds.splice(loadingSounds.indexOf(path), 1) + try { + audioContext ??= new window.AudioContext() + + const res = await window.fetch(contents) + if (!res.ok) { + const error = `Failed to load sound ${path}` + if (isCypress()) throw new Error(error) + else console.warn(error) + return + } + const arrayBuffer = await res.arrayBuffer() + + // Decode the audio data immediately + const audioBuffer = await audioContext.decodeAudioData(arrayBuffer) + sounds[path] = audioBuffer + convertedSounds.push(path) // Mark as converted immediately + + loadingSounds.splice(loadingSounds.indexOf(path), 1) + } catch (err) { + console.warn(`Failed to load sound ${path}:`, err) + loadingSounds.splice(loadingSounds.indexOf(path), 1) + if (isCypress()) throw err + } } export const loadOrPlaySound = async (url, soundVolume = 1, loadTimeout = 500) => { @@ -53,13 +66,6 @@ export async function playSound (url, soundVolume = 1) { return } - for (const [soundName, sound] of Object.entries(sounds)) { - if (convertedSounds.includes(soundName)) continue - // eslint-disable-next-line no-await-in-loop - sounds[soundName] = await audioContext.decodeAudioData(sound) - convertedSounds.push(soundName) - } - const soundBuffer = sounds[url] if (!soundBuffer) { console.warn(`Sound ${url} not loaded yet`) From 0db49e78799021d90223f989abec11a99ff87f86 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Wed, 5 Mar 2025 15:11:42 +0300 Subject: [PATCH 405/851] feat: Full support for rendering blocks in inventory GUI powered by deeplsate (#292) --- package.json | 2 +- pnpm-lock.yaml | 22 +- renderer/viewer/lib/guiRenderer.ts | 275 +++++++++++++++++++++ renderer/viewer/lib/worldrendererCommon.ts | 13 + src/inventoryWindows.ts | 34 ++- src/react/EnterFullscreenButton.tsx | 9 +- 6 files changed, 336 insertions(+), 19 deletions(-) create mode 100644 renderer/viewer/lib/guiRenderer.ts diff --git a/package.json b/package.json index 0e0aa6a8..1a195884 100644 --- a/package.json +++ b/package.json @@ -148,8 +148,8 @@ "http-browserify": "^1.7.0", "http-server": "^14.1.1", "https-browserify": "^1.0.0", + "mc-assets": "^0.2.42", "mineflayer-mouse": "^0.0.5", - "mc-assets": "^0.2.37", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", "mineflayer-pathfinder": "^2.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dbbac60e..d5c2446f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -350,8 +350,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 mc-assets: - specifier: ^0.2.37 - version: 0.2.37 + specifier: ^0.2.42 + version: 0.2.42 minecraft-inventory-gui: specifier: github:zardoy/minecraft-inventory-gui#next version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/75e940a4cd50d89e0ba03db3733d5d704917a3c8(@types/react@18.2.20)(react@18.2.0) @@ -3589,9 +3589,6 @@ packages: resolution: {integrity: sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==} engines: {node: '>=8'} - apl-image-packer@1.1.0: - resolution: {integrity: sha512-Pb1Q76cp8xpY8X4OqVrnk5v1/tB5kOtCzwgOnx8IxMNeekFh/eNUiUKeX5fvGNViZiLmuKAAQc5ICuBDspZ4cA==} - app-root-dir@1.0.2: resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} @@ -6571,8 +6568,11 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mc-assets@0.2.37: - resolution: {integrity: sha512-49tk3shwxsDoV0PrrbORZEKg613vUQPULgusWjXNl8JEma5y41LEo57D6q4aHliXsV3Gb9ThrkFf6hIb0WlY1Q==} + maxrects-packer@2.7.3: + resolution: {integrity: sha512-bG6qXujJ1QgttZVIH4WDanhoJtvbud/xP/XPyf6A69C9RdA61BM4TomFALCq2nrTa+tARRIBB4LuIFsnUQU2wA==} + + mc-assets@0.2.42: + resolution: {integrity: sha512-j2D1RNYtB5Z9gFu9MVjyDBbiALI0mWZ3xW/A3PPefVAHm3HJ2T1vH+1XBov1spBGPl7u+Zo7mRXza3X0egbeOg==} engines: {node: '>=18.0.0'} mcraft-fun-mineflayer@0.1.8: @@ -13609,8 +13609,6 @@ snapshots: apache-md5@1.1.8: {} - apl-image-packer@1.1.0: {} - app-root-dir@1.0.2: {} aproba@2.0.0: @@ -17367,9 +17365,11 @@ snapshots: math-intrinsics@1.1.0: {} - mc-assets@0.2.37: + maxrects-packer@2.7.3: {} + + mc-assets@0.2.42: dependencies: - apl-image-packer: 1.1.0 + maxrects-packer: 2.7.3 zod: 3.24.1 mcraft-fun-mineflayer@0.1.8(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): diff --git a/renderer/viewer/lib/guiRenderer.ts b/renderer/viewer/lib/guiRenderer.ts new file mode 100644 index 00000000..b197040e --- /dev/null +++ b/renderer/viewer/lib/guiRenderer.ts @@ -0,0 +1,275 @@ +// Import placeholders - replace with actual imports for your environment +import { ItemRenderer, Identifier, ItemStack, NbtString, Structure, StructureRenderer, ItemRendererResources, BlockDefinition, BlockModel, TextureAtlas, Resources, ItemModel } from 'deepslate' +import { mat4, vec3 } from 'gl-matrix' +import { AssetsParser } from 'mc-assets/dist/assetsParser' +import { getLoadedImage } from 'mc-assets/dist/utils' +import { BlockModel as BlockModelMcAssets, AtlasParser } from 'mc-assets' +import { getLoadedBlockstatesStore, getLoadedModelsStore } from 'mc-assets/dist/stores' +import { makeTextureAtlas } from 'mc-assets/dist/atlasCreator' +import { proxy, ref } from 'valtio' +import { getItemDefinition } from 'mc-assets/dist/itemDefinitions' + +export const activeGuiAtlas = proxy({ + atlas: null as null | { json, image }, +}) + +export const getNonFullBlocksModels = () => { + const version = viewer.world.texturesVersion ?? 'latest' + const itemsDefinitions = viewer.world.itemsDefinitionsStore.data.latest + const blockModelsResolved = {} as Record + const itemsModelsResolved = {} as Record + const fullBlocksWithNonStandardDisplay = [] as string[] + const handledItemsWithDefinitions = new Set() + const assetsParser = new AssetsParser(version, getLoadedBlockstatesStore(viewer.world.blockstatesModels), getLoadedModelsStore(viewer.world.blockstatesModels)) + + const standardGuiDisplay = { + 'rotation': [ + 30, + 225, + 0 + ], + 'translation': [ + 0, + 0, + 0 + ], + 'scale': [ + 0.625, + 0.625, + 0.625 + ] + } + + const arrEqual = (a: number[], b: number[]) => a.length === b.length && a.every((x, i) => x === b[i]) + const addModelIfNotFullblock = (name: string, model: BlockModelMcAssets) => { + if (blockModelsResolved[name]) return + if (!model?.elements?.length) return + const isFullBlock = model.elements.length === 1 && arrEqual(model.elements[0].from, [0, 0, 0]) && arrEqual(model.elements[0].to, [16, 16, 16]) + if (isFullBlock) return + model['display'] ??= {} + model['display']['gui'] ??= standardGuiDisplay + blockModelsResolved[name] = model + } + + for (const [name, definition] of Object.entries(itemsDefinitions)) { + const item = getItemDefinition(viewer.world.itemsDefinitionsStore, { + version, + name, + properties: { + 'minecraft:display_context': 'gui', + }, + }) + if (item) { + const { resolvedModel } = assetsParser.getResolvedModelsByModel((item.special ? name : item.model).replace('minecraft:', '')) ?? {} + if (resolvedModel) { + handledItemsWithDefinitions.add(name) + } + if (resolvedModel?.elements) { + + let hasStandardDisplay = true + if (resolvedModel['display']?.gui) { + hasStandardDisplay = + arrEqual(resolvedModel['display'].gui.rotation, standardGuiDisplay.rotation) + && arrEqual(resolvedModel['display'].gui.translation, standardGuiDisplay.translation) + && arrEqual(resolvedModel['display'].gui.scale, standardGuiDisplay.scale) + } + + addModelIfNotFullblock(name, resolvedModel) + + if (!blockModelsResolved[name] && !hasStandardDisplay) { + fullBlocksWithNonStandardDisplay.push(name) + } + const notSideLight = resolvedModel['gui_light'] && resolvedModel['gui_light'] !== 'side' + if (!hasStandardDisplay || notSideLight) { + blockModelsResolved[name] = resolvedModel + } + } + if (!blockModelsResolved[name] && item.tints && resolvedModel) { + resolvedModel['tints'] = item.tints + if (resolvedModel.elements) { + blockModelsResolved[name] = resolvedModel + } else { + itemsModelsResolved[name] = resolvedModel + } + } + } + } + + for (const [name, blockstate] of Object.entries(viewer.world.blockstatesModels.blockstates.latest)) { + if (handledItemsWithDefinitions.has(name)) { + continue + } + const resolvedModel = assetsParser.getResolvedModelFirst({ name: name.replace('minecraft:', ''), properties: {} }, true) + if (resolvedModel) { + addModelIfNotFullblock(name, resolvedModel[0]) + } + } + + return { + blockModelsResolved, + itemsModelsResolved + } +} + +// customEvents.on('gameLoaded', () => { +// const res = getNonFullBlocksModels() +// }) + +const RENDER_SIZE = 64 + +const generateItemsGui = async (models: Record, isItems = false) => { + const img = await getLoadedImage(isItems ? viewer.world.itemsAtlasParser!.latestImage : viewer.world.blocksAtlasParser!.latestImage) + const canvasTemp = document.createElement('canvas') + canvasTemp.width = img.width + canvasTemp.height = img.height + canvasTemp.style.imageRendering = 'pixelated' + const ctx = canvasTemp.getContext('2d')! + ctx.imageSmoothingEnabled = false + ctx.drawImage(img, 0, 0) + + const atlasParser = isItems ? viewer.world.itemsAtlasParser! : viewer.world.blocksAtlasParser! + const textureAtlas = new TextureAtlas( + ctx.getImageData(0, 0, img.width, img.height), + Object.fromEntries(Object.entries(atlasParser.atlas.latest.textures).map(([key, value]) => { + return [key, [ + value.u, + value.v, + (value.u + (value.su ?? atlasParser.atlas.latest.suSv)), + (value.v + (value.sv ?? atlasParser.atlas.latest.suSv)), + ]] as [string, [number, number, number, number]] + })) + ) + + const PREVIEW_ID = Identifier.parse('preview:preview') + const PREVIEW_DEFINITION = new BlockDefinition({ '': { model: PREVIEW_ID.toString() } }, undefined) + + let modelData: any + let currentModelName: string | undefined + const resources: ItemRendererResources = { + getBlockModel (id) { + if (id.equals(PREVIEW_ID)) { + return BlockModel.fromJson(modelData ?? {}) + } + return null + }, + getTextureUV (texture) { + return textureAtlas.getTextureUV(texture.toString().slice(1).split('/').slice(1).join('/') as any) + }, + getTextureAtlas () { + return textureAtlas.getTextureAtlas() + }, + getItemComponents (id) { + return new Map() + }, + getItemModel (id) { + // const isSpecial = currentModelName === 'shield' || currentModelName === 'conduit' || currentModelName === 'trident' + const isSpecial = false + if (id.equals(PREVIEW_ID)) { + return ItemModel.fromJson({ + type: isSpecial ? 'minecraft:special' : 'minecraft:model', + model: isSpecial ? { + type: currentModelName, + } : PREVIEW_ID.toString(), + base: PREVIEW_ID.toString(), + tints: modelData?.tints, + }) + } + return null + }, + } + + const canvas = document.createElement('canvas') + canvas.width = RENDER_SIZE + canvas.height = RENDER_SIZE + const gl = canvas.getContext('webgl2', { preserveDrawingBuffer: true }) + if (!gl) { + throw new Error('Cannot get WebGL2 context') + } + + function resetGLContext (gl) { + gl.clearColor(0, 0, 0, 0) + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) + } + + // const includeOnly = ['powered_repeater', 'wooden_door'] + const includeOnly = [] as string[] + + const images: Record = {} + const item = new ItemStack(PREVIEW_ID, 1, new Map(Object.entries({ + 'minecraft:item_model': new NbtString(PREVIEW_ID.toString()), + }))) + const renderer = new ItemRenderer(gl, item, resources, { display_context: 'gui' }) + const missingTextures = new Set() + for (const [modelName, model] of Object.entries(models)) { + if (includeOnly.length && !includeOnly.includes(modelName)) continue + + const patchMissingTextures = () => { + for (const element of model.elements ?? []) { + for (const [faceName, face] of Object.entries(element.faces)) { + if (face.texture.startsWith('#')) { + missingTextures.add(`${modelName} ${faceName}: ${face.texture}`) + face.texture = 'block/unknown' + } + } + } + } + patchMissingTextures() + // TODO eggs + + modelData = model + currentModelName = modelName + resetGLContext(gl) + if (!modelData) continue + renderer.setItem(item, { display_context: 'gui' }) + renderer.drawItem() + const url = canvas.toDataURL() + // eslint-disable-next-line no-await-in-loop + const img = await getLoadedImage(url) + images[modelName] = img + } + + if (missingTextures.size) { + console.warn(`[guiRenderer] Missing textures in ${[...missingTextures].join(', ')}`) + } + + return images +} + +const generateAtlas = async (images: Record) => { + const atlas = makeTextureAtlas({ + input: Object.keys(images), + tileSize: RENDER_SIZE, + getLoadedImage (name) { + return { + image: images[name], + } + }, + }) + + // const atlasParser = new AtlasParser({ latest: atlas.json }, atlas.canvas.toDataURL()) + // const a = document.createElement('a') + // a.href = await atlasParser.createDebugImage(true) + // a.download = 'blocks_atlas.png' + // a.click() + + activeGuiAtlas.atlas = { + json: atlas.json, + image: ref(await getLoadedImage(atlas.canvas.toDataURL())), + } + + return atlas +} + +export const generateGuiAtlas = async () => { + const { blockModelsResolved, itemsModelsResolved } = getNonFullBlocksModels() + + // Generate blocks atlas + console.time('generate blocks gui atlas') + const blockImages = await generateItemsGui(blockModelsResolved, false) + console.timeEnd('generate blocks gui atlas') + console.time('generate items gui atlas') + const itemImages = await generateItemsGui(itemsModelsResolved, true) + console.timeEnd('generate items gui atlas') + await generateAtlas({ ...blockImages, ...itemImages }) + // await generateAtlas(blockImages) +} diff --git a/renderer/viewer/lib/worldrendererCommon.ts b/renderer/viewer/lib/worldrendererCommon.ts index a54f9fe8..261b18e6 100644 --- a/renderer/viewer/lib/worldrendererCommon.ts +++ b/renderer/viewer/lib/worldrendererCommon.ts @@ -24,6 +24,7 @@ import { chunkPos } from './simpleUtils' import { HandItemBlock } from './holdingBlock' import { updateStatText } from './ui/newStats' import { WorldRendererThree } from './worldrendererThree' +import { generateGuiAtlas } from './guiRenderer' function mod (x, n) { return ((x % n) + n) % n @@ -354,6 +355,10 @@ export abstract class WorldRendererCommon } } + async generateGuiTextures () { + await generateGuiAtlas() + } + async updateAssetsData (resourcePackUpdate = false, prioritizeBlockTextures?: string[]) { const blocksAssetsParser = new AtlasParser(this.sourceData.blocksAtlases, blocksAtlasLatest, blocksAtlasLegacy) const itemsAssetsParser = new AtlasParser(this.sourceData.itemsAtlases, itemsAtlasLatest, itemsAtlasLegacy) @@ -379,6 +384,7 @@ export abstract class WorldRendererCommon return texture }, this.customTextures?.items?.tileSize, undefined, customItemTextures) console.timeEnd('createItemsAtlas') + this.blocksAtlasParser = new AtlasParser({ latest: blocksAtlas }, blocksCanvas.toDataURL()) this.itemsAtlasParser = new AtlasParser({ latest: itemsAtlas }, itemsCanvas.toDataURL()) @@ -418,13 +424,20 @@ export abstract class WorldRendererCommon config: this.mesherConfig, }) } + if (!this.itemsAtlasParser) return const itemsTexture = await new THREE.TextureLoader().loadAsync(this.itemsAtlasParser.latestImage) itemsTexture.magFilter = THREE.NearestFilter itemsTexture.minFilter = THREE.NearestFilter itemsTexture.flipY = false viewer.entities.itemsTexture = itemsTexture + if (!this.itemsAtlasParser) return this.renderUpdateEmitter.emit('textureDownloaded') + + console.time('generateGuiTextures') + await this.generateGuiTextures() + console.timeEnd('generateGuiTextures') + if (!this.itemsAtlasParser) return this.renderUpdateEmitter.emit('itemsTextureDownloaded') console.log('textures loaded') } diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index d473f5be..4e4616cd 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -10,6 +10,7 @@ import { versionToNumber } from 'renderer/viewer/prepare/utils' import { getRenamedData } from 'flying-squid/dist/blockRenames' import PrismarineChatLoader from 'prismarine-chat' import { BlockModel } from 'mc-assets' +import { activeGuiAtlas } from 'renderer/viewer/lib/guiRenderer' import Generic95 from '../assets/generic_95.png' import { appReplacableResources } from './generated/resources' import { activeModalStack, hideCurrentModal, hideModal, miscUiState, showModal } from './globalState' @@ -155,7 +156,10 @@ const getImageSrc = (path): string | HTMLImageElement => { return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' } -const getImage = ({ path = undefined as string | undefined, texture = undefined as string | undefined, blockData = undefined as any }, onLoad = () => { }) => { +const getImage = ({ path = undefined as string | undefined, texture = undefined as string | undefined, blockData = undefined as any, image = undefined as HTMLImageElement | undefined }, onLoad = () => { }) => { + if (image) { + return image + } if (!path && !texture) throw new Error('Either pass path or texture') const loadPath = (blockData ? 'blocks' : path ?? texture)! if (loadedImagesCache.has(loadPath)) { @@ -184,7 +188,8 @@ export const renderSlot = (model: ResolvedItemModelRender, debugIsQuickbar = fal blockData?: Record & { resolvedModel: BlockModel }, scale?: number, slice?: number[], - modelName?: string + modelName?: string, + image?: HTMLImageElement } | undefined => { let itemModelName = model.modelName const originalItemName = itemModelName @@ -196,6 +201,23 @@ export const renderSlot = (model: ResolvedItemModelRender, debugIsQuickbar = fal let itemTexture + + if (!fullBlockModelSupport) { + const atlas = activeGuiAtlas.atlas?.json + // todo atlas holds all rendered blocks, not all possibly rendered item/block models, need to request this on demand instead (this is how vanilla works) + const item = atlas?.textures[itemModelName.replace('minecraft:', '').replace('block/', '').replace('blocks/', '').replace('item/', '').replace('items/', '')] + if (item) { + const x = item.u * atlas.width + const y = item.v * atlas.height + return { + texture: 'gui', + image: activeGuiAtlas.atlas!.image, + slice: [x, y, atlas.tileSize, atlas.tileSize], + scale: 0.25, + } + } + } + try { assertDefined(viewer.world.itemsRenderer) itemTexture = @@ -205,6 +227,8 @@ export const renderSlot = (model: ResolvedItemModelRender, debugIsQuickbar = fal inGameError(`Failed to render item ${itemModelName} (original: ${originalItemName}) on ${bot.version} (resourcepack: ${options.enabledResourcepack}): ${err.stack}`) itemTexture = viewer.world.itemsRenderer!.getItemTexture('block/errored')! } + + if ('type' in itemTexture) { // is item return { @@ -230,13 +254,13 @@ const getItemName = (slot: Item | RenderItem | null) => { return text.join('') } -const mapSlots = (slots: Array) => { +const mapSlots = (slots: Array, isJei = false) => { return slots.map((slot, i) => { // todo stateid if (!slot) return try { - const debugIsQuickbar = i === bot.inventory.hotbarStart + bot.quickBarSlot + const debugIsQuickbar = !isJei && i === bot.inventory.hotbarStart + bot.quickBarSlot const modelName = getItemModelName(slot, { 'minecraft:display_context': 'gui', }) const slotCustomProps = renderSlot({ modelName }, debugIsQuickbar) const itemCustomName = getItemName(slot) @@ -305,7 +329,7 @@ const upJei = (search: string) => { return new PrismarineItem(x.id, 1) }).filter(a => a !== null) lastWindow.pwindow.win.jeiSlotsPage = 0 - lastWindow.pwindow.win.jeiSlots = mapSlots(matchedSlots) + lastWindow.pwindow.win.jeiSlots = mapSlots(matchedSlots, true) } export const openItemsCanvas = (type, _bot = bot as typeof bot | null) => { diff --git a/src/react/EnterFullscreenButton.tsx b/src/react/EnterFullscreenButton.tsx index 3901b9ae..0bc41632 100644 --- a/src/react/EnterFullscreenButton.tsx +++ b/src/react/EnterFullscreenButton.tsx @@ -4,6 +4,7 @@ import { activeModalStack, miscUiState } from '../globalState' import Button from './Button' import { useUsingTouch } from './utilsApp' import { pixelartIcons } from './PixelartIcon' +import { showNotification } from './NotificationProvider' const hideOnModals = new Set(['chat']) @@ -33,8 +34,12 @@ export default () => { left: inMainMenu ? 35 : 5, width: 22, }} - onClick={() => { - void document.documentElement.requestFullscreen() + onClick={async () => { + try { + await document.documentElement.requestFullscreen() + } catch (err) { + showNotification(`${err.message ?? err}`, undefined, true) + } }} /> } From b9df1bcf9ec4b099eede64fde721327c082a95b1 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 15:12:05 +0300 Subject: [PATCH 406/851] fix enabling lighting falsey when load for chunks is enabled --- src/watchOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watchOptions.ts b/src/watchOptions.ts index 724fb2ba..3e607f49 100644 --- a/src/watchOptions.ts +++ b/src/watchOptions.ts @@ -75,7 +75,7 @@ export const watchOptionsAfterViewerInit = () => { viewer.world.mesherConfig.enableLighting = !bot.supportFeature('blockStateId') || options.newVersionsLighting; (viewer.world as WorldRendererThree).rerenderAllChunks() }) - customEvents.on('gameLoaded', () => { + customEvents.on('mineflayerBotCreated', () => { viewer.world.mesherConfig.enableLighting = !bot.supportFeature('blockStateId') || options.newVersionsLighting }) From 6fb18d44382b729163fa5c7ca2fb96313ea6455b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 15:26:59 +0300 Subject: [PATCH 407/851] fixes & workarounds rendering items in inventory (some were broken since last commit) --- src/inventoryWindows.ts | 2 +- src/resourcesManager.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/inventoryWindows.ts b/src/inventoryWindows.ts index 4e4616cd..6a5ab0d6 100644 --- a/src/inventoryWindows.ts +++ b/src/inventoryWindows.ts @@ -205,7 +205,7 @@ export const renderSlot = (model: ResolvedItemModelRender, debugIsQuickbar = fal if (!fullBlockModelSupport) { const atlas = activeGuiAtlas.atlas?.json // todo atlas holds all rendered blocks, not all possibly rendered item/block models, need to request this on demand instead (this is how vanilla works) - const item = atlas?.textures[itemModelName.replace('minecraft:', '').replace('block/', '').replace('blocks/', '').replace('item/', '').replace('items/', '')] + const item = atlas?.textures[itemModelName.replace('minecraft:', '').replace('block/', '').replace('blocks/', '').replace('item/', '').replace('items/', '').replace('_inventory', '').replace('_bottom', '')] if (item) { const x = item.u * atlas.width const y = item.v * atlas.height diff --git a/src/resourcesManager.ts b/src/resourcesManager.ts index fd92e58b..43c3882e 100644 --- a/src/resourcesManager.ts +++ b/src/resourcesManager.ts @@ -14,10 +14,11 @@ export const getItemModelName = (item: GeneralInputItem, specificProps: ItemSpec const itemSelector = playerState.getItemSelector({ ...specificProps }) - const model = getItemDefinition(viewer.world.itemsDefinitionsStore, { + const modelFromDef = getItemDefinition(viewer.world.itemsDefinitionsStore, { name: itemModelName, version: viewer.world.texturesVersion!, properties: itemSelector - })?.model ?? itemModelName + })?.model + const model = (modelFromDef === 'minecraft:special' ? undefined : modelFromDef) ?? itemModelName return model } From a846eb4500912578742518878c57903d22a28cf4 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 20:45:34 +0300 Subject: [PATCH 408/851] hotfix: fix world interaction crashes --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 1a195884..1bc36f9c 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "http-server": "^14.1.1", "https-browserify": "^1.0.0", "mc-assets": "^0.2.42", - "mineflayer-mouse": "^0.0.5", + "mineflayer-mouse": "^0.0.7", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", "mineflayer-pathfinder": "^2.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d5c2446f..491ef376 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -359,8 +359,8 @@ importers: specifier: github:zardoy/mineflayer version: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13) mineflayer-mouse: - specifier: ^0.0.5 - version: 0.0.5(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + specifier: ^0.0.7 + version: 0.0.7(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -6801,8 +6801,8 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824} version: 1.2.0 - mineflayer-mouse@0.0.5: - resolution: {integrity: sha512-0r/AOGTq+wZH9vrBcW93jH2dGRSlwlO6xc1Z67VJUFlZZ8oBefAOpiZq7LIGc7ROVbpcKEKjROdNv/iCFmzXYA==} + mineflayer-mouse@0.0.7: + resolution: {integrity: sha512-/cSDsc2ZPlvakc3BX+/K9VD64HAIa+LGiz34RpQvUy7hwx3nXdZjJHDjzEdn86BBzRF5pZOxIoXm8hlZKCYeeQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} mineflayer-pathfinder@2.4.4: @@ -17742,7 +17742,7 @@ snapshots: - encoding - supports-color - mineflayer-mouse@0.0.5(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + mineflayer-mouse@0.0.7(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): dependencies: change-case: 5.4.4 debug: 4.4.0(supports-color@8.1.1) From c6b8efe4e8b4885ecaad7403b41bcbc33675e3a4 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 22:22:35 +0300 Subject: [PATCH 409/851] hotfix: should fix edge case when canvas was out of viewport bounds on ios --- src/react/DebugEdges.tsx | 10 +++++++++- src/styles.css | 14 ++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/react/DebugEdges.tsx b/src/react/DebugEdges.tsx index a65b9f92..150e1f16 100644 --- a/src/react/DebugEdges.tsx +++ b/src/react/DebugEdges.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import { useIsHashActive } from './simpleHooks' export default () => { - const MODES_COUNT = 4 + const MODES_COUNT = 5 const [mode, setMode] = useState(0) const isHashActive = useIsHashActive('#edges') @@ -41,6 +41,14 @@ export default () => { styles.height = '100dvh' text = 'top 0 fixed 100dvh' } + if (mode === 4) { + styles.position = 'fixed' + styles.top = 0 + styles.left = 0 + styles.right = 0 + styles.height = '100dvh' + text = 'top 0 bottom 0 fixed 100dvh' + } return
Date: Wed, 5 Mar 2025 22:49:36 +0300 Subject: [PATCH 410/851] feat: Add interaction hint for touch-based entity targeting --- src/react/GameInteractionOverlay.tsx | 8 +++-- src/react/InteractionHint.module.css | 19 ++++++++++ src/react/InteractionHint.module.css.d.ts | 10 ++++++ src/react/InteractionHint.tsx | 44 +++++++++++++++++++++++ src/reactUi.tsx | 2 ++ 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/react/InteractionHint.module.css create mode 100644 src/react/InteractionHint.module.css.d.ts create mode 100644 src/react/InteractionHint.tsx diff --git a/src/react/GameInteractionOverlay.tsx b/src/react/GameInteractionOverlay.tsx index cb7a39f8..7c406e8d 100644 --- a/src/react/GameInteractionOverlay.tsx +++ b/src/react/GameInteractionOverlay.tsx @@ -150,9 +150,13 @@ function GameInteractionOverlayInner ({ document.dispatchEvent(new MouseEvent('mouseup', { button: 0 })) virtualClickActive = false } else if (!capturedPointer.active.activateCameraMove && (Date.now() - capturedPointer.active.time < touchStartBreakingBlockMs)) { - document.dispatchEvent(new MouseEvent('mousedown', { button: 2 })) + // single click action + const MOUSE_BUTTON_RIGHT = 2 + const MOUSE_BUTTON_LEFT = 0 + const gonnaAttack = !!bot.mouse.getCursorState().entity + document.dispatchEvent(new MouseEvent('mousedown', { button: gonnaAttack ? MOUSE_BUTTON_LEFT : MOUSE_BUTTON_RIGHT })) bot.mouse.update() - document.dispatchEvent(new MouseEvent('mouseup', { button: 2 })) + document.dispatchEvent(new MouseEvent('mouseup', { button: gonnaAttack ? MOUSE_BUTTON_LEFT : MOUSE_BUTTON_RIGHT })) } if (screenTouches > 0) { diff --git a/src/react/InteractionHint.module.css b/src/react/InteractionHint.module.css new file mode 100644 index 00000000..026b49c8 --- /dev/null +++ b/src/react/InteractionHint.module.css @@ -0,0 +1,19 @@ +.hint_container { + position: fixed; + top: 20%; + left: 0; + right: 0; + margin: 0 auto; + width: fit-content; + display: flex; + align-items: center; + gap: 8px; + pointer-events: none; + z-index: 1000; + text-shadow: 1px 1px 8px rgba(0, 0, 0, 1); +} + +.hint_text { + color: white; + font-size: 10px; +} diff --git a/src/react/InteractionHint.module.css.d.ts b/src/react/InteractionHint.module.css.d.ts new file mode 100644 index 00000000..45bdf30b --- /dev/null +++ b/src/react/InteractionHint.module.css.d.ts @@ -0,0 +1,10 @@ +// This file is automatically generated. +// Please do not change this file! +interface CssExports { + hintContainer: string; + hintText: string; + hint_container: string; + hint_text: string; +} +declare const cssExports: CssExports; +export default cssExports; diff --git a/src/react/InteractionHint.tsx b/src/react/InteractionHint.tsx new file mode 100644 index 00000000..9121249e --- /dev/null +++ b/src/react/InteractionHint.tsx @@ -0,0 +1,44 @@ +import { useEffect, useState } from 'react' +import { useSnapshot } from 'valtio' +import { options } from '../optionsStorage' +import PixelartIcon, { pixelartIcons } from './PixelartIcon' +import styles from './InteractionHint.module.css' +import { useUsingTouch } from './utilsApp' + +export default () => { + const usingTouch = useUsingTouch() + const { touchInteractionType } = useSnapshot(options) + const [hintText, setHintText] = useState(null) + + useEffect(() => { + const update = () => { + const cursorState = bot.mouse.getCursorState() + if (cursorState.entity) { + const entityName = cursorState.entity.displayName ?? cursorState.entity.name + setHintText(`Attack ${entityName}`) + } else { + setHintText(null) + } + } + + // Initial update + update() + + // Subscribe to physics ticks + bot.on('physicsTick', update) + + return () => { + bot.removeListener('physicsTick', update) + } + }, []) + + if (!usingTouch || touchInteractionType !== 'classic') return null + if (!hintText) return null + + return ( +
+ + {hintText} +
+ ) +} diff --git a/src/reactUi.tsx b/src/reactUi.tsx index ac2dbe74..f07c01ce 100644 --- a/src/reactUi.tsx +++ b/src/reactUi.tsx @@ -52,6 +52,7 @@ import MineflayerPluginConsole from './react/MineflayerPluginConsole' import { UIProvider } from './react/UIProvider' import { useAppScale } from './scaleInterface' import PacketsReplayProvider from './react/PacketsReplayProvider' +import InteractionHint from './react/InteractionHint' const RobustPortal = ({ children, to }) => { return createPortal({children}, to) @@ -146,6 +147,7 @@ const InGameUi = () => { + {showUI && }
{!disabledUiParts.includes('xp-bar') && } {!disabledUiParts.includes('hud-bars') && } From e7b012c08d270d813e365adb55fe3d99251c96f0 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 5 Mar 2025 22:58:11 +0300 Subject: [PATCH 411/851] feat: Display players list on long chat button hold --- src/react/InteractionHint.tsx | 4 ++- src/react/MobileTopButtons.tsx | 33 +++++++++++++++++++------ src/react/PlayerListOverlayProvider.tsx | 4 +++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/react/InteractionHint.tsx b/src/react/InteractionHint.tsx index 9121249e..5c60cdc8 100644 --- a/src/react/InteractionHint.tsx +++ b/src/react/InteractionHint.tsx @@ -1,12 +1,14 @@ import { useEffect, useState } from 'react' import { useSnapshot } from 'valtio' import { options } from '../optionsStorage' +import { activeModalStack } from '../globalState' import PixelartIcon, { pixelartIcons } from './PixelartIcon' import styles from './InteractionHint.module.css' import { useUsingTouch } from './utilsApp' export default () => { const usingTouch = useUsingTouch() + const modalStack = useSnapshot(activeModalStack) const { touchInteractionType } = useSnapshot(options) const [hintText, setHintText] = useState(null) @@ -32,7 +34,7 @@ export default () => { } }, []) - if (!usingTouch || touchInteractionType !== 'classic') return null + if (!usingTouch || touchInteractionType !== 'classic' || modalStack.length > 0) return null if (!hintText) return null return ( diff --git a/src/react/MobileTopButtons.tsx b/src/react/MobileTopButtons.tsx index f686d8af..4d18f817 100644 --- a/src/react/MobileTopButtons.tsx +++ b/src/react/MobileTopButtons.tsx @@ -33,6 +33,28 @@ export default () => { } const longPressEvent = useLongPress(onLongPress, () => {}, defaultOptions) + + const onChatLongPress = () => { + document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' })) + } + + const onChatClick = () => { + if (activeModalStack.at(-1)?.reactType === 'chat') { + hideCurrentModal() + } else { + showModal({ reactType: 'chat' }) + } + } + + const chatLongPressEvent = useLongPress( + onChatLongPress, + onChatClick, + { + shouldPreventDefault: true, + delay: 300, + } + ) + // ios note: just don't use
From 09cd2c3f644c0cd52d53778aaa167a3c050e06ad Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 14 Mar 2025 01:50:54 +0300 Subject: [PATCH 440/851] fix(guiRenderer): dont break textures with custom namespaces rendering --- renderer/viewer/lib/guiRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderer/viewer/lib/guiRenderer.ts b/renderer/viewer/lib/guiRenderer.ts index f4af3d20..d2987ce6 100644 --- a/renderer/viewer/lib/guiRenderer.ts +++ b/renderer/viewer/lib/guiRenderer.ts @@ -155,7 +155,7 @@ const generateItemsGui = async (models: Record, isIt return null }, getTextureUV (texture) { - return textureAtlas.getTextureUV(texture.toString().slice(1).split('/').slice(1).join('/') as any) + return textureAtlas.getTextureUV(texture.toString().replace('minecraft:', '').replace('block/', '').replace('item/', '').replace('blocks/', '').replace('items/', '') as any) }, getTextureAtlas () { return textureAtlas.getTextureAtlas() From 518d6ad8661079eb84d948917a1fbb9a80a14130 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 14 Mar 2025 02:13:04 +0300 Subject: [PATCH 441/851] fix always display reconnect and better last packets display (time) --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ src/index.ts | 4 ++-- src/mineflayer/plugins/packetsRecording.ts | 2 ++ src/packetsReplay/replayPackets.ts | 11 +++++++++++ src/react/ServersListProvider.tsx | 2 +- 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index a1fcff61..f6ae254e 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "mojangson": "^2.0.4", "net-browserify": "github:zardoy/prismarinejs-net-browserify", "node-gzip": "^1.1.2", - "mcraft-fun-mineflayer": "^0.1.8", + "mcraft-fun-mineflayer": "^0.1.10", "peerjs": "^1.5.0", "pixelarticons": "^1.8.1", "pretty-bytes": "^6.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02a0a671..2b09f583 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,8 +135,8 @@ importers: specifier: ^4.17.21 version: 4.17.21 mcraft-fun-mineflayer: - specifier: ^0.1.8 - version: 0.1.8(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) + specifier: ^0.1.10 + version: 0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) minecraft-data: specifier: 3.83.1 version: 3.83.1 @@ -6690,9 +6690,9 @@ packages: resolution: {integrity: sha512-j2D1RNYtB5Z9gFu9MVjyDBbiALI0mWZ3xW/A3PPefVAHm3HJ2T1vH+1XBov1spBGPl7u+Zo7mRXza3X0egbeOg==} engines: {node: '>=18.0.0'} - mcraft-fun-mineflayer@0.1.8: - resolution: {integrity: sha512-jyJTihNHfeToBPwVs3QMKBlVcaCABJ25YN2eoIBQEVTRVFzaXh13XRpElphLzTMj1Q5XFYqufHtMoR4tsb08qQ==} - version: 0.1.8 + mcraft-fun-mineflayer@0.1.10: + resolution: {integrity: sha512-KHzPts82I39nTDZlGwqJo1JXLwaIUHphBbmGWv7oYztUrq3iPiJDEIFgst0ROO/apjtHjzbCM9eb19qWw1JM3Q==} + version: 0.1.10 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: '@roamhq/wrtc': '*' @@ -17579,7 +17579,7 @@ snapshots: maxrects-packer: 2.7.3 zod: 3.24.1 - mcraft-fun-mineflayer@0.1.8(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): + mcraft-fun-mineflayer@0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): dependencies: '@zardoy/flying-squid': 0.0.49(encoding@0.1.13) exit-hook: 2.2.1 diff --git a/src/index.ts b/src/index.ts index 67187c1c..83f9f289 100644 --- a/src/index.ts +++ b/src/index.ts @@ -362,8 +362,8 @@ export async function connect (connectOptions: ConnectOptions) { miscUiState.hasErrors = true if (miscUiState.gameLoaded) return - appStatusState.showReconnect = true setLoadingScreenStatus(`Error encountered. ${err}`, true) + appStatusState.showReconnect = true onPossibleErrorDisconnect() destroyAll() } @@ -712,8 +712,8 @@ export async function connect (connectOptions: ConnectOptions) { bot.on('kicked', (kickReason) => { console.log('You were kicked!', kickReason) const { formatted: kickReasonFormatted, plain: kickReasonString } = parseFormattedMessagePacket(kickReason) - appStatusState.showReconnect = true setLoadingScreenStatus(`The Minecraft server kicked you. Kick reason: ${kickReasonString}`, true, undefined, undefined, kickReasonFormatted) + appStatusState.showReconnect = true destroyAll() }) diff --git a/src/mineflayer/plugins/packetsRecording.ts b/src/mineflayer/plugins/packetsRecording.ts index f1ff18cf..a6b761e7 100644 --- a/src/mineflayer/plugins/packetsRecording.ts +++ b/src/mineflayer/plugins/packetsRecording.ts @@ -93,6 +93,8 @@ declare module 'mineflayer' { export const getLastAutoCapturedPackets = () => circularBuffer?.size export const downloadAutoCapturedPackets = () => { const logger = new PacketsLogger({ minecraftVersion: lastConnectVersion }) + logger.relativeTime = false + logger.formattedTime = true for (const packet of circularBuffer?.getLastElements() ?? []) { logger.log(packet.isFromServer, { name: packet.name, state: packet.state, time: packet.timestamp }, packet.params) } diff --git a/src/packetsReplay/replayPackets.ts b/src/packetsReplay/replayPackets.ts index 9e777b38..13891899 100644 --- a/src/packetsReplay/replayPackets.ts +++ b/src/packetsReplay/replayPackets.ts @@ -188,6 +188,10 @@ const mainPacketsReplayer = async (client: ServerClient, packets: ParsedReplayPa } if (packet.isFromServer) { + if (packet.params === null) { + console.warn('packet.params is null', packet) + continue + } playServerPacket(packet.name, packet.params) await new Promise(resolve => { setTimeout(resolve, packet.diff * packetsReplayState.speed + ADDITIONAL_DELAY * (packetsReplayState.customButtons.packetsSenderDelay.state ? 1 : 0)) @@ -216,6 +220,7 @@ const mainPacketsReplayer = async (client: ServerClient, packets: ParsedReplayPa setTimeout(resolve, 1000) })] : []) ]) + clientsPacketsWaiter.stopWaiting() clientPackets = [] } } @@ -236,6 +241,7 @@ interface PacketsWaiterOptions { interface PacketsWaiter { addPacket(name: string, params: any): void waitForPackets(packets: string[]): Promise + stopWaiting(): void } const createPacketsWaiter = (options: PacketsWaiterOptions = {}): PacketsWaiter => { @@ -296,6 +302,11 @@ const createPacketsWaiter = (options: PacketsWaiterOptions = {}): PacketsWaiter isWaiting = false packetHandler = null } + }, + stopWaiting () { + isWaiting = false + packetHandler = null + queuedPackets.length = 0 } } } diff --git a/src/react/ServersListProvider.tsx b/src/react/ServersListProvider.tsx index f0543e21..bf440c69 100644 --- a/src/react/ServersListProvider.tsx +++ b/src/react/ServersListProvider.tsx @@ -407,7 +407,7 @@ const Inner = ({ hidden, customServersList }: { hidden?: boolean, customServersL worldNameRightGrayed: additional?.textNameRightGrayed ?? '', iconSrc: additional?.icon, offline: additional?.offline, - group: 'Custom Servers' + group: 'Your Servers' } })} initialProxies={{ From a67b9d7aa215bb8de6fd5c23b2cfd58e0ac14fea Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 14 Mar 2025 19:11:14 +0300 Subject: [PATCH 442/851] active back all vanilla mechanics like hotbar wheel when replay window is minimized --- src/react/ReplayPanel.tsx | 8 +++++--- src/react/state/packetsReplayState.ts | 1 + src/utils.ts | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/react/ReplayPanel.tsx b/src/react/ReplayPanel.tsx index fd4082af..3b709882 100644 --- a/src/react/ReplayPanel.tsx +++ b/src/react/ReplayPanel.tsx @@ -1,9 +1,11 @@ import { useState, useEffect } from 'react' +import { useSnapshot } from 'valtio' import { filterPackets } from './packetsFilter' import { DARK_COLORS } from './components/replay/constants' import FilterInput from './components/replay/FilterInput' import PacketList from './components/replay/PacketList' import ProgressBar from './components/replay/ProgressBar' +import { packetsReplayState } from './state/packetsReplayState' interface Props { replayName: string @@ -41,7 +43,7 @@ export default function ReplayPanel ({ style }: Props) { const [filter, setFilter] = useState(defaultFilter) - const [isMinimized, setIsMinimized] = useState(false) + const { isMinimized } = useSnapshot(packetsReplayState) const { filtered: filteredPackets, hiddenCount } = filterPackets(packets.slice(-500), filter) useEffect(() => { @@ -50,7 +52,7 @@ export default function ReplayPanel ({ const handlePlayPauseClick = () => { if (isMinimized) { - setIsMinimized(false) + packetsReplayState.isMinimized = false } else { onPlayPause?.(!isPlaying) } @@ -113,7 +115,7 @@ export default function ReplayPanel ({
{replayName || 'Unnamed Replay'}
- - GitHub - - {linksButton} +
diff --git a/src/react/MainMenuRenderApp.tsx b/src/react/MainMenuRenderApp.tsx index cdcfc096..c112cb0e 100644 --- a/src/react/MainMenuRenderApp.tsx +++ b/src/react/MainMenuRenderApp.tsx @@ -8,7 +8,6 @@ import { setLoadingScreenStatus } from '../appStatus' import { openFilePicker, copyFilesAsync, mkdirRecursive, openWorldDirectory, removeFileRecursiveAsync } from '../browserfs' import MainMenu from './MainMenu' -import { DiscordButton } from './DiscordButton' const isMainMenu = () => { return activeModalStack.length === 0 && !miscUiState.gameLoaded @@ -145,7 +144,6 @@ export default () => { }} githubAction={() => openGithub()} optionsAction={() => openOptionsMenu('main')} - linksButton={} bottomRightLinks={process.env.MAIN_MENU_LINKS} openFileAction={e => { if (!!window.showDirectoryPicker && !e.shiftKey) { diff --git a/src/react/PauseLinkButtons.tsx b/src/react/PauseLinkButtons.tsx new file mode 100644 index 00000000..18331f2d --- /dev/null +++ b/src/react/PauseLinkButtons.tsx @@ -0,0 +1,50 @@ +import { useSnapshot } from 'valtio' +import { openURL } from 'renderer/viewer/lib/simpleUtils' +import { ErrorBoundary } from '@zardoy/react-util' +import { miscUiState } from '../globalState' +import { openGithub } from '../utils' +import Button from './Button' +import { DiscordButton } from './DiscordButton' +import styles from './PauseScreen.module.css' + +function PauseLinkButtonsInner () { + const { appConfig } = useSnapshot(miscUiState) + const pauseLinksConfig = appConfig?.pauseLinks + + if (!pauseLinksConfig) return null + + const renderButton = (button: Record, style: React.CSSProperties, key: number) => { + if (button.type === 'discord') { + return + } + if (button.type === 'github') { + return + } + if (button.type === 'url' && button.text) { + return + } + return null + } + + return ( + <> + {pauseLinksConfig.map((row, i) => { + const style = { width: (204 / row.length - (row.length > 1 ? 4 : 0)) + 'px' } + return ( +
+ {row.map((button, k) => renderButton(button, style, k))} +
+ ) + })} + + ) +} + +export default () => { + return { + console.error(error) + return null + }}> + + +} diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index 35d873ea..55ffe47f 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -34,6 +34,7 @@ import { DiscordButton } from './DiscordButton' import { showNotification } from './NotificationProvider' import { appStatusState, reconnectReload } from './AppStatusProvider' import NetworkStatus from './NetworkStatus' +import PauseLinkButtons from './PauseLinkButtons' const waitForPotentialRender = async () => { return new Promise(resolve => { @@ -227,26 +228,6 @@ export default () => { if (!isModalActive) return null - const pauseLinks: React.ReactNode[] = [] - const pauseLinksConfig = miscUiState.appConfig?.pauseLinks - if (pauseLinksConfig) { - for (const [i, row] of pauseLinksConfig.entries()) { - const rowButtons: React.ReactNode[] = [] - for (const [k, button] of row.entries()) { - const key = `${i}-${k}` - const style = { width: (204 / row.length - (row.length > 1 ? 4 : 0)) + 'px' } - if (button.type === 'discord') { - rowButtons.push() - } else if (button.type === 'github') { - rowButtons.push() - } else if (button.type === 'url' && button.text) { - rowButtons.push() - } - } - pauseLinks.push(
{rowButtons}
) - } - } - return
- {pauseLinks} + {singleplayer ? (
From 72028d925d4f01be0094ceb2badbe4a839006046 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 14 Mar 2025 23:25:13 +0300 Subject: [PATCH 445/851] feat: revamp right click experience by reworking block placing prediction and extending activatble items list --- package.json | 2 +- pnpm-lock.yaml | 35 ++++++++++++++++++++++------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index f6ae254e..6485c25e 100644 --- a/package.json +++ b/package.json @@ -151,7 +151,7 @@ "http-server": "^14.1.1", "https-browserify": "^1.0.0", "mc-assets": "^0.2.42", - "mineflayer-mouse": "^0.0.9", + "mineflayer-mouse": "^0.1.0", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", "mineflayer-pathfinder": "^2.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b09f583..dc4b1555 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -136,7 +136,7 @@ importers: version: 4.17.21 mcraft-fun-mineflayer: specifier: ^0.1.10 - version: 0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)) + version: 0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)) minecraft-data: specifier: 3.83.1 version: 3.83.1 @@ -360,10 +360,10 @@ importers: version: https://codeload.github.com/zardoy/minecraft-inventory-gui/tar.gz/75e940a4cd50d89e0ba03db3733d5d704917a3c8(@types/react@18.2.20)(react@18.2.0) mineflayer: specifier: github:zardoy/mineflayer - version: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13) + version: https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13) mineflayer-mouse: - specifier: ^0.0.9 - version: 0.0.9(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + specifier: ^0.1.0 + version: 0.1.0(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -4129,6 +4129,10 @@ packages: resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} engines: {node: '>= 0.4'} + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -6916,8 +6920,8 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824} version: 1.2.0 - mineflayer-mouse@0.0.9: - resolution: {integrity: sha512-oViJrou2tziPuox/ZFJWZJMCnaF5+KPEsrbBgKmXVr3eK35iPohdhYwoKgqgBY8uXS/bNaFnkCR0K7ZDqyBF8g==} + mineflayer-mouse@0.1.0: + resolution: {integrity: sha512-NFfHASMo3iZOECoYOHVqGBFROVapzDJRlgANMiWnynO/oPUZkOJF6oRrcE33FCVHGlwMCT2S6N7TcqCYqF21Uw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} mineflayer-pathfinder@2.4.4: @@ -6927,8 +6931,8 @@ packages: resolution: {integrity: sha512-q7cmpZFaSI6sodcMJxc2GkV8IO84HbsUP+xNipGKfGg+FMISKabzdJ838Axb60qRtZrp6ny7LluQE7lesHvvxQ==} engines: {node: '>=18'} - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49: - resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49} + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d: + resolution: {tarball: https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d} version: 4.25.0 engines: {node: '>=18'} @@ -14365,6 +14369,11 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + callsites@3.1.0: {} camel-case@4.1.2: @@ -15424,7 +15433,7 @@ snapshots: es-iterator-helpers@1.2.1: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 @@ -17579,12 +17588,12 @@ snapshots: maxrects-packer: 2.7.3 zod: 3.24.1 - mcraft-fun-mineflayer@0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13)): + mcraft-fun-mineflayer@0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)): dependencies: '@zardoy/flying-squid': 0.0.49(encoding@0.1.13) exit-hook: 2.2.1 minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/5ec3dd4b367fcc039fbcb3edd214fe3cf8178a6d(patch_hash=dkeyukcqlupmk563gwxsmjr3yu)(encoding@0.1.13) - mineflayer: https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13) + mineflayer: https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13) prismarine-item: 1.16.0 ws: 8.18.0 transitivePeerDependencies: @@ -17949,7 +17958,7 @@ snapshots: - encoding - supports-color - mineflayer-mouse@0.0.9(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + mineflayer-mouse@0.1.0(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): dependencies: change-case: 5.4.4 debug: 4.4.0(supports-color@8.1.1) @@ -18010,7 +18019,7 @@ snapshots: - encoding - supports-color - mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/748163e536abe94f3dc8ada7a542bcd689bbbf49(encoding@0.1.13): + mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13): dependencies: minecraft-data: 3.83.1 minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/5ec3dd4b367fcc039fbcb3edd214fe3cf8178a6d(patch_hash=dkeyukcqlupmk563gwxsmjr3yu)(encoding@0.1.13) From 3e056946ec07c45e30182a7d012604564f5ab188 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 15 Mar 2025 02:20:47 +0300 Subject: [PATCH 446/851] add world download button --- src/react/PauseScreen.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/react/PauseScreen.tsx b/src/react/PauseScreen.tsx index 55ffe47f..f051f9e1 100644 --- a/src/react/PauseScreen.tsx +++ b/src/react/PauseScreen.tsx @@ -35,6 +35,7 @@ import { showNotification } from './NotificationProvider' import { appStatusState, reconnectReload } from './AppStatusProvider' import NetworkStatus from './NetworkStatus' import PauseLinkButtons from './PauseLinkButtons' +import { pixelartIcons } from './PixelartIcon' const waitForPotentialRender = async () => { return new Promise(resolve => { @@ -161,7 +162,7 @@ export default () => { const { singleplayer, wanOpened, wanOpening } = useSnapshot(miscUiState) const { noConnection } = useSnapshot(gameAdditionalState) const { active: packetsReplaceActive, hasRecordedPackets: packetsReplaceHasRecordedPackets } = useSnapshot(packetsRecordingState) - const { displayRecordButton } = useSnapshot(options) + const { displayRecordButton: displayPacketsButtons } = useSnapshot(options) const handlePointerLockChange = () => { if (!pointerLock.hasPointerLock && activeModalStack.length === 0) { @@ -234,7 +235,7 @@ export default () => { icon="pixelarticons:folder" onClick={async () => openWorldActions()} /> - {displayRecordButton && ( + {displayPacketsButtons && ( <>
From da35cfb8a23aa3978ad53bdca0e6bcf45e697a03 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sat, 15 Mar 2025 16:46:51 +0300 Subject: [PATCH 447/851] up mouse --- package.json | 4 ++-- pnpm-lock.yaml | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 6485c25e..9dd43eaf 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "mojangson": "^2.0.4", "net-browserify": "github:zardoy/prismarinejs-net-browserify", "node-gzip": "^1.1.2", - "mcraft-fun-mineflayer": "^0.1.10", + "mcraft-fun-mineflayer": "^0.1.12", "peerjs": "^1.5.0", "pixelarticons": "^1.8.1", "pretty-bytes": "^6.1.1", @@ -151,7 +151,7 @@ "http-server": "^14.1.1", "https-browserify": "^1.0.0", "mc-assets": "^0.2.42", - "mineflayer-mouse": "^0.1.0", + "mineflayer-mouse": "^0.1.1", "minecraft-inventory-gui": "github:zardoy/minecraft-inventory-gui#next", "mineflayer": "github:zardoy/mineflayer", "mineflayer-pathfinder": "^2.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc4b1555..d8d28545 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,8 +135,8 @@ importers: specifier: ^4.17.21 version: 4.17.21 mcraft-fun-mineflayer: - specifier: ^0.1.10 - version: 0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)) + specifier: ^0.1.12 + version: 0.1.12(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)) minecraft-data: specifier: 3.83.1 version: 3.83.1 @@ -362,8 +362,8 @@ importers: specifier: github:zardoy/mineflayer version: https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13) mineflayer-mouse: - specifier: ^0.1.0 - version: 0.1.0(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) + specifier: ^0.1.1 + version: 0.1.1(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1) mineflayer-pathfinder: specifier: ^2.4.4 version: 2.4.4 @@ -6694,9 +6694,9 @@ packages: resolution: {integrity: sha512-j2D1RNYtB5Z9gFu9MVjyDBbiALI0mWZ3xW/A3PPefVAHm3HJ2T1vH+1XBov1spBGPl7u+Zo7mRXza3X0egbeOg==} engines: {node: '>=18.0.0'} - mcraft-fun-mineflayer@0.1.10: - resolution: {integrity: sha512-KHzPts82I39nTDZlGwqJo1JXLwaIUHphBbmGWv7oYztUrq3iPiJDEIFgst0ROO/apjtHjzbCM9eb19qWw1JM3Q==} - version: 0.1.10 + mcraft-fun-mineflayer@0.1.12: + resolution: {integrity: sha512-BhfkagVJX+QmD/dt3qNQS5f7g3/7NI//OfSW4VnRolCnZtrLU8ekr59bLRcNmUWsvtTjkg+wbMeXwclHshSWOA==} + version: 0.1.12 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: '@roamhq/wrtc': '*' @@ -6920,8 +6920,8 @@ packages: resolution: {tarball: https://codeload.github.com/zardoy/mineflayer-item-map-downloader/tar.gz/a8d210ecdcf78dd082fa149a96e1612cc9747824} version: 1.2.0 - mineflayer-mouse@0.1.0: - resolution: {integrity: sha512-NFfHASMo3iZOECoYOHVqGBFROVapzDJRlgANMiWnynO/oPUZkOJF6oRrcE33FCVHGlwMCT2S6N7TcqCYqF21Uw==} + mineflayer-mouse@0.1.1: + resolution: {integrity: sha512-7jKN+6pIGtQVfYxEIm4tA9CYwTS8Mgn/qJ2wyhrAoIEW8smCHUu0kj5Sdo0TwTCdlOQClKt8aEBZ13E7MGqOhg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} mineflayer-pathfinder@2.4.4: @@ -17588,7 +17588,7 @@ snapshots: maxrects-packer: 2.7.3 zod: 3.24.1 - mcraft-fun-mineflayer@0.1.10(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)): + mcraft-fun-mineflayer@0.1.12(encoding@0.1.13)(mineflayer@https://codeload.github.com/zardoy/mineflayer/tar.gz/06e3050ddf4d9aa655fea6e2bed182937a81705d(encoding@0.1.13)): dependencies: '@zardoy/flying-squid': 0.0.49(encoding@0.1.13) exit-hook: 2.2.1 @@ -17958,7 +17958,7 @@ snapshots: - encoding - supports-color - mineflayer-mouse@0.1.0(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): + mineflayer-mouse@0.1.1(@types/debug@4.1.12)(@types/node@22.8.1)(terser@5.31.3)(tsx@4.7.0)(yaml@2.4.1): dependencies: change-case: 5.4.4 debug: 4.4.0(supports-color@8.1.1) From 36bf18b02f616b900852016a93ab22f79061f39b Mon Sep 17 00:00:00 2001 From: Vitaly Date: Mon, 17 Mar 2025 16:05:04 +0300 Subject: [PATCH 448/851] feat: refactor all app storage managment (#310) --- src/appConfig.ts | 3 + src/browserfs.ts | 20 +--- src/controls.ts | 5 +- src/optionsGuiScheme.tsx | 112 ++++++++++++++++++- src/optionsStorage.ts | 33 ++++-- src/react/AddServerOrConnect.tsx | 18 +-- src/react/Chat.css | 5 +- src/react/Input.tsx | 18 ++- src/react/MainMenuRenderApp.tsx | 18 --- src/react/SelectOption.tsx | 131 +++++++++++++++++++--- src/react/ServersList.tsx | 79 +++++++------ src/react/ServersListProvider.tsx | 177 +++++++++--------------------- src/react/appStorageProvider.ts | 135 +++++++++++++++++++++++ src/react/serversStorage.ts | 62 +++-------- src/react/storageProvider.ts | 13 --- 15 files changed, 525 insertions(+), 304 deletions(-) create mode 100644 src/react/appStorageProvider.ts delete mode 100644 src/react/storageProvider.ts diff --git a/src/appConfig.ts b/src/appConfig.ts index 3d6d8f93..b8f83ad1 100644 --- a/src/appConfig.ts +++ b/src/appConfig.ts @@ -1,6 +1,7 @@ import { disabledSettings, options, qsOptions } from './optionsStorage' import { miscUiState } from './globalState' import { setLoadingScreenStatus } from './appStatus' +import { setStorageDataOnAppConfigLoad } from './react/appStorageProvider' export type AppConfig = { // defaultHost?: string @@ -42,6 +43,8 @@ export const loadAppConfig = (appConfig: AppConfig) => { } } } + + setStorageDataOnAppConfigLoad() } export const isBundledConfigUsed = !!process.env.INLINED_APP_CONFIG diff --git a/src/browserfs.ts b/src/browserfs.ts index 0f4579b8..9fb0771f 100644 --- a/src/browserfs.ts +++ b/src/browserfs.ts @@ -15,6 +15,7 @@ import { getFixedFilesize } from './downloadAndOpenFile' import { packetsReplayState } from './react/state/packetsReplayState' import { createFullScreenProgressReporter } from './core/progressReporter' import { showNotification } from './react/NotificationProvider' +import { resetAppStorage } from './react/appStorageProvider' const { GoogleDriveFileSystem } = require('google-drive-browserfs/src/backends/GoogleDrive') browserfs.install(window) @@ -620,24 +621,13 @@ export const openWorldZip = async (...args: Parameters } } -export const resetLocalStorageWorld = () => { - for (const key of Object.keys(localStorage)) { - if (/^[\da-fA-F]{8}(?:\b-[\da-fA-F]{4}){3}\b-[\da-fA-F]{12}$/g.test(key) || key === '/') { - localStorage.removeItem(key) - } - } -} - -export const resetLocalStorageWithoutWorld = () => { - for (const key of Object.keys(localStorage)) { - if (!/^[\da-fA-F]{8}(?:\b-[\da-fA-F]{4}){3}\b-[\da-fA-F]{12}$/g.test(key) && key !== '/') { - localStorage.removeItem(key) - } - } +export const resetLocalStorage = () => { resetOptions() + resetAppStorage() } -window.resetLocalStorageWorld = resetLocalStorageWorld +window.resetLocalStorage = resetLocalStorage + export const openFilePicker = (specificCase?: 'resourcepack') => { // create and show input picker let picker: HTMLInputElement = document.body.querySelector('input#file-zip-picker')! diff --git a/src/controls.ts b/src/controls.ts index a3a54ffb..69b94636 100644 --- a/src/controls.ts +++ b/src/controls.ts @@ -25,11 +25,12 @@ import { showNotification } from './react/NotificationProvider' import { lastConnectOptions } from './react/AppStatusProvider' import { onCameraMove, onControInit } from './cameraRotationControls' import { createNotificationProgressReporter } from './core/progressReporter' +import { appStorage } from './react/appStorageProvider' -export const customKeymaps = proxy(JSON.parse(localStorage.keymap || '{}')) as UserOverridesConfig +export const customKeymaps = proxy(appStorage.keybindings) subscribe(customKeymaps, () => { - localStorage.keymap = JSON.stringify(customKeymaps) + appStorage.keybindings = customKeymaps }) const controlOptions = { diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index 7032e644..5851dd8d 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -3,19 +3,21 @@ import { useSnapshot } from 'valtio' import { openURL } from 'renderer/viewer/lib/simpleUtils' import { noCase } from 'change-case' import { gameAdditionalState, miscUiState, openOptionsMenu, showModal } from './globalState' -import { AppOptions, options } from './optionsStorage' +import { AppOptions, getChangedSettings, options, resetOptions } from './optionsStorage' import Button from './react/Button' import { OptionMeta, OptionSlider } from './react/OptionsItems' import Slider from './react/Slider' import { getScreenRefreshRate } from './utils' import { setLoadingScreenStatus } from './appStatus' -import { openFilePicker, resetLocalStorageWithoutWorld } from './browserfs' +import { openFilePicker, resetLocalStorage } from './browserfs' import { completeResourcepackPackInstall, getResourcePackNames, resourcePackState, uninstallResourcePack } from './resourcePack' import { downloadPacketsReplay, packetsRecordingState } from './packetsReplay/packetsReplayLegacy' -import { showOptionsModal } from './react/SelectOption' +import { showInputsModal, showOptionsModal } from './react/SelectOption' import supportedVersions from './supportedVersions.mjs' import { getVersionAutoSelect } from './connect' import { createNotificationProgressReporter } from './core/progressReporter' +import { customKeymaps } from './controls' +import { appStorage } from './react/appStorageProvider' export const guiOptionsScheme: { [t in OptionsGroupType]: Array<{ [K in keyof AppOptions]?: Partial> } & { custom? }> @@ -450,9 +452,19 @@ export const guiOptionsScheme: { return + >Reset settings + }, + }, + { + custom () { + return }, }, { @@ -460,6 +472,11 @@ export const guiOptionsScheme: { return Developer }, }, + { + custom () { + return + } + }, + { + custom () { + return + } + }, + { + custom () { + return + } + }, + { + custom () { + return + } + } + ], } -export type OptionsGroupType = 'main' | 'render' | 'interface' | 'controls' | 'sound' | 'advanced' | 'VR' +export type OptionsGroupType = 'main' | 'render' | 'interface' | 'controls' | 'sound' | 'advanced' | 'VR' | 'export-import' const Category = ({ children }) =>
>) => { export type AppOptions = typeof defaultOptions -// when opening html file locally in browser, localStorage is shared between all ever opened html files, so we try to avoid conflicts -const localStorageKey = process.env?.SINGLE_FILE_BUILD ? 'minecraftWebClientOptions' : 'options' +const isDeepEqual = (a: any, b: any): boolean => { + if (a === b) return true + if (typeof a !== typeof b) return false + if (typeof a !== 'object') return false + if (a === null || b === null) return a === b + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) return false + return a.every((item, index) => isDeepEqual(item, b[index])) + } + const keysA = Object.keys(a) + const keysB = Object.keys(b) + if (keysA.length !== keysB.length) return false + return keysA.every(key => isDeepEqual(a[key], b[key])) +} + +export const getChangedSettings = () => { + return Object.fromEntries( + Object.entries(options).filter(([key, value]) => !isDeepEqual(defaultOptions[key], value)) + ) +} + export const options: AppOptions = proxy({ ...defaultOptions, ...initialAppConfig.defaultSettings, - ...migrateOptions(JSON.parse(localStorage[localStorageKey] || '{}')), + ...migrateOptions(appStorage.options), ...qsOptions }) @@ -181,14 +198,14 @@ export const resetOptions = () => { Object.defineProperty(window, 'debugChangedOptions', { get () { - return Object.fromEntries(Object.entries(options).filter(([key, v]) => defaultOptions[key] !== v)) + return getChangedSettings() }, }) subscribe(options, () => { // Don't save disabled settings to localStorage const saveOptions = omitObj(options, [...disabledSettings.value] as any) - localStorage[localStorageKey] = JSON.stringify(saveOptions) + appStorage.options = saveOptions }) type WatchValue = >(proxy: T, callback: (p: T, isChanged: boolean) => void) => () => void diff --git a/src/react/AddServerOrConnect.tsx b/src/react/AddServerOrConnect.tsx index 1186fd9a..c25db793 100644 --- a/src/react/AddServerOrConnect.tsx +++ b/src/react/AddServerOrConnect.tsx @@ -3,7 +3,7 @@ import { appQueryParams } from '../appParams' import { fetchServerStatus, isServerValid } from '../api/mcStatusApi' import { parseServerAddress } from '../parseServerAddress' import Screen from './Screen' -import Input from './Input' +import Input, { INPUT_LABEL_WIDTH, InputWithLabel } from './Input' import Button from './Button' import SelectGameVersion from './SelectGameVersion' import { usePassesScaledDimensions } from './UIProvider' @@ -32,8 +32,6 @@ interface Props { allowAutoConnect?: boolean } -const ELEMENTS_WIDTH = 190 - export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQs, onQsConnect, placeholders, accounts, versions, allowAutoConnect }: Props) => { const isSmallHeight = !usePassesScaledDimensions(null, 350) const qsParamName = parseQs ? appQueryParams.name : undefined @@ -256,20 +254,8 @@ export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQ const ButtonWrapper = ({ ...props }: React.ComponentProps) => { props.style ??= {} - props.style.width = ELEMENTS_WIDTH + props.style.width = INPUT_LABEL_WIDTH return
} + +export default Input + +export const INPUT_LABEL_WIDTH = 190 + +export const InputWithLabel = ({ label, span, ...props }: React.ComponentProps & { label, span? }) => { + return
+ + +
+} diff --git a/src/react/MainMenuRenderApp.tsx b/src/react/MainMenuRenderApp.tsx index c112cb0e..f678e7f7 100644 --- a/src/react/MainMenuRenderApp.tsx +++ b/src/react/MainMenuRenderApp.tsx @@ -122,24 +122,6 @@ export default () => { singleplayerAvailable={singleplayerAvailable} connectToServerAction={() => showModal({ reactType: 'serversList' })} singleplayerAction={async () => { - const oldFormatSave = fs.existsSync('./world/level.dat') - if (oldFormatSave) { - setLoadingScreenStatus('Migrating old save, don\'t close the page') - try { - await mkdirRecursive('/data/worlds/local') - await copyFilesAsync('/world/', '/data/worlds/local') - try { - await removeFileRecursiveAsync('/world/') - } catch (err) { - console.error(err) - } - } catch (err) { - console.warn(err) - alert('Failed to migrate world from localStorage') - } finally { - setLoadingScreenStatus(undefined) - } - } showModal({ reactType: 'singleplayer' }) }} githubAction={() => openGithub()} diff --git a/src/react/SelectOption.tsx b/src/react/SelectOption.tsx index 9db20c86..4df471c0 100644 --- a/src/react/SelectOption.tsx +++ b/src/react/SelectOption.tsx @@ -1,10 +1,14 @@ import { proxy, useSnapshot } from 'valtio' +import { useEffect, useRef } from 'react' +import { noCase } from 'change-case' +import { titleCase } from 'title-case' import { hideCurrentModal, showModal } from '../globalState' import { parseFormattedMessagePacket } from '../botUtils' import Screen from './Screen' import { useIsModalActive } from './utilsApp' import Button from './Button' import MessageFormattedString from './MessageFormattedString' +import Input, { InputWithLabel } from './Input' const state = proxy({ title: '', @@ -12,6 +16,8 @@ const state = proxy({ showCancel: true, minecraftJsonMessage: null as null | Record, behavior: 'resolve-close' as 'resolve-close' | 'close-resolve', + inputs: {} as Record, + inputsConfirmButton: '' }) let resolve @@ -35,17 +41,63 @@ export const showOptionsModal = async ( title, options, showCancel: cancel, - minecraftJsonMessage: minecraftJsonMessageParsed + minecraftJsonMessage: minecraftJsonMessageParsed, + inputs: {}, + inputsConfirmButton: '' + }) + }) +} + +type InputOption = { + type: 'text' | 'checkbox' + defaultValue?: string | boolean + label?: string +} +export const showInputsModal = async >( + title: string, + inputs: T, + { cancel = true, minecraftJsonMessage }: { cancel?: boolean, minecraftJsonMessage? } = {} +): Promise<{ + [K in keyof T]: T[K] extends { type: 'text' } + ? string + : T[K] extends { type: 'checkbox' } + ? boolean + : never +}> => { + showModal({ reactType: 'general-select' }) + let minecraftJsonMessageParsed + if (minecraftJsonMessage) { + const parseResult = parseFormattedMessagePacket(minecraftJsonMessage) + minecraftJsonMessageParsed = parseResult.formatted + if (parseResult.plain) { + title += ` (${parseResult.plain})` + } + } + return new Promise((_resolve) => { + resolve = _resolve + Object.assign(state, { + title, + inputs, + showCancel: cancel, + minecraftJsonMessage: minecraftJsonMessageParsed, + options: [], + inputsConfirmButton: 'Confirm' }) }) } export default () => { - const { title, options, showCancel, minecraftJsonMessage } = useSnapshot(state) + const { title, options, showCancel, minecraftJsonMessage, inputs, inputsConfirmButton } = useSnapshot(state) const isModalActive = useIsModalActive('general-select') + const inputValues = useRef({}) + + useEffect(() => { + inputValues.current = Object.fromEntries(Object.entries(inputs).map(([key, input]) => [key, input.defaultValue ?? (input.type === 'checkbox' ? false : '')])) + }, [inputs]) + if (!isModalActive) return - const resolveClose = (value: string | undefined) => { + const resolveClose = (value: any) => { if (state.behavior === 'resolve-close') { resolve(value) hideCurrentModal() @@ -59,17 +111,66 @@ export default () => { {minecraftJsonMessage &&
} - {options.map(option => )} - {showCancel && } +
+ {options.length > 0 &&
+ {options.map(option => )} +
} +
+ {Object.entries(inputs).map(([key, input]) => { + const label = input.label ?? titleCase(noCase(key)) + return
+ {input.type === 'text' && ( + { + inputValues.current[key] = e.target.value + }} + /> + )} + {input.type === 'checkbox' && ( + + )} +
+ })} +
+ {inputs && inputsConfirmButton && ( + + )} + {showCancel && ( + + )} +
} diff --git a/src/react/ServersList.tsx b/src/react/ServersList.tsx index 46541af6..7b34f016 100644 --- a/src/react/ServersList.tsx +++ b/src/react/ServersList.tsx @@ -1,64 +1,61 @@ -import React from 'react' +import React, { useMemo } from 'react' +import { useSnapshot } from 'valtio' +import { miscUiState } from '../globalState' +import { appQueryParams } from '../appParams' import Singleplayer from './Singleplayer' import Input from './Input' import Button from './Button' import PixelartIcon, { pixelartIcons } from './PixelartIcon' - import Select from './Select' import { BaseServerInfo } from './AddServerOrConnect' import { useIsSmallWidth } from './simpleHooks' +import { appStorage, SavedProxiesData, ServerHistoryEntry } from './appStorageProvider' + +const getInitialProxies = () => { + const proxies = [] as string[] + if (miscUiState.appConfig?.defaultProxy) { + proxies.push(miscUiState.appConfig.defaultProxy) + } + return proxies +} + +export const getCurrentProxy = (): string | undefined => { + return appQueryParams.proxy ?? appStorage.proxiesData?.selected ?? getInitialProxies()[0] +} + +export const getCurrentUsername = () => { + return appQueryParams.username ?? appStorage.username +} interface Props extends React.ComponentProps { joinServer: (info: BaseServerInfo | string, additional: { shouldSave?: boolean index?: number }) => void - initialProxies: SavedProxiesLocalStorage - updateProxies: (proxies: SavedProxiesLocalStorage) => void - username: string - setUsername: (username: string) => void onProfileClick?: () => void setQuickConnectIp?: (ip: string) => void - serverHistory?: Array<{ - ip: string - versionOverride?: string - numConnects: number - }> -} - -export interface SavedProxiesLocalStorage { - proxies: readonly string[] - selected: string -} - -type ProxyStatusResult = { - time: number - ping: number - status: 'success' | 'error' | 'unknown' } export default ({ - initialProxies, - updateProxies: updateProxiesProp, joinServer, - username, - setUsername, onProfileClick, setQuickConnectIp, - serverHistory, ...props }: Props) => { - const [proxies, setProxies] = React.useState(initialProxies) - - const updateProxies = (newData: SavedProxiesLocalStorage) => { - setProxies(newData) - updateProxiesProp(newData) - } - + const snap = useSnapshot(appStorage) + const username = useMemo(() => getCurrentUsername(), [appQueryParams.username, appStorage.username]) const [serverIp, setServerIp] = React.useState('') const [save, setSave] = React.useState(true) const [activeHighlight, setActiveHighlight] = React.useState(undefined as 'quick-connect' | 'server-list' | undefined) + const updateProxies = (newData: SavedProxiesData) => { + appStorage.proxiesData = newData + } + + const setUsername = (username: string) => { + appStorage.username = username + } + const getActiveHighlightStyles = (type: typeof activeHighlight) => { const styles: React.CSSProperties = { transition: 'filter 0.2s', @@ -71,6 +68,8 @@ export default ({ const isSmallWidth = useIsSmallWidth() + const initialProxies = getInitialProxies() + const proxiesData = snap.proxiesData ?? { proxies: initialProxies, selected: initialProxies[0] } return setActiveHighlight('quick-connect')} onMouseLeave={() => setActiveHighlight(undefined)} > - {/* todo history */} - {serverHistory?.map((server) => ( -