initial implementation that should work already!
This commit is contained in:
parent
b9f3e670ff
commit
e9aa0e9891
14 changed files with 253 additions and 52 deletions
41
.github/workflows/benchmark.yml
vendored
Normal file
41
.github/workflows/benchmark.yml
vendored
Normal file
|
|
@ -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 }}
|
||||
|
|
@ -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__/*'],
|
||||
},
|
||||
})
|
||||
|
|
|
|||
37
cypress/e2e/rendering_performance.spec.ts
Normal file
37
cypress/e2e/rendering_performance.spec.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/// <reference types="cypress" />
|
||||
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'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
33
pnpm-lock.yaml
generated
33
pnpm-lock.yaml
generated
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|||
signsCache = new Map<string, any>()
|
||||
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) {
|
||||
|
|
|
|||
73
src/benchmark.ts
Normal file
73
src/benchmark.ts
Normal file
|
|
@ -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()
|
||||
}
|
||||
})
|
||||
}
|
||||
7
src/benchmarkAdapter.ts
Normal file
7
src/benchmarkAdapter.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export interface BenchmarkAdapter {
|
||||
worldLoadTime: number
|
||||
averageRenderTime: number
|
||||
worstRenderTime: number
|
||||
memoryUsageAverage: number
|
||||
memoryUsageWorst: number
|
||||
}
|
||||
|
|
@ -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<ConnectOptions>) => {
|
||||
await new Promise<void>(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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ConnectOptions>) => {
|
||||
// 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
|
||||
|
|
|
|||
24
src/index.ts
24
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<ConnectOptions> = {}) => {
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -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<string, any> }
|
||||
}
|
||||
|
||||
export const loadSave = async (root = '/world') => {
|
||||
export const loadSave = async (root = '/world', connectOptions?: Partial<ConnectOptions>) => {
|
||||
// todo test
|
||||
if (miscUiState.gameLoaded) {
|
||||
await disconnect()
|
||||
|
|
@ -189,7 +190,8 @@ export const loadSave = async (root = '/world') => {
|
|||
} : {},
|
||||
...root === '/world' ? {} : {
|
||||
'worldFolder': root
|
||||
}
|
||||
},
|
||||
connectOptions
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue