cubbot/src/modules/State.ts

151 lines
4.4 KiB
TypeScript

import Module from '../utils/Module'
import { IDelta, IPosition, IState, gameMode } from '../utils/types'
import Spawn from './Spawn'
// MAYBE: split
interface IDifficulty {
level: number,
locked: boolean
}
interface IAbilities {
// TODO: Flags Byte Bit mask. 0x08: damage disabled (god mode), 0x04: can fly, 0x02: is flying, 0x01: is Creative
flyingSpeed: number,
walkingSpeed: number
}
interface IPositionPacket extends IPosition {
flags: number
teleportId: number
}
interface IPlayerInfo {
UUID: string
name: string
properties: any[]
gamemode: gameMode
ping: number
}
/** Handle client state and position */
export default class State extends Module<{ positionConfirm: boolean }> {
private _state!: IState
public get state() {
return this._state
}
private _difficulty!: IDifficulty
public get difficulty() {
return this._difficulty
}
private _abilities!: IAbilities
public get abilities() {
return this._abilities
}
private _position!: IPosition
public get position() {
return this._position
}
private _players = new Map<string, IPlayerInfo>()
public get players() {
return this._players
}
public translate(delta: IDelta, onGround: boolean = true) {
this.client.write('position', {
onGround,
x: this.position.x += delta.dX,
y: this.position.y += delta.dY,
z: this.position.z += delta.dZ,
})
}
protected mount() {
const spawn = this.load<Spawn>(Spawn)
spawn.addCheck('state')
spawn.addCheck('abilities')
spawn.addCheck('position')
this.client.on('login', data => {
this._state = data
spawn.validateCheck('state')
})
this.client.on('difficulty', data => this._difficulty = {
level: data.difficulty, locked: data.difficultyLocked,
})
this.client.on('abilities', data => {
this._abilities = data
spawn.validateCheck('abilities')
})
this.client.on('position', (data: IPositionPacket) => {
if (this._position == null) {
if (data.flags) {
this.logger.error('First position is relative')
return
} else {
this._position = data
}
} else {
// tslint:disable: no-bitwise
this._position = {
x: data.x + (data.flags & 0x01 ? this._position.x : 0),
y: data.y + (data.flags & 0x02 ? this._position.y : 0),
z: data.z + (data.flags & 0x04 ? this._position.z : 0),
yaw: data.yaw + (data.flags & 0x08 ? this._position.yaw : 0),
pitch: data.pitch + (data.flags & 0x10 ? this._position.pitch : 0),
}
}
if (this.conf.positionConfirm) {
this.client.write('teleport_confirm', { teleportId: data.teleportId })
}
this.logger.debug({ msg: 'Teleported', type: 'position', value: this.position })
spawn.validateCheck('position')
})
this.client.on('player_info', (packet: { action: number, data: IPlayerInfo[] }) => {
for (const player of packet.data) {
switch (packet.action) {
case 0:
this._players.set(player.UUID, player)
break
case 1:
const p1 = this._players.get(player.UUID)
if (p1) {
p1.gamemode = player.gamemode
}
break
case 2:
const p2 = this._players.get(player.UUID)
if (p2) {
p2.ping = player.ping
}
break
case 3:
const p3 = this._players.get(player.UUID)
if (p3) {
p3.name = player.name
}
break
case 4:
this._players.delete(player.UUID)
break
}
}
})
}
protected getConf() {
return { positionConfirm: true }
}
}
// TODO: vehicule_move