UNPKG

minecraft-protocol

Version:

Parse and serialize minecraft packets, plus authentication and encryption.

307 lines (280 loc) 10.6 kB
/// <reference types="node" /> import { EventEmitter } from 'events'; import { Socket } from 'net' import * as Stream from 'stream' import { Agent } from 'http' import { Transform } from "readable-stream"; import { BinaryLike, KeyObject } from 'crypto'; import { Realm } from "prismarine-realms" import NodeRSA from 'node-rsa'; type PromiseLike = Promise<void> | void declare module 'minecraft-protocol' { export class Client extends EventEmitter { constructor(isServer: boolean, version: string, customPackets?: any) state: States isServer: boolean socket: Socket uuid: string username: string session?: SessionOption profile?: any deserializer: FullPacketParser serializer: Serializer latency: number customPackets: any protocolVersion: number version: string write(name: string, params: any): void writeRaw(buffer: any): void compressionThreshold: string ended: boolean connect(port: number, host: string): void setSocket(socket: Socket): void end(reason?: string): void registerChannel(name: string, typeDefinition: any, custom?: boolean): void unregisterChannel(name: string): void writeChannel(channel: any, params: any): void signMessage(message: string, timestamp: BigInt, salt?: number, preview?: string, acknowledgements?: Buffer[]): Buffer verifyMessage(publicKey: Buffer | KeyObject, packet: object): boolean reportPlayer(uuid: string, reason: 'FALSE_REPORTING' | 'HATE_SPEECH' | 'TERRORISM_OR_VIOLENT_EXTREMISM' | 'CHILD_SEXUAL_EXPLOITATION_OR_ABUSE' | 'IMMINENT_HARM' | 'NON_CONSENSUAL_INTIMATE_IMAGERY' | 'HARASSMENT_OR_BULLYING' | 'DEFAMATION_IMPERSONATION_FALSE_INFORMATION' | 'SELF_HARM_OR_SUICIDE' | 'ALCOHOL_TOBACCO_DRUGS', signatures: Buffer[], comment?: string): Promise<true> chat(message: string, options?: { timestamp?: BigInt, salt?: BigInt, preview?: BinaryLike, didPreview?: boolean }): void on(event: 'error', listener: (error: Error) => PromiseLike): this on(event: 'packet', handler: (data: any, packetMeta: PacketMeta, buffer: Buffer, fullBuffer: Buffer) => PromiseLike): this on(event: 'raw', handler: (buffer: Buffer, packetMeta: PacketMeta) => PromiseLike): this on(event: 'session', handler: (session: SessionObject) => PromiseLike): this on(event: 'state', handler: (newState: States, oldState: States) => PromiseLike): this on(event: 'end', handler: (reason: string) => PromiseLike): this on(event: 'connect', handler: () => PromiseLike): this on(event: string, handler: (data: any, packetMeta: PacketMeta) => PromiseLike): this on(event: `raw.${string}`, handler: (buffer: Buffer, packetMeta: PacketMeta) => PromiseLike): this on(event: 'playerChat', handler: (data: { // (JSON string) The chat message preformatted, if done on server side formattedMessage: string, // (Plaintext) The chat message without formatting (for example no `<username> message` ; instead `message`), on version 1.19+ plainMessage: string, // (JSON string) Unsigned formatted chat contents. Should only be present when the message is modified and server has chat previews disabled. Only on versions 1.19.0, 1.19.1 and 1.19.2 unsignedContent?: string, type: string, sender: string, senderName: string, senderTeam: string, targetName: string, verified?: boolean }) => PromiseLike): this on(event: 'systemChat', handler: (data: { positionId: number, formattedMessage: string }) => PromiseLike): this // Emitted after the player enters the PLAY state and can send and recieve game packets on(event: 'playerJoin', handler: () => void): this once(event: 'error', listener: (error: Error) => PromiseLike): this once(event: 'packet', handler: (data: any, packetMeta: PacketMeta, buffer: Buffer, fullBuffer: Buffer) => PromiseLike): this once(event: 'raw', handler: (buffer: Buffer, packetMeta: PacketMeta) => PromiseLike): this once(event: 'sessionce', handler: (sessionce: any) => PromiseLike): this once(event: 'state', handler: (newState: States, oldState: States) => PromiseLike): this once(event: 'end', handler: (reasonce: string) => PromiseLike): this once(event: 'connect', handler: () => PromiseLike): this once(event: string, handler: (data: any, packetMeta: PacketMeta) => PromiseLike): this once(event: `raw.${string}`, handler: (buffer: Buffer, packetMeta: PacketMeta) => PromiseLike): this } class FullPacketParser extends Transform { proto: any mainType: any noErrorLogging: boolean constructor (proto: any, mainType: any, noErrorLogging?: boolean) parsePacketBuffer(buffer: Buffer): any } class Serializer extends Transform { proto: any mainType: any queue: Buffer constructor(proto: any, mainType: any) createPacketBuffer(packet: any): any } export interface SessionOption { accessToken: string, /** My be needed for mojang auth. Is send by mojang on username + password auth */ clientToken?: string, selectedProfile: SessionProfile } export interface SessionObject { accessToken: string, /** My be needed for mojang auth. Is send by mojang on username + password auth */ clientToken?: string, selectedProfile: { name: string id: string } availableProfiles?: SessionProfile[] availableProfile?: SessionProfile[] } interface SessionProfile { /** Character in game name */ name: string /** Character UUID in short form */ id: string } export interface ClientOptions { username: string port?: number auth?: 'mojang' | 'microsoft' | 'offline' | ((client: Client, options: ClientOptions) => void) password?: string host?: string clientToken?: string accessToken?: string authServer?: string authTitle?: string sessionServer?: string keepAlive?: boolean closeTimeout?: number noPongTimeout?: number checkTimeoutInterval?: number version?: string customPackets?: any hideErrors?: boolean skipValidation?: boolean stream?: Stream connect?: (client: Client) => void agent?: Agent fakeHost?: string profilesFolder?: string | false onMsaCode?: (data: MicrosoftDeviceAuthorizationResponse) => void id?: number session?: SessionOption validateChannelProtocol?: boolean, realms?: RealmsOptions // 1.19+ disableChatSigning?: boolean /** Pass custom client implementation if needed. */ Client?: Client } export class Server extends EventEmitter { constructor(version: string, customPackets?: any) writeToClients(clients: Client[], name: string, params: any): void onlineModeExceptions: object clients: { [key: number]: ServerClient } playerCount: number maxPlayers: number motd: string motdMsg?: Object favicon: string serverKey: NodeRSA close(): void on(event: 'connection', handler: (client: ServerClient) => PromiseLike): this on(event: 'error', listener: (error: Error) => PromiseLike): this on(event: 'login', handler: (client: ServerClient) => PromiseLike): this on(event: 'listening', listener: () => PromiseLike): this // Emitted after the player enters the PLAY state and can send and recieve game packets on(event: 'playerJoin', handler: (client: ServerClient) => void): this once(event: 'connection', handler: (client: ServerClient) => PromiseLike): this once(event: 'error', listener: (error: Error) => PromiseLike): this once(event: 'login', handler: (client: ServerClient) => PromiseLike): this once(event: 'listening', listener: () => PromiseLike): this } export interface ServerClient extends Client { id: number /** You must call this function when the server receives a message from a player and that message gets broadcast to other players in player_chat packets. This function stores these packets so the server can then verify a player's lastSeenMessages field in inbound chat packets to ensure chain integrity. */ logSentMessageFromPeer(packet: object): boolean } export interface ServerOptions { port?: number host?: string kickTimeout?: number checkTimeoutInterval?: number 'online-mode'?: boolean beforePing?: (response: any, client: Client, callback?: (error: any, result: any) => any) => any beforeLogin?: (client: Client) => void motd?: string motdMsg?: Object maxPlayers?: number keepAlive?: boolean version?: string | false fallbackVersion?: string favicon?: string customPackets?: any errorHandler?: (client: Client, error: Error) => void hideErrors?: boolean agent?: Agent validateChannelProtocol?: boolean /** (1.19+) Require connecting clients to have chat signing support enabled */ enforceSecureProfile?: boolean /** 1.19.1 & 1.19.2 only: If client should send previews of messages they are typing to the server */ enableChatPreview?: boolean socketType?: 'tcp' | 'ipc' Server?: Server } export interface SerializerOptions { customPackets: any isServer?: boolean state?: States version: string } export interface MicrosoftDeviceAuthorizationResponse { device_code: string user_code: string verification_uri: string expires_in: number interval: number message: string } enum States { HANDSHAKING = 'handshaking', LOGIN = 'login', PLAY = 'play', CONFIGURATION = 'configuration', STATUS = 'status', } export interface PacketMeta { name: string state: States } export interface PingOptions { host?: string majorVersion?: string port?: number protocolVersion?: string version?: string closeTimeout?: number noPongTimeout?: number } export interface OldPingResult { maxPlayers: number, motd: string playerCount: number prefix: string protocol: number version: string } export interface NewPingResult { description: { text?: string extra?: any[] } | string players: { max: number online: number sample?: { id: string name: string }[] } version: { name: string protocol: number } favicon?: string latency: number } export interface RealmsOptions { realmId?: string pickRealm?: (realms: Realm[]) => Realm } export const states: typeof States export const supportedVersions: string[] export const defaultVersion: string export function createServer(options: ServerOptions): Server export function createClient(options: ClientOptions): Client // TODO: Create typings on protodef to define here export function createSerializer({ state, isServer, version, customPackets }: SerializerOptions): any export function createDeserializer({ state, isServer, version, customPackets }: SerializerOptions): any export function ping(options: PingOptions, callback?: (error: Error, result: OldPingResult | NewPingResult) => void): Promise<OldPingResult | NewPingResult> }