WIP: Add some modules
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
94d62f2afc
commit
6ca0227fa4
|
@ -7,3 +7,4 @@ node_modules/
|
|||
build/
|
||||
tmp/
|
||||
temp/
|
||||
server/
|
18
src/app.ts
18
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)
|
||||
}
|
||||
const modules = modulesTypes.map(t => new t(client))
|
||||
}
|
|
@ -5,6 +5,10 @@ export default class Env {
|
|||
public static get(key: string) {
|
||||
return process.env[key]
|
||||
}
|
||||
public static getAs<T>(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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export function longToNumber(arr: number[]) {
|
||||
return arr[1] + 4294967296 * arr[0]
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,9 @@
|
|||
//TODO: window_items
|
||||
//TODO: set_slot
|
||||
|
||||
//TODO: declare_recipes
|
||||
|
||||
/* Action */
|
||||
//TODO: Pick Item
|
||||
//TDOO: craft
|
||||
//TODO: Held Item Change
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 })
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
]
|
Loading…
Reference in New Issue