cleanup, better resource pack support (#173)

This commit is contained in:
Vitaly 2024-08-06 20:42:33 +03:00 committed by GitHub
commit 6bac74b6c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 114 additions and 4890 deletions

View file

@ -135,7 +135,9 @@ export class Viewer {
let yOffset = this.playerHeight
if (this.isSneaking) yOffset -= 0.3
if (this.world instanceof WorldRendererThree) this.world.camera = cam as THREE.PerspectiveCamera
if (this.world instanceof WorldRendererThree) {
this.world.camera = cam as THREE.PerspectiveCamera
}
this.world.updateCamera(pos?.offset(0, yOffset, 0) ?? null, yaw, pitch)
}
@ -149,7 +151,7 @@ export class Viewer {
const audioLoader = new THREE.AudioLoader()
const start = Date.now()
audioLoader.loadAsync(path).then((buffer) => {
void audioLoader.loadAsync(path).then((buffer) => {
if (Date.now() - start > 500) return
// play
sound.setBuffer(buffer)

View file

@ -10,13 +10,11 @@ import itemsAtlases from 'mc-assets/dist/itemsAtlases.json'
import itemsAtlasLatest from 'mc-assets/dist/itemsAtlasLatest.png'
import itemsAtlasLegacy from 'mc-assets/dist/itemsAtlasLegacy.png'
import { AtlasParser } from 'mc-assets'
import TypedEmitter from 'typed-emitter'
import { dynamicMcDataFiles } from '../../buildMesherConfig.mjs'
import { getResourcepackTiles } from '../../../src/resourcePack'
import { toMajorVersion } from '../../../src/utils'
import { buildCleanupDecorator } from './cleanupDecorator'
import { defaultMesherConfig } from './mesher/shared'
import { loadTexture } from './utils.web'
import { loadJSON } from './utils'
import { chunkPos } from './simpleUtils'
function mod (x, n) {
@ -55,8 +53,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
sectionsOutstanding = new Map<string, number>()
@worldCleanup()
renderUpdateEmitter = new EventEmitter()
renderUpdateEmitter = new EventEmitter() as unknown as TypedEmitter<{
dirty (pos: Vec3, value: boolean): void
update (/* pos: Vec3, value: boolean */): void
textureDownloaded (): void
}>
customTexturesDataUrl = undefined as string | undefined
currentTextureImage = undefined as any
workers: any[] = []

View file

@ -4,10 +4,12 @@ import nbt from 'prismarine-nbt'
import PrismarineChatLoader from 'prismarine-chat'
import * as tweenJs from '@tweenjs/tween.js'
import { BloomPass, RenderPass, UnrealBloomPass, EffectComposer, WaterPass, GlitchPass } from 'three-stdlib'
import worldBlockProvider from 'mc-assets/dist/worldBlockProvider'
import { renderSign } from '../sign-renderer'
import { chunkPos, sectionPos } from './simpleUtils'
import { WorldRendererCommon, WorldRendererConfig } from './worldrendererCommon'
import { disposeObject } from './threeJsUtils'
import { renderBlockThree } from './mesher/standaloneRenderer'
export class WorldRendererThree extends WorldRendererCommon {
outputFormat = 'threeJs' as const
@ -17,6 +19,7 @@ export class WorldRendererThree extends WorldRendererCommon {
signsCache = new Map<string, any>()
starField: StarField
cameraSectionPos: Vec3 = new Vec3(0, 0, 0)
cameraGroup = new THREE.Group()
get tilesRendered () {
return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).tilesCount, 0)
@ -25,6 +28,8 @@ 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()
}
timeUpdated (newTime: number): void {

View file

@ -23,11 +23,14 @@ fs.writeFileSync('./generated/minecraft-data-data.js', mcDataContents, 'utf8')
// app resources
let headerImports = ''
let resourcesContent = 'export const appReplacableResources: { [key: string]: { content: any, resourcePackPath: string, cssVar?: string, cssVarRepeat?: number } } = {\n'
let resourcesContent = 'export const appReplacableResources: { [key in Keys]: { content: any, resourcePackPath: string, cssVar?: string, cssVarRepeat?: number } } = {\n'
let resourcesContentOriginal = 'export const resourcesContentOriginal = {\n'
const keys = [] as string[]
for (const resource of appReplacableResources) {
const { path, ...rest } = resource
const name = path.split('/').slice(-4).join('_').replace('.png', '').replaceAll('-', '_').replaceAll('.', '_')
keys.push(name)
headerImports += `import ${name} from '${path.replace('../node_modules/', '')}'\n`
resourcesContent += `
'${name}': {
@ -35,10 +38,16 @@ for (const resource of appReplacableResources) {
resourcePackPath: 'minecraft/textures/${path.slice(path.indexOf('other-textures/') + 'other-textures/'.length).split('/').slice(1).join('/')}',
...${JSON.stringify(rest)}
},
`
resourcesContentOriginal += `
'${name}': ${name},
`
}
resourcesContent += '}'
resourcesContent += '}\n'
resourcesContent += `type Keys = ${keys.map(k => `'${k}'`).join(' | ')}\n`
resourcesContentOriginal += '}\n'
resourcesContent += resourcesContentOriginal
fs.mkdirSync('./src/generated', { recursive: true })
fs.writeFileSync('./src/generated/resources.ts', headerImports + '\n' + resourcesContent, 'utf8')

View file

@ -72,7 +72,7 @@ import { startLocalServer, unsupportedLocalServerFeatures } from './createLocalS
import defaultServerOptions from './defaultLocalServerOptions'
import dayCycle from './dayCycle'
import { onAppLoad, resourcepackOnWorldLoad } from './resourcePack'
import { onAppLoad, resourcepackReload } from './resourcePack'
import { connectToPeer } from './localServerMultiplayer'
import CustomChannelClient from './customClient'
import { loadScript } from 'prismarine-viewer/viewer/lib/utils'
@ -403,7 +403,7 @@ async function connect (connectOptions: ConnectOptions) {
await loadScript(`./mc-data/${toMajorVersion(version)}.js`)
miscUiState.loadedDataVersion = version
try {
await resourcepackOnWorldLoad(version)
await resourcepackReload(version)
} catch (err) {
console.error(err)
const doContinue = confirm('Failed to apply texture pack. See errors in the console. Continue?')

View file

@ -1,23 +1,8 @@
import { proxy, subscribe } from 'valtio'
import { showInventory } from 'minecraft-inventory-gui/web/ext.mjs'
import InventoryGui from 'mc-assets/dist/other-textures/latest/gui/container/inventory.png'
import ChestLikeGui from 'mc-assets/dist/other-textures/latest/gui/container/shulker_box.png'
import LargeChestLikeGui from 'mc-assets/dist/other-textures/latest/gui/container/generic_54.png'
import FurnaceGui from 'mc-assets/dist/other-textures/latest/gui/container/furnace.png'
import CraftingTableGui from 'mc-assets/dist/other-textures/latest/gui/container/crafting_table.png'
import DispenserGui from 'mc-assets/dist/other-textures/latest/gui/container/dispenser.png'
import HopperGui from 'mc-assets/dist/other-textures/latest/gui/container/hopper.png'
import HorseGui from 'mc-assets/dist/other-textures/latest/gui/container/horse.png'
import VillagerGui from 'mc-assets/dist/other-textures/latest/gui/container/villager2.png'
import EnchantingGui from 'mc-assets/dist/other-textures/latest/gui/container/enchanting_table.png'
import AnvilGui from 'mc-assets/dist/other-textures/latest/gui/container/anvil.png'
import BeaconGui from 'mc-assets/dist/other-textures/latest/gui/container/beacon.png'
import WidgetsGui from 'mc-assets/dist/other-textures/latest/gui/widgets.png'
// import Dirt from 'mc-assets/dist/other-textures/latest/blocks/dirt.png'
import { RecipeItem } from 'minecraft-data'
import { versionToNumber } from 'prismarine-viewer/viewer/prepare/utils'
import _itemsAtlases from 'prismarine-viewer/public/textures/items.json'
import { flat, fromFormattedString } from '@xmcl/text-component'
import mojangson from 'mojangson'
import nbt from 'prismarine-nbt'
@ -25,36 +10,18 @@ import { splitEvery, equals } from 'rambda'
import PItem, { Item } from 'prismarine-item'
import { ItemsRenderer } from 'mc-assets/dist/itemsRenderer'
import Generic95 from '../assets/generic_95.png'
import { appReplacableResources } from './generated/resources'
import { activeModalStack, hideCurrentModal, hideModal, miscUiState, showModal } from './globalState'
import invspriteJson from './invsprite.json'
import { options } from './optionsStorage'
import { assertDefined, inGameError } from './utils'
import { MessageFormatPart } from './botUtils'
import { currentScaling } from './scaleInterface'
import { descriptionGenerators, getItemDescription } from './itemsDescriptions'
import { getItemDescription } from './itemsDescriptions'
const loadedImagesCache = new Map<string, HTMLImageElement>()
const cleanLoadedImagesCache = () => {
loadedImagesCache.delete('blocks')
}
export type BlockStates = Record<string, null | {
variants: Record<string, {
model: {
elements: [{
faces: {
[face: string]: {
texture: {
u
v
su
sv
}
}
}
}]
}
}>
}>
let lastWindow: ReturnType<typeof showInventory>
/** bot version */
@ -143,22 +110,22 @@ export const onGameLoad = (onLoad) => {
const getImageSrc = (path): string | HTMLImageElement => {
assertDefined(viewer)
switch (path) {
case 'gui/container/inventory': return InventoryGui
case 'gui/container/inventory': return appReplacableResources.latest_gui_container_inventory.content
case 'blocks': return viewer.world.blocksAtlasParser!.latestImage
case 'items': return viewer.world.itemsAtlasParser!.latestImage
case 'gui/container/dispenser': return DispenserGui
case 'gui/container/furnace': return FurnaceGui
case 'gui/container/crafting_table': return CraftingTableGui
case 'gui/container/shulker_box': return ChestLikeGui
case 'gui/container/generic_54': return LargeChestLikeGui
case 'gui/container/dispenser': return appReplacableResources.latest_gui_container_dispenser.content
case 'gui/container/furnace': return appReplacableResources.latest_gui_container_furnace.content
case 'gui/container/crafting_table': return appReplacableResources.latest_gui_container_crafting_table.content
case 'gui/container/shulker_box': return appReplacableResources.latest_gui_container_shulker_box.content
case 'gui/container/generic_54': return appReplacableResources.latest_gui_container_generic_54.content
case 'gui/container/generic_95': return Generic95
case 'gui/container/hopper': return HopperGui
case 'gui/container/horse': return HorseGui
case 'gui/container/villager2': return VillagerGui
case 'gui/container/enchanting_table': return EnchantingGui
case 'gui/container/anvil': return AnvilGui
case 'gui/container/beacon': return BeaconGui
case 'gui/widgets': return WidgetsGui
case 'gui/container/hopper': return appReplacableResources.latest_gui_container_hopper.content
case 'gui/container/horse': return appReplacableResources.latest_gui_container_horse.content
case 'gui/container/villager2': return appReplacableResources.latest_gui_container_villager2.content
case 'gui/container/enchanting_table': return appReplacableResources.latest_gui_container_enchanting_table.content
case 'gui/container/anvil': return appReplacableResources.latest_gui_container_anvil.content
case 'gui/container/beacon': return appReplacableResources.latest_gui_container_beacon.content
case 'gui/widgets': return appReplacableResources.other_textures_latest_gui_widgets.content
}
// empty texture
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='

File diff suppressed because it is too large Load diff

View file

@ -62,7 +62,7 @@ export default () => {
return
}
const upStatus = () => {
setVersionStatus(`(${isLatest ? 'latest' : 'new version available'}) ${mainMenuState.serviceWorkerLoaded ? '(Available Offline)' : ''}`)
setVersionStatus(`(${isLatest ? 'latest' : 'new version available'}${mainMenuState.serviceWorkerLoaded ? ' - Available Offline' : ''})`)
}
subscribe(mainMenuState, upStatus)
setVersionTitle(`Loaded: ${process.env.BUILD_VERSION}. Remote: ${contents}`)

View file

@ -1,20 +1,17 @@
import { join, dirname, basename } from 'path'
import fs from 'fs'
import JSZip from 'jszip'
import { proxy, ref } from 'valtio'
import type { BlockStates } from './inventoryWindows'
import { copyFilesAsync, copyFilesAsyncWithProgress, mkdirRecursive, removeFileRecursiveAsync } from './browserfs'
import { proxy, subscribe } from 'valtio'
import { mkdirRecursive, removeFileRecursiveAsync } from './browserfs'
import { setLoadingScreenStatus } from './utils'
import { showNotification } from './react/NotificationProvider'
import { options } from './optionsStorage'
import { showOptionsModal } from './react/SelectOption'
import { appStatusState } from './react/AppStatusProvider'
import { appReplacableResources } from './generated/resources'
import { appReplacableResources, resourcesContentOriginal } from './generated/resources'
export const resourcePackState = proxy({
resourcePackInstalled: false,
currentTexturesDataUrl: undefined as string | undefined,
currentTexturesBlockStates: undefined as BlockStates | undefined,
})
const getLoadedImage = async (url: string) => {
@ -111,9 +108,7 @@ export const completeTexturePackInstall = async (displayName: string, name: stri
const basePath = texturePackBasePath2 + name
await fs.promises.writeFile(join(basePath, 'name.txt'), displayName, 'utf8')
if (viewer?.world.active) {
await updateTextures()
}
await updateTextures()
setLoadingScreenStatus(undefined)
showNotification('Texturepack installed & enabled')
await updateTexturePackInstalledState()
@ -290,11 +285,15 @@ export const onAppLoad = () => {
bot.acceptResourcePack()
})
})
subscribe(resourcePackState, () => {
if (!resourcePackState.resourcePackInstalled) return
void updateAllReplacableTextures()
})
}
const setOtherTexturesCss = async () => {
const updateAllReplacableTextures = async () => {
const basePath = await getActiveTexturepackBasePath()
// TODO! fallback to default
const setCustomCss = async (path: string | null, varName: string, repeat = 1) => {
if (path && await existsAsync(path)) {
const contents = await fs.promises.readFile(path, 'base64')
@ -304,10 +303,25 @@ const setOtherTexturesCss = async () => {
document.body.style.setProperty(varName, '')
}
}
const vars = Object.values(appReplacableResources).filter(x => x.cssVar)
for (const { cssVar, cssVarRepeat, resourcePackPath } of vars) {
// eslint-disable-next-line no-await-in-loop
await setCustomCss(`${basePath}/assets/${resourcePackPath}`, cssVar!, cssVarRepeat ?? 1)
const setCustomPicture = async (key: string, path: string) => {
let contents = resourcesContentOriginal[key]
if (await existsAsync(path)) {
const file = await fs.promises.readFile(path, 'base64')
const dataUrl = `data:image/png;base64,${file}`
contents = dataUrl
}
appReplacableResources[key].content = contents
}
const vars = Object.entries(appReplacableResources).filter(([, x]) => x.cssVar)
for (const [key, { cssVar, cssVarRepeat, resourcePackPath }] of vars) {
const resPath = `${basePath}/assets/${resourcePackPath}`
if (cssVar) {
// eslint-disable-next-line no-await-in-loop
await setCustomCss(resPath, cssVar, cssVarRepeat ?? 1)
} else {
// eslint-disable-next-line no-await-in-loop
await setCustomPicture(key, resPath)
}
}
}
@ -318,7 +332,7 @@ const updateTextures = async () => {
const itemsFiles = Object.keys(viewer.world.itemsAtlases.latest.textures)
const blocksData = await getResourcepackTiles('blocks', blocksFiles)
const itemsData = await getResourcepackTiles('items', itemsFiles)
await setOtherTexturesCss()
await updateAllReplacableTextures()
await prepareBlockstatesAndModels()
viewer.world.customTextures = {}
if (blocksData) {
@ -338,6 +352,6 @@ const updateTextures = async () => {
}
}
export const resourcepackOnWorldLoad = async (version) => {
export const resourcepackReload = async (version) => {
await updateTextures()
}

View file

@ -3,6 +3,7 @@ export const appReplacableResources: Array<{
cssVar?: string
cssVarRepeat?: number
}> = [
// GUI
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/title/minecraft.png',
cssVar: '--title-gui',
@ -19,5 +20,42 @@ export const appReplacableResources: Array<{
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/bars.png',
cssVar: '--bars-gui-atlas',
}
},
// container
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/inventory.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/shulker_box.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/generic_54.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/furnace.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/crafting_table.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/dispenser.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/hopper.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/horse.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/villager2.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/enchanting_table.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/anvil.png',
},
{
path: '../node_modules/mc-assets/dist/other-textures/latest/gui/container/beacon.png',
},
]