big resourcemanager refactor
This commit is contained in:
parent
1861edf567
commit
d197859d47
9 changed files with 218 additions and 147 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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') {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
154
src/appViewer.ts
154
src/appViewer.ts
|
|
@ -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
|
||||
|
|
|
|||
18
src/index.ts
18
src/index.ts
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
|
|
|||
|
|
@ -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?.()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue