From 6ca0227fa4921870fbba0abfd97110dead85c27e Mon Sep 17 00:00:00 2001 From: Shu Date: Fri, 17 Apr 2020 16:26:33 +0200 Subject: [PATCH] WIP: Add some modules --- .gitignore | 3 +- src/app.ts | 20 ++++---- src/core/Env.ts | 4 ++ src/core/logger.ts | 2 +- src/core/utils.ts | 3 ++ src/modules/Chat.ts | 13 ++--- src/modules/Combat.ts | 33 ++++++++++--- src/modules/Entities.ts | 12 +++++ src/modules/Inventory.ts | 9 ++++ src/modules/Life.ts | 68 ++++++++++++++++++++++++++ src/modules/Map.ts | 16 ++++++ src/modules/Module.ts | 15 +++--- src/modules/State.ts | 102 +++++++++++++++++++++++++++++++++++++++ src/modules/Time.ts | 57 ++++++++++++++++++++++ src/modules/index.ts | 12 +++-- 15 files changed, 331 insertions(+), 38 deletions(-) create mode 100644 src/core/utils.ts create mode 100644 src/modules/Entities.ts create mode 100644 src/modules/Inventory.ts create mode 100644 src/modules/Life.ts create mode 100644 src/modules/Map.ts create mode 100644 src/modules/State.ts create mode 100644 src/modules/Time.ts diff --git a/.gitignore b/.gitignore index da090ad..944bc0c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ coverage/ node_modules/ build/ tmp/ -temp/ \ No newline at end of file +temp/ +server/ \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index ab090f9..29316ef 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,28 +1,26 @@ -import { logger } from './core/logger' -import modules from './modules' - import * as mc from 'minecraft-protocol' import Env from './core/Env' +import { logger } from './core/logger' +import modulesTypes from './modules' export default () => { logger.warn('Cubbot start') const client = mc.createClient({ - host: Env.orFail('CORE_HOST'), + host: Env.get('CORE_HOST'), password: Env.orFail('CORE_PASS'), + port: Env.getAs('CORE_PORT', Number.parseInt), username: Env.orFail('CORE_USER'), }) client.on('connect', () => { - logger.trace('connected') + logger.trace('Connected') }) client.on('disconnect', packet => { - logger.info('disconnected', packet.reason) + logger.warn('Disconnected ' + packet.reason) }) client.on('login', () => { - logger.trace('logged') + logger.trace('Logged') }) - for (const module of modules) { - module.mount(client) - } -} \ No newline at end of file + const modules = modulesTypes.map(t => new t(client)) +} diff --git a/src/core/Env.ts b/src/core/Env.ts index 39128a2..b65e3e6 100644 --- a/src/core/Env.ts +++ b/src/core/Env.ts @@ -5,6 +5,10 @@ export default class Env { public static get(key: string) { return process.env[key] } + public static getAs(key: string, mapper: (val: string) => T): T | undefined { + const val = this.get(key) + return val ? mapper(val) : undefined + } public static orElse(key: string, fallback: string) { return this.get(key) || fallback diff --git a/src/core/logger.ts b/src/core/logger.ts index 30f10ee..86f4883 100644 --- a/src/core/logger.ts +++ b/src/core/logger.ts @@ -5,7 +5,7 @@ export const logger = pino({ level: Env.orFail('CORE_LOG') }) logger.trace('Logger created') export function child(name: string, level?: string) { - const l = logger.child({ name, level: level || Env.orFail(name.toUpperCase() + '_LOG') }) + const l = logger.child({ name, level: level || Env.get(name.toUpperCase() + '_LOG') }) l.trace('Logger created') return l } diff --git a/src/core/utils.ts b/src/core/utils.ts new file mode 100644 index 0000000..1f5d57a --- /dev/null +++ b/src/core/utils.ts @@ -0,0 +1,3 @@ +export function longToNumber(arr: number[]) { + return arr[1] + 4294967296 * arr[0] +} diff --git a/src/modules/Chat.ts b/src/modules/Chat.ts index 7f840c4..b521e97 100644 --- a/src/modules/Chat.ts +++ b/src/modules/Chat.ts @@ -3,16 +3,13 @@ import Module from './Module' export default class Chat extends Module { - public get key(): string { - return 'chat' - } - - public mount(client: Client) { - this.prepare() - client.on('chat', packet => { + public mount() { + this.client.on('chat', packet => { const message = JSON.parse(packet.message) - this.logger.info(message.text || message.extra.map((e: any) => e.text).join()) + this.logger.info(message.text || (message.extra ? message.extra.map((e: any) => e.text).join() : packet)) }) } + //TODO: chat write + } diff --git a/src/modules/Combat.ts b/src/modules/Combat.ts index 1c2150e..bca5e4b 100644 --- a/src/modules/Combat.ts +++ b/src/modules/Combat.ts @@ -3,15 +3,34 @@ import Module from './Module' export default class Combat extends Module { - public get key(): string { - return 'combat' - } + private respawn: boolean = true - public mount(client: Client) { - this.prepare() - client.on('combat_event', packet => { - this.logger.warn('death ?', packet) + public mount() { + this.client.on('combat_event', packet => { + switch (packet.event) { + case 0: + this.logger.warn('fighting') + break + + case 1: + this.logger.info('fighting entity %s', packet.entityId) + break + + case 2: + this.logger.info('dead player %s by %s', packet.playerId, packet.entityId) + //TODO: is me ? + //TODO: print chat message + if (this.respawn) { + //TODO: use Life.respawn() + this.client.write('client_command', { action: 1 }) + } + } }) } + //TODO: respawn or disconnect + //TODO: aura + + //TODO: Interact Entity + } diff --git a/src/modules/Entities.ts b/src/modules/Entities.ts new file mode 100644 index 0000000..8049e4a --- /dev/null +++ b/src/modules/Entities.ts @@ -0,0 +1,12 @@ +//TODO: spawn_entity_living +//TODO: entity_metadata +//TODO: entity_update_attributes +//TODO: entity_equipment +//TODO: entity_move_look +//TODO: entity_head_rotation +//TODO: rel_entity_move +//TODO: entity_velocity +//TODO: entity_destroy +//TODO: entity_teleport +//TODO: entity_look +//TODO: set_passengers \ No newline at end of file diff --git a/src/modules/Inventory.ts b/src/modules/Inventory.ts new file mode 100644 index 0000000..207ba0d --- /dev/null +++ b/src/modules/Inventory.ts @@ -0,0 +1,9 @@ +//TODO: window_items +//TODO: set_slot + +//TODO: declare_recipes + +/* Action */ +//TODO: Pick Item +//TDOO: craft +//TODO: Held Item Change \ No newline at end of file diff --git a/src/modules/Life.ts b/src/modules/Life.ts new file mode 100644 index 0000000..2acffbf --- /dev/null +++ b/src/modules/Life.ts @@ -0,0 +1,68 @@ +import { Client } from 'minecraft-protocol' +import Module from './Module' + +export default class Life extends Module { + + private _health!: number + public get health() { + return this._health + } + + private _food!: number + public get food() { + return this._food + } + + private _saturation!: number + public get saturation() { + return this._saturation + } + + public get alive() { + return this.health > 0 + } + + private _experienceBar!: number + public get experienceBar() { + return this._experienceBar + } + + private _level!: number + public get level() { + return this._level + } + + private _totalExperience!: number + public get totalExperience() { + return this._totalExperience + } + + public mount() { + this.client.on('update_health', data => { + if (this._health == null) { + setTimeout(this.respawn.bind(this), 500) + } + this._health = data.health + this._food = data.food + this._saturation = data.foodSaturation + if (this.health > 5) { + this.logger.info('health: %s', this.health) + } else { + this.logger.warn('low health: %s', this.health) + } + }) + this.client.on('experience', data => { + this._experienceBar = data.experienceBar + this._level = data.level + this._totalExperience = data.totalExperience + }) + } + + public respawn() { + this.logger.info('respawn') + this.client.write('client_command', { action: 1 }) + } + + //TODO: send client settings + +} diff --git a/src/modules/Map.ts b/src/modules/Map.ts new file mode 100644 index 0000000..66cc31a --- /dev/null +++ b/src/modules/Map.ts @@ -0,0 +1,16 @@ +/* Data */ +//TODO: map_chunk +//TODO: world_border +//TODO: spawn_position +//TODO: block_change multi_block_change + +//TODO: weather_entity aka thunderbolt + + + + + +/* Actions */ +//TODO: query_block_nbt + +//TODO: digging \ No newline at end of file diff --git a/src/modules/Module.ts b/src/modules/Module.ts index 5cc8c60..d033170 100644 --- a/src/modules/Module.ts +++ b/src/modules/Module.ts @@ -3,13 +3,14 @@ import { Logger } from 'pino' import { child } from '../core/logger' export default abstract class IModule { - protected logger!: Logger + protected logger: Logger + protected client: Client - public abstract get key(): string; - - public abstract mount(client: Client): void - - protected prepare(): void { - this.logger = child(this.key) + constructor(client: Client) { + this.client = client + this.logger = child(this.constructor.name) + this.mount() } + + protected abstract mount(): void } diff --git a/src/modules/State.ts b/src/modules/State.ts new file mode 100644 index 0000000..2a71142 --- /dev/null +++ b/src/modules/State.ts @@ -0,0 +1,102 @@ +import { Client } from 'minecraft-protocol' +import Module from './Module' + +//MAYBE: split + +interface IState { + entityId: number, + gameMode: 0|1|2|3, + dimension: number, + hashedSeed: number[], + maxPlayers: number, + levelType: string, + viewDistance: number, + reducedDebugInfo: boolean, + enableRespawnScreen: boolean +} + +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 IPosition { + x: number, + y: number, + z: number, + yaw: number, + pitch: number, + flags: number, + teleportId: number +} + +//TODO: entity_status {entityId, entityStatus} aka animations + +//TODO: player_info + +export default class State extends Module { + + public positionConfirm: boolean = true + + private _state!: IState + public get state() { + return this._state + } + + private _difficulty!: IDifficulty + public get difficulty() { + return this._difficulty + } + + private _abilities!: IDifficulty + public get abilities() { + return this._abilities + } + + private _position!: IPosition + public get position() { + return this._position + } + + public mount() { + this.client.on('login', data => this._state = data) + this.client.on('difficulty', data => this._difficulty = { + level: data.difficulty, locked: data.difficultyLocked, + }) + this.client.on('abilities', data => this._abilities = data) + + this.client.on('position', (data: IPosition) => { + if (this._position == null) { + if (data.flags) { + this.logger.error('first postion 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), + flags: 0, + teleportId : -1, + } + } + + if (this.positionConfirm) { + this.client.write('teleport_confirm', { teleportId: data.teleportId }) + } + this.logger.debug({ type: 'teleported', position: this.position }) + }) + } + +} diff --git a/src/modules/Time.ts b/src/modules/Time.ts new file mode 100644 index 0000000..24adbcf --- /dev/null +++ b/src/modules/Time.ts @@ -0,0 +1,57 @@ +import { Client } from 'minecraft-protocol' +import { longToNumber } from '../core/utils' +import Module from './Module' + +interface IUpdateTime { + age: number[] + time: number[] +} + +export default class Time extends Module { + + private _age!: number + public get age() { + return this._age + } + + private _time!: number + public get time() { + return this._time + } + + private _tps!: number + public get tps() { + return this._tps + } + + private _tpsLocal!: number + public get tpsLocal() { + return this._tpsLocal + } + + private _at: number = Date.now() + + public mount() { + this.client.on('update_time', (data: IUpdateTime) => { + const now = Date.now() + const age = longToNumber(data.age) + + if (this._age) { + this._tps = age - this._age + if (this.tps < 20) { + this.logger.debug('Server Lag: %s', this.tps) + } + + this._tpsLocal = (now - this._at) / 50 + if (this._tpsLocal > 20 + .5) { + this.logger.debug('Client Lag: %s', this.tpsLocal) + } + } + + this._age = age + this._at = now + this._time = Math.abs(data.time[1]) % 24000 + }) + } + +} diff --git a/src/modules/index.ts b/src/modules/index.ts index 9ae3b5e..7a438e2 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -1,7 +1,13 @@ import Chat from './Chat' import Combat from './Combat' +import Life from './Life' +import State from './State' +import Time from './Time' export default [ - new Chat(), - new Combat(), -] + State, + Time, + Life, + Chat, + Combat, +] \ No newline at end of file