From 32d52e987852fdf139c6d351b88223845f212fa3 Mon Sep 17 00:00:00 2001 From: extremeheat Date: Sat, 3 Apr 2021 22:54:42 -0400 Subject: [PATCH] viewer: refactor and cleanup --- examples/viewer/client/BotProvider.js | 316 ++--------------------- examples/viewer/client/BotViewer.js | 11 +- examples/viewer/client/Chunk.js | 40 +-- examples/viewer/client/ClientProvider.js | 68 +++++ examples/viewer/client/ProxyProvider.js | 62 +---- examples/viewer/client/movements.js | 244 +++++++++++++++++ examples/viewer/client/util.js | 16 ++ src/connection.js | 4 +- tools/startVanillaServer.js | 2 +- 9 files changed, 366 insertions(+), 397 deletions(-) create mode 100644 examples/viewer/client/ClientProvider.js create mode 100644 examples/viewer/client/movements.js create mode 100644 examples/viewer/client/util.js diff --git a/examples/viewer/client/BotProvider.js b/examples/viewer/client/BotProvider.js index f4d0d37..fe225cf 100644 --- a/examples/viewer/client/BotProvider.js +++ b/examples/viewer/client/BotProvider.js @@ -1,15 +1,9 @@ /* eslint-disable */ -const { Client } = require('bedrock-protocol') const { Version } = require('bedrock-provider') const { WorldView } = require('prismarine-viewer/viewer') -const vec3 = require('vec3') const World = require('prismarine-world')() const ChunkColumn = require('./Chunk')() -const { Physics, PlayerState } = require('prismarine-physics') -const { performance } = require('perf_hooks') - -const PHYSICS_INTERVAL_MS = 50 -const PHYSICS_TIMESTEP = PHYSICS_INTERVAL_MS / 1000 +const { MovementManager } = require('./movements') class BotProvider extends WorldView { chunks = {} @@ -21,72 +15,7 @@ class BotProvider extends WorldView { this.connect() this.listenToBot() this.world = new World() - - // Server auth movement : we send inputs, server calculates position & sends back - this.serverMovements = true - this.tick = 0n - } - - connect() { - const client = new Client({ hostname: '127.0.0.1', version: '1.16.210', port: 19132, connectTimeout: 100000 }) - - client.once('resource_packs_info', (packet) => { - client.write('resource_pack_client_response', { - response_status: 'completed', - resourcepackids: [] - }) - - client.once('resource_pack_stack', (stack) => { - client.write('resource_pack_client_response', { - response_status: 'completed', - resourcepackids: [] - }) - }) - - client.queue('client_cache_status', { enabled: false }) - client.queue('request_chunk_radius', { chunk_radius: 1 }) - client.queue('tick_sync', { request_time: BigInt(Date.now()), response_time: 0n }) - }) - - this.client = client - } - - close() { - this.client?.close() - } - - listenToBot() { - this.client.on('connect', () => { - console.log('Bot has connected!') - }) - this.client.on('start_game', packet => { - this.updatePosition(packet.player_position) - }) - - this.client.on('spawn', () => { - // server allows client to render chunks & spawn in world - this.emit('spawn', { position: this.lastPos }) - - this.tickLoop = setInterval(() => { - this.client.queue('tick_sync', { request_time: BigInt(Date.now()), response_time: 0n }) - }) - }) - - this.client.on('level_chunk', packet => { - this.handleChunk(packet) - }) - - this.client.on('move_player', packet => { - if (packet.runtime_id === this.client.entityId) this.updatePosition(packet.position) - }) - - this.client.on('set_entity_motion', packet => { - if (packet.runtime_id === this.client.entityId) this.updatePosition(packet.position) - }) - - this.client.on('tick_sync', (packet) => { - this.lastTick = packet.response_time - }) + this.movements = new MovementManager(this) } handleChunk(packet, render = true) { @@ -102,232 +31,27 @@ class BotProvider extends WorldView { }) } + updatePlayerCamera(id, pos, yaw, pitch, updateState) { + // TODO: do this properly + window.viewer.viewer.entities.update({ + name: 'player', + id, + pos, + width: 0.6, + height: 1.8, + yaw, + pitch + }) + + if (updateState) { + this.movements.updatePosition(pos, yaw, pitch) + } + } + stopBot() { clearInterval(this.tickLoop) - } - - // Server gives us a new position - updatePosition(pos) { - this.lastPos ??= vec3(pos) - super.updatePosition(this.lastPos) - } - - // Ask the server to be in a new position - requestPosition(time, inputState) { - const positionUpdated = !this.lastSentPos || !this.lastPos.equals(this.lastSentPos) - // if (globalThis.logging)console.log('New pos', this.lastSentPos,this.lastPos) - - if (positionUpdated) { - this.lastSentPos = this.lastPos.clone() - console.log('We computed', this.lastPos) - this.pushCamera({ - position: this.lastSentPos, - input_data: {}, - yaw: this.playerState.yaw, pitch: this.playerState.pitch - }, 2) - return - this.client.queue('player_auth_input', { - pitch: this.player.pitch, - yaw: this.player.yaw, - position: { - x: this.lastPos.x, - y: this.lastPos.y, - z: this.lastPos.z - }, - move_vector: { // Minecraft coords, N: Z+1, S: Z-1, W: X+1, E: X-1 - x: inputState.left ? 1 : (inputState.right ? -1 : 0), - z: inputState.up ? 1 : (inputState.down ? -1 : 0) - }, - head_yaw: this.player.headYaw, - input_data: inputState, - input_mode: 'mouse', - play_mode: 'screen', - tick: this.tick, - delta: this.lastSentPos?.minus(this.lastPos) ?? { x: 0, y: 0, z: 0 } - }) - this.positionUpdated = false - this.lastSentPos = this.lastPos.clone() - } - } - - initPhys(position, velocity, yaw = 0, pitch = 0, headYaw = 0) { - this.lastPos = position ? vec3(position) : vec3(0, 0, 0) - this.lastVel = velocity ? vec3(velocity) : vec3(0, 0, 0) - this.player = { - version: '1.16.1', - inventory: { - slots: [] - }, - entity: { - effects: {}, - position: this.lastPos, - velocity: this.lastVel, - onGround: false, - isInWater: false, - isInLava: false, - isInWeb: false, - isCollidedHorizontally: false, - isCollidedVertically: false, - yaw, - pitch, - headYaw // bedrock - }, - events: { // Control events to send next tick - startSprint: false, - stopSprint: false, - startSneak: false, - stopSneak: false - }, - jumpTicks: 0, - jumpQueued: false, - downJump: false - } - - const mcData = require('minecraft-data')('1.16.1') - this.physics = Physics(mcData, this.world) - this.controls = { - forward: false, - back: false, - left: false, - right: false, - jump: false, - sprint: false, - sneak: false - } - this.playerState = new PlayerState(this.player, this.controls) - } - - // This function should be executed each tick (every 0.05 seconds) - // How it works: https://gafferongames.com/post/fix_your_timestep/ - timeAccumulator = 0 - lastPhysicsFrameTime = null - inputQueue = [] - doPhysics() { - const now = performance.now() - const deltaSeconds = (now - this.lastPhysicsFrameTime) / 1000 - this.lastPhysicsFrameTime = now - - this.timeAccumulator += deltaSeconds - - while (this.timeAccumulator >= PHYSICS_TIMESTEP) { - let q = this.inputQueue.shift() - if (q) { - Object.assign(this.playerState.control, q) - if (q.yaw) { this.player.entity.yaw = q.yaw; this.playerState.yaw = q.yaw; } - if (q.pitch) this.player.entity.pitch = q.pitch - } - this.physics.simulatePlayer(this.playerState, this.world.sync).apply(this.player) - this.lastPos = this.playerState.pos - this.requestPosition(PHYSICS_TIMESTEP, { - ascend: false, - descend: false, - // Players bob up and down in water, north jump is true when going up. - // In water this is only true after the player has reached max height before bobbing back down. - north_jump: this.player.jumpTicks > 0, // Jump - jump_down: this.controls.jump, // Jump - sprint_down: this.controls.sprint, - change_height: false, - jumping: this.controls.jump, // Jump - auto_jumping_in_water: false, - sneaking: false, - sneak_down: false, - up: this.controls.forward, - down: this.controls.back, - left: this.controls.left, - right: this.controls.right, - up_left: false, - up_right: false, - want_up: this.controls.jump, // Jump - want_down: false, - want_down_slow: false, - want_up_slow: false, - sprinting: false, - ascend_scaffolding: false, - descend_scaffolding: false, - sneak_toggle_down: false, - persist_sneak: false, - start_sprinting: this.player.events.startSprint || false, - stop_sprinting: this.player.events.stopSprint || false, - start_sneaking: this.player.events.startSneak || false, - stop_sneaking: this.player.events.stopSneak || false, - // Player is Update Aqatic swimming - start_swimming: false, - // Player stops Update Aqatic swimming - stop_swimming: false, - start_jumping: this.player.jumpTicks === 1, // Jump - start_gliding: false, - stop_gliding: false, - }) - this.timeAccumulator -= PHYSICS_TIMESTEP - } - } - - startPhys() { - console.log('Start phys') - this.physicsLoop = setInterval(() => { - this.doPhysics() - }, PHYSICS_INTERVAL_MS) - } - - setControlState(control, state) { - if (this.controls[control] === state) return - if (control === 'sprint') { - this.player.events.startSprint = state - this.player.events.stopSprint = !state - this.controls.sprint = true - } else if (control === 'sneak') { - this.player.events.startSneak = state - this.player.events.stopSneak = !state - this.controls.sprint = true - } - } - - pushInputState(state, yaw, pitch) { - const yawRad = d2r(yaw) - const pitchRad = d2r(pitch) - this.inputQueue.push({ - forward: state.up, - back: state.down,// TODO: left and right switched ??? - left: state.right, - right: state.left, - jump: state.jump_down, - sneak: state.sprint_down, - yaw: yawRad, pitch: pitchRad, - }) - globalThis.yaw = [yaw, yawRad] - if (global.logYaw) console.log('Pushed', yaw, pitch) - } - - pushCamera(state, id = 1) { - let { x, y, z } = state.position - if (id == 1) y -= 1.62 // account for player bb - const pos = vec3({ x, y, z }) - if (state.position) { - viewer.viewer.entities.update({ - name: 'player', - id, pos, width: 0.6, height: 1.8, - yaw: id == 1 ? d2r(state.yaw) : state.yaw - }) - - //viewer.viewer.camera.position.set(x, y, z) - } - - if (state.input_data.sneak_down) { - this.player.entity.position = pos - this.playerState.pos = this.player.entity.position - } - } - - onCameraMovement(newYaw, newPitch, newHeadYaw) { - this.player.yaw = newYaw - this.player.pitch = newPitch - this.player.headYaw = newHeadYaw - } - - stopPhys() { - clearInterval(this.physicsLoop) + this.movements.stopPhys() } } -const d2r = deg => (180 - (deg < 0 ? (360 + deg) : deg)) * (Math.PI / 180) module.exports = { BotProvider } diff --git a/examples/viewer/client/BotViewer.js b/examples/viewer/client/BotViewer.js index 7600b0a..a0987e7 100644 --- a/examples/viewer/client/BotViewer.js +++ b/examples/viewer/client/BotViewer.js @@ -1,21 +1,16 @@ /* global THREE */ const { Viewer, MapControls } = require('prismarine-viewer/viewer') -const { Vec3 } = require('vec3') -const { BotProvider } = require('./BotProvider') +// const { Vec3 } = require('vec3') +// const { BotProvider } = require('./BotProvider') const { ProxyProvider } = require('./ProxyProvider') global.THREE = require('three') const MCVER = '1.16.1' class BotViewer { - constructor () { - - } - start () { // this.bot = new BotProvider() this.bot = new ProxyProvider() - // return // Create three.js context, add to page this.renderer = new THREE.WebGLRenderer() this.renderer.setPixelRatio(window.devicePixelRatio || 1) @@ -61,8 +56,6 @@ class BotViewer { onKeyDown = (evt) => { console.log('Key down', evt) - // this.bot.initPhys() - // this.bot.startPhys() } registerBrowserEvents () { diff --git a/examples/viewer/client/Chunk.js b/examples/viewer/client/Chunk.js index b2ecc57..7733cc3 100644 --- a/examples/viewer/client/Chunk.js +++ b/examples/viewer/client/Chunk.js @@ -1,6 +1,4 @@ -const { ChunkColumn, Version } = require('bedrock-provider') -const { SubChunk } = require('bedrock-provider/js/SubChunk') -try { const v8 = require('v8') } catch { } +const { ChunkColumn } = require('bedrock-provider') const Block = require('prismarine-block')('1.16.1') @@ -13,42 +11,6 @@ class ChunkColumnWrapped extends ChunkColumn { // pchunk compatiblity wrapper getBlockStateId (pos) { return super.getBlock(pos.x, pos.y, pos.z)?.stateId } - - // // Serialization - // serialize() { - // if (typeof v8 === 'undefined') { - // return JSON.stringify(this) - // } else { - // const copy = { ...this, sections: [] } - // for (const section of this.sections) { - // copy.sections.push(v8.serialize(section)) - // } - // return v8.serialize(copy) - // } - // } - - // toJson() { return this.serialize() } - - // static deserialize(obj) { - // if (typeof obj === 'string') { - // Oject.assign(this, JSON.parse(obj)) - // } else { // Buffer - // const chunk = new ChunkColumnWrapped() - // const des = v8.deserialize(obj) - // Object.assign(chunk, des) - // chunk.sections = [] - // for (const section of des.sections) { - // const s = new SubChunk() - // chunk.sections.push(Object.assign(s, v8.deserialize(section))) - // } - // // console.log('Des',obj,chunk) - // return chunk - // } - // } - - // static fromJson(obj) { - // return ChunkColumnWrapped.deserialize(obj) - // } } module.exports = (version) => { diff --git a/examples/viewer/client/ClientProvider.js b/examples/viewer/client/ClientProvider.js new file mode 100644 index 0000000..8c13da8 --- /dev/null +++ b/examples/viewer/client/ClientProvider.js @@ -0,0 +1,68 @@ +const { Client } = require('bedrock-protocol') +const { BotProvider } = require('./BotProvider') + +class ClientProvider extends BotProvider { + connect () { + const client = new Client({ hostname: '127.0.0.1', version: '1.16.210', port: 19132, connectTimeout: 100000 }) + + client.once('resource_packs_info', (packet) => { + client.write('resource_pack_client_response', { + response_status: 'completed', + resourcepackids: [] + }) + + client.once('resource_pack_stack', (stack) => { + client.write('resource_pack_client_response', { + response_status: 'completed', + resourcepackids: [] + }) + }) + + client.queue('client_cache_status', { enabled: false }) + client.queue('request_chunk_radius', { chunk_radius: 1 }) + client.queue('tick_sync', { request_time: BigInt(Date.now()), response_time: 0n }) + }) + + this.client = client + } + + close () { + this.client?.close() + } + + listenToBot () { + this.client.on('connect', () => { + console.log('Bot has connected!') + }) + this.client.on('start_game', packet => { + this.updatePosition(packet.player_position) + }) + + this.client.on('spawn', () => { + // server allows client to render chunks & spawn in world + this.emit('spawn', { position: this.lastPos }) + + this.tickLoop = setInterval(() => { + this.client.queue('tick_sync', { request_time: BigInt(Date.now()), response_time: 0n }) + }) + }) + + this.client.on('level_chunk', packet => { + this.handleChunk(packet) + }) + + this.client.on('move_player', packet => { + if (packet.runtime_id === this.client.entityId) this.updatePosition(packet.position) + }) + + this.client.on('set_entity_motion', packet => { + if (packet.runtime_id === this.client.entityId) this.updatePosition(packet.position) + }) + + this.client.on('tick_sync', (packet) => { + this.lastTick = packet.response_time + }) + } +} + +module.exports = { ClientProvider } diff --git a/examples/viewer/client/ProxyProvider.js b/examples/viewer/client/ProxyProvider.js index 0db00df..305b4a4 100644 --- a/examples/viewer/client/ProxyProvider.js +++ b/examples/viewer/client/ProxyProvider.js @@ -1,6 +1,6 @@ const { Relay } = require('bedrock-protocol') const { BotProvider } = require('./BotProvider') -const vec3 = require('vec3') +const { diff } = require('./util') class ProxyProvider extends BotProvider { lastPlayerMovePacket @@ -15,61 +15,36 @@ class ProxyProvider extends BotProvider { port: 19132 } }) - proxy.listen() - console.info('Waiting for connect') - const maxChunks = 40 - proxy.on('join', (client, server) => { client.on('clientbound', ({ name, params }) => { - if (name == 'level_chunk') { - // maxChunks-- - // if (maxChunks >= 0) { - // this.handleChunk(params) - // } + if (name === 'level_chunk') { this.handleChunk(params, true) - } else if (name == 'start_game') { - this.initPhys(params.player_position, null, params.rotation.z, params.rotation.x, 0) + } else if (name === 'start_game') { + this.movements.init('', params.player_position, null, params.rotation.z, params.rotation.x, 0) } else if (name === 'play_status') { - // this.emit('spawn', { position: server.startGameData.player_position }) - - this.startPhys() + this.movements.startPhys() console.info('Started physics!') } else if (name === 'move_player') { - console.log('move_player', packet) - // if (packet.runtime_id === server.entityId) { - // this.updatePosition(packet.position) - // if (this.lastServerMovement.x == packet.position.x && this.lastServerMovement.y == packet.position.y && this.lastServerMovement.z == packet.position.z) { - - // } else { - // console.log('Server computed', packet.position) - // } - // this.lastServerMovement = { ...packet.position } - // } + console.log('move_player', params) + this.movements.updatePosition(params.position, params.yaw, params.pitch, params.head_yaw, params.tick) } + if (name.includes('entity') || name.includes('network_chunk_publisher_update') || name.includes('tick') || name.includes('level')) return console.log('CB', name) }) client.on('serverbound', ({ name, params }) => { // { name, params } - if (name == 'player_auth_input') { - // console.log('player_auth_input', this.lastPlayerMovePacket, params) - - // this.controls.forward = params.input_data.up - // this.controls.back = params.input_data.down - // this.controls.left = params.input_data.left - // this.controls.right = params.input_data.right - // this.player.entity.pitch = params.pitch - // this.player.entity.yaw = params.yaw - this.pushInputState(params.input_data, params.yaw, params.pitch) - this.pushCamera(params) - this.lastMovePacket = params + if (name === 'player_auth_input') { + this.movements.pushInputState(params.input_data, params.yaw, params.pitch) + this.movements.pushCameraControl(params, 1) // Log Movement deltas { + this.lastMovePacket = params if (this.firstPlayerMovePacket) { const id = diff(this.firstPlayerMovePacket.input_data, params.input_data) const md = diff(this.firstPlayerMovePacket.move_vector, params.move_vector) @@ -109,18 +84,5 @@ class ProxyProvider extends BotProvider { } } -const difference = (o1, o2) => Object.keys(o2).reduce((diff, key) => { - if (o1[key] === o2[key]) return diff - return { - ...diff, - [key]: o2[key] - } -}, {}) - -// console.log = () => {} -// console.debug = () => {} - -const diff = (o1, o2) => { const dif = difference(o1, o2); return Object.keys(dif).length ? dif : null } - module.exports = { ProxyProvider } globalThis.logging = true diff --git a/examples/viewer/client/movements.js b/examples/viewer/client/movements.js new file mode 100644 index 0000000..a405722 --- /dev/null +++ b/examples/viewer/client/movements.js @@ -0,0 +1,244 @@ +const { Physics, PlayerState } = require('prismarine-physics') +const { performance } = require('perf_hooks') +const { d2r } = require('./util') +const vec3 = require('vec3') + +const PHYSICS_INTERVAL_MS = 50 +const PHYSICS_TIMESTEP = PHYSICS_INTERVAL_MS / 1000 + +class MovementManager { + // Server auth movement : we send inputs, server calculates position & sends back + serverMovements = false + + constructor (bot) { + this.bot = bot + this.world = bot.world + // Physics tick + this.tick = 0n + } + + get lastPos () { return this.player.entity.position.clone() } + set lastPos (newPos) { this.player.entity.position.set(newPos.x, newPos.y, newPos.z) } + + get lastRot () { return vec3(this.player.entity.yaw, this.player.entity.pitch, this.player.entity.headYaw) } + + set lastRot (rot) { + this.player.entity.yaw = rot.x + this.player.entity.pitch = rot.y + if (rot.z) this.player.entity.headYaw = rot.z + } + + // Ask the server to be in a new position + requestPosition (time, inputState) { + const positionUpdated = !this.lastSentPos || !this.lastPos.equals(this.lastSentPos) + const rotationUpdated = !this.lastSentRot || !this.lastRot.equals(this.lastSentRot) + + if (positionUpdated) { + this.lastSentPos = this.lastPos.clone() + console.log('We computed', this.lastPos) + this.bot.updatePlayerCamera(2, this.lastSentPos, this.playerState.yaw, this.playerState.pitch) + if (this.serverMovements) { + this.client.queue('player_auth_input', { + pitch: this.player.pitch, + yaw: this.player.yaw, + position: { + x: this.lastPos.x, + y: this.lastPos.y, + z: this.lastPos.z + }, + move_vector: { // Minecraft coords, N: Z+1, S: Z-1, W: X+1, E: X-1 + x: inputState.left ? 1 : (inputState.right ? -1 : 0), + z: inputState.up ? 1 : (inputState.down ? -1 : 0) + }, + head_yaw: this.player.headYaw, + input_data: inputState, + input_mode: 'mouse', + play_mode: 'screen', + tick: this.tick, + delta: this.lastSentPos?.minus(this.lastPos) ?? { x: 0, y: 0, z: 0 } + }) + this.positionUpdated = false + } + + this.lastSentPos = this.lastPos + this.lastSentRot = this.lastRot + } + } + + init (movementAuthority, position, velocity, yaw = 0, pitch = 0, headYaw = 0) { + if (movementAuthority.includes('server')) { + this.serverMovements = true + } + this.player = { + version: '1.16.1', + inventory: { + slots: [] + }, + entity: { + effects: {}, + position: vec3(position), + velocity: vec3(velocity), + onGround: false, + isInWater: false, + isInLava: false, + isInWeb: false, + isCollidedHorizontally: false, + isCollidedVertically: false, + yaw, + pitch, + headYaw // bedrock + }, + events: { // Control events to send next tick + startSprint: false, + stopSprint: false, + startSneak: false, + stopSneak: false + }, + jumpTicks: 0, + jumpQueued: false, + downJump: false + } + + const mcData = require('minecraft-data')('1.16.1') + this.physics = Physics(mcData, this.world) + this.controls = { + forward: false, + back: false, + left: false, + right: false, + jump: false, + sprint: false, + sneak: false + } + } + + // This function should be executed each tick (every 0.05 seconds) + // How it works: https://gafferongames.com/post/fix_your_timestep/ + timeAccumulator = 0 + lastPhysicsFrameTime = null + inputQueue = [] + doPhysics () { + const now = performance.now() + const deltaSeconds = (now - this.lastPhysicsFrameTime) / 1000 + this.lastPhysicsFrameTime = now + + this.timeAccumulator += deltaSeconds + + while (this.timeAccumulator >= PHYSICS_TIMESTEP) { + const q = this.inputQueue.shift() + if (q) { + Object.assign(this.playerState.control, q) + if (q.yaw) this.player.entity.yaw = q.yaw + if (q.pitch) this.player.entity.pitch = q.pitch + } + this.playerState = new PlayerState(this.player, this.controls) + this.physics.simulatePlayer(this.playerState, this.world.sync).apply(this.player) + this.lastPos = this.playerState.pos + this.requestPosition(PHYSICS_TIMESTEP, { + ascend: false, + descend: false, + // Players bob up and down in water, north jump is true when going up. + // In water this is only true after the player has reached max height before bobbing back down. + north_jump: this.player.jumpTicks > 0, // Jump + jump_down: this.controls.jump, // Jump + sprint_down: this.controls.sprint, + change_height: false, + jumping: this.controls.jump, // Jump + auto_jumping_in_water: false, + sneaking: false, + sneak_down: false, + up: this.controls.forward, + down: this.controls.back, + left: this.controls.left, + right: this.controls.right, + up_left: false, + up_right: false, + want_up: this.controls.jump, // Jump + want_down: false, + want_down_slow: false, + want_up_slow: false, + sprinting: false, + ascend_scaffolding: false, + descend_scaffolding: false, + sneak_toggle_down: false, + persist_sneak: false, + start_sprinting: this.player.events.startSprint || false, + stop_sprinting: this.player.events.stopSprint || false, + start_sneaking: this.player.events.startSneak || false, + stop_sneaking: this.player.events.stopSneak || false, + // Player is Update Aqatic swimming + start_swimming: false, + // Player stops Update Aqatic swimming + stop_swimming: false, + start_jumping: this.player.jumpTicks === 1, // Jump + start_gliding: false, + stop_gliding: false + }) + this.timeAccumulator -= PHYSICS_TIMESTEP + } + } + + startPhys () { + console.log('Start phys') + this.physicsLoop = setInterval(() => { + this.doPhysics() + }, PHYSICS_INTERVAL_MS) + } + + setControlState (control, state) { + if (this.controls[control] === state) return + if (control === 'sprint') { + this.player.events.startSprint = state + this.player.events.stopSprint = !state + this.controls.sprint = true + } else if (control === 'sneak') { + this.player.events.startSneak = state + this.player.events.stopSneak = !state + this.controls.sprint = true + } + } + + stopPhys () { + clearInterval(this.physicsLoop) + } + + pushInputState (state, yaw, pitch) { + const yawRad = d2r(yaw) + const pitchRad = d2r(pitch) + this.inputQueue.push({ + forward: state.up, + back: state.down, // TODO: left and right switched ??? + left: state.right, + right: state.left, + jump: state.jump_down, + sneak: state.sneak_down, + yaw: yawRad, + pitch: pitchRad + }) + // debug + globalThis.debugYaw = [yaw, yawRad] + } + + pushCameraControl (state, id = 1) { + let { x, y, z } = state.position + if (id === 1) y -= 1.62 // account for player bb + const adjPos = vec3({ x, y, z }) + // Sneak resyncs the position for easy testing + this.bot.updatePlayerCamera(id, adjPos, d2r(state.yaw), d2r(state.pitch), state.input_data.sneak_down) + } + + // Server gives us a new position + updatePosition (pos, yaw, pitch, headYaw, tick) { + this.lastPos = pos + this.lastRot = { x: yaw, y: pitch, z: headYaw } + if (tick) this.tick = tick + } + + onViewerCameraMove (newYaw, newPitch, newHeadYaw) { + this.player.yaw = newYaw + this.player.pitch = newPitch + this.player.headYaw = newHeadYaw + } +} + +module.exports = { MovementManager } diff --git a/examples/viewer/client/util.js b/examples/viewer/client/util.js new file mode 100644 index 0000000..a22a646 --- /dev/null +++ b/examples/viewer/client/util.js @@ -0,0 +1,16 @@ +const difference = (o1, o2) => Object.keys(o2).reduce((diff, key) => { + if (o1[key] === o2[key]) return diff + return { + ...diff, + [key]: o2[key] + } +}, {}) + +const diff = (o1, o2) => { const dif = difference(o1, o2); return Object.keys(dif).length ? dif : null } + +const d2r = deg => (180 - (deg < 0 ? (360 + deg) : deg)) * (Math.PI / 180) + +module.exports = { + diff, + d2r +} diff --git a/src/connection.js b/src/connection.js index 6a55a1a..f4476bc 100644 --- a/src/connection.js +++ b/src/connection.js @@ -24,7 +24,7 @@ class Connection extends EventEmitter { } set status (val) { - this.inLog('* new status', val) + debug('* new status', val) this.#status = val } @@ -126,7 +126,7 @@ class Connection extends EventEmitter { sendEncryptedBatch (batch) { const buf = batch.stream.getBuffer() - debug('Sending encrypted batch', batch) + // debug('Sending encrypted batch', batch) this.encrypt(buf) } diff --git a/tools/startVanillaServer.js b/tools/startVanillaServer.js index 07587ae..a409fca 100644 --- a/tools/startVanillaServer.js +++ b/tools/startVanillaServer.js @@ -104,7 +104,7 @@ async function startServerAndWait (version, withTimeout, options) { if (!module.parent) { // if (process.argv.length < 3) throw Error('Missing version argument') - startServer(process.argv[2] || '1.16.201') + startServer(process.argv[2] || '1.16.201', null, process.argv[3] ? { 'server-port': process.argv[3] } : undefined) } module.exports = { fetchLatestStable, startServer, startServerAndWait }