294 lines
8.9 KiB
TypeScript
294 lines
8.9 KiB
TypeScript
import { applyDelta, applyVelocity } from '../utils/func'
|
|
import Module from '../utils/Module'
|
|
import { IDelta, IMovable, IPosition, IRotation, ISlot, IState, IVelocity } from '../utils/types'
|
|
import Time from './Time'
|
|
|
|
interface IId {
|
|
entityId: number
|
|
}
|
|
|
|
interface IPositioned extends IId, IPosition { }
|
|
interface IMovableE extends IId, IMovable { }
|
|
interface IMove extends IId, IDelta { }
|
|
interface ILook extends IId, IRotation { }
|
|
interface IMoveLook extends IMove, ILook { }
|
|
interface IHead extends IId {
|
|
headPitch: number
|
|
}
|
|
interface IVelocityE extends IId, IVelocity { }
|
|
|
|
interface IObject extends IMovableE, IMetadatable {
|
|
objectId: number
|
|
type: number
|
|
data: number
|
|
}
|
|
|
|
interface IOrb extends IMovableE, IMetadatable {
|
|
count: number
|
|
}
|
|
|
|
interface IEquipment extends IId {
|
|
slot: number,
|
|
item: ISlot
|
|
}
|
|
interface IEquipable {
|
|
equipment?: Map<number, ISlot>
|
|
}
|
|
|
|
interface IAttribute {
|
|
key: string
|
|
value: number
|
|
modifiers: IModifier[]
|
|
}
|
|
interface IModifier {
|
|
uuid: string
|
|
amount: number
|
|
operation: number
|
|
}
|
|
interface IAttributes extends IId {
|
|
properties: IAttribute[]
|
|
}
|
|
interface IAttributable {
|
|
attributes?: IAttribute[]
|
|
}
|
|
|
|
interface IMetadata {
|
|
key: number
|
|
type: number
|
|
value: any
|
|
}
|
|
interface IMetadatas extends IId {
|
|
metadata: IMetadata[]
|
|
}
|
|
interface IMetadatable {
|
|
metadata?: Map<number, IMetadata>
|
|
}
|
|
|
|
export interface IAlive extends IMovableE, IEquipable, IAttributable, IMetadatable {
|
|
headPitch: number
|
|
}
|
|
|
|
interface ILiving extends IAlive {
|
|
entityUUID: string
|
|
type: number
|
|
}
|
|
|
|
interface IPlayerSpawn extends IPositioned {
|
|
playerUUID: string
|
|
}
|
|
interface IPlayer extends IPlayerSpawn, IAlive {
|
|
me?: boolean
|
|
}
|
|
|
|
/** Track entities */
|
|
export default class Entities extends Module<{}> {
|
|
|
|
private _objects = new Map<number, IObject>()
|
|
public get objects() {
|
|
return this._objects.values()
|
|
}
|
|
|
|
private _orbs = new Map<number, IOrb>()
|
|
public get orbs() {
|
|
return this._orbs.values()
|
|
}
|
|
|
|
private _livings = new Map<number, ILiving>()
|
|
public get livings() {
|
|
return this._livings.values()
|
|
}
|
|
|
|
private _players = new Map<number, IPlayer>()
|
|
public get players() {
|
|
return this._players.values()
|
|
}
|
|
|
|
public getEntity(entityId: number): IMovableE | undefined {
|
|
return this._players.get(entityId) || this._livings.get(entityId) ||
|
|
this._objects.get(entityId) || this._orbs.get(entityId)
|
|
}
|
|
|
|
public getAlive(entityId: number): IAlive | undefined {
|
|
return this._players.get(entityId) || this._livings.get(entityId)
|
|
}
|
|
|
|
public getMetadatable(entityId: number): IMetadatable | undefined {
|
|
return this._players.get(entityId) || this._livings.get(entityId) ||
|
|
this._objects.get(entityId) || this._orbs.get(entityId)
|
|
}
|
|
|
|
public deleteEntity(entityId: number) {
|
|
return this._players.delete(entityId) || this._livings.delete(entityId) ||
|
|
this._objects.delete(entityId) || this._orbs.delete(entityId)
|
|
}
|
|
|
|
public attack(entityId: number, mainHand: boolean = true) {
|
|
this.client.write('use_entity', { target: entityId, mouse: 1, hand: mainHand ? 0 : 1 })
|
|
}
|
|
|
|
protected mount() {
|
|
|
|
this.load<Time>(Time).events.on('tick', () => {
|
|
this._objects.forEach(applyVelocity)
|
|
// MAYBE: apply velocity to others
|
|
})
|
|
|
|
this.client.on('spawn_entity', (packet: IObject) => {
|
|
this._objects.set(packet.entityId, packet)
|
|
})
|
|
this.client.on('spawn_entity_experience_orb', (packet: IOrb) => {
|
|
this._orbs.set(packet.entityId, packet)
|
|
})
|
|
this.client.on('spawn_entity_living', (packet: ILiving) => {
|
|
this._livings.set(packet.entityId, packet)
|
|
})
|
|
this.client.on('named_entity_spawn', (packet: IPlayerSpawn) => {
|
|
const player = {
|
|
velocityX: 0,
|
|
velocityY: 0,
|
|
velocityZ: 0,
|
|
headPitch: 0,
|
|
...packet,
|
|
}
|
|
this._players.set(player.entityId, player)
|
|
})
|
|
this.client.on('login', (packet: IState) => {
|
|
this._players.set(packet.entityId, { // MAYBE: link with State
|
|
me: true,
|
|
entityId: packet.entityId,
|
|
playerUUID: '',
|
|
x: 0,
|
|
y: 0,
|
|
z: 0,
|
|
pitch: 0,
|
|
yaw: 0,
|
|
velocityX: 0,
|
|
velocityY: 0,
|
|
velocityZ: 0,
|
|
headPitch: 0,
|
|
})
|
|
})
|
|
|
|
// MAYBE: this.client.on('animation', packet => { })
|
|
// MAYBE: this.client.on('entity_status', packet => { })
|
|
|
|
this.client.on('rel_entity_move', (packet: IMove) => {
|
|
const entity = this.getEntity(packet.entityId)
|
|
if (entity) {
|
|
applyDelta(entity, packet)
|
|
} else {
|
|
this.logger.warn('rel_move of unknown entity %s', packet.entityId)
|
|
}
|
|
})
|
|
this.client.on('entity_move_look', (packet: IMoveLook) => {
|
|
const entity = this.getEntity(packet.entityId)
|
|
if (entity) {
|
|
applyDelta(entity, packet)
|
|
entity.pitch = packet.pitch
|
|
entity.yaw = packet.yaw
|
|
} else {
|
|
this.logger.warn('move_look of unknown entity %s', packet.entityId)
|
|
}
|
|
})
|
|
this.client.on('entity_look', (packet: ILook) => {
|
|
const entity = this.getEntity(packet.entityId)
|
|
if (entity) {
|
|
entity.pitch = packet.pitch
|
|
entity.yaw = packet.yaw
|
|
} else {
|
|
this.logger.warn('look of unknown entity %s', packet.entityId)
|
|
}
|
|
})
|
|
this.client.on('entity_head_rotation', (packet: IHead) => {
|
|
const entity = this.getAlive(packet.entityId)
|
|
if (entity) {
|
|
entity.headPitch = packet.headPitch
|
|
} else {
|
|
this.logger.warn('head_rotation of unknown entity %s', packet.entityId)
|
|
}
|
|
})
|
|
this.client.on('entity_velocity', (packet: IVelocityE) => {
|
|
const entity = this.getEntity(packet.entityId)
|
|
if (entity) {
|
|
entity.velocityX = packet.velocityX
|
|
entity.velocityY = packet.velocityY
|
|
entity.velocityZ = packet.velocityZ
|
|
} else {
|
|
this.logger.warn('velocity of unknown entity %s', packet.entityId)
|
|
}
|
|
})
|
|
this.client.on('entity_teleport', (packet: IPositioned) => {
|
|
const entity = this.getEntity(packet.entityId)
|
|
if (entity) {
|
|
entity.x = packet.x
|
|
entity.y = packet.y
|
|
entity.z = packet.z
|
|
entity.pitch = packet.pitch
|
|
entity.yaw = packet.yaw
|
|
} else {
|
|
this.logger.warn('teleport of unknown entity %s', packet.entityId)
|
|
}
|
|
})
|
|
|
|
this.client.on('entity_destroy', (packet: { entityIds: number[] }) => {
|
|
packet.entityIds.forEach(id => {
|
|
if (!this.deleteEntity(id)) {
|
|
this.logger.debug('can not delete entity %s', id)
|
|
}
|
|
})
|
|
})
|
|
|
|
// TODO: identify metadatas
|
|
this.client.on('entity_metadata', (packet: IMetadatas) => {
|
|
const entity = this.getMetadatable(packet.entityId)
|
|
if (entity) {
|
|
if (!entity.metadata) {
|
|
entity.metadata = new Map()
|
|
}
|
|
for (const data of packet.metadata) {
|
|
entity.metadata.set(data.key, data)
|
|
}
|
|
} else {
|
|
this.logger.warn('metadata of unknown entity %s (%s)', packet.entityId)
|
|
}
|
|
})
|
|
|
|
// MAYBE: this.client.on('entity', (packet: IId) => { })
|
|
|
|
// TODO: entity_effect, remove_entity_effect
|
|
this.client.on('entity_equipment', (packet: IEquipment) => {
|
|
const entity = this.getAlive(packet.entityId)
|
|
if (entity) {
|
|
entity.equipment = entity.equipment ?
|
|
entity.equipment.set(packet.slot, packet.item) :
|
|
new Map([[packet.slot, packet.item]])
|
|
} else {
|
|
this.logger.warn('equipment of unknown entity %s', packet.entityId)
|
|
}
|
|
})
|
|
|
|
// TODO: identify (with properties)
|
|
this.client.on('entity_update_attributes', (packet: IAttributes) => {
|
|
const entity = this.getAlive(packet.entityId)
|
|
if (entity) {
|
|
entity.attributes = packet.properties
|
|
} else {
|
|
this.logger.warn('attributes of unknown entity %s', packet.entityId)
|
|
}
|
|
})
|
|
|
|
}
|
|
|
|
protected getConf() {
|
|
return {}
|
|
}
|
|
|
|
}
|
|
|
|
// TODO: player info
|
|
|
|
// MAYBE: animation
|
|
// MAYBE: attach_entity
|
|
// MAYBE: entity_sound_effect
|
|
// MAYBE: set_passengers
|