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
|
@ -6,4 +6,5 @@ coverage/
|
||||||
node_modules/
|
node_modules/
|
||||||
build/
|
build/
|
||||||
tmp/
|
tmp/
|
||||||
temp/
|
temp/
|
||||||
|
server/
|
20
src/app.ts
20
src/app.ts
|
@ -1,28 +1,26 @@
|
||||||
import { logger } from './core/logger'
|
|
||||||
import modules from './modules'
|
|
||||||
|
|
||||||
import * as mc from 'minecraft-protocol'
|
import * as mc from 'minecraft-protocol'
|
||||||
import Env from './core/Env'
|
import Env from './core/Env'
|
||||||
|
import { logger } from './core/logger'
|
||||||
|
import modulesTypes from './modules'
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
logger.warn('Cubbot start')
|
logger.warn('Cubbot start')
|
||||||
const client = mc.createClient({
|
const client = mc.createClient({
|
||||||
host: Env.orFail('CORE_HOST'),
|
host: Env.get('CORE_HOST'),
|
||||||
password: Env.orFail('CORE_PASS'),
|
password: Env.orFail('CORE_PASS'),
|
||||||
|
port: Env.getAs('CORE_PORT', Number.parseInt),
|
||||||
username: Env.orFail('CORE_USER'),
|
username: Env.orFail('CORE_USER'),
|
||||||
})
|
})
|
||||||
|
|
||||||
client.on('connect', () => {
|
client.on('connect', () => {
|
||||||
logger.trace('connected')
|
logger.trace('Connected')
|
||||||
})
|
})
|
||||||
client.on('disconnect', packet => {
|
client.on('disconnect', packet => {
|
||||||
logger.info('disconnected', packet.reason)
|
logger.warn('Disconnected ' + packet.reason)
|
||||||
})
|
})
|
||||||
client.on('login', () => {
|
client.on('login', () => {
|
||||||
logger.trace('logged')
|
logger.trace('Logged')
|
||||||
})
|
})
|
||||||
|
|
||||||
for (const module of modules) {
|
const modules = modulesTypes.map(t => new t(client))
|
||||||
module.mount(client)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,6 +5,10 @@ export default class Env {
|
||||||
public static get(key: string) {
|
public static get(key: string) {
|
||||||
return process.env[key]
|
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) {
|
public static orElse(key: string, fallback: string) {
|
||||||
return this.get(key) || fallback
|
return this.get(key) || fallback
|
||||||
|
|
|
@ -5,7 +5,7 @@ export const logger = pino({ level: Env.orFail('CORE_LOG') })
|
||||||
logger.trace('Logger created')
|
logger.trace('Logger created')
|
||||||
|
|
||||||
export function child(name: string, level?: string) {
|
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')
|
l.trace('Logger created')
|
||||||
return l
|
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 {
|
export default class Chat extends Module {
|
||||||
|
|
||||||
public get key(): string {
|
public mount() {
|
||||||
return 'chat'
|
this.client.on('chat', packet => {
|
||||||
}
|
|
||||||
|
|
||||||
public mount(client: Client) {
|
|
||||||
this.prepare()
|
|
||||||
client.on('chat', packet => {
|
|
||||||
const message = JSON.parse(packet.message)
|
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 {
|
export default class Combat extends Module {
|
||||||
|
|
||||||
public get key(): string {
|
private respawn: boolean = true
|
||||||
return 'combat'
|
|
||||||
}
|
|
||||||
|
|
||||||
public mount(client: Client) {
|
public mount() {
|
||||||
this.prepare()
|
this.client.on('combat_event', packet => {
|
||||||
client.on('combat_event', packet => {
|
switch (packet.event) {
|
||||||
this.logger.warn('death ?', packet)
|
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'
|
import { child } from '../core/logger'
|
||||||
|
|
||||||
export default abstract class IModule {
|
export default abstract class IModule {
|
||||||
protected logger!: Logger
|
protected logger: Logger
|
||||||
|
protected client: Client
|
||||||
|
|
||||||
public abstract get key(): string;
|
constructor(client: Client) {
|
||||||
|
this.client = client
|
||||||
public abstract mount(client: Client): void
|
this.logger = child(this.constructor.name)
|
||||||
|
this.mount()
|
||||||
protected prepare(): void {
|
|
||||||
this.logger = child(this.key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 Chat from './Chat'
|
||||||
import Combat from './Combat'
|
import Combat from './Combat'
|
||||||
|
import Life from './Life'
|
||||||
|
import State from './State'
|
||||||
|
import Time from './Time'
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
new Chat(),
|
State,
|
||||||
new Combat(),
|
Time,
|
||||||
]
|
Life,
|
||||||
|
Chat,
|
||||||
|
Combat,
|
||||||
|
]
|
Loading…
Reference in New Issue