diff --git a/src/events/events.gateway.ts b/src/events/events.gateway.ts index 7aa5d95..64a840a 100644 --- a/src/events/events.gateway.ts +++ b/src/events/events.gateway.ts @@ -65,7 +65,7 @@ export class EventsGateway await this.world.initializePlayer(player); } - if (this.players.handlePlayerPacket(data, player)) return false; + if (this.world.handlePlayerPacket(data, player)) return false; return false; } diff --git a/src/game/humanoid.object.ts b/src/game/humanoid.object.ts index 8b2e4e5..687bf58 100644 --- a/src/game/humanoid.object.ts +++ b/src/game/humanoid.object.ts @@ -1,4 +1,4 @@ -import { Quaternion, Vector3 } from 'three'; +import { Matrix4, Quaternion, Vector3 } from 'three'; import type Rapier from '@dimforge/rapier3d-compat'; import { PhysicsTicking } from 'src/physics'; import { GameObject } from './game-object'; @@ -21,8 +21,6 @@ export class Humanoid extends GameObject implements PhysicsTicking { private _appliedGravity = new Vector3(0, 0, 0); private _grounded = true; private _lookAt = new Vector3(0, 0, 1); - private _lookAtTarget = new Vector3(0, 0, 1); - private _currentLookAt = new Vector3(0, 0, 1); private _animState = 0; protected collider?: Rapier.Collider; @@ -109,7 +107,7 @@ export class Humanoid extends GameObject implements PhysicsTicking { } setLook(vector: Vector3) { - this._lookAtTarget.copy(vector); + this._lookAt.lerp(vector, 0.15); } jump() { @@ -146,13 +144,17 @@ export class Humanoid extends GameObject implements PhysicsTicking { this._velocity.clone().add(this._appliedGravity).multiplyScalar(dt), ); - // Apply look vector - this._currentLookAt.copy(this.parent!.position); - this._lookAt.lerp(this._lookAtTarget, 0.15); - this._currentLookAt.add(this._lookAt); - this.parent?.lookAt(this._currentLookAt); - - this.applyRotation(this.parent!.quaternion); + // Apply look direction to the physics engine + const sink = new Vector3(); + const rotQuat = new Quaternion(); + new Matrix4() + .lookAt( + this.parent!.position, + this.parent!.position.clone().sub(this._lookAt), + new Vector3(0, 1, 0), + ) + .decompose(sink, rotQuat, sink); + this.rigidBody?.setRotation(rotQuat, false); } die() { @@ -184,10 +186,6 @@ export class Humanoid extends GameObject implements PhysicsTicking { this._grounded = grounded; } - private applyRotation(quat: Quaternion) { - this.rigidBody?.setRotation(quat, false); - } - dispose(): void { if (this.collider && !this.rigidBody) { this.physicsWorldRef?.removeCollider(this.collider, false); diff --git a/src/player/player-store.service.ts b/src/player/player-store.service.ts index db30e74..862a33e 100644 --- a/src/player/player-store.service.ts +++ b/src/player/player-store.service.ts @@ -5,7 +5,6 @@ import { PlayerAuthService } from './player-auth.service'; import { Packet } from 'src/net/packet'; import { getRandomName } from 'src/utils/random'; import { PacketType } from 'src/types/packet-type.enum'; -import { Vector3 } from 'three'; @Injectable() export class PlayerStoreService { @@ -78,34 +77,4 @@ export class PlayerStoreService { .toBuffer(); }); } - - public handlePlayerPacket(packet: Packet, player: Player) { - if (packet.packet === PacketType.PLAYER_MOVEMENT) { - const playerId = packet.read(String); - const velocity = packet.read('vec3'); - const lookAt = packet.read('vec3'); - const jump = packet.read('bool'); - - this.broadcastExcept( - new Packet(PacketType.PLAYER_MOVEMENT) - .write(playerId, String) - .write(velocity, 'vec3') - .write(lookAt, 'vec3') - .write(jump, 'bool') - .toBuffer(), - [player.id], - ); - - if (player.controller) { - player.controller.setVelocity(velocity); - if (!(velocity.x === 0 && velocity.y === 0 && velocity.z === 0)) { - player.controller.setLook(lookAt); - } - if (jump) player.controller.jump(); - return true; - } - } - - return false; - } } diff --git a/src/world/physics.service.ts b/src/world/physics.service.ts index 9981997..787ebb9 100644 --- a/src/world/physics.service.ts +++ b/src/world/physics.service.ts @@ -9,6 +9,7 @@ import { PhysicsTicking } from 'src/physics'; const PHYSICS_STEPPING = 1000 / 60; const PHYSICS_STEPPING_S = 1 / 60; +const UPDATE_STEPPING = 1000 / 500; @Injectable() export class PhysicsService { @@ -18,6 +19,7 @@ export class PhysicsService { private running = false; private sceneInitialized = false; + private updateTicker: ReturnType; private physicsTicker: ReturnType; private trackedObjects: PhysicsTicking[] = []; private tickCallback?: () => void; @@ -29,6 +31,11 @@ export class PhysicsService { for (const object of this.trackedObjects) object.tick(PHYSICS_STEPPING_S); this.tickCallback?.(); }, PHYSICS_STEPPING); + + this.updateTicker = setInterval( + () => this.tickCallback?.(), + UPDATE_STEPPING, + ); } async start(world: World, cb?: () => void) { @@ -45,6 +52,7 @@ export class PhysicsService { stop() { this.running = false; clearInterval(this.physicsTicker); + clearInterval(this.updateTicker); } getObjectPackets() { diff --git a/src/world/world.service.ts b/src/world/world.service.ts index 754a3d3..9acdfa3 100644 --- a/src/world/world.service.ts +++ b/src/world/world.service.ts @@ -5,7 +5,7 @@ import { HttpService } from '@nestjs/axios'; import { WorldFile } from 'src/types/world-file'; import { Environment, World, instancableGameObjects } from 'src/game'; import { SerializedObject } from 'src/types/serialized'; -import { Object3D } from 'three'; +import { Object3D, Vector3 } from 'three'; import { lastValueFrom } from 'rxjs'; import { Packet } from 'src/net/packet'; import { PacketType } from 'src/types/packet-type.enum'; @@ -19,7 +19,6 @@ export class WorldService implements OnModuleInit { private logger = new Logger(WorldService.name); private world = new World(); private environment = new Environment(); - private broadcastTick = 0; constructor( private readonly players: PlayerStoreService, @@ -28,24 +27,17 @@ export class WorldService implements OnModuleInit { private readonly http: HttpService, ) {} - onModuleInit() { + async onModuleInit() { this.logger.log('Loading world file from environment'); - this.loadWorld() - .then(() => { - this.logger.log('World file loaded!'); - this.physics.start(this.world, () => { - this.broadcastWorldState(); - if (this.broadcastTick >= 0.5) { - this.broadcastTick = 0; - return; - } - this.broadcastTick += 0.016; - }); - }) - .catch((err) => { - this.logger.error('Failed to load world:', err.stack); - process.exit(1); - }); + try { + await this.loadWorld(); + + this.logger.log('World file loaded!'); + this.physics.start(this.world, () => this.broadcastWorldState()); + } catch (err) { + this.logger.error('Failed to load world:', err.stack); + process.exit(1); + } } async loadWorld() { @@ -207,6 +199,39 @@ export class WorldService implements OnModuleInit { this.logger.log(`PLAYER ${player.name} (${player.id}) has left the game.`); } + public handlePlayerPacket(packet: Packet, player: Player) { + if (packet.packet === PacketType.PLAYER_MOVEMENT) { + const playerId = packet.read(String); + // drop bogus packet + if (playerId !== player.id) return false; + const velocity = packet.read('vec3'); + const lookAt = packet.read('vec3'); + const jump = packet.read('bool'); + + // this.players.broadcastExcept( + // new Packet(PacketType.PLAYER_MOVEMENT) + // .write(playerId, String) + // .write(velocity, 'vec3') + // .write(lookAt, 'vec3') + // .write(jump, 'bool') + // .toBuffer(), + // [player.id], + // ); + + if (player.controller) { + player.controller.setVelocity(velocity); + if (!(velocity.x === 0 && velocity.y === 0 && velocity.z === 0)) { + // console.log('lookat', lookAt); + player.controller.setLook(lookAt); + } + if (jump) player.controller.jump(); + } + return true; + } + + return false; + } + private recursiveCreate(entry: SerializedObject, setParent?: Object3D) { const parent = setParent || this.world; const newObject = this.createObject(entry.objectType, parent, true);