fix: restore minimal and full map (#348)
Co-authored-by: gguio <nikvish150@gmail.com>
This commit is contained in:
parent
a9b94ec897
commit
785ab490f2
9 changed files with 85 additions and 40 deletions
|
|
@ -3,7 +3,8 @@ import { RendererReactiveState } from '../../src/appViewer'
|
|||
export const getDefaultRendererState = (): RendererReactiveState => {
|
||||
return {
|
||||
world: {
|
||||
chunksLoaded: [],
|
||||
chunksLoaded: new Set(),
|
||||
heightmaps: new Map(),
|
||||
chunksTotalNumber: 0,
|
||||
allChunksLoaded: true,
|
||||
mesherWork: false,
|
||||
|
|
|
|||
|
|
@ -148,6 +148,29 @@ const handleMessage = data => {
|
|||
global.postMessage({ type: 'customBlockModel', chunkKey, customBlockModel })
|
||||
break
|
||||
}
|
||||
case 'getHeightmap': {
|
||||
const heightmap = new Uint8Array(256)
|
||||
|
||||
const blockPos = new Vec3(0, 0, 0)
|
||||
for (let z = 0; z < 16; z++) {
|
||||
for (let x = 0; x < 16; x++) {
|
||||
const blockX = x + data.x
|
||||
const blockZ = z + data.z
|
||||
blockPos.x = blockX; blockPos.z = blockZ
|
||||
blockPos.y = 256
|
||||
let block = world.getBlock(blockPos)
|
||||
while (block?.name.includes('air')) {
|
||||
blockPos.y -= 1
|
||||
block = world.getBlock(blockPos)
|
||||
}
|
||||
const index = z * 16 + x
|
||||
heightmap[index] = block ? blockPos.y : 0
|
||||
}
|
||||
}
|
||||
postMessage({ type: 'heightmap', key: `${Math.floor(data.x / 16)},${Math.floor(data.z / 16)}`, heightmap })
|
||||
|
||||
break
|
||||
}
|
||||
// No default
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -510,7 +510,7 @@ export function getSectionGeometry (sx, sy, sz, world: World) {
|
|||
heads: {},
|
||||
signs: {},
|
||||
// isFull: true,
|
||||
highestBlocks: {},
|
||||
highestBlocks: new Map(),
|
||||
hadErrors: false,
|
||||
blocksCount: 0
|
||||
}
|
||||
|
|
@ -521,9 +521,9 @@ export function getSectionGeometry (sx, sy, sz, world: World) {
|
|||
for (cursor.x = sx; cursor.x < sx + 16; cursor.x++) {
|
||||
let block = world.getBlock(cursor, blockProvider, attr)!
|
||||
if (!INVISIBLE_BLOCKS.has(block.name)) {
|
||||
const highest = attr.highestBlocks[`${cursor.x},${cursor.z}`]
|
||||
const highest = attr.highestBlocks.get(`${cursor.x},${cursor.z}`)
|
||||
if (!highest || highest.y < cursor.y) {
|
||||
attr.highestBlocks[`${cursor.x},${cursor.z}`] = { y: cursor.y, stateId: block.stateId, biomeId: block.biome.id }
|
||||
attr.highestBlocks.set(`${cursor.x},${cursor.z}`, { y: cursor.y, stateId: block.stateId, biomeId: block.biome.id })
|
||||
}
|
||||
}
|
||||
if (INVISIBLE_BLOCKS.has(block.name)) continue
|
||||
|
|
|
|||
|
|
@ -40,12 +40,21 @@ export type MesherGeometryOutput = {
|
|||
heads: Record<string, any>,
|
||||
signs: Record<string, any>,
|
||||
// isFull: boolean
|
||||
highestBlocks: Record<string, HighestBlockInfo>
|
||||
highestBlocks: Map<string, HighestBlockInfo>
|
||||
hadErrors: boolean
|
||||
blocksCount: number
|
||||
customBlockModels?: CustomBlockModels
|
||||
}
|
||||
|
||||
export interface MesherMainEvents {
|
||||
geometry: { type: 'geometry'; key: string; geometry: MesherGeometryOutput; workerIndex: number };
|
||||
sectionFinished: { type: 'sectionFinished'; key: string; workerIndex: number; processTime?: number };
|
||||
blockStateModelInfo: { type: 'blockStateModelInfo'; info: Record<string, BlockStateModelInfo> };
|
||||
heightmap: { type: 'heightmap'; key: string; heightmap: Uint8Array };
|
||||
}
|
||||
|
||||
export type MesherMainEvent = MesherMainEvents[keyof MesherMainEvents]
|
||||
|
||||
export type HighestBlockInfo = { y: number, stateId: number | undefined, biomeId: number | undefined }
|
||||
|
||||
export type BlockStateModelInfo = {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { ResourcesManager } from '../../../src/resourcesManager'
|
|||
import { DisplayWorldOptions, GraphicsInitOptions, RendererReactiveState } from '../../../src/appViewer'
|
||||
import { SoundSystem } from '../three/threeJsSound'
|
||||
import { buildCleanupDecorator } from './cleanupDecorator'
|
||||
import { HighestBlockInfo, MesherGeometryOutput, CustomBlockModels, BlockStateModelInfo, getBlockAssetsCacheKey, MesherConfig } from './mesher/shared'
|
||||
import { HighestBlockInfo, MesherGeometryOutput, CustomBlockModels, BlockStateModelInfo, getBlockAssetsCacheKey, MesherConfig, MesherMainEvent } from './mesher/shared'
|
||||
import { chunkPos } from './simpleUtils'
|
||||
import { addNewStat, removeAllStats, removeStat, updatePanesVisibility, updateStatText } from './ui/newStats'
|
||||
import { WorldDataEmitter } from './worldDataEmitter'
|
||||
|
|
@ -85,6 +85,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
dirty (pos: Vec3, value: boolean): void
|
||||
update (/* pos: Vec3, value: boolean */): void
|
||||
chunkFinished (key: string): void
|
||||
heightmap (key: string, heightmap: Uint8Array): void
|
||||
}>
|
||||
customTexturesDataUrl = undefined as string | undefined
|
||||
workers: any[] = []
|
||||
|
|
@ -103,8 +104,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
ONMESSAGE_TIME_LIMIT = 30 // ms
|
||||
|
||||
handleResize = () => { }
|
||||
highestBlocksByChunks = {} as Record<string, { [chunkKey: string]: HighestBlockInfo }>
|
||||
highestBlocksBySections = {} as Record<string, { [sectionKey: string]: HighestBlockInfo }>
|
||||
highestBlocksByChunks = new Map<string, { [chunkKey: string]: HighestBlockInfo }>()
|
||||
highestBlocksBySections = new Map<string, { [sectionKey: string]: HighestBlockInfo }>()
|
||||
blockEntities = {}
|
||||
|
||||
workersProcessAverageTime = 0
|
||||
|
|
@ -247,7 +248,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
}
|
||||
|
||||
async getHighestBlocks (chunkKey: string) {
|
||||
return this.highestBlocksByChunks[chunkKey]
|
||||
return this.highestBlocksByChunks.get(chunkKey)
|
||||
}
|
||||
|
||||
updateCustomBlock (chunkKey: string, blockPos: string, model: string) {
|
||||
|
|
@ -365,19 +366,20 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
this.isProcessingQueue = false
|
||||
}
|
||||
|
||||
handleMessage (data) {
|
||||
handleMessage (rawData: any) {
|
||||
const data = rawData as MesherMainEvent
|
||||
if (!this.active) return
|
||||
this.mesherLogReader?.workerMessageReceived(data.type, data)
|
||||
if (data.type !== 'geometry' || !this.debugStopGeometryUpdate) {
|
||||
const start = performance.now()
|
||||
this.handleWorkerMessage(data)
|
||||
this.handleWorkerMessage(data as WorkerReceive)
|
||||
this.workerCustomHandleTime += performance.now() - start
|
||||
}
|
||||
if (data.type === 'geometry') {
|
||||
this.logWorkerWork(() => `-> ${data.workerIndex} geometry ${data.key} ${JSON.stringify({ dataSize: JSON.stringify(data).length })}`)
|
||||
this.geometryReceiveCount[data.workerIndex] ??= 0
|
||||
this.geometryReceiveCount[data.workerIndex]++
|
||||
const geometry = data.geometry as MesherGeometryOutput
|
||||
const { geometry } = data
|
||||
this.highestBlocksBySections[data.key] = geometry.highestBlocks
|
||||
const chunkCoords = data.key.split(',').map(Number)
|
||||
this.lastChunkDistance = Math.max(...this.getDistance(new Vec3(chunkCoords[0], 0, chunkCoords[2])))
|
||||
|
|
@ -404,6 +406,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
if (loaded) {
|
||||
// CHUNK FINISHED
|
||||
this.finishedChunks[chunkKey] = true
|
||||
this.reactiveState.world.chunksLoaded.add(`${Math.floor(chunkCoords[0] / 16)},${Math.floor(chunkCoords[2] / 16)}`)
|
||||
this.renderUpdateEmitter.emit(`chunkFinished`, `${chunkCoords[0]},${chunkCoords[2]}`)
|
||||
this.checkAllFinished()
|
||||
// merge highest blocks by sections into highest blocks by chunks
|
||||
|
|
@ -442,6 +445,10 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
this.blockStateModelInfo.set(cacheKey, info)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.type === 'heightmap') {
|
||||
appViewer.rendererState.world.heightmaps.set(data.key, new Uint8Array(data.heightmap))
|
||||
}
|
||||
}
|
||||
|
||||
downloadMesherLog () {
|
||||
|
|
@ -599,7 +606,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
|
||||
updateChunksStats () {
|
||||
const loadedChunks = Object.keys(this.finishedChunks)
|
||||
this.displayOptions.nonReactiveState.world.chunksLoaded = loadedChunks
|
||||
this.displayOptions.nonReactiveState.world.chunksLoaded = new Set(loadedChunks)
|
||||
this.displayOptions.nonReactiveState.world.chunksTotalNumber = this.chunksLength
|
||||
this.reactiveState.world.allChunksLoaded = this.allChunksFinished
|
||||
|
||||
|
|
@ -628,6 +635,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
customBlockModels: customBlockModels || undefined
|
||||
})
|
||||
}
|
||||
this.workers[0].postMessage({
|
||||
type: 'getHeightmap',
|
||||
x,
|
||||
z,
|
||||
})
|
||||
this.logWorkerWork(() => `-> chunk ${JSON.stringify({ x, z, chunkLength: chunk.length, customBlockModelsLength: customBlockModels ? Object.keys(customBlockModels).length : 0 })}`)
|
||||
this.mesherLogReader?.chunkReceived(x, z, chunk.length)
|
||||
for (let y = this.worldMinYRender; y < this.worldSizeParams.worldHeight; y += 16) {
|
||||
|
|
@ -664,9 +676,9 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
for (let y = this.worldSizeParams.minY; y < this.worldSizeParams.worldHeight; y += 16) {
|
||||
this.setSectionDirty(new Vec3(x, y, z), false)
|
||||
delete this.finishedSections[`${x},${y},${z}`]
|
||||
delete this.highestBlocksBySections[`${x},${y},${z}`]
|
||||
this.highestBlocksBySections.delete(`${x},${y},${z}`)
|
||||
}
|
||||
delete this.highestBlocksByChunks[`${x},${z}`]
|
||||
this.highestBlocksByChunks.delete(`${x},${z}`)
|
||||
|
||||
this.updateChunksStats()
|
||||
|
||||
|
|
@ -992,7 +1004,6 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|||
this.active = false
|
||||
|
||||
this.renderUpdateEmitter.removeAllListeners()
|
||||
this.displayOptions.worldView.removeAllListeners() // todo
|
||||
this.abortController.abort()
|
||||
removeAllStats()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import { watchOptionsAfterWorldViewInit } from './watchOptions'
|
|||
|
||||
export interface RendererReactiveState {
|
||||
world: {
|
||||
chunksLoaded: string[]
|
||||
chunksLoaded: Set<string>
|
||||
heightmaps: Map<string, Uint8Array>
|
||||
chunksTotalNumber: number
|
||||
allChunksLoaded: boolean
|
||||
mesherWork: boolean
|
||||
|
|
@ -28,7 +29,7 @@ export interface RendererReactiveState {
|
|||
}
|
||||
export interface NonReactiveState {
|
||||
world: {
|
||||
chunksLoaded: string[]
|
||||
chunksLoaded: Set<string>
|
||||
chunksTotalNumber: number
|
||||
allChunksLoaded: boolean
|
||||
mesherWork: boolean
|
||||
|
|
|
|||
|
|
@ -708,7 +708,7 @@ export async function connect (connectOptions: ConnectOptions) {
|
|||
resolve()
|
||||
unsub()
|
||||
} else {
|
||||
const perc = Math.round(appViewer.rendererState.world.chunksLoaded.length / appViewer.rendererState.world.chunksTotalNumber * 100)
|
||||
const perc = Math.round(appViewer.rendererState.world.chunksLoaded.size / appViewer.rendererState.world.chunksTotalNumber * 100)
|
||||
progress?.reportProgress('chunks', perc / 100)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ export default (
|
|||
top: '0px',
|
||||
padding: '5px 5px 0px 0px',
|
||||
textAlign: 'center',
|
||||
zIndex: 7,
|
||||
}}
|
||||
onClick={() => {
|
||||
toggleFullMap?.()
|
||||
|
|
@ -106,7 +107,7 @@ export default (
|
|||
textShadow: '0.1em 0 black, 0 0.1em black, -0.1em 0 black, 0 -0.1em black, -0.1em -0.1em black, -0.1em 0.1em black, 0.1em -0.1em black, 0.1em 0.1em black'
|
||||
}}
|
||||
>
|
||||
{position.x.toFixed(2)} {position.y.toFixed(2)} {position.z.toFixed(2)}
|
||||
{Math.round(position.x)} {Math.round(position.y)} {Math.round(position.z)}
|
||||
</div>
|
||||
</div> : null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { Chunk } from 'prismarine-world/types/world'
|
|||
import { Block } from 'prismarine-block'
|
||||
import { INVISIBLE_BLOCKS } from 'renderer/viewer/lib/mesher/worldConstants'
|
||||
import { getRenamedData } from 'flying-squid/dist/blockRenames'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useSnapshot, subscribe } from 'valtio'
|
||||
import { subscribeKey } from 'valtio/utils'
|
||||
import { getThreeJsRendererMethods } from 'renderer/viewer/three/threeJsMethods'
|
||||
import BlockData from '../../renderer/viewer/lib/moreBlockDataGenerated.json'
|
||||
|
|
@ -42,7 +42,7 @@ export class DrawerAdapterImpl extends TypedEventEmitter<MapUpdates> implements
|
|||
yaw: number
|
||||
world: string
|
||||
warps: WorldWarp[] = gameAdditionalState.warps
|
||||
chunksStore = new Map<string, undefined | null | 'requested' | ChunkInfo >()
|
||||
chunksStore = new Map<string, undefined | null | 'requested' | ChunkInfo>()
|
||||
loadingChunksQueue = new Set<string>()
|
||||
loadChunk: (key: string) => Promise<void> = this.loadChunkMinimap
|
||||
mapDrawer = new MinimapDrawer(this.loadChunk, this.warps, this.loadingChunksQueue, this.chunksStore)
|
||||
|
|
@ -119,9 +119,9 @@ export class DrawerAdapterImpl extends TypedEventEmitter<MapUpdates> implements
|
|||
this.blockData.set(renamedKey, BlockData.colors[blockKey])
|
||||
}
|
||||
|
||||
subscribeKey(appViewer.rendererState, 'world', () => {
|
||||
subscribe(appViewer.rendererState.world, () => {
|
||||
for (const key of this.loadingChunksQueue) {
|
||||
if (appViewer.rendererState.world.chunksLoaded.includes(key)) {
|
||||
if (appViewer.rendererState.world.chunksLoaded.has(key)) {
|
||||
this.loadingChunksQueue.delete(key)
|
||||
void this.loadChunk(key)
|
||||
}
|
||||
|
|
@ -205,33 +205,31 @@ export class DrawerAdapterImpl extends TypedEventEmitter<MapUpdates> implements
|
|||
const [chunkX, chunkZ] = key.split(',').map(Number)
|
||||
const chunkWorldX = chunkX * 16
|
||||
const chunkWorldZ = chunkZ * 16
|
||||
if (appViewer.rendererState.world.chunksLoaded.includes(`${chunkWorldX},${chunkWorldZ}`)) {
|
||||
const highestBlocks = await getThreeJsRendererMethods()?.getHighestBlocks(`${chunkWorldX},${chunkWorldZ}`)
|
||||
if (!highestBlocks) return undefined
|
||||
const heightmap = new Uint8Array(256)
|
||||
if (appViewer.rendererState.world.chunksLoaded.has(key)) {
|
||||
// console.log('[MinimapProvider] loading chunk for minimap', key)
|
||||
const heightmap = appViewer.rendererState.world.heightmaps.get(key)
|
||||
if (heightmap) {
|
||||
// console.log('[MinimapProvider] did get highest blocks')
|
||||
} else {
|
||||
console.warn('[MinimapProvider] no highestBlocks from renderMethods')
|
||||
return undefined
|
||||
}
|
||||
const colors = Array.from({ length: 256 }).fill('') as string[]
|
||||
// avoid creating new object every time
|
||||
const blockPos = new Vec3(0, 0, 0)
|
||||
// filling up colors and heightmap
|
||||
// filling up colors
|
||||
for (let z = 0; z < 16; z += 1) {
|
||||
for (let x = 0; x < 16; x += 1) {
|
||||
const blockX = chunkWorldX + x
|
||||
const blockZ = chunkWorldZ + z
|
||||
const hBlock = highestBlocks[`${blockX},${blockZ}`]
|
||||
blockPos.x = blockX; blockPos.z = blockZ; blockPos.y = hBlock?.y ?? 0
|
||||
let block = bot.world.getBlock(blockPos)
|
||||
while (block?.name.includes('air')) {
|
||||
blockPos.y -= 1
|
||||
block = bot.world.getBlock(blockPos)
|
||||
}
|
||||
const index = z * 16 + x
|
||||
blockPos.x = blockX; blockPos.z = blockZ; blockPos.y = heightmap[index]
|
||||
const block = bot.world.getBlock(blockPos)
|
||||
// blocks which are not set are shown as half transparent
|
||||
if (!block || !hBlock) {
|
||||
heightmap[index] = 0
|
||||
if (!block) {
|
||||
colors[index] = 'rgba(0, 0, 0, 0.5)'
|
||||
continue
|
||||
}
|
||||
heightmap[index] = block.position.y
|
||||
colors[index] = this.setColor(block)
|
||||
}
|
||||
}
|
||||
|
|
@ -242,6 +240,7 @@ export class DrawerAdapterImpl extends TypedEventEmitter<MapUpdates> implements
|
|||
} else {
|
||||
this.loadingChunksQueue.add(`${chunkX},${chunkZ}`)
|
||||
this.chunksStore.set(key, 'requested')
|
||||
// console.log('[MinimapProvider] requested new chunk', key)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -339,7 +338,7 @@ export class DrawerAdapterImpl extends TypedEventEmitter<MapUpdates> implements
|
|||
const chunkWorldX = chunkX * 16
|
||||
const chunkWorldZ = chunkZ * 16
|
||||
const highestBlocks = await getThreeJsRendererMethods()?.getHighestBlocks(`${chunkWorldX},${chunkWorldZ}`)
|
||||
if (appViewer.rendererState.world.chunksLoaded.includes(`${chunkWorldX},${chunkWorldZ}`)) {
|
||||
if (appViewer.rendererState.world.chunksLoaded.has(`${chunkWorldX},${chunkWorldZ}`)) {
|
||||
const heightmap = new Uint8Array(256)
|
||||
const colors = Array.from({ length: 256 }).fill('') as string[]
|
||||
if (!highestBlocks) return null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue