From e9aa0e989177f02473ab52bc13317ae650bd7e5c Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Jun 2024 09:25:57 +0300 Subject: [PATCH 01/18] initial implementation that should work already! --- .github/workflows/benchmark.yml | 41 +++++++++++ cypress.config.ts | 4 +- cypress/e2e/rendering_performance.spec.ts | 37 ++++++++++ package.json | 3 +- pnpm-lock.yaml | 33 +++++++-- prismarine-viewer/viewer/lib/viewerWrapper.ts | 2 + .../viewer/lib/worldrendererThree.ts | 20 ++--- src/benchmark.ts | 73 +++++++++++++++++++ src/benchmarkAdapter.ts | 7 ++ src/browserfs.ts | 5 +- src/connect.ts | 29 +++++--- src/downloadAndOpenFile.ts | 21 +++--- src/index.ts | 24 ++++-- src/loadSave.ts | 6 +- 14 files changed, 253 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/benchmark.yml create mode 100644 cypress/e2e/rendering_performance.spec.ts create mode 100644 src/benchmark.ts create mode 100644 src/benchmarkAdapter.ts diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000..b98d140b --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,41 @@ +name: Vercel Deploy Preview +env: + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} +on: + issue_comment: + types: [created] + push: + branches: + - perf-test +jobs: + deploy: + runs-on: ubuntu-latest + # todo skip already created deploys on that commit + if: >- + github.event.issue.pull_request != '' && + ( + contains(github.event.comment.body, '/benchmark') + ) + permissions: + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + ref: refs/pull/${{ github.event.issue.number }}/head + - run: npm i -g pnpm@9.0.4 + - uses: actions/setup-node@v4 + with: + node-version: 18 + cache: "pnpm" + - run: pnpm install + - run: pnpm build + - run: pnpm test:benchmark + # read benchmark results from stdout + - run: echo "BENCHMARK_RESULT=$(cat benchmark.txt)" >> $GITHUB_ENV + - uses: mshick/add-pr-comment@v2 + with: + allow-repeats: true + message: | + Benchmark result: ${{ env.BENCHMARK_RESULT }} diff --git a/cypress.config.ts b/cypress.config.ts index f9bd9478..7c4098a9 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,5 +1,7 @@ import { defineConfig } from 'cypress' +const isPerformanceTest = process.env.PERFORMANCE_TEST === 'true' + export default defineConfig({ video: false, chromeWebSecurity: false, @@ -31,7 +33,7 @@ export default defineConfig({ return require('./cypress/plugins/index.js')(on, config) }, baseUrl: 'http://localhost:8080', - specPattern: 'cypress/e2e/**/*.spec.ts', + specPattern: !isPerformanceTest ? 'cypress/e2e/**/*.spec.ts' : 'cypress/e2e/rendering_performance.spec.ts', excludeSpecPattern: ['**/__snapshots__/*', '**/__image_snapshots__/*'], }, }) diff --git a/cypress/e2e/rendering_performance.spec.ts b/cypress/e2e/rendering_performance.spec.ts new file mode 100644 index 00000000..4a7390ab --- /dev/null +++ b/cypress/e2e/rendering_performance.spec.ts @@ -0,0 +1,37 @@ +/// +import { BenchmarkAdapter } from '../../src/benchmarkAdapter' +import { setOptions, cleanVisit, visit } from './shared' + +it('Benchmark rendering performance', () => { + cleanVisit('/?openBenchmark=true&renderDistance=5') + // wait for render end event + return cy.document().then({ timeout: 120_000 }, doc => { + return new Cypress.Promise(resolve => { + cy.log('Waiting for world to load') + doc.addEventListener('cypress-world-ready', resolve) + }).then(() => { + cy.log('World loaded') + }) + }).then(() => { + cy.window().then(win => { + const adapter = win.benchmarkAdapter as BenchmarkAdapter + const renderTimeWorst = adapter.worstRenderTime + const renderTimeAvg = adapter.averageRenderTime + const fpsWorst = 1000 / renderTimeWorst + const fpsAvg = 1000 / renderTimeAvg + const totalTime = adapter.worldLoadTime + + const messages = [ + `Worst FPS: ${fpsWorst.toFixed(2)}`, + `Average FPS: ${fpsAvg.toFixed(2)}`, + `Total time: ${totalTime.toFixed(2)}s`, + `Memory usage average: ${adapter.memoryUsageAverage.toFixed(2)}MB`, + `Memory usage worst: ${adapter.memoryUsageWorst.toFixed(2)}MB`, + ] + for (const message of messages) { + cy.log(message) + } + cy.writeFile('benchmark.txt', messages.join('\n')) + }) + }) +}) diff --git a/package.json b/package.json index c0342260..f3b68bf4 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build": "node scripts/build.js copyFiles && node scripts/prepareData.mjs -f && node esbuild.mjs --minify --prod", "check-build": "tsc && pnpm build", "test:cypress": "cypress run", + "test:benchmark": "PERFORMANCE_TEST=true cypress run", "test-unit": "vitest", "test:e2e": "start-test http-get://localhost:8080 test:cypress", "prod-start": "node server.js", @@ -63,7 +64,7 @@ "esbuild-plugin-polyfill-node": "^0.3.0", "express": "^4.18.2", "filesize": "^10.0.12", - "flying-squid": "npm:@zardoy/flying-squid@^0.0.29", + "flying-squid": "npm:@zardoy/flying-squid@^0.0.32", "fs-extra": "^11.1.1", "google-drive-browserfs": "github:zardoy/browserfs#google-drive", "iconify-icon": "^1.0.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f2b4809..a2222096 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,8 +107,8 @@ importers: specifier: ^10.0.12 version: 10.0.12 flying-squid: - specifier: npm:@zardoy/flying-squid@^0.0.29 - version: '@zardoy/flying-squid@0.0.29(encoding@0.1.13)' + specifier: npm:@zardoy/flying-squid@^0.0.32 + version: '@zardoy/flying-squid@0.0.32(encoding@0.1.13)' fs-extra: specifier: ^11.1.1 version: 11.1.1 @@ -3078,8 +3078,8 @@ packages: resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==} engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'} - '@zardoy/flying-squid@0.0.29': - resolution: {integrity: sha512-E5Nk1gMeH+fAHM5aJY8kIxjBS/zuPtPD6QPeZg+laPV5H58Jx3Et17clF1zC9MT2wyFQ5wi5uTnfdGBTpSEqHw==} + '@zardoy/flying-squid@0.0.32': + resolution: {integrity: sha512-Ifj8XrnsE3j3+lCeyUQ426LzsOzU/Z+qKG+aZNf90VstBhCvjmVAOmG7J5N74ivvujx+x6eXCgjjw6gcd/XKNQ==} engines: {node: '>=8'} hasBin: true @@ -6750,6 +6750,11 @@ packages: version: 1.35.0 engines: {node: '>=14'} + prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef: + resolution: {tarball: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef} + version: 1.35.0 + engines: {node: '>=14'} + prismarine-entity@2.3.1: resolution: {integrity: sha512-HOv8l7IetHNf4hwZ7V/W4vM3GNl+e6VCtKDkH9h02TRq7jWngsggKtJV+VanCce/sNwtJUhJDjORGs728ep4MA==} @@ -11974,7 +11979,7 @@ snapshots: '@types/emscripten': 1.39.8 tslib: 1.14.1 - '@zardoy/flying-squid@0.0.29(encoding@0.1.13)': + '@zardoy/flying-squid@0.0.32(encoding@0.1.13)': dependencies: '@tootallnate/once': 2.0.0 change-case: 4.1.2 @@ -11989,7 +11994,7 @@ snapshots: mkdirp: 2.1.6 node-gzip: 1.1.2 node-rsa: 1.1.1 - prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/9662306deea57d8d0ba0a2a3f3f7adb95f0131e3(minecraft-data@3.65.0) + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.65.0) prismarine-entity: 2.3.1 prismarine-item: 1.14.0 prismarine-nbt: 2.5.0 @@ -12000,6 +12005,7 @@ snapshots: random-seed: 0.3.0 range: 0.0.3 readline: 1.3.0 + sanitize-filename: 1.6.3 typed-emitter: 1.4.0 uuid-1345: 1.0.2 vec3: 0.1.8 @@ -13263,7 +13269,7 @@ snapshots: diamond-square@https://codeload.github.com/zardoy/diamond-square/tar.gz/4bbe28dcad35403abaa925055e91f601a61b9015: dependencies: minecraft-data: 3.65.0 - prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/9662306deea57d8d0ba0a2a3f3f7adb95f0131e3(minecraft-data@3.65.0) + prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.65.0) prismarine-registry: 1.7.0 random-seed: 0.3.0 vec3: 0.1.8 @@ -16592,6 +16598,19 @@ snapshots: transitivePeerDependencies: - minecraft-data + prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/cea0b6c792d7dcbb69dfd20fa48be5fd60ce83ef(minecraft-data@3.65.0): + dependencies: + prismarine-biome: 1.3.0(minecraft-data@3.65.0)(prismarine-registry@1.7.0) + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/dd4954fff3b334f8ce063d18e39b2e9414ece5b8 + prismarine-nbt: 2.5.0 + prismarine-registry: 1.7.0 + smart-buffer: 4.2.0 + uint4: 0.1.2 + vec3: 0.1.8 + xxhash-wasm: 0.4.2 + transitivePeerDependencies: + - minecraft-data + prismarine-entity@2.3.1: dependencies: prismarine-chat: 1.10.1 diff --git a/prismarine-viewer/viewer/lib/viewerWrapper.ts b/prismarine-viewer/viewer/lib/viewerWrapper.ts index 57317f42..3b042f4a 100644 --- a/prismarine-viewer/viewer/lib/viewerWrapper.ts +++ b/prismarine-viewer/viewer/lib/viewerWrapper.ts @@ -52,9 +52,11 @@ export class ViewerWrapper { windowFocused = true trackWindowFocus () { window.addEventListener('focus', () => { + console.log('window focused') this.windowFocused = true }) window.addEventListener('blur', () => { + console.log('window blurred') this.windowFocused = false }) } diff --git a/prismarine-viewer/viewer/lib/worldrendererThree.ts b/prismarine-viewer/viewer/lib/worldrendererThree.ts index cc89a823..9f5b94b5 100644 --- a/prismarine-viewer/viewer/lib/worldrendererThree.ts +++ b/prismarine-viewer/viewer/lib/worldrendererThree.ts @@ -17,6 +17,8 @@ export class WorldRendererThree extends WorldRendererCommon { signsCache = new Map() starField: StarField cameraSectionPos: Vec3 = new Vec3(0, 0, 0) + worstRenderTime = 0 + avgRenderTime = 0 get tilesRendered () { return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).tilesCount, 0) @@ -72,20 +74,6 @@ export class WorldRendererThree extends WorldRendererCommon { const chunkCoords = data.key.split(',') if (!this.loadedChunks[chunkCoords[0] + ',' + chunkCoords[2]] || !data.geometry.positions.length || !this.active) return - // if (!this.initialChunksLoad && this.enableChunksLoadDelay) { - // const newPromise = new Promise(resolve => { - // if (this.droppedFpsPercentage > 0.5) { - // setTimeout(resolve, 1000 / 50 * this.droppedFpsPercentage) - // } else { - // setTimeout(resolve) - // } - // }) - // this.promisesQueue.push(newPromise) - // for (const promise of this.promisesQueue) { - // await promise - // } - // } - const geometry = new THREE.BufferGeometry() geometry.setAttribute('position', new THREE.BufferAttribute(data.geometry.positions, 3)) geometry.setAttribute('normal', new THREE.BufferAttribute(data.geometry.normals, 3)) @@ -163,7 +151,11 @@ export class WorldRendererThree extends WorldRendererCommon { render () { tweenJs.update() const cam = this.camera instanceof THREE.Group ? this.camera.children.find(child => child instanceof THREE.PerspectiveCamera) as THREE.PerspectiveCamera : this.camera + const start = performance.now() this.renderer.render(this.scene, cam) + const totalTime = performance.now() - start + this.avgRenderTime = this.avgRenderTime * 0.9 + totalTime * 0.1 // exponential moving average + this.worstRenderTime = Math.max(this.worstRenderTime, totalTime) } renderSign (position: Vec3, rotation: number, isWall: boolean, isHanging: boolean, blockEntity) { diff --git a/src/benchmark.ts b/src/benchmark.ts new file mode 100644 index 00000000..a9a342d6 --- /dev/null +++ b/src/benchmark.ts @@ -0,0 +1,73 @@ +import { Vec3 } from 'vec3' +import { downloadAndOpenFileFromUrl } from './downloadAndOpenFile' +import { activeModalStack, miscUiState } from './globalState' +import { options } from './optionsStorage' +import { BenchmarkAdapter } from './benchmarkAdapter' + +const testWorldFixtureUrl = 'https://bucket.mcraft.fun/Future CITY 4.4-slim.zip' +const testWorldFixtureSpawn = [-133, 87, 309] as const + +export const openBenchmark = async (renderDistance = 8) => { + let memoryUsageAverage = 0 + let memoryUsageSamples = 0 + let memoryUsageWorst = 0 + setInterval(() => { + const memoryUsage = (window.performance as any)?.memory?.usedJSHeapSize + if (memoryUsage) { + memoryUsageAverage = (memoryUsageAverage * memoryUsageSamples + memoryUsage) / (memoryUsageSamples + 1) + memoryUsageSamples++ + if (memoryUsage > memoryUsageWorst) { + memoryUsageWorst = memoryUsage + } + } + }, 200) + + const benchmarkAdapter: BenchmarkAdapter = { + get worldLoadTime () { + return window.worldLoadTime + }, + get averageRenderTime () { + return window.viewer.world.avgRenderTime + }, + get worstRenderTime () { + return window.viewer.world.worstRenderTime + }, + get memoryUsageAverage () { + return memoryUsageAverage + }, + get memoryUsageWorst () { + return memoryUsageWorst + } + } + window.benchmarkAdapter = benchmarkAdapter + + options.renderDistance = renderDistance + void downloadAndOpenFileFromUrl(testWorldFixtureUrl, undefined, { + connectEvents: { + serverCreated () { + if (testWorldFixtureSpawn) { + localServer!.spawnPoint = new Vec3(...testWorldFixtureSpawn) + localServer!.on('newPlayer', (player) => { + player.on('dataLoaded', () => { + player.position = new Vec3(...testWorldFixtureSpawn) + }) + }) + } + }, + } + }) +} + +export const registerOpenBenchmarkListener = () => { + const params = new URLSearchParams(window.location.search) + if (params.get('openBenchmark')) { + void openBenchmark(params.has('renderDistance') ? +params.get('renderDistance')! : undefined) + } + + window.addEventListener('keydown', (e) => { + if (e.code === 'KeyB' && e.shiftKey && !miscUiState.gameLoaded && activeModalStack.length === 0) { + e.preventDefault() + void openBenchmark() + } + }) +} diff --git a/src/benchmarkAdapter.ts b/src/benchmarkAdapter.ts new file mode 100644 index 00000000..9eef1121 --- /dev/null +++ b/src/benchmarkAdapter.ts @@ -0,0 +1,7 @@ +export interface BenchmarkAdapter { + worldLoadTime: number + averageRenderTime: number + worstRenderTime: number + memoryUsageAverage: number + memoryUsageWorst: number +} diff --git a/src/browserfs.ts b/src/browserfs.ts index ebe8acfd..aa2c0e9e 100644 --- a/src/browserfs.ts +++ b/src/browserfs.ts @@ -10,6 +10,7 @@ import { fsState, loadSave } from './loadSave' import { installTexturePack, installTexturePackFromHandle, updateTexturePackInstalledState } from './texturePack' import { miscUiState } from './globalState' import { setLoadingScreenStatus } from './utils' +import { ConnectOptions } from './connect' const { GoogleDriveFileSystem } = require('google-drive-browserfs/src/backends/GoogleDrive') // disable type checking browserfs.install(window) @@ -434,7 +435,7 @@ export const copyFilesAsync = async (pathSrc: string, pathDest: string, fileCopi } // todo rename method -const openWorldZipInner = async (file: File | ArrayBuffer, name = file['name']) => { +const openWorldZipInner = async (file: File | ArrayBuffer, name = file['name'], connectOptions?: Partial) => { await new Promise(async resolve => { browserfs.configure({ // todo @@ -478,7 +479,7 @@ const openWorldZipInner = async (file: File | ArrayBuffer, name = file['name']) } if (availableWorlds.length === 1) { - await loadSave(`/world/${availableWorlds[0]}`) + await loadSave(`/world/${availableWorlds[0]}`, connectOptions) return } diff --git a/src/connect.ts b/src/connect.ts index 5e4df859..fc716c84 100644 --- a/src/connect.ts +++ b/src/connect.ts @@ -1,15 +1,24 @@ export type ConnectOptions = { - server?: string; - singleplayer?: any; - username: string; - password?: any; - proxy?: any; - botVersion?: any; - serverOverrides?; - serverOverridesFlat?; - peerId?: string; - ignoreQs?: boolean; + server?: string + singleplayer?: any + username: string + password?: any + proxy?: any + botVersion?: any + serverOverrides? + serverOverridesFlat? + peerId?: string + ignoreQs?: boolean onSuccessfulPlay?: () => void autoLoginPassword?: string serverIndex?: string + + connectEvents?: { + serverCreated?: () => void + // connect: () => void; + // disconnect: () => void; + // error: (err: any) => void; + // ready: () => void; + // end: () => void; + } } diff --git a/src/downloadAndOpenFile.ts b/src/downloadAndOpenFile.ts index 7ac154fc..ad0f831c 100644 --- a/src/downloadAndOpenFile.ts +++ b/src/downloadAndOpenFile.ts @@ -2,27 +2,25 @@ import prettyBytes from 'pretty-bytes' import { openWorldZip } from './browserfs' import { getResourcePackName, installTexturePack, resourcePackState, updateTexturePackInstalledState } from './texturePack' import { setLoadingScreenStatus } from './utils' +import { ConnectOptions } from './connect' export const getFixedFilesize = (bytes: number) => { return prettyBytes(bytes, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) } -const inner = async () => { - const qs = new URLSearchParams(window.location.search) - let mapUrl = qs.get('map') - const texturepack = qs.get('texturepack') +export const downloadAndOpenFileFromUrl = async (mapUrl: string | undefined, texturepackUrl: string | undefined, connectOptions?: Partial) => { // fixme - if (texturepack) mapUrl = texturepack + if (texturepackUrl) mapUrl = texturepackUrl if (!mapUrl) return false - if (texturepack) { + if (texturepackUrl) { await updateTexturePackInstalledState() if (resourcePackState.resourcePackInstalled) { if (!confirm(`You are going to install a new resource pack, which will REPLACE the current one: ${await getResourcePackName()} Continue?`)) return } } const name = mapUrl.slice(mapUrl.lastIndexOf('/') + 1).slice(-25) - const downloadThing = texturepack ? 'texturepack' : 'world' + const downloadThing = texturepackUrl ? 'texturepack' : 'world' setLoadingScreenStatus(`Downloading ${downloadThing} ${name}...`) const response = await fetch(mapUrl) @@ -63,17 +61,20 @@ const inner = async () => { }, }) ).arrayBuffer() - if (texturepack) { + if (texturepackUrl) { const name = mapUrl.slice(mapUrl.lastIndexOf('/') + 1).slice(-30) await installTexturePack(buffer, name) } else { - await openWorldZip(buffer) + await openWorldZip(buffer, undefined, connectOptions) } } export default async () => { try { - return await inner() + const qs = new URLSearchParams(window.location.search) + const mapUrl = qs.get('map') + const texturepack = qs.get('texturepack') + return await downloadAndOpenFileFromUrl(mapUrl ?? undefined, texturepack ?? undefined) } catch (err) { setLoadingScreenStatus(`Failed to download. Either refresh page or remove map param from URL. Reason: ${err.message}`) return true diff --git a/src/index.ts b/src/index.ts index 72f270d4..4c667c6f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,6 +42,7 @@ import debug from 'debug' import { defaultsDeep } from 'lodash-es' import { initVR } from './vr' +import { registerOpenBenchmarkListener } from './benchmark' import { AppConfig, activeModalStack, @@ -218,8 +219,14 @@ function hideCurrentScreens () { insertActiveModalStack('', []) } -const loadSingleplayer = (serverOverrides = {}, flattenedServerOverrides = {}) => { - void connect({ singleplayer: true, username: options.localUsername, password: '', serverOverrides, serverOverridesFlat: flattenedServerOverrides }) +const loadSingleplayer = (serverOverrides = {}, flattenedServerOverrides = {}, otherOptions: Partial = {}) => { + void connect({ + singleplayer: true, + username: options.localUsername, + serverOverrides, + serverOverridesFlat: flattenedServerOverrides, + ...otherOptions + }) } function listenGlobalEvents () { window.addEventListener('connect', e => { @@ -227,7 +234,9 @@ function listenGlobalEvents () { void connect(options) }) window.addEventListener('singleplayer', (e) => { - loadSingleplayer((e as CustomEvent).detail) + const { detail } = (e as CustomEvent) + const { connectOptions, ...rest } = detail + loadSingleplayer(rest, {}, connectOptions) }) } @@ -406,6 +415,7 @@ async function connect (connectOptions: ConnectOptions) { setLoadingScreenStatus('Starting local server') localServer = window.localServer = window.server = startLocalServer(serverOptions) + connectOptions?.connectEvents?.serverCreated?.() // todo need just to call quit if started // loadingScreen.maybeRecoverable = false // init world, todo: do it for any async plugins @@ -578,6 +588,7 @@ async function connect (connectOptions: ConnectOptions) { const spawnEarlier = !singleplayer && !p2pMultiplayer // don't use spawn event, player can be dead bot.once(spawnEarlier ? 'forcedMove' : 'health', () => { + window.worldStartLoad = Date.now() errorAbortController.abort() const mcData = MinecraftData(bot.version) window.PrismarineBlock = PrismarineBlock(mcData.version.minecraftVersion!) @@ -805,7 +816,9 @@ async function connect (connectOptions: ConnectOptions) { // 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') + const worldLoadTime = (Date.now() - start) / 1000 + window.worldLoadTime = worldLoadTime + console.log('All done and ready! In', worldLoadTime, 's') viewer.render() // ensure the last state is rendered document.dispatchEvent(new Event('cypress-world-ready')) }) @@ -944,7 +957,7 @@ downloadAndOpenFile().then((downloadAction) => { }) }, (err) => { console.error(err) - alert(`Failed to download file: ${err}`) + alert(`Somethin went wrong: ${err}`) }) // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion @@ -956,3 +969,4 @@ if (initialLoader) { window.pageLoaded = true void possiblyHandleStateVariable() +registerOpenBenchmarkListener() diff --git a/src/loadSave.ts b/src/loadSave.ts index 7ca454ff..5f3241df 100644 --- a/src/loadSave.ts +++ b/src/loadSave.ts @@ -11,6 +11,7 @@ import { isMajorVersionGreater } from './utils' import { activeModalStacks, insertActiveModalStack, miscUiState } from './globalState' import supportedVersions from './supportedVersions.mjs' +import { ConnectOptions } from './connect' // todo include name of opened handle (zip)! // additional fs metadata @@ -46,7 +47,7 @@ export const readLevelDat = async (path) => { return { levelDat, dataRaw: parsed.value.Data!.value as Record } } -export const loadSave = async (root = '/world') => { +export const loadSave = async (root = '/world', connectOptions?: Partial) => { // todo test if (miscUiState.gameLoaded) { await disconnect() @@ -189,7 +190,8 @@ export const loadSave = async (root = '/world') => { } : {}, ...root === '/world' ? {} : { 'worldFolder': root - } + }, + connectOptions }, })) } From 092e270e54202332f3594d3fff9386f7181ae37d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Jun 2024 09:27:06 +0300 Subject: [PATCH 02/18] up file --- .github/workflows/benchmark.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b98d140b..24f0593f 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -1,7 +1,4 @@ -name: Vercel Deploy Preview -env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} +name: Benchmark on: issue_comment: types: [created] From e94b4a0fec67c1c3c6530871d0b0a276120f0211 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Jun 2024 09:27:31 +0300 Subject: [PATCH 03/18] do not skip for now --- .github/workflows/benchmark.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 24f0593f..e88131ff 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -8,12 +8,11 @@ on: jobs: deploy: runs-on: ubuntu-latest - # todo skip already created deploys on that commit - if: >- - github.event.issue.pull_request != '' && - ( - contains(github.event.comment.body, '/benchmark') - ) + # if: >- + # github.event.issue.pull_request != '' && + # ( + # contains(github.event.comment.body, '/benchmark') + # ) permissions: pull-requests: write steps: From e52bec4481e6702671b46af141ac9b92a47a194b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Jun 2024 09:29:23 +0300 Subject: [PATCH 04/18] [skip ci] print lscpu --- .github/workflows/benchmark.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index e88131ff..a04d210e 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -16,6 +16,7 @@ jobs: permissions: pull-requests: write steps: + - run: lscpu - name: Checkout uses: actions/checkout@v2 with: From fc6874294ea890ca2e9e6556b959e390383fe09b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Jun 2024 09:36:06 +0300 Subject: [PATCH 05/18] dont use ref for now --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index a04d210e..f274e62b 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -19,8 +19,8 @@ jobs: - run: lscpu - name: Checkout uses: actions/checkout@v2 - with: - ref: refs/pull/${{ github.event.issue.number }}/head + # with: + # ref: refs/pull/${{ github.event.issue.number }}/head - run: npm i -g pnpm@9.0.4 - uses: actions/setup-node@v4 with: From 92187a6caa2b479435ff513d98228023755b8d0f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 27 Jun 2024 15:15:31 +0300 Subject: [PATCH 06/18] start the server & gpu info! --- .github/workflows/benchmark.yml | 1 + cypress/e2e/rendering_performance.spec.ts | 2 ++ src/benchmark.ts | 4 ++++ src/benchmarkAdapter.ts | 1 + 4 files changed, 8 insertions(+) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index f274e62b..b2822270 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -28,6 +28,7 @@ jobs: cache: "pnpm" - run: pnpm install - run: pnpm build + - run: nohup pnpm prod-start & - run: pnpm test:benchmark # read benchmark results from stdout - run: echo "BENCHMARK_RESULT=$(cat benchmark.txt)" >> $GITHUB_ENV diff --git a/cypress/e2e/rendering_performance.spec.ts b/cypress/e2e/rendering_performance.spec.ts index 4a7390ab..03b91cb8 100644 --- a/cypress/e2e/rendering_performance.spec.ts +++ b/cypress/e2e/rendering_performance.spec.ts @@ -20,6 +20,7 @@ it('Benchmark rendering performance', () => { const fpsWorst = 1000 / renderTimeWorst const fpsAvg = 1000 / renderTimeAvg const totalTime = adapter.worldLoadTime + const { gpuInfo } = adapter const messages = [ `Worst FPS: ${fpsWorst.toFixed(2)}`, @@ -27,6 +28,7 @@ it('Benchmark rendering performance', () => { `Total time: ${totalTime.toFixed(2)}s`, `Memory usage average: ${adapter.memoryUsageAverage.toFixed(2)}MB`, `Memory usage worst: ${adapter.memoryUsageWorst.toFixed(2)}MB`, + `GPU info: ${gpuInfo}`, ] for (const message of messages) { cy.log(message) diff --git a/src/benchmark.ts b/src/benchmark.ts index a9a342d6..78e5e027 100644 --- a/src/benchmark.ts +++ b/src/benchmark.ts @@ -37,6 +37,10 @@ export const openBenchmark = async (renderDistance = 8) => { }, get memoryUsageWorst () { return memoryUsageWorst + }, + get gpuInfo () { + const gl = window.viewer.renderer.getContext() + return gl.getParameter(gl.getExtension('WEBGL_debug_renderer_info')!.UNMASKED_RENDERER_WEBGL) } } window.benchmarkAdapter = benchmarkAdapter diff --git a/src/benchmarkAdapter.ts b/src/benchmarkAdapter.ts index 9eef1121..327af715 100644 --- a/src/benchmarkAdapter.ts +++ b/src/benchmarkAdapter.ts @@ -4,4 +4,5 @@ export interface BenchmarkAdapter { worstRenderTime: number memoryUsageAverage: number memoryUsageWorst: number + gpuInfo: string } From 2afba817451e4783569b5ab995d63f4bddaca140 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 11 Jul 2024 00:39:08 +0300 Subject: [PATCH 07/18] up lock --- pnpm-lock.yaml | 6 +++--- src/connect.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd4d44de..45ebdda4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3075,8 +3075,8 @@ packages: resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==} engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'} - '@zardoy/flying-squid@0.0.29': - resolution: {integrity: sha512-E5Nk1gMeH+fAHM5aJY8kIxjBS/zuPtPD6QPeZg+laPV5H58Jx3Et17clF1zC9MT2wyFQ5wi5uTnfdGBTpSEqHw==} + '@zardoy/flying-squid@0.0.33': + resolution: {integrity: sha512-zCgHinWrNbS4HugnA1GBMuKQ0rUemBg//b+XhefxKeGBg9ngk8UVlJoR6cCAaa67zjiauEq/rhnNKnA4V7vtuQ==} engines: {node: '>=8'} hasBin: true @@ -11971,7 +11971,7 @@ snapshots: '@types/emscripten': 1.39.8 tslib: 1.14.1 - '@zardoy/flying-squid@0.0.29(encoding@0.1.13)': + '@zardoy/flying-squid@0.0.33(encoding@0.1.13)': dependencies: '@tootallnate/once': 2.0.0 change-case: 4.1.2 diff --git a/src/connect.ts b/src/connect.ts index e4fc765b..5e2d8b43 100644 --- a/src/connect.ts +++ b/src/connect.ts @@ -5,7 +5,7 @@ export type ConnectOptions = { singleplayer?: any username: string proxy?: string - botVersion?: any + botVersion?: string serverOverrides? serverOverridesFlat? peerId?: string @@ -13,6 +13,7 @@ export type ConnectOptions = { onSuccessfulPlay?: () => void autoLoginPassword?: string serverIndex?: string + authenticatedAccount?: AuthenticatedAccount | true connectEvents?: { serverCreated?: () => void From c8bdbf0b59e31f1e1c198e731ccb11f900191ddc Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 6 Apr 2025 21:21:08 +0300 Subject: [PATCH 08/18] minor imp --- renderer/viewer/lib/worldrendererCommon.ts | 2 ++ src/downloadAndOpenFile.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/renderer/viewer/lib/worldrendererCommon.ts b/renderer/viewer/lib/worldrendererCommon.ts index 0225bb87..5f8bf5e5 100644 --- a/renderer/viewer/lib/worldrendererCommon.ts +++ b/renderer/viewer/lib/worldrendererCommon.ts @@ -109,6 +109,8 @@ export abstract class WorldRendererCommon geometryReceiveCount = {} as Record allLoadedIn: undefined | number onWorldSwitched = [] as Array<() => void> + worstRenderTime = 0 + avgRenderTime = 0 edgeChunks = {} as Record lastAddChunk = null as null | { diff --git a/src/downloadAndOpenFile.ts b/src/downloadAndOpenFile.ts index 92924b0c..32d5ae10 100644 --- a/src/downloadAndOpenFile.ts +++ b/src/downloadAndOpenFile.ts @@ -139,7 +139,7 @@ export default async () => { try { return await inner() } catch (err) { - setLoadingScreenStatus(`Failed to download. Either refresh page or remove map param from URL. Reason: ${err.message}`) + setLoadingScreenStatus(`Failed to download/open. Either refresh page or remove map param from URL. Reason: ${err.message}`) return true } } From 536eb0d201ba250dbebe1622e02dedddd6f3279a Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 6 Apr 2025 23:34:38 +0300 Subject: [PATCH 09/18] NEW! WAKE LOCK API SUPPORT! --- src/mineflayer/timers.ts | 63 ++++++++++++++++++++++++-- src/optionsGuiScheme.tsx | 6 ++- src/react/IndicatorEffects.tsx | 4 +- src/react/IndicatorEffectsProvider.tsx | 12 ++++- 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/mineflayer/timers.ts b/src/mineflayer/timers.ts index 698b624e..b1cb5c41 100644 --- a/src/mineflayer/timers.ts +++ b/src/mineflayer/timers.ts @@ -1,9 +1,66 @@ +import { subscribeKey } from 'valtio/utils' import { preventThrottlingWithSound } from '../core/timers' import { options } from '../optionsStorage' customEvents.on('mineflayerBotCreated', () => { - if (options.preventBackgroundTimeoutKick) { - const unsub = preventThrottlingWithSound() - bot.on('end', unsub) + const abortController = new AbortController() + + const maybeGoBackgroundKickPrevention = () => { + if (options.preventBackgroundTimeoutKick && !bot.backgroundKickPrevention) { + const unsub = preventThrottlingWithSound() + bot.on('end', unsub) + bot.backgroundKickPrevention = true + } } + maybeGoBackgroundKickPrevention() + subscribeKey(options, 'preventBackgroundTimeoutKick', (value) => { + maybeGoBackgroundKickPrevention() + }) + + // wake lock + const requestWakeLock = async () => { + if (options.preventSleep && !bot.wakeLock && !bot.lockRequested) { + bot.lockRequested = true + bot.wakeLock = await navigator.wakeLock.request('screen').finally(() => { + bot.lockRequested = false + }) + + bot.wakeLock.addEventListener('release', () => { + bot.wakeLock = undefined + }, { + once: true, + }) + } + + if (!options.preventSleep && bot.wakeLock) { + void bot.wakeLock.release() + } + } + document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'visible') { + // we are back to the tab, request wake lock again + void requestWakeLock() + } + }, { + signal: abortController.signal, + }) + void requestWakeLock() + subscribeKey(options, 'preventSleep', (value) => { + void requestWakeLock() + }) + + bot.on('end', () => { + if (bot.wakeLock) { + void bot.wakeLock.release() + } + abortController.abort() + }) }) + +declare module 'mineflayer' { + interface Bot { + backgroundKickPrevention?: boolean + wakeLock?: WakeLockSentinel + lockRequested?: boolean + } +} diff --git a/src/optionsGuiScheme.tsx b/src/optionsGuiScheme.tsx index 5b83922f..ef8d9a8e 100644 --- a/src/optionsGuiScheme.tsx +++ b/src/optionsGuiScheme.tsx @@ -526,7 +526,11 @@ export const guiOptionsScheme: { }, }, { - preventBackgroundTimeoutKick: {} + preventBackgroundTimeoutKick: {}, + preventSleep: { + disabledReason: navigator.wakeLock ? undefined : 'Your browser does not support wake lock API', + enableWarning: 'When connected to a server, prevent PC from sleeping or screen dimming. Useful for purpusely staying AFK for long time. Some events might still prevent this like loosing tab focus or going low power mode.', + }, }, { custom () { diff --git a/src/react/IndicatorEffects.tsx b/src/react/IndicatorEffects.tsx index 18ab0785..5b05290f 100644 --- a/src/react/IndicatorEffects.tsx +++ b/src/react/IndicatorEffects.tsx @@ -46,7 +46,8 @@ export const defaultIndicatorsState = { readonlyFiles: false, writingFiles: false, // saving appHasErrors: false, - connectionIssues: 0 + connectionIssues: 0, + preventSleep: false, } const indicatorIcons: Record = { @@ -56,6 +57,7 @@ const indicatorIcons: Record = { appHasErrors: 'alert', readonlyFiles: 'file-off', connectionIssues: pixelartIcons['cellular-signal-off'], + preventSleep: pixelartIcons.moon, } const colorOverrides = { diff --git a/src/react/IndicatorEffectsProvider.tsx b/src/react/IndicatorEffectsProvider.tsx index 83b321ee..1913fb7b 100644 --- a/src/react/IndicatorEffectsProvider.tsx +++ b/src/react/IndicatorEffectsProvider.tsx @@ -1,5 +1,5 @@ import { proxy, subscribe, useSnapshot } from 'valtio' -import { useEffect, useMemo } from 'react' +import { useEffect, useMemo, useState } from 'react' import { subscribeKey } from 'valtio/utils' import { inGameError } from '../utils' import { fsState } from '../loadSave' @@ -51,6 +51,7 @@ const getEffectIndex = (newEffect: EffectType) => { } export default () => { + const [dummyState, setDummyState] = useState(false) const stateIndicators = useSnapshot(state.indicators) const chunksLoading = !useSnapshot(appViewer.rendererState).world.allChunksLoaded const { mesherWork } = useSnapshot(appViewer.rendererState).world @@ -66,12 +67,21 @@ export default () => { appHasErrors: hasErrors, connectionIssues: poorConnection ? 1 : noConnection ? 2 : 0, chunksLoading, + preventSleep: !!bot.wakeLock, // mesherWork, ...stateIndicators, } const effects = useSnapshot(state.effects) + useEffect(() => { + // update bot related states + const interval = setInterval(() => { + setDummyState(s => !s) + }, 1000) + return () => clearInterval(interval) + }, []) + useMemo(() => { const effectsImages = Object.fromEntries(loadedData.effectsArray.map((effect) => { const nameKebab = effect.name.replaceAll(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`).slice(1) From 701cc63e28bf959b99da65d23e06aa94bba0c8e6 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 6 Apr 2025 23:39:18 +0300 Subject: [PATCH 10/18] FINISH! --- cypress/e2e/rendering_performance.spec.ts | 29 ++--- package.json | 2 +- pnpm-lock.yaml | 64 +++++++--- renderer/viewer/lib/worldrendererCommon.ts | 6 +- renderer/viewer/three/worldrendererThree.ts | 6 +- src/appParams.ts | 5 + src/benchmark.ts | 122 ++++++++++++++++---- src/benchmarkAdapter.ts | 58 ++++++++-- src/index.ts | 24 ++-- src/optionsStorage.ts | 1 + 10 files changed, 239 insertions(+), 78 deletions(-) diff --git a/cypress/e2e/rendering_performance.spec.ts b/cypress/e2e/rendering_performance.spec.ts index 03b91cb8..a12429d4 100644 --- a/cypress/e2e/rendering_performance.spec.ts +++ b/cypress/e2e/rendering_performance.spec.ts @@ -1,6 +1,6 @@ /// -import { BenchmarkAdapter } from '../../src/benchmarkAdapter' -import { setOptions, cleanVisit, visit } from './shared' +import { BenchmarkAdapterInfo, getAllInfoLines } from '../../src/benchmarkAdapter' +import { cleanVisit } from './shared' it('Benchmark rendering performance', () => { cleanVisit('/?openBenchmark=true&renderDistance=5') @@ -14,26 +14,19 @@ it('Benchmark rendering performance', () => { }) }).then(() => { cy.window().then(win => { - const adapter = win.benchmarkAdapter as BenchmarkAdapter - const renderTimeWorst = adapter.worstRenderTime - const renderTimeAvg = adapter.averageRenderTime - const fpsWorst = 1000 / renderTimeWorst - const fpsAvg = 1000 / renderTimeAvg - const totalTime = adapter.worldLoadTime - const { gpuInfo } = adapter + const adapter = win.benchmarkAdapter as BenchmarkAdapterInfo - const messages = [ - `Worst FPS: ${fpsWorst.toFixed(2)}`, - `Average FPS: ${fpsAvg.toFixed(2)}`, - `Total time: ${totalTime.toFixed(2)}s`, - `Memory usage average: ${adapter.memoryUsageAverage.toFixed(2)}MB`, - `Memory usage worst: ${adapter.memoryUsageWorst.toFixed(2)}MB`, - `GPU info: ${gpuInfo}`, - ] + const messages = getAllInfoLines(adapter) + // wait for 10 seconds + cy.wait(10_000) + const messages2 = getAllInfoLines(adapter, true) for (const message of messages) { cy.log(message) } - cy.writeFile('benchmark.txt', messages.join('\n')) + for (const message of messages2) { + cy.log(message) + } + cy.writeFile('benchmark.txt', [...messages, ...messages2].join('\n')) }) }) }) diff --git a/package.json b/package.json index 9d23c2ce..c72e591f 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "esbuild-plugin-polyfill-node": "^0.3.0", "express": "^4.18.2", "filesize": "^10.0.12", - "flying-squid": "npm:@zardoy/flying-squid@^0.0.51", + "flying-squid": "npm:@zardoy/flying-squid@^0.0.58", "fs-extra": "^11.1.1", "google-drive-browserfs": "github:zardoy/browserfs#google-drive", "jszip": "^3.10.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24c4e332..933acaa3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -120,8 +120,8 @@ importers: specifier: ^10.0.12 version: 10.0.12 flying-squid: - specifier: npm:@zardoy/flying-squid@^0.0.51 - version: '@zardoy/flying-squid@0.0.51(encoding@0.1.13)' + specifier: npm:@zardoy/flying-squid@^0.0.58 + version: '@zardoy/flying-squid@0.0.58(encoding@0.1.13)' fs-extra: specifier: ^11.1.1 version: 11.1.1 @@ -438,7 +438,7 @@ importers: version: 1.3.6 prismarine-block: specifier: github:zardoy/prismarine-block#next-era - version: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9 + version: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9(prismarine-registry@1.11.0) prismarine-chunk: specifier: github:zardoy/prismarine-chunk#master version: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/e68e9a423b5b1907535878fb636f12c28a1a9374(minecraft-data@3.83.1) @@ -3541,8 +3541,8 @@ packages: engines: {node: '>=8'} hasBin: true - '@zardoy/flying-squid@0.0.51': - resolution: {integrity: sha512-HHZ79H9NkS44lL9vk6gVEuJDJqj88gpiBt9Ihh5p4rHXTVbRid95riiNK5dD0kHI94P5/DXdtNalvmJDPU86oQ==} + '@zardoy/flying-squid@0.0.58': + resolution: {integrity: sha512-qkSoaYRpVQaAvcVgZDTe0i4PxaK2l2B6i7GfRCEsyYFl3UaNQYBwwocXqLrIwhsc63bwXa0XQe8UNUubz+A4eA==} engines: {node: '>=8'} hasBin: true @@ -6926,6 +6926,11 @@ packages: version: 1.54.0 engines: {node: '>=22'} + minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284: + resolution: {tarball: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284} + version: 1.57.0 + engines: {node: '>=22'} + minecraft-wrap@1.5.1: resolution: {integrity: sha512-7DZ2WhrcRD3fUMau84l9Va0KWzV92SHNdB7mnNdNhgXID2aW6pjWuYPZi8MepEBemA4XKKdnDx7HmhTbkoiR8A==} hasBin: true @@ -13652,7 +13657,7 @@ snapshots: - encoding - supports-color - '@zardoy/flying-squid@0.0.51(encoding@0.1.13)': + '@zardoy/flying-squid@0.0.58(encoding@0.1.13)': dependencies: '@tootallnate/once': 2.0.0 chalk: 5.3.0 @@ -13663,14 +13668,14 @@ snapshots: flatmap: 0.0.3 long: 5.2.3 minecraft-data: 3.83.1 - minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/5ec3dd4b367fcc039fbcb3edd214fe3cf8178a6d(patch_hash=dkeyukcqlupmk563gwxsmjr3yu)(encoding@0.1.13) + minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284(encoding@0.1.13) mkdirp: 2.1.6 node-gzip: 1.1.2 node-rsa: 1.1.1 prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/e68e9a423b5b1907535878fb636f12c28a1a9374(minecraft-data@3.83.1) prismarine-entity: 2.3.1 prismarine-item: 1.16.0 - prismarine-nbt: 2.5.0 + prismarine-nbt: 2.7.0 prismarine-provider-anvil: https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/1d548fac63fe977c8281f0a9a522b37e4d92d0b7(minecraft-data@3.83.1) prismarine-windows: 2.9.0 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/ab2146c9933eef3247c3f64446de4ccc2c484c7c @@ -17981,6 +17986,31 @@ snapshots: - encoding - supports-color + minecraft-protocol@https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/9e116c3dd4682b17c4e2c80249a2447a093d9284(encoding@0.1.13): + dependencies: + '@types/node-rsa': 1.1.4 + '@types/readable-stream': 4.0.12 + aes-js: 3.1.2 + buffer-equal: 1.0.1 + debug: 4.4.0(supports-color@8.1.1) + endian-toggle: 0.0.0 + lodash.merge: 4.6.2 + minecraft-data: 3.83.1 + minecraft-folder-path: 1.2.0 + node-fetch: 2.7.0(encoding@0.1.13) + node-rsa: 0.4.2 + prismarine-auth: 2.4.2(encoding@0.1.13) + prismarine-chat: 1.10.1 + prismarine-nbt: 2.7.0 + prismarine-realms: 1.3.2(encoding@0.1.13) + protodef: 1.18.0 + readable-stream: 4.5.2 + uuid-1345: 1.0.2 + yggdrasil: 1.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + minecraft-wrap@1.5.1(encoding@0.1.13): dependencies: debug: 4.4.0(supports-color@8.1.1) @@ -18043,7 +18073,7 @@ snapshots: mineflayer-pathfinder@2.4.4: dependencies: minecraft-data: 3.83.1 - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9 + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9(prismarine-registry@1.11.0) prismarine-entity: 2.3.1 prismarine-item: 1.16.0 prismarine-nbt: 2.5.0 @@ -18055,7 +18085,7 @@ snapshots: minecraft-data: 3.83.1 minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/5ec3dd4b367fcc039fbcb3edd214fe3cf8178a6d(patch_hash=dkeyukcqlupmk563gwxsmjr3yu)(encoding@0.1.13) prismarine-biome: 1.3.0(minecraft-data@3.83.1)(prismarine-registry@1.11.0) - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9 + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9(prismarine-registry@1.11.0) prismarine-chat: 1.10.1 prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/e68e9a423b5b1907535878fb636f12c28a1a9374(minecraft-data@3.83.1) prismarine-entity: 2.3.1 @@ -18078,7 +18108,7 @@ snapshots: minecraft-data: 3.83.1 minecraft-protocol: https://codeload.github.com/PrismarineJS/node-minecraft-protocol/tar.gz/5ec3dd4b367fcc039fbcb3edd214fe3cf8178a6d(patch_hash=dkeyukcqlupmk563gwxsmjr3yu)(encoding@0.1.13) prismarine-biome: 1.3.0(minecraft-data@3.83.1)(prismarine-registry@1.11.0) - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9 + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9(prismarine-registry@1.11.0) prismarine-chat: 1.10.1 prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/e68e9a423b5b1907535878fb636f12c28a1a9374(minecraft-data@3.83.1) prismarine-entity: 2.3.1 @@ -18867,7 +18897,7 @@ snapshots: minecraft-data: 3.83.1 prismarine-registry: 1.11.0 - prismarine-block@https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9: + prismarine-block@https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9(prismarine-registry@1.11.0): dependencies: minecraft-data: 3.83.1 prismarine-biome: 1.3.0(minecraft-data@3.83.1)(prismarine-registry@1.11.0) @@ -18875,6 +18905,8 @@ snapshots: prismarine-item: 1.16.0 prismarine-nbt: 2.5.0 prismarine-registry: 1.11.0 + transitivePeerDependencies: + - prismarine-registry prismarine-chat@1.10.1: dependencies: @@ -18885,7 +18917,7 @@ snapshots: prismarine-chunk@https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/e68e9a423b5b1907535878fb636f12c28a1a9374(minecraft-data@3.83.1): dependencies: prismarine-biome: 1.3.0(minecraft-data@3.83.1)(prismarine-registry@1.11.0) - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9 + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9(prismarine-registry@1.11.0) prismarine-nbt: 2.5.0 prismarine-registry: 1.11.0 smart-buffer: 4.2.0 @@ -18923,7 +18955,7 @@ snapshots: prismarine-provider-anvil@https://codeload.github.com/zardoy/prismarine-provider-anvil/tar.gz/1d548fac63fe977c8281f0a9a522b37e4d92d0b7(minecraft-data@3.83.1): dependencies: - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9 + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9(prismarine-registry@1.11.0) prismarine-chunk: https://codeload.github.com/zardoy/prismarine-chunk/tar.gz/e68e9a423b5b1907535878fb636f12c28a1a9374(minecraft-data@3.83.1) prismarine-nbt: 2.5.0 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/ab2146c9933eef3247c3f64446de4ccc2c484c7c @@ -18947,13 +18979,13 @@ snapshots: prismarine-registry@1.11.0: dependencies: minecraft-data: 3.83.1 - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9 + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9(prismarine-registry@1.11.0) prismarine-nbt: 2.7.0 prismarine-schematic@1.2.3: dependencies: minecraft-data: 3.83.1 - prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9 + prismarine-block: https://codeload.github.com/zardoy/prismarine-block/tar.gz/853c559bff2b402863ee9a75b125a3ca320838f9(prismarine-registry@1.11.0) prismarine-nbt: 2.5.0 prismarine-world: https://codeload.github.com/zardoy/prismarine-world/tar.gz/ab2146c9933eef3247c3f64446de4ccc2c484c7c vec3: 0.1.10 diff --git a/renderer/viewer/lib/worldrendererCommon.ts b/renderer/viewer/lib/worldrendererCommon.ts index 5f8bf5e5..2e3ff5d2 100644 --- a/renderer/viewer/lib/worldrendererCommon.ts +++ b/renderer/viewer/lib/worldrendererCommon.ts @@ -109,9 +109,9 @@ export abstract class WorldRendererCommon geometryReceiveCount = {} as Record allLoadedIn: undefined | number onWorldSwitched = [] as Array<() => void> - worstRenderTime = 0 - avgRenderTime = 0 - + renderTimeMax = 0 + renderTimeAvg = 0 + renderTimeAvgCount = 0 edgeChunks = {} as Record lastAddChunk = null as null | { timeout: any diff --git a/renderer/viewer/three/worldrendererThree.ts b/renderer/viewer/three/worldrendererThree.ts index 1daa9732..7b82953b 100644 --- a/renderer/viewer/three/worldrendererThree.ts +++ b/renderer/viewer/three/worldrendererThree.ts @@ -50,6 +50,7 @@ export class WorldRendererThree extends WorldRendererCommon { media: ThreeJsMedia waitingChunksToDisplay = {} as { [chunkKey: string]: SectionKey[] } camera: THREE.PerspectiveCamera + renderTimeAvg = 0 get tilesRendered () { return Object.values(this.sectionObjects).reduce((acc, obj) => acc + (obj as any).tilesCount, 0) @@ -430,8 +431,9 @@ export class WorldRendererThree extends WorldRendererCommon { } const end = performance.now() const totalTime = end - start - this.avgRenderTime = this.avgRenderTime * 0.9 + totalTime * 0.1 // exponential moving average - this.worstRenderTime = Math.max(this.worstRenderTime, totalTime) + this.renderTimeAvgCount++ + this.renderTimeAvg = ((this.renderTimeAvg * (this.renderTimeAvgCount - 1)) + totalTime) / this.renderTimeAvgCount + this.renderTimeMax = Math.max(this.renderTimeMax, totalTime) } renderHead (position: Vec3, rotation: number, isWall: boolean, blockEntity) { diff --git a/src/appParams.ts b/src/appParams.ts index b550ea02..eb2b4ba2 100644 --- a/src/appParams.ts +++ b/src/appParams.ts @@ -51,6 +51,11 @@ export type AppQsParams = { replayStopOnError?: string replaySkipMissingOnTimeout?: string replayPacketsSenderDelay?: string + + // Benchmark params + openBenchmark?: string + renderDistance?: string + downloadBenchmark?: string } export type AppQsParamsArray = { diff --git a/src/benchmark.ts b/src/benchmark.ts index 78e5e027..dba92f65 100644 --- a/src/benchmark.ts +++ b/src/benchmark.ts @@ -1,13 +1,28 @@ import { Vec3 } from 'vec3' -import { downloadAndOpenFileFromUrl } from './downloadAndOpenFile' +import { WorldRendererCommon } from 'renderer/viewer/lib/worldrendererCommon' +import prettyBytes from 'pretty-bytes' +import { subscribe } from 'valtio' +import { downloadAndOpenMapFromUrl } from './downloadAndOpenFile' import { activeModalStack, miscUiState } from './globalState' -import { options } from './optionsStorage' -import { BenchmarkAdapter } from './benchmarkAdapter' +import { disabledSettings, options } from './optionsStorage' +import { BenchmarkAdapterInfo, getAllInfoLines } from './benchmarkAdapter' +import { appQueryParams } from './appParams' -const testWorldFixtureUrl = 'https://bucket.mcraft.fun/Future CITY 4.4-slim.zip' -const testWorldFixtureSpawn = [-133, 87, 309] as const +const DEFAULT_RENDER_DISTANCE = 8 -export const openBenchmark = async (renderDistance = 8) => { +const fixtures = { + default: { + url: 'https://bucket.mcraft.fun/Future CITY 4.4-slim.zip', + spawn: [-133, 87, 309] as const, + }, +} + +const testWorldFixtureUrl = fixtures.default.url +const testWorldFixtureSpawn = fixtures.default.spawn + +Error.stackTraceLimit = Error.stackTraceLimit < 30 ? 30 : Error.stackTraceLimit + +export const openBenchmark = async (renderDistance = DEFAULT_RENDER_DISTANCE) => { let memoryUsageAverage = 0 let memoryUsageSamples = 0 let memoryUsageWorst = 0 @@ -22,31 +37,51 @@ export const openBenchmark = async (renderDistance = 8) => { } }, 200) - const benchmarkAdapter: BenchmarkAdapter = { - get worldLoadTime () { + const benchmarkAdapter: BenchmarkAdapterInfo = { + get worldLoadTimeSeconds () { return window.worldLoadTime }, - get averageRenderTime () { - return window.viewer.world.avgRenderTime + get averageRenderTimeMs () { + return (window.world as WorldRendererCommon).renderTimeAvg }, - get worstRenderTime () { - return window.viewer.world.worstRenderTime + get worstRenderTimeMs () { + return (window.world as WorldRendererCommon).renderTimeMax + }, + get fpsAveragePrediction () { + const avgRenderTime = (window.world as WorldRendererCommon).renderTimeAvg + return 1000 / avgRenderTime + }, + get fpsWorstPrediction () { + const maxRenderTime = (window.world as WorldRendererCommon).renderTimeMax + return 1000 / maxRenderTime + }, + get fpsAverageReal () { + return -1 + }, + get fpsWorstReal () { + return -1 }, get memoryUsageAverage () { - return memoryUsageAverage + return prettyBytes(memoryUsageAverage) }, get memoryUsageWorst () { - return memoryUsageWorst + return prettyBytes(memoryUsageWorst) }, get gpuInfo () { - const gl = window.viewer.renderer.getContext() - return gl.getParameter(gl.getExtension('WEBGL_debug_renderer_info')!.UNMASKED_RENDERER_WEBGL) - } + return appViewer.rendererState.renderer + }, + get hardwareConcurrency () { + return navigator.hardwareConcurrency + }, + get userAgent () { + return navigator.userAgent + }, } window.benchmarkAdapter = benchmarkAdapter + disabledSettings.value.add('renderDistance') options.renderDistance = renderDistance - void downloadAndOpenFileFromUrl(testWorldFixtureUrl, undefined, { + void downloadAndOpenMapFromUrl(testWorldFixtureUrl, undefined, { connectEvents: { serverCreated () { if (testWorldFixtureSpawn) { @@ -60,12 +95,57 @@ export const openBenchmark = async (renderDistance = 8) => { }, } }) + document.addEventListener('cypress-world-ready', () => { + let stats = getAllInfoLines(window.benchmarkAdapter) + if (appQueryParams.downloadBenchmark) { + const a = document.createElement('a') + a.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(stats.join('\n')) + a.download = `benchmark-${appViewer.backend?.id}.txt` + a.click() + } + + const panel = document.createElement('div') + panel.style.position = 'fixed' + panel.style.top = '10px' + panel.style.right = '10px' + panel.style.backgroundColor = 'rgba(0,0,0,0.8)' + panel.style.color = 'white' + panel.style.padding = '10px' + panel.style.zIndex = '1000' + panel.style.fontFamily = 'monospace' + panel.id = 'benchmark-panel' + + const pre = document.createElement('pre') + panel.appendChild(pre) + + pre.textContent = stats.join('\n') + const updateStats = () => { + stats = getAllInfoLines(window.benchmarkAdapter) + pre.textContent = stats.join('\n') + } + + document.body.appendChild(panel) + // setInterval(updateStats, 100) + }) } +document.addEventListener('pointerlockchange', (e) => { + const panel = document.querySelector('#benchmark-panel') + if (panel) { + panel.hidden = !!document.pointerLockElement + } +}) + +subscribe(activeModalStack, () => { + const panel = document.querySelector('#benchmark-panel') + if (panel && activeModalStack.length > 1) { + panel.hidden = true + } +}) + export const registerOpenBenchmarkListener = () => { - const params = new URLSearchParams(window.location.search) - if (params.get('openBenchmark')) { - void openBenchmark(params.has('renderDistance') ? +params.get('renderDistance')! : undefined) + if (appQueryParams.openBenchmark) { + void openBenchmark(appQueryParams.renderDistance ? +appQueryParams.renderDistance : undefined) } window.addEventListener('keydown', (e) => { diff --git a/src/benchmarkAdapter.ts b/src/benchmarkAdapter.ts index 327af715..446a6df6 100644 --- a/src/benchmarkAdapter.ts +++ b/src/benchmarkAdapter.ts @@ -1,8 +1,52 @@ -export interface BenchmarkAdapter { - worldLoadTime: number - averageRenderTime: number - worstRenderTime: number - memoryUsageAverage: number - memoryUsageWorst: number - gpuInfo: string +import { noCase } from 'change-case' + +export interface BenchmarkAdapterInfo { + // general load info + worldLoadTimeSeconds: number + + // rendering backend + averageRenderTimeMs: number + worstRenderTimeMs: number + fpsAveragePrediction: number + fpsWorstPrediction: number + fpsAverageReal: number + fpsWorstReal: number + + // memory total + memoryUsageAverage: string + memoryUsageWorst: string + + // context info + gpuInfo: string + hardwareConcurrency: number + userAgent: string +} + +export const getAllInfo = (adapter: BenchmarkAdapterInfo) => { + return Object.fromEntries( + Object.entries(adapter).map(([key, value]) => { + if (typeof value === 'function') { + value = (value as () => any)() + } + if (typeof value === 'number') { + value = value.toFixed(2) + } + return [noCase(key), value] + }) + ) +} + +export const getAllInfoLines = (adapter: BenchmarkAdapterInfo, delayed = false) => { + const info = getAllInfo(adapter) + if (delayed) { + for (const key in info) { + if (key !== 'fpsAveragePrediction' && key !== 'fpsAverageReal') { + delete info[key] + } + } + } + + return Object.entries(info).map(([key, value]) => { + return `${key}${delayed ? ' (delayed)' : ''}: ${value}` + }) } diff --git a/src/index.ts b/src/index.ts index dddd003d..fca2d8c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -657,13 +657,15 @@ export async function connect (connectOptions: ConnectOptions) { setLoadingScreenStatus('Loading world') }) - const loadStart = Date.now() let worldWasReady = false const waitForChunksToLoad = async (progress?: ProgressReporter) => { await new Promise(resolve => { + if (worldWasReady) { + resolve() + return + } const unsub = subscribe(appViewer.rendererState, () => { - if (worldWasReady) return - if (appViewer.rendererState.world.allChunksLoaded) { + if (appViewer.rendererState.world.allChunksLoaded && appViewer.nonReactiveState.world.chunksTotalNumber) { worldWasReady = true resolve() unsub() @@ -675,11 +677,6 @@ export async function connect (connectOptions: ConnectOptions) { }) } - void waitForChunksToLoad().then(() => { - console.log('All chunks done and ready! Time from renderer connect to ready', (Date.now() - loadStart) / 1000, 's') - document.dispatchEvent(new Event('cypress-world-ready')) - }) - const spawnEarlier = !singleplayer && !p2pMultiplayer const displayWorld = async () => { if (resourcePackState.isServerInstalling) { @@ -692,11 +689,18 @@ export async function connect (connectOptions: ConnectOptions) { }) await appViewer.resourcesManager.promiseAssetsReady } - console.log('try to focus window') - window.focus?.() errorAbortController.abort() if (appStatusState.isError) return + const loadWorldStart = Date.now() + console.log('try to focus window') + window.focus?.() + void waitForChunksToLoad().then(() => { + window.worldLoadTime = (Date.now() - loadWorldStart) / 1000 + console.log('All chunks done and ready! Time from renderer connect to ready', (Date.now() - loadWorldStart) / 1000, 's') + document.dispatchEvent(new Event('cypress-world-ready')) + }) + try { if (p2pConnectTimeout) clearTimeout(p2pConnectTimeout) playerState.onlineMode = !!connectOptions.authenticatedAccount diff --git a/src/optionsStorage.ts b/src/optionsStorage.ts index 3d021903..4d76ba0c 100644 --- a/src/optionsStorage.ts +++ b/src/optionsStorage.ts @@ -65,6 +65,7 @@ const defaultOptions = { waitForChunksRender: 'sp-only' as 'sp-only' | boolean, jeiEnabled: true as boolean | Array<'creative' | 'survival' | 'adventure' | 'spectator'>, preventBackgroundTimeoutKick: false, + preventSleep: false, // antiAliasing: false, From 7ee48643b61b858ba5b18510045da9afe4fe2ee0 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 6 Apr 2025 23:49:54 +0300 Subject: [PATCH 11/18] workers info --- src/benchmark.ts | 9 +++++++++ src/benchmarkAdapter.ts | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/src/benchmark.ts b/src/benchmark.ts index dba92f65..66763b68 100644 --- a/src/benchmark.ts +++ b/src/benchmark.ts @@ -41,6 +41,15 @@ export const openBenchmark = async (renderDistance = DEFAULT_RENDER_DISTANCE) => get worldLoadTimeSeconds () { return window.worldLoadTime }, + get mesherWorkersCount () { + return (window.world as WorldRendererCommon).worldRendererConfig.mesherWorkers + }, + get mesherProcessAvgMs () { + return (window.world as WorldRendererCommon).workersProcessAverageTime + }, + get mesherProcessWorstMs () { + return (window.world as WorldRendererCommon).maxWorkersProcessTime + }, get averageRenderTimeMs () { return (window.world as WorldRendererCommon).renderTimeAvg }, diff --git a/src/benchmarkAdapter.ts b/src/benchmarkAdapter.ts index 446a6df6..456654fe 100644 --- a/src/benchmarkAdapter.ts +++ b/src/benchmarkAdapter.ts @@ -4,6 +4,11 @@ export interface BenchmarkAdapterInfo { // general load info worldLoadTimeSeconds: number + // mesher + mesherWorkersCount: number + mesherProcessAvgMs: number + mesherProcessWorstMs: number + // rendering backend averageRenderTimeMs: number worstRenderTimeMs: number From 10855f8a6669b6cc95adbd5d8a78ff9940b1e8c1 Mon Sep 17 00:00:00 2001 From: Vitaly Date: Sun, 6 Apr 2025 23:52:59 +0300 Subject: [PATCH 12/18] Update src/mineflayer/timers.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/mineflayer/timers.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mineflayer/timers.ts b/src/mineflayer/timers.ts index b1cb5c41..f4b6045f 100644 --- a/src/mineflayer/timers.ts +++ b/src/mineflayer/timers.ts @@ -19,19 +19,24 @@ customEvents.on('mineflayerBotCreated', () => { // wake lock const requestWakeLock = async () => { ++ if (!('wakeLock' in navigator)) { ++ console.warn('Wake Lock API is not supported in this browser'); ++ return; ++ } + if (options.preventSleep && !bot.wakeLock && !bot.lockRequested) { bot.lockRequested = true bot.wakeLock = await navigator.wakeLock.request('screen').finally(() => { bot.lockRequested = false }) - + bot.wakeLock.addEventListener('release', () => { bot.wakeLock = undefined }, { once: true, }) } - + if (!options.preventSleep && bot.wakeLock) { void bot.wakeLock.release() } From 3685c842f27af4f0c5e835e9ca0bf1b7ce7ef361 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 6 Apr 2025 23:56:49 +0300 Subject: [PATCH 13/18] fixes --- .github/workflows/benchmark.yml | 19 +++++++++++++------ src/mineflayer/timers.ts | 14 +++++++------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b2822270..d78c2e98 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -8,11 +8,11 @@ on: jobs: deploy: runs-on: ubuntu-latest - # if: >- - # github.event.issue.pull_request != '' && - # ( - # contains(github.event.comment.body, '/benchmark') - # ) + if: >- + github.event.issue.pull_request != '' && + ( + contains(github.event.comment.body, '/benchmark') + ) permissions: pull-requests: write steps: @@ -30,8 +30,15 @@ jobs: - run: pnpm build - run: nohup pnpm prod-start & - run: pnpm test:benchmark + id: benchmark + continue-on-error: true # read benchmark results from stdout - - run: echo "BENCHMARK_RESULT=$(cat benchmark.txt)" >> $GITHUB_ENV + - run: | + if [ -f benchmark.txt ]; then + echo "BENCHMARK_RESULT=$(cat benchmark.txt)" >> $GITHUB_ENV + else + echo "BENCHMARK_RESULT=Benchmark failed to run or produce results" >> $GITHUB_ENV + fi - uses: mshick/add-pr-comment@v2 with: allow-repeats: true diff --git a/src/mineflayer/timers.ts b/src/mineflayer/timers.ts index f4b6045f..99110718 100644 --- a/src/mineflayer/timers.ts +++ b/src/mineflayer/timers.ts @@ -19,24 +19,24 @@ customEvents.on('mineflayerBotCreated', () => { // wake lock const requestWakeLock = async () => { -+ if (!('wakeLock' in navigator)) { -+ console.warn('Wake Lock API is not supported in this browser'); -+ return; -+ } - + if (!('wakeLock' in navigator)) { + console.warn('Wake Lock API is not supported in this browser') + return + } + if (options.preventSleep && !bot.wakeLock && !bot.lockRequested) { bot.lockRequested = true bot.wakeLock = await navigator.wakeLock.request('screen').finally(() => { bot.lockRequested = false }) - + bot.wakeLock.addEventListener('release', () => { bot.wakeLock = undefined }, { once: true, }) } - + if (!options.preventSleep && bot.wakeLock) { void bot.wakeLock.release() } From 2071095c32b7eb0c37a9efe90bee0a16f40776e3 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 7 Apr 2025 00:11:25 +0300 Subject: [PATCH 14/18] increase timeout --- cypress/e2e/rendering_performance.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/rendering_performance.spec.ts b/cypress/e2e/rendering_performance.spec.ts index a12429d4..2ca85329 100644 --- a/cypress/e2e/rendering_performance.spec.ts +++ b/cypress/e2e/rendering_performance.spec.ts @@ -5,7 +5,7 @@ import { cleanVisit } from './shared' it('Benchmark rendering performance', () => { cleanVisit('/?openBenchmark=true&renderDistance=5') // wait for render end event - return cy.document().then({ timeout: 120_000 }, doc => { + return cy.document().then({ timeout: 180_000 }, doc => { return new Cypress.Promise(resolve => { cy.log('Waiting for world to load') doc.addEventListener('cypress-world-ready', resolve) From 2e781f993b7bab40505a25bcbf5fee178176083c Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 7 Apr 2025 01:37:19 +0300 Subject: [PATCH 15/18] Update Cypress specPattern to use smoke tests instead of e2e tests --- cypress.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress.config.ts b/cypress.config.ts index d5735087..3bf2c720 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -34,7 +34,7 @@ export default defineConfig({ return require('./cypress/plugins/index.js')(on, config) }, baseUrl: 'http://localhost:8080', - specPattern: !isPerformanceTest ? 'cypress/e2e/**/*.spec.ts' : 'cypress/e2e/rendering_performance.spec.ts', + specPattern: !isPerformanceTest ? 'cypress/e2e/smoke.spec.ts' : 'cypress/e2e/rendering_performance.spec.ts', excludeSpecPattern: ['**/__snapshots__/*', '**/__image_snapshots__/*'], }, }) From 7398435f08e7733a664c5a81c4dc11afb52178ea Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 7 Apr 2025 01:37:56 +0300 Subject: [PATCH 16/18] jklh --- .github/workflows/benchmark.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index d78c2e98..da0ed806 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -8,11 +8,11 @@ on: jobs: deploy: runs-on: ubuntu-latest - if: >- - github.event.issue.pull_request != '' && - ( - contains(github.event.comment.body, '/benchmark') - ) + # if: >- + # github.event.issue.pull_request != '' && + # ( + # contains(github.event.comment.body, '/benchmark') + # ) permissions: pull-requests: write steps: From 14bdceb869e97322006dc8177d4de28c33ca4a20 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 7 Apr 2025 02:07:06 +0300 Subject: [PATCH 17/18] a --- .github/workflows/benchmark.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index da0ed806..1bee9e5c 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -35,7 +35,11 @@ jobs: # read benchmark results from stdout - run: | if [ -f benchmark.txt ]; then - echo "BENCHMARK_RESULT=$(cat benchmark.txt)" >> $GITHUB_ENV + # Format the benchmark results for GitHub comment + BENCHMARK_RESULT=$(cat benchmark.txt | sed 's/^/- /') + echo "BENCHMARK_RESULT<> $GITHUB_ENV + echo "$BENCHMARK_RESULT" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV else echo "BENCHMARK_RESULT=Benchmark failed to run or produce results" >> $GITHUB_ENV fi From 3ffcef40fa66317ec18299c23f871ea3816cfd0e Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Mon, 7 Apr 2025 02:21:12 +0300 Subject: [PATCH 18/18] [skip ci] bench write name, map --- src/appParams.ts | 2 ++ src/benchmark.ts | 29 +++++++++++++++++++++-------- src/benchmarkAdapter.ts | 1 + 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/appParams.ts b/src/appParams.ts index eb2b4ba2..994a5e16 100644 --- a/src/appParams.ts +++ b/src/appParams.ts @@ -56,6 +56,8 @@ export type AppQsParams = { openBenchmark?: string renderDistance?: string downloadBenchmark?: string + benchmarkMapZipUrl?: string + benchmarkPosition?: string } export type AppQsParamsArray = { diff --git a/src/benchmark.ts b/src/benchmark.ts index 66763b68..b3efa307 100644 --- a/src/benchmark.ts +++ b/src/benchmark.ts @@ -13,16 +13,21 @@ const DEFAULT_RENDER_DISTANCE = 8 const fixtures = { default: { url: 'https://bucket.mcraft.fun/Future CITY 4.4-slim.zip', - spawn: [-133, 87, 309] as const, + spawn: [-133, 87, 309] as [number, number, number], }, } -const testWorldFixtureUrl = fixtures.default.url -const testWorldFixtureSpawn = fixtures.default.spawn - Error.stackTraceLimit = Error.stackTraceLimit < 30 ? 30 : Error.stackTraceLimit export const openBenchmark = async (renderDistance = DEFAULT_RENDER_DISTANCE) => { + const fixture: { + url: string + spawn?: [number, number, number] + } = appQueryParams.benchmarkMapZipUrl ? { + url: appQueryParams.benchmarkMapZipUrl, + spawn: appQueryParams.benchmarkPosition ? appQueryParams.benchmarkPosition.split(',').map(Number) as [number, number, number] : fixtures.default.spawn, + } : fixtures.default + let memoryUsageAverage = 0 let memoryUsageSamples = 0 let memoryUsageWorst = 0 @@ -37,7 +42,15 @@ export const openBenchmark = async (renderDistance = DEFAULT_RENDER_DISTANCE) => } }, 200) + let benchmarkName = `${fixture.url}` + if (fixture.spawn) { + benchmarkName += ` - ${fixture.spawn.join(',')}` + } + benchmarkName += ` - ${renderDistance}` const benchmarkAdapter: BenchmarkAdapterInfo = { + get benchmarkName () { + return benchmarkName + }, get worldLoadTimeSeconds () { return window.worldLoadTime }, @@ -90,14 +103,14 @@ export const openBenchmark = async (renderDistance = DEFAULT_RENDER_DISTANCE) => disabledSettings.value.add('renderDistance') options.renderDistance = renderDistance - void downloadAndOpenMapFromUrl(testWorldFixtureUrl, undefined, { + void downloadAndOpenMapFromUrl(fixture.url, undefined, { connectEvents: { serverCreated () { - if (testWorldFixtureSpawn) { - localServer!.spawnPoint = new Vec3(...testWorldFixtureSpawn) + if (fixture.spawn) { + localServer!.spawnPoint = new Vec3(...fixture.spawn) localServer!.on('newPlayer', (player) => { player.on('dataLoaded', () => { - player.position = new Vec3(...testWorldFixtureSpawn) + player.position = new Vec3(...fixture.spawn!) }) }) } diff --git a/src/benchmarkAdapter.ts b/src/benchmarkAdapter.ts index 456654fe..85fb360b 100644 --- a/src/benchmarkAdapter.ts +++ b/src/benchmarkAdapter.ts @@ -1,6 +1,7 @@ import { noCase } from 'change-case' export interface BenchmarkAdapterInfo { + benchmarkName: string // general load info worldLoadTimeSeconds: number