big resourcemanager refactor

This commit is contained in:
Vitaly Turovsky 2025-03-05 15:30:04 +03:00
commit d197859d47
9 changed files with 218 additions and 147 deletions

View file

@ -3,27 +3,20 @@ import { EventEmitter } from 'events'
import { Vec3 } from 'vec3'
import * as THREE from 'three'
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'
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, getLoadedItemDefinitionsStore } from 'mc-assets'
import { AtlasParser } from 'mc-assets'
import TypedEmitter from 'typed-emitter'
import { LineMaterial } from 'three-stdlib'
import christmasPack from 'mc-assets/dist/textureReplacements/christmas'
import { ItemsRenderer } from 'mc-assets/dist/itemsRenderer'
import itemDefinitionsJson from 'mc-assets/dist/itemDefinitions.json'
import worldBlockProvider, { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
import { dynamicMcDataFiles } from '../../buildMesherConfig.mjs'
import { toMajorVersion } from '../../../src/utils'
import { ResourcesManager } from '../../../src/appViewer'
import { buildCleanupDecorator } from './cleanupDecorator'
import { defaultMesherConfig, HighestBlockInfo, MesherGeometryOutput, CustomBlockModels, BlockStateModelInfo } from './mesher/shared'
import { chunkPos } from './simpleUtils'
import { HandItemBlock } from './holdingBlock'
import { updateStatText } from './ui/newStats'
import { WorldRendererThree } from './worldrendererThree'
const appViewer = undefined
function mod (x, n) {
return ((x % n) + n) % n
@ -93,8 +86,6 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
renderUpdateEmitter = new EventEmitter() as unknown as TypedEmitter<{
dirty (pos: Vec3, value: boolean): void
update (/* pos: Vec3, value: boolean */): void
textureDownloaded (): void
itemsTextureDownloaded (): void
chunkFinished (key: string): void
}>
customTexturesDataUrl = undefined as string | undefined
@ -120,29 +111,16 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
mesherConfig = defaultMesherConfig
camera: THREE.PerspectiveCamera
highestBlocks = new Map<string, HighestBlockInfo>()
blockstatesModels: any
customBlockStates: Record<string, any> | undefined
customModels: Record<string, any> | undefined
itemsAtlasParser: AtlasParser | undefined
blocksAtlasParser: AtlasParser | undefined
sourceData = {
blocksAtlases,
itemsAtlases,
itemDefinitionsJson
}
customTextures: {
items?: CustomTexturesData
blocks?: CustomTexturesData
armor?: CustomTexturesData
} = {}
itemsDefinitionsStore = getLoadedItemDefinitionsStore(this.sourceData.itemDefinitionsJson)
workersProcessAverageTime = 0
workersProcessAverageTimeCount = 0
maxWorkersProcessTime = 0
geometryReceiveCount = {}
allLoadedIn: undefined | number
rendererDevice = '...'
edgeChunks = {} as Record<string, boolean>
lastAddChunk = null as null | {
@ -154,14 +132,6 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
lastChunkDistance = 0
debugStopGeometryUpdate = false
@worldCleanup()
freeFlyMode = false
@worldCleanup()
freeFlyState = {
yaw: 0,
pitch: 0,
position: new Vec3(0, 0, 0)
}
@worldCleanup()
itemsRenderer: ItemsRenderer | undefined
@ -175,7 +145,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
abstract changeBackgroundColor (color: [number, number, number]): void
constructor (public config: WorldRendererConfig) {
constructor (private readonly resourcesManager: ResourcesManager, private readonly worldRendererConfig: WorldRendererConfig) {
// this.initWorkers(1) // preload script on page load
this.snapshotInitialValues()
@ -183,11 +153,15 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
const loadedChunks = Object.keys(this.finishedChunks).length
updateStatText('loaded-chunks', `${loadedChunks}/${this.chunksLength} chunks (${this.lastChunkDistance}/${this.viewDistance})`)
})
this.resourcesManager.on('assetsTexturesUpdated', () => {
void this.updateAssetsData()
})
}
snapshotInitialValues () { }
initWorkers (numWorkers = this.config.mesherWorkers) {
initWorkers (numWorkers = this.worldRendererConfig.mesherWorkers) {
// init workers
for (let i = 0; i < numWorkers + 1; i++) {
// Node environment needs an absolute path, but browser needs the url of the file
@ -332,7 +306,6 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
// new game load happens here
async setVersion (version, texturesVersion = version) {
if (!this.blockstatesModels) throw new Error('Blockstates models is not loaded yet')
this.version = version
this.texturesVersion = texturesVersion
this.resetWorld()
@ -342,7 +315,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
this.mesherConfig.version = this.version!
this.sendMesherMcData()
await this.updateAssetsData()
await this.resourcesManager.updateAssetsData()
}
sendMesherMcData () {
@ -359,38 +332,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
}
}
async updateAssetsData (resourcePackUpdate = false, prioritizeBlockTextures?: string[]) {
const blocksAssetsParser = new AtlasParser(this.sourceData.blocksAtlases, blocksAtlasLatest, blocksAtlasLegacy)
const itemsAssetsParser = new AtlasParser(this.sourceData.itemsAtlases, itemsAtlasLatest, itemsAtlasLegacy)
const blockTexturesChanges = {} as Record<string, string>
const date = new Date()
if ((date.getMonth() === 11 && date.getDate() >= 24) || (date.getMonth() === 0 && date.getDate() <= 6)) {
Object.assign(blockTexturesChanges, christmasPack)
}
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]
return blockTexturesChanges[textureName] ?? texture
}, /* this.customTextures?.blocks?.tileSize */undefined, prioritizeBlockTextures, customBlockTextures)
console.timeEnd('createBlocksAtlas')
console.time('createItemsAtlas')
const { atlas: itemsAtlas, canvas: itemsCanvas } = await itemsAssetsParser.makeNewAtlas(this.texturesVersion ?? this.version ?? 'latest', (textureName) => {
const texture = this.customTextures?.items?.textures[textureName]
if (!texture) return
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())
this.itemsRenderer = new ItemsRenderer(this.version!, this.blockstatesModels, this.itemsAtlasParser, this.blocksAtlasParser)
this.worldBlockProvider = worldBlockProvider(this.blockstatesModels, this.blocksAtlasParser.atlas, 'latest')
const texture = await new THREE.TextureLoader().loadAsync(this.blocksAtlasParser.latestImage)
async updateAssetsData () {
const texture = await new THREE.TextureLoader().loadAsync(this.resourcesManager.blocksAtlasParser!.latestImage)
texture.magFilter = THREE.NearestFilter
texture.minFilter = THREE.NearestFilter
texture.flipY = false
@ -399,39 +342,34 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
this.mesherConfig.textureSize = this.material.map.image.width
for (const [i, worker] of this.workers.entries()) {
const { blockstatesModels } = this
if (this.customBlockStates) {
// TODO! remove from other versions as well
const { blockstatesModels } = this.resourcesManager
if (this.resourcesManager.customBlockStates) {
blockstatesModels.blockstates.latest = {
...blockstatesModels.blockstates.latest,
...this.customBlockStates
...this.resourcesManager.customBlockStates
}
}
if (this.customModels) {
if (this.resourcesManager.customModels) {
blockstatesModels.models.latest = {
...blockstatesModels.models.latest,
...this.customModels
...this.resourcesManager.customModels
}
}
worker.postMessage({
type: 'mesherData',
workerIndex: i,
blocksAtlas: {
latest: blocksAtlas
latest: this.resourcesManager.blocksAtlasParser!.atlas
},
blockstatesModels,
config: this.mesherConfig,
})
}
const itemsTexture = await new THREE.TextureLoader().loadAsync(this.itemsAtlasParser.latestImage)
const itemsTexture = await new THREE.TextureLoader().loadAsync(this.resourcesManager.itemsAtlasParser!.latestImage)
itemsTexture.magFilter = THREE.NearestFilter
itemsTexture.minFilter = THREE.NearestFilter
itemsTexture.flipY = false
viewer.entities.itemsTexture = itemsTexture
this.renderUpdateEmitter.emit('textureDownloaded')
this.renderUpdateEmitter.emit('itemsTextureDownloaded')
console.log('textures loaded')
}
async downloadDebugAtlas (isItems = false) {

View file

@ -17,6 +17,8 @@ import { IPlayerState } from './basePlayerState'
import { getMesh } from './entity/EntityMesh'
import { armorModel } from './entity/armorModels'
const appViewer = undefined
export class WorldRendererThree extends WorldRendererCommon {
interactionLines: null | { blockPos; mesh } = null
outputFormat = 'threeJs' as const
@ -38,15 +40,15 @@ export class WorldRendererThree extends WorldRendererCommon {
return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).blocksCount, 0)
}
constructor (public scene: THREE.Scene, public renderer: THREE.WebGLRenderer, public worldOptionsHolder: DisplayWorldOptions) {
super(worldOptionsHolder.inWorldRenderingConfig)
const { playerState } = worldOptionsHolder
constructor (public scene: THREE.Scene, public renderer: THREE.WebGLRenderer, public options: DisplayWorldOptions) {
super(options.resourcesManager, options.inWorldRenderingConfig)
const { playerState, inWorldRenderingConfig: config } = options
this.starField = new StarField(scene)
this.holdingBlock = new HoldingBlock(playerState, this.config)
this.holdingBlockLeft = new HoldingBlock(playerState, this.config, true)
this.holdingBlock = new HoldingBlock(playerState, config)
this.holdingBlockLeft = new HoldingBlock(playerState, config, true)
this.renderUpdateEmitter.on('itemsTextureDownloaded', () => {
this.options.resourcesManager.on('assetsTexturesUpdated', () => {
this.holdingBlock.ready = true
this.holdingBlock.updateItem()
this.holdingBlockLeft.ready = true
@ -168,7 +170,7 @@ export class WorldRendererThree extends WorldRendererCommon {
object.name = 'chunk';
(object as any).tilesCount = data.geometry.positions.length / 3 / 4;
(object as any).blocksCount = data.geometry.blocksCount
if (!this.config.showChunkBorders) {
if (!this.options.inWorldRenderingConfig.showChunkBorders) {
boxHelper.visible = false
}
// should not compute it once
@ -225,15 +227,15 @@ export class WorldRendererThree extends WorldRendererCommon {
}
updateCamera (pos: Vec3 | null, yaw: number, pitch: number): void {
if (this.freeFlyMode) {
pos = this.freeFlyState.position
pitch = this.freeFlyState.pitch
yaw = this.freeFlyState.yaw
}
// if (this.freeFlyMode) {
// pos = this.freeFlyState.position
// pitch = this.freeFlyState.pitch
// yaw = this.freeFlyState.yaw
// }
if (pos) {
new tweenJs.Tween(this.camera.position).to({ x: pos.x, y: pos.y, z: pos.z }, 50).start()
this.freeFlyState.position = pos
// this.freeFlyState.position = pos
}
this.camera.rotation.set(pitch, yaw, this.cameraRoll, 'ZYX')
}
@ -243,7 +245,7 @@ export class WorldRendererThree extends WorldRendererCommon {
// 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)
if (this.config.showHand && !this.freeFlyMode) {
if (this.options.inWorldRenderingConfig.showHand/* && !this.freeFlyMode */) {
this.holdingBlock.render(this.camera, this.renderer, viewer.ambientLight, viewer.directionalLight)
this.holdingBlockLeft.render(this.camera, this.renderer, viewer.ambientLight, viewer.directionalLight)
}
@ -347,7 +349,7 @@ export class WorldRendererThree extends WorldRendererCommon {
}
updateShowChunksBorder (value: boolean) {
this.config.showChunkBorders = value
this.options.inWorldRenderingConfig.showChunkBorders = value
for (const object of Object.values(this.sectionObjects)) {
for (const child of object.children) {
if (child.name === 'helper') {

View file

@ -1,5 +1,6 @@
import * as THREE from 'three'
import { Vec3 } from 'vec3'
import { proxy } from 'valtio'
import { GraphicsBackendLoader, GraphicsBackend, GraphicsBackendOptions, DisplayWorldOptions } from '../../../src/appViewer'
import { ProgressReporter } from '../../../src/core/progressReporter'
import { ThreeJsWorldRenderer } from '../lib/viewer'
@ -18,6 +19,11 @@ const createGraphicsBackend: GraphicsBackendLoader = (options: GraphicsBackendOp
let panoramaRenderer: PanoramaRenderer | null = null
let worldRenderer: ThreeJsWorldRenderer | null = null
const worldState = proxy({
chunksLoaded: 0,
chunksTotal: 0
})
const startPanorama = () => {
if (worldRenderer) return
if (!panoramaRenderer) {
@ -28,7 +34,6 @@ const createGraphicsBackend: GraphicsBackendLoader = (options: GraphicsBackendOp
let version = ''
const updateResources = async (ver: string, progressReporter: ProgressReporter): Promise<void> => {
// Implementation for updating resources will be added here
version = ver
}
@ -49,7 +54,6 @@ const createGraphicsBackend: GraphicsBackendLoader = (options: GraphicsBackendOp
if (documentRenderer) {
documentRenderer.dispose()
}
if (worldRenderer) {
worldRenderer.dispose()
worldRenderer = null
@ -77,7 +81,8 @@ const createGraphicsBackend: GraphicsBackendLoader = (options: GraphicsBackendOp
},
setRoll (roll: number) {
worldRenderer?.setCameraRoll(roll)
}
},
worldState
}
return backend

View file

@ -1,14 +1,33 @@
import { EventEmitter } from 'events'
import { WorldDataEmitter } from 'renderer/viewer/lib/worldDataEmitter'
import { IPlayerState } from 'renderer/viewer/lib/basePlayerState'
import { subscribeKey } from 'valtio/utils'
import { defaultWorldRendererConfig, WorldRendererConfig } from 'renderer/viewer/lib/worldrendererCommon'
import { Vec3 } from 'vec3'
import { proxy } from 'valtio'
import blocksAtlases from 'mc-assets/dist/blocksAtlases.json'
import itemsAtlases from 'mc-assets/dist/itemsAtlases.json'
import itemDefinitionsJson from 'mc-assets/dist/itemDefinitions.json'
import blocksAtlasLatest from 'mc-assets/dist/blocksAtlasLatest.png'
import blocksAtlasLegacy from 'mc-assets/dist/blocksAtlasLegacy.png'
import itemsAtlasLatest from 'mc-assets/dist/itemsAtlasLatest.png'
import itemsAtlasLegacy from 'mc-assets/dist/itemsAtlasLegacy.png'
import christmasPack from 'mc-assets/dist/textureReplacements/christmas'
import { AtlasParser, getLoadedItemDefinitionsStore } from 'mc-assets'
import TypedEmitter from 'typed-emitter'
import { ItemsRenderer } from 'mc-assets/dist/itemsRenderer'
import worldBlockProvider, { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
import { playerState, PlayerStateManager } from './mineflayer/playerState'
import { createNotificationProgressReporter, ProgressReporter } from './core/progressReporter'
import { setLoadingScreenStatus } from './appStatus'
import { activeModalStack, miscUiState } from './globalState'
import { options } from './optionsStorage'
export interface WorldReactiveState {
chunksLoaded: number
chunksTotal: number
}
export interface GraphicsBackendConfig {
fpsLimit?: number
powerPreference?: 'high-performance' | 'low-power'
@ -41,23 +60,17 @@ export interface GraphicsBackend {
updateResources: (version: string, progressReporter: ProgressReporter) => Promise<void>
startWorld: (options: DisplayWorldOptions) => void
disconnect: () => void
setRendering: (rendering: boolean) => void
getRenderer: () => string
getDebugOverlay: () => {
entitiesString?: string
right?: Record<string, string>
left?: Record<string, string>
}
getDebugOverlay: () => Record<string, any>
updateCamera: (pos: Vec3 | null, yaw: number, pitch: number) => void
setRoll: (roll: number) => void
worldState: WorldReactiveState
}
export class AppViewer {
resourcesManager: ResourcesManager
resourcesManager: ResourcesManager = new ResourcesManager()
worldView: WorldDataEmitter
// playerState: IPlayerState
readonly config: GraphicsBackendConfig = {
...defaultGraphicsBackendConfig,
powerPreference: options.gpuPreference === 'default' ? undefined : options.gpuPreference
@ -105,14 +118,14 @@ export class AppViewer {
async updateResources (version: string, progressReporter: ProgressReporter) {
if (this.backend) {
await this.backend.updateResources(version, progressReporter)
// await this.backend.updateResources(version, progressReporter)
}
}
startWorld (world, renderDistance, startPosition) {
async startWorld (world, renderDistance, startPosition) {
this.worldView = new WorldDataEmitter(world, renderDistance, startPosition)
window.worldView = this.worldView
// this.playerState = new PlayerStateManager()
if (this.backend) {
this.backend.startWorld({
resourcesManager: this.resourcesManager,
@ -133,12 +146,123 @@ export class AppViewer {
}
}
export interface UpdateAssetsRequest {
includeOnlyBlocks?: string[]
}
type ResourceManagerEvents = {
assetsTexturesUpdated: () => void
}
export class ResourcesManager extends (EventEmitter as new () => TypedEmitter<ResourceManagerEvents>) {
// Source data (imported, not changing)
sourceBlockStatesModels: any = null
sourceBlocksAtlases: any = blocksAtlases
sourceItemsAtlases: any = itemsAtlases
sourceItemDefinitionsJson: any = itemDefinitionsJson
itemsDefinitionsStore = getLoadedItemDefinitionsStore(this.sourceItemDefinitionsJson)
// Atlas parsers
itemsAtlasParser: AtlasParser | undefined
blocksAtlasParser: AtlasParser | undefined
// User data (specific to current resourcepack/version)
customBlockStates?: Record<string, any>
customModels?: Record<string, any>
customTextures: {
items?: { tileSize: number | undefined, textures: Record<string, HTMLImageElement> }
blocks?: { tileSize: number | undefined, textures: Record<string, HTMLImageElement> }
armor?: { tileSize: number | undefined, textures: Record<string, HTMLImageElement> }
} = {}
// Moved from WorldRendererCommon
itemsRenderer: ItemsRenderer | undefined
worldBlockProvider: WorldBlockProvider | undefined
blockstatesModels: any = null
version?: string
texturesVersion?: string
async updateAssetsData (request: UpdateAssetsRequest = {}) {
const blocksAssetsParser = new AtlasParser(this.sourceBlocksAtlases, blocksAtlasLatest, blocksAtlasLegacy)
const itemsAssetsParser = new AtlasParser(this.sourceItemsAtlases, itemsAtlasLatest, itemsAtlasLegacy)
const blockTexturesChanges = {} as Record<string, string>
const date = new Date()
if ((date.getMonth() === 11 && date.getDate() >= 24) || (date.getMonth() === 0 && date.getDate() <= 6)) {
Object.assign(blockTexturesChanges, christmasPack)
}
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) => {
if (request.includeOnlyBlocks && !request.includeOnlyBlocks.includes(textureName)) return false
const texture = this.customTextures?.blocks?.textures[textureName]
return blockTexturesChanges[textureName] ?? texture
},
undefined,
undefined,
customBlockTextures
)
console.timeEnd('createBlocksAtlas')
console.time('createItemsAtlas')
const { atlas: itemsAtlas, canvas: itemsCanvas } = await itemsAssetsParser.makeNewAtlas(
this.texturesVersion ?? this.version ?? 'latest',
(textureName) => {
const texture = this.customTextures?.items?.textures[textureName]
if (!texture) return
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())
// Initialize ItemsRenderer and WorldBlockProvider
if (this.version && this.blockstatesModels && this.itemsAtlasParser && this.blocksAtlasParser) {
this.itemsRenderer = new ItemsRenderer(
this.version,
this.blockstatesModels,
this.itemsAtlasParser,
this.blocksAtlasParser
)
this.worldBlockProvider = worldBlockProvider(
this.blockstatesModels,
this.blocksAtlasParser.atlas,
'latest'
)
}
// Emit event that textures were updated
this.emit('assetsTexturesUpdated')
return {
blocksAtlas,
itemsAtlas,
blocksCanvas,
itemsCanvas
}
}
async setVersion (version: string, texturesVersion?: string) {
this.version = version
this.texturesVersion = texturesVersion
await this.updateAssetsData()
}
}
export const appViewer = new AppViewer()
window.appViewer = appViewer
class ResourcesManager {
}
const modalStackUpdate = () => {
if (activeModalStack.length === 0 && !miscUiState.gameLoaded) {
// tood reset backend

View file

@ -372,7 +372,7 @@ export async function connect (connectOptions: ConnectOptions) {
await progress.executeWithMessage(
'Loading minecraft models',
async () => {
// viewer.world.blockstatesModels = await import('mc-assets/dist/blockStatesModels.json')
appViewer.resourcesManager.sourceBlockStatesModels ??= await import('mc-assets/dist/blockStatesModels.json')
// void viewer.setVersion(version, options.useVersionsTextures === 'latest' ? version : options.useVersionsTextures)
void appViewer.updateResources(version, createConsoleLogProgressReporter())
miscUiState.loadedDataVersion = version
@ -699,14 +699,14 @@ export async function connect (connectOptions: ConnectOptions) {
const start = Date.now()
let worldWasReady = false
void viewer.world.renderUpdateEmitter.on('update', () => {
// todo might not emit as servers simply don't send chunk if it's empty
if (!viewer.world.allChunksFinished || worldWasReady) return
worldWasReady = true
console.log('All chunks done and ready! Time from renderer open to ready', (Date.now() - start) / 1000, 's')
viewer.render() // ensure the last state is rendered
document.dispatchEvent(new Event('cypress-world-ready'))
})
// void viewer.world.renderUpdateEmitter.on('update', () => {
// // todo might not emit as servers simply don't send chunk if it's empty
// if (!viewer.world.allChunksFinished || worldWasReady) return
// worldWasReady = true
// console.log('All chunks done and ready! Time from renderer open to ready', (Date.now() - start) / 1000, 's')
// viewer.render() // ensure the last state is rendered
// document.dispatchEvent(new Event('cypress-world-ready'))
// })
const spawnEarlier = !singleplayer && !p2pMultiplayer
// don't use spawn event, player can be dead

View file

@ -41,7 +41,7 @@ export const onGameLoad = (onLoad) => {
version = bot.version
const checkIfLoaded = () => {
if (!viewer.world.itemsAtlasParser) return
if (!appViewer.resourcesManager.itemsAtlasParser) return
if (!allImagesLoadedState.value) {
onLoad?.()
}
@ -50,7 +50,7 @@ export const onGameLoad = (onLoad) => {
allImagesLoadedState.value = true
}, 0)
}
viewer.world.renderUpdateEmitter.on('textureDownloaded', checkIfLoaded)
appViewer.resourcesManager.on('assetsTexturesUpdated', checkIfLoaded)
checkIfLoaded()
PrismarineItem = PItem(version)

View file

@ -143,7 +143,7 @@ export default (bot: Bot) => {
bot.loadPlugin(createMouse({}))
domListeners(bot)
createDisplayManager(bot, viewer.scene, viewer.renderer)
// createDisplayManager(bot, viewer.scene, viewer.renderer)
otherListeners()
}

View file

@ -110,7 +110,7 @@ const HotbarInner = () => {
inv.canvas.style.pointerEvents = 'auto'
container.current.appendChild(inv.canvas)
const upHotbarItems = () => {
if (!viewer.world.currentTextureImage || !allImagesLoadedState.value) return
if (!appViewer.resourcesManager.itemsAtlasParser || !allImagesLoadedState.value) return
upInventoryItems(true, inv)
}
@ -124,7 +124,7 @@ const HotbarInner = () => {
upHotbarItems()
bot.inventory.on('updateSlot', upHotbarItems)
viewer.world.renderUpdateEmitter.on('textureDownloaded', upHotbarItems)
appViewer.resourcesManager.on('assetsTexturesUpdated', upHotbarItems)
const unsub2 = subscribe(allImagesLoadedState, () => {
upHotbarItems()
})
@ -197,7 +197,7 @@ const HotbarInner = () => {
inv.destroy()
controller.abort()
unsub2()
viewer.world.renderUpdateEmitter.off('textureDownloaded', upHotbarItems)
appViewer.resourcesManager.off('assetsTexturesUpdated', upHotbarItems)
}
}, [])

View file

@ -14,6 +14,7 @@ import { appReplacableResources, resourcesContentOriginal } from './generated/re
import { gameAdditionalState, miscUiState } from './globalState'
import { watchUnloadForCleanup } from './gameUnload'
import { createConsoleLogProgressReporter, createFullScreenProgressReporter, ProgressReporter } from './core/progressReporter'
import { appViewer } from './appViewer'
export const resourcePackState = proxy({
resourcePackInstalled: false,
@ -312,8 +313,8 @@ export const getResourcepackTiles = async (type: 'blocks' | 'items' | 'armor', e
}
const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) => {
viewer.world.customBlockStates = {}
viewer.world.customModels = {}
appViewer.resourcesManager.customBlockStates = {}
appViewer.resourcesManager.customModels = {}
const usedBlockTextures = new Set<string>()
const usedItemTextures = new Set<string>()
const basePath = await getActiveResourcepackBasePath()
@ -360,9 +361,9 @@ const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) =
const blockModelsPath = `${basePath}/assets/${namespaceDir}/models/block`
const itemModelsPath = `${basePath}/assets/${namespaceDir}/models/item`
Object.assign(viewer.world.customBlockStates!, await readModelData(blockstatesPath, 'blockstates', namespaceDir))
Object.assign(viewer.world.customModels!, await readModelData(blockModelsPath, 'models', namespaceDir))
Object.assign(viewer.world.customModels!, await readModelData(itemModelsPath, 'models', namespaceDir))
Object.assign(appViewer.resourcesManager.customBlockStates!, await readModelData(blockstatesPath, 'blockstates', namespaceDir))
Object.assign(appViewer.resourcesManager.customModels!, await readModelData(blockModelsPath, 'models', namespaceDir))
Object.assign(appViewer.resourcesManager.customModels!, await readModelData(itemModelsPath, 'models', namespaceDir))
}
try {
@ -372,8 +373,8 @@ const prepareBlockstatesAndModels = async (progressReporter: ProgressReporter) =
}
} catch (err) {
console.error('Failed to read some of resource pack blockstates and models', err)
viewer.world.customBlockStates = undefined
viewer.world.customModels = undefined
appViewer.resourcesManager.customBlockStates = undefined
appViewer.resourcesManager.customModels = undefined
}
return {
usedBlockTextures,
@ -523,38 +524,39 @@ const repeatArr = (arr, i) => Array.from({ length: i }, () => arr)
const updateTextures = async (progressReporter = createConsoleLogProgressReporter()) => {
currentErrors = []
const origBlocksFiles = Object.keys(viewer.world.sourceData.blocksAtlases.latest.textures)
const origItemsFiles = Object.keys(viewer.world.sourceData.itemsAtlases.latest.textures)
const origBlocksFiles = Object.keys(appViewer.resourcesManager.sourceBlocksAtlases.latest.textures)
const origItemsFiles = Object.keys(appViewer.resourcesManager.sourceItemsAtlases.latest.textures)
const origArmorFiles = Object.keys(armorTextures)
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 = {}
appViewer.resourcesManager.customTextures = {}
if (blocksData) {
viewer.world.customTextures.blocks = {
appViewer.resourcesManager.customTextures.blocks = {
tileSize: blocksData.firstTextureSize,
textures: blocksData.textures
}
}
if (itemsData) {
viewer.world.customTextures.items = {
appViewer.resourcesManager.customTextures.items = {
tileSize: itemsData.firstTextureSize,
textures: itemsData.textures
}
}
if (armorData) {
viewer.world.customTextures.armor = {
appViewer.resourcesManager.customTextures.armor = {
tileSize: armorData.firstTextureSize,
textures: armorData.textures
}
}
if (viewer.world.active) {
await viewer.world.updateAssetsData()
if (viewer.world instanceof WorldRendererThree) {
viewer.world.rerenderAllChunks?.()
if (appViewer.backend) {
await appViewer.resourcesManager.updateAssetsData()
if (appViewer.backend instanceof WorldRendererThree) {
appViewer.backend.rerenderAllChunks?.()
}
}
}