feat: toggle chunk section border visibility by f3+g

This commit is contained in:
Vitaly Turovsky 2023-10-08 21:32:43 +03:00
commit 1d4e0955c4
7 changed files with 62 additions and 42 deletions

View file

@ -54,9 +54,9 @@ However, there are many things that can be done in online version. You can acces
- `bot` - Mineflayer bot instance. See Mineflayer documentation for more.
- `viewer` - Three.js viewer instance, basically does all the rendering.
- `viewer.world.sectionMeshs` - Object with all active chunk sections (geometries) in the world. Each chunk section is a Three.js mesh or group.
- `localServer` - Only for singleplayer host / guest mode. Flying Squid server instance, see it's documentation for more.
- `localServer.overworld.storageProvider.regions` - See ALL LOADED region files with all raw data!
- `viewer.world.sectionObjects` - Object with all active chunk sections (geometries) in the world. Each chunk section is a Three.js mesh or group.
- `localServer` - Only for singleplayer mode/host. Flying Squid server instance, see it's documentation for more.
- `localServer.overworld.storageProvider.regions` - See ALL LOADED region files with all raw data.
- `nbt.simplify(someNbt)` - Simplifies nbt data, so it's easier to read.

View file

@ -42,6 +42,7 @@
"socket.io": "^4.0.0",
"socket.io-client": "^4.0.0",
"three.meshline": "^1.3.0",
"vec3": "^0.1.7"
"vec3": "^0.1.7",
"node-canvas-webgl": "^0.3.0"
}
}

View file

@ -18,7 +18,8 @@ function mod (x, n) {
class WorldRenderer {
constructor (scene, numWorkers = 4) {
this.blockEntities = {}
this.sectionMeshs = {}
this.sectionObjects = {}
this.showChunkBorders = false
this.active = false
this.version = undefined
/** @type {THREE.Scene} */
@ -47,11 +48,11 @@ class WorldRenderer {
})
if (data.type === 'geometry') {
/** @type {THREE.Object3D} */
let mesh = this.sectionMeshs[data.key]
if (mesh) {
this.scene.remove(mesh)
dispose3(mesh)
delete this.sectionMeshs[data.key]
let object = this.sectionObjects[data.key]
if (object) {
this.scene.remove(object)
dispose3(object)
delete this.sectionObjects[data.key]
}
const chunkCoords = data.key.split(',')
@ -64,25 +65,25 @@ class WorldRenderer {
geometry.setAttribute('uv', new THREE.BufferAttribute(data.geometry.uvs, 2))
geometry.setIndex(data.geometry.indices)
const _mesh = new THREE.Mesh(geometry, this.material)
_mesh.position.set(data.geometry.sx, data.geometry.sy, data.geometry.sz)
const boxHelper = new THREE.BoxHelper(_mesh, 0xffff00)
// shouldnt it compute once
const mesh = new THREE.Mesh(geometry, this.material)
mesh.position.set(data.geometry.sx, data.geometry.sy, data.geometry.sz)
object = new THREE.Group()
object.add(mesh)
if (this.showChunkBorders) {
const boxHelper = new THREE.BoxHelper(mesh, 0xffff00)
object.add(boxHelper)
}
// should not it compute once
if (Object.keys(data.geometry.signs).length) {
mesh = new THREE.Group()
mesh.add(_mesh)
mesh.add(boxHelper)
for (const [posKey, { isWall, rotation }] of Object.entries(data.geometry.signs)) {
const [x, y, z] = posKey.split(',')
const signBlockEntity = this.blockEntities[posKey]
if (!signBlockEntity) continue
mesh.add(this.renderSign(new Vec3(+x, +y, +z), rotation, isWall, nbt.simplify(signBlockEntity)))
object.add(this.renderSign(new Vec3(+x, +y, +z), rotation, isWall, nbt.simplify(signBlockEntity)))
}
} else {
mesh = _mesh
}
this.sectionMeshs[data.key] = mesh
this.scene.add(mesh)
this.sectionObjects[data.key] = object
this.scene.add(object)
} else if (data.type === 'sectionFinished') {
this.sectionsOutstanding.delete(data.key)
this.renderUpdateEmitter.emit('update')
@ -125,10 +126,10 @@ class WorldRenderer {
resetWorld () {
this.active = false
for (const mesh of Object.values(this.sectionMeshs)) {
for (const mesh of Object.values(this.sectionObjects)) {
this.scene.remove(mesh)
}
this.sectionMeshs = {}
this.sectionObjects = {}
this.loadedChunks = {}
this.sectionsOutstanding = new Set()
for (const worker of this.workers) {
@ -196,12 +197,12 @@ class WorldRenderer {
for (let y = 0; y < 256; y += 16) {
this.setSectionDirty(new Vec3(x, y, z), false)
const key = `${x},${y},${z}`
const mesh = this.sectionMeshs[key]
const mesh = this.sectionObjects[key]
if (mesh) {
this.scene.remove(mesh)
dispose3(mesh)
}
delete this.sectionMeshs[key]
delete this.sectionObjects[key]
}
}

View file

@ -8,6 +8,7 @@ import { CommandEventArgument, SchemaCommandInput } from 'contro-max/build/types
import { stringStartsWith } from 'contro-max/build/stringUtils'
import { isGameActive, showModal, gameAdditionalState, activeModalStack, hideCurrentModal } from './globalState'
import { reloadChunks } from './utils'
import { options } from './optionsStorage'
// doesnt seem to work for now
const customKeymaps = proxy(JSON.parse(localStorage.keymap || '{}'))
@ -48,12 +49,12 @@ export const contro = new ControMax({
},
}, {
target: document,
captureEvents() {
captureEvents () {
return bot && isGameActive(false)
},
storeProvider: {
load: () => customKeymaps,
save() { },
save () { },
},
gamepadPollingInterval: 10
})
@ -103,10 +104,10 @@ let lastCommandTrigger = null as { command: string, time: number } | null
const secondActionActivationTimeout = 300
const secondActionCommands = {
'general.jump'() {
'general.jump' () {
toggleFly()
},
'general.forward'() {
'general.forward' () {
setSprinting(true)
}
}
@ -233,14 +234,23 @@ document.addEventListener('keydown', (e) => {
}
reloadChunks()
}
if (e.code === 'KeyG') {
// todo make it work without reload
options.showChunkBorders = !options.showChunkBorders
}
return
}
if (hardcodedPressedKeys.has(e.code)) return
hardcodedPressedKeys.add(e.code)
})
document.addEventListener('keyup', (e) => {
hardcodedPressedKeys.delete(e.code)
})
document.addEventListener('visibilitychange', (e) => {
if (document.visibilityState === 'hidden') {
hardcodedPressedKeys.clear()
}
})
// #region creative fly
// these controls are more like for gamemode 3

View file

@ -33,7 +33,7 @@ import { contro } from './controls'
import './dragndrop'
import './browserfs'
import './eruda'
import './watchOptions'
import { watchOptionsAfterViewerInit } from './watchOptions'
import downloadAndOpenFile from './downloadAndOpenFile'
import net from 'net'
@ -111,6 +111,7 @@ window.viewer = viewer
viewer.entities.entitiesOptions = {
fontFamily: 'mojangles'
}
watchOptionsAfterViewerInit()
initPanoramaOptions(viewer)
watchTexturepackInViewer(viewer)
@ -163,7 +164,7 @@ const updateCursor = () => {
debugMenu ??= hud.shadowRoot.querySelector('#debug-overlay')
debugMenu.cursorBlock = blockInteraction.cursorBlock
}
function onCameraMove(e) {
function onCameraMove (e) {
if (e.type !== 'touchmove' && !pointerLock.hasPointerLock) return
e.stopPropagation?.()
const now = performance.now()
@ -181,12 +182,12 @@ function onCameraMove(e) {
window.addEventListener('mousemove', onCameraMove, { capture: true })
function hideCurrentScreens() {
function hideCurrentScreens () {
activeModalStacks['main-menu'] = [...activeModalStack]
insertActiveModalStack('', [])
}
async function main() {
async function main () {
const menu = document.getElementById('play-screen')
menu.addEventListener('connect', e => {
const options = e.detail
@ -234,7 +235,7 @@ const cleanConnectIp = (host: string | undefined, defaultPort: string | undefine
}
}
async function connect(connectOptions: {
async function connect (connectOptions: {
server?: string; singleplayer?: any; username?: string; password?: any; proxy?: any; botVersion?: any; serverOverrides?; peerId?: string
}) {
document.getElementById('play-screen').style = 'display: none;'
@ -377,7 +378,7 @@ async function connect(connectOptions: {
} : {},
...singeplayer ? {
version: serverOptions.version,
connect() { },
connect () { },
Client: CustomChannelClient as any,
} : {},
username,
@ -387,7 +388,7 @@ async function connect(connectOptions: {
noPongTimeout: 240 * 1000,
closeTimeout: 240 * 1000,
respawn: options.autoRespawn,
async versionSelectedHook(client) {
async versionSelectedHook (client) {
// todo keep in sync with esbuild preload, expose cache ideally
if (client.version === '1.20.1') {
// ignore cache hit
@ -542,7 +543,7 @@ async function connect(connectOptions: {
dayCycle()
// Bot position callback
function botPosition() {
function botPosition () {
// this might cause lag, but not sure
viewer.setFirstPersonCamera(bot.entity.position, bot.entity.yaw, bot.entity.pitch)
worldView.updatePosition(bot.entity.position)
@ -560,7 +561,7 @@ async function connect(connectOptions: {
bot.entity.yaw -= x
}
function changeCallback() {
function changeCallback () {
notification.show = false
if (!pointerLock.hasPointerLock && activeModalStack.length === 0) {
showModal(pauseMenu)

View file

@ -27,6 +27,7 @@ const defaultOptions = {
touchButtonsSize: 40,
highPerformanceGpu: false,
showChunkBorders: false,
frameLimit: false as number | false,
alwaysBackupWorldBeforeLoading: undefined as boolean | undefined | null,
alwaysShowMobileControls: false,
@ -61,7 +62,7 @@ type WatchValue = <T extends Record<string, any>>(proxy: T, callback: (p: T) =>
export const watchValue: WatchValue = (proxy, callback) => {
const watchedProps = new Set<string>()
callback(new Proxy(proxy, {
get(target, p, receiver) {
get (target, p, receiver) {
watchedProps.add(p.toString())
return Reflect.get(target, p, receiver)
},

View file

@ -1,7 +1,13 @@
// not all options are watched here
import { subscribeKey } from 'valtio/utils'
import { options } from './optionsStorage'
import { options, watchValue } from './optionsStorage'
import { reloadChunks } from './utils'
subscribeKey(options, 'renderDistance', reloadChunks)
export const watchOptionsAfterViewerInit = () => {
watchValue(options, o => {
viewer.world.showChunkBorders = o.showChunkBorders
})
}