From 9a8ff32f5ac1bdbfa486cd2a32d2ca2fe11ac22c Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Wed, 17 Apr 2024 05:34:20 +0300 Subject: [PATCH] a few minor but important world renderer fixes & impr --- cypress.config.ts | 23 ++++++++++++++++++- prismarine-viewer/viewer/lib/mesher/mesher.ts | 5 ++++ prismarine-viewer/viewer/lib/mesher/world.ts | 2 +- prismarine-viewer/viewer/lib/viewer.ts | 1 + prismarine-viewer/viewer/lib/viewerWrapper.ts | 6 +---- .../viewer/lib/worldrendererCommon.ts | 8 +++++++ .../viewer/lib/worldrendererThree.ts | 7 ++++++ src/index.ts | 10 ++++++-- src/soundSystem.ts | 1 - src/utils.ts | 12 ++++++++-- 10 files changed, 63 insertions(+), 12 deletions(-) diff --git a/cypress.config.ts b/cypress.config.ts index ae01d42f..f9bd9478 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -6,7 +6,28 @@ export default defineConfig({ e2e: { // We've imported your old cypress plugins here. // You may want to clean this up later by importing these. - setupNodeEvents(on, config) { + setupNodeEvents (on, config) { + // https://medium.com/automation-with-donald/get-memory-consumption-of-web-app-with-cypress-84e2656e5a0f + on('before:browser:launch', (browser = { + name: "", + family: "chromium", + channel: "", + displayName: "", + version: "", + majorVersion: "", + path: "", + isHeaded: false, + isHeadless: false + }, launchOptions) => { + if (browser.family === 'chromium' && browser.name !== 'electron') { + // auto open devtools + launchOptions.args.push('--enable-precise-memory-info') + } + + return launchOptions + + }) + return require('./cypress/plugins/index.js')(on, config) }, baseUrl: 'http://localhost:8080', diff --git a/prismarine-viewer/viewer/lib/mesher/mesher.ts b/prismarine-viewer/viewer/lib/mesher/mesher.ts index a51b2da7..28859a3e 100644 --- a/prismarine-viewer/viewer/lib/mesher/mesher.ts +++ b/prismarine-viewer/viewer/lib/mesher/mesher.ts @@ -38,6 +38,10 @@ function setSectionDirty (pos, value = true) { } } +const softCleanup = () => { + world.blockCache = {} +} + self.onmessage = ({ data }) => { const globalVar: any = globalThis @@ -58,6 +62,7 @@ self.onmessage = ({ data }) => { world.addColumn(data.x, data.z, data.chunk) } else if (data.type === 'unloadChunk') { world.removeColumn(data.x, data.z) + if (Object.keys(world.columns).length === 0) softCleanup() } else if (data.type === 'blockUpdate') { const loc = new Vec3(data.pos.x, data.pos.y, data.pos.z).floored() world.setBlockStateId(loc, data.stateId) diff --git a/prismarine-viewer/viewer/lib/mesher/world.ts b/prismarine-viewer/viewer/lib/mesher/world.ts index eebe8cc7..c27d2b8d 100644 --- a/prismarine-viewer/viewer/lib/mesher/world.ts +++ b/prismarine-viewer/viewer/lib/mesher/world.ts @@ -127,8 +127,8 @@ export class World { // todo export in chunk instead const hasChunkSection = (column, pos) => { if (column._getSection) return column._getSection(pos) - if (column.sections) return column.sections[pos.y >> 4] if (column.skyLightSections) return column.skyLightSections[getLightSectionIndex(pos, column.minY)] + if (column.sections) return column.sections[pos.y >> 4] } function posInChunk (pos) { diff --git a/prismarine-viewer/viewer/lib/viewer.ts b/prismarine-viewer/viewer/lib/viewer.ts index f0c7e79b..85c15719 100644 --- a/prismarine-viewer/viewer/lib/viewer.ts +++ b/prismarine-viewer/viewer/lib/viewer.ts @@ -168,6 +168,7 @@ export class Viewer { emitter.on('renderDistance', (d) => { this.world.viewDistance = d this.world.chunksLength = d === 0 ? 1 : generateSpiralMatrix(d).length + this.world.allChunksFinished = Object.keys(this.world.finishedChunks).length === this.world.chunksLength }) emitter.on('updateLight', ({ pos }) => { diff --git a/prismarine-viewer/viewer/lib/viewerWrapper.ts b/prismarine-viewer/viewer/lib/viewerWrapper.ts index 43604231..c6419dff 100644 --- a/prismarine-viewer/viewer/lib/viewerWrapper.ts +++ b/prismarine-viewer/viewer/lib/viewerWrapper.ts @@ -111,10 +111,6 @@ export class ViewerWrapper { if (this.renderer) { this.renderer.setSize(width, height) } - // canvas updated by renderer - - // if (viewer.composer) { - // viewer.updateComposerSize() - // } + viewer.world.handleResize() } } diff --git a/prismarine-viewer/viewer/lib/worldrendererCommon.ts b/prismarine-viewer/viewer/lib/worldrendererCommon.ts index 7e4d5cab..08dfa2c7 100644 --- a/prismarine-viewer/viewer/lib/worldrendererCommon.ts +++ b/prismarine-viewer/viewer/lib/worldrendererCommon.ts @@ -40,6 +40,8 @@ export abstract class WorldRendererCommon skyLight = 15 smoothLighting = true enableLighting = true + allChunksFinished = false + handleResize = () => { } abstract outputFormat: 'threeJs' | 'webgl' @@ -76,6 +78,7 @@ export abstract class WorldRendererCommon const allFinished = Object.keys(this.finishedChunks).length === this.chunksLength if (allFinished) { this.allChunksLoaded?.() + this.allChunksFinished = true } } @@ -127,6 +130,8 @@ export abstract class WorldRendererCommon this.active = false this.loadedChunks = {} this.sectionsOutstanding = new Map() + this.finishedChunks = {} + this.allChunksFinished = false for (const worker of this.workers) { worker.postMessage({ type: 'reset' }) } @@ -198,6 +203,8 @@ export abstract class WorldRendererCommon for (const worker of this.workers) { worker.postMessage({ type: 'unloadChunk', x, z }) } + this.allChunksFinished = Object.keys(this.finishedChunks).length === this.chunksLength + delete this.finishedChunks[`${x},${z}`] } setBlockStateId (pos: Vec3, stateId: number) { @@ -215,6 +222,7 @@ export abstract class WorldRendererCommon setSectionDirty (pos: Vec3, value = true) { if (this.viewDistance === -1) throw new Error('viewDistance not set') + this.allChunksFinished = false const distance = this.getDistance(pos) if (distance[0] > this.viewDistance || distance[1] > this.viewDistance) return const key = `${Math.floor(pos.x / 16) * 16},${Math.floor(pos.y / 16) * 16},${Math.floor(pos.z / 16) * 16}` diff --git a/prismarine-viewer/viewer/lib/worldrendererThree.ts b/prismarine-viewer/viewer/lib/worldrendererThree.ts index 0e9c7982..05d9a76f 100644 --- a/prismarine-viewer/viewer/lib/worldrendererThree.ts +++ b/prismarine-viewer/viewer/lib/worldrendererThree.ts @@ -7,6 +7,7 @@ import { renderSign } from '../sign-renderer/' import { chunkPos, sectionPos } from './simpleUtils' import { WorldRendererCommon } from './worldrendererCommon' import * as tweenJs from '@tweenjs/tween.js' +import { BloomPass, RenderPass, UnrealBloomPass, EffectComposer, WaterPass, GlitchPass } from 'three-stdlib' function mod (x, n) { return ((x % n) + n) % n @@ -20,6 +21,10 @@ export class WorldRendererThree extends WorldRendererCommon { chunkTextures = new Map() signsCache = new Map() + get tilesRendered () { + return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).tilesCount, 0) + } + constructor(public scene: THREE.Scene, public renderer: THREE.WebGLRenderer, public camera: THREE.PerspectiveCamera, numWorkers = 4) { super(numWorkers) } @@ -87,6 +92,8 @@ export class WorldRendererThree extends WorldRendererCommon { boxHelper.name = 'helper' object.add(boxHelper) object.name = 'chunk' + //@ts-ignore + object.tilesCount = data.geometry.positions.length / 3 / 4 if (!this.showChunkBorders) { boxHelper.visible = false } diff --git a/src/index.ts b/src/index.ts index 0476a665..c6981fd3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -759,8 +759,14 @@ async function connect (connectOptions: { if (appStatusState.isError) return setLoadingScreenStatus(undefined) - void viewer.waitForChunksToRender().then(() => { - console.log('All done and ready!') + const start = Date.now() + let done = 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 || done) return + done = true + console.log('All done and ready! In', (Date.now() - start) / 1000, 's') + viewer.render() // ensure the last state is rendered document.dispatchEvent(new Event('cypress-world-ready')) }) }) diff --git a/src/soundSystem.ts b/src/soundSystem.ts index 09c87d4b..e48f8e60 100644 --- a/src/soundSystem.ts +++ b/src/soundSystem.ts @@ -40,7 +40,6 @@ subscribeKey(miscUiState, 'gameLoaded', async () => { return } if (!options.volume) return - console.debug('play sound', soundId) const parts = soundString.split(';') const soundVolume = +parts[0]! const soundName = parts[1]! diff --git a/src/utils.ts b/src/utils.ts index 39bf940e..fc73514c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -161,11 +161,19 @@ let prevRenderDistance = options.renderDistance export const setRenderDistance = () => { assertDefined(worldView) const { renderDistance: singleplayerRenderDistance, multiplayerRenderDistance } = options - const renderDistance = miscUiState.singleplayer ? singleplayerRenderDistance : multiplayerRenderDistance + let renderDistance = miscUiState.singleplayer ? singleplayerRenderDistance : multiplayerRenderDistance + const zeroRenderDistance = miscUiState.singleplayer && renderDistance === 0 + if (zeroRenderDistance) { + renderDistance = 1 // mineflayer limitation workaround + } bot.setSettings({ viewDistance: renderDistance }) - worldView.viewDistance = renderDistance + if (zeroRenderDistance) { + localServer!.players[0].view = 0 + renderDistance = 0 + } + worldView.updateViewDistance(renderDistance) prevRenderDistance = renderDistance } export const reloadChunks = async () => {