lots of minor changes

This commit is contained in:
Dinhero21 2024-01-11 16:00:34 -03:00
parent 6cba62e78c
commit c5cc2c8c27
13 changed files with 248 additions and 84 deletions

6
.swcrc
View file

@ -11,9 +11,13 @@
"useDefineForClassFields": false
},
"externalHelpers": true,
"target": "es5",
"target": "esnext",
"loose": true
},
"exclude": [
"\\.js$",
"\\.d\\.ts$"
],
"module": {
"type": "es6"
},

5
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"files.exclude": {
"src/module/*": false
}
}

32
package-lock.json generated
View file

@ -7,8 +7,9 @@
"": {
"name": "modular-minecraft-proxy",
"version": "1.0.0",
"license": "ISC",
"license": "MIT",
"dependencies": {
"chalk": "^5.3.0",
"minecraft-protocol": "^1.26.5"
},
"devDependencies": {
@ -1350,16 +1351,11 @@
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
"engines": {
"node": ">=10"
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
@ -2090,6 +2086,22 @@
"concat-map": "0.0.1"
}
},
"node_modules/eslint/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/eslint/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",

View file

@ -8,13 +8,15 @@
"test": "echo \"Error: no test specified\" && exit 1",
"build": "scripty",
"watch:build": "scripty",
"copy": "scripty",
"watch:copy": "scripty",
"run": "scripty",
"watch:run": "scripty",
"watch": "scripty"
},
"keywords": [],
"author": "",
"license": "ISC",
"license": "MIT",
"config": {
"scripty": {
"parallel": true,
@ -37,6 +39,7 @@
"typescript": "^5.3.3"
},
"dependencies": {
"chalk": "^5.3.0",
"minecraft-protocol": "^1.26.5"
}
}

15
script/copy.sh Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
SOURCE=$1
if [ -z "$SOURCE" ]
then
SOURCE="src/"
fi
# length of "src/" = 4
TARGET="${SOURCE:4}"
TARGET="dist/$TARGET"
rsync -rav --exclude="*.ts" $SOURCE $TARGET

1
script/watch/copy.sh Executable file
View file

@ -0,0 +1 @@
inotifywait -rm -e CLOSE_WRITE --format "%w" src | stdbuf -o0 sed 's@/$@@' | xargs -n1 -I{} ./script/copy.sh {}

View file

@ -1,19 +1,15 @@
import Instance from './instance/index.js'
import { SERVER_OPTIONS } from './settings.js'
import { createServer } from 'minecraft-protocol'
// See: https://nodejs.org/api/worker_threads.html#considerations-when-transferring-typedarrays-and-buffers
Object.assign(Uint8Array.prototype, Buffer.prototype)
const VERSION = '1.19.4'
export const SERVER_OPTIONS = {
host: '127.0.0.1',
port: 25565,
keepAlive: false,
version: VERSION
}
export const server = createServer(SERVER_OPTIONS)
export const server = createServer({
'online-mode': false,
...SERVER_OPTIONS,
keepAlive: false
})
server.on('login', client => {
// eslint-disable-next-line no-new

View file

@ -1,10 +1,12 @@
import { type Message } from '../worker/parent.js'
import { importModulesGenerator } from '../util/import-modules.js'
import { Channel } from '../util/channel.js'
import { TARGET_OPTIONS } from '../settings.js'
import { dirname, resolve } from 'path'
import { fileURLToPath } from 'url'
import { Worker } from 'worker_threads'
import { createClient, type ServerClient } from 'minecraft-protocol'
import chalk from 'chalk'
if (!('require' in globalThis)) {
globalThis.__filename = fileURLToPath(import.meta.url)
@ -14,16 +16,6 @@ if (!('require' in globalThis)) {
const WORKER_PATH = resolve(__dirname, '../worker')
const MODULE_DIR_PATH = resolve(__dirname, '../module')
const VERSION = '1.19.4'
export const TARGET_OPTIONS = {
host: 'kaboom.pw',
port: 25565,
keepAlive: false,
username: 'Player137',
version: VERSION
}
export class Instance {
public readonly client
public readonly server
@ -32,7 +24,13 @@ export class Instance {
constructor (client: ServerClient) {
this.client = client
const target = createClient(TARGET_OPTIONS)
const target = createClient({
auth: 'offline',
username: client.username,
...TARGET_OPTIONS,
keepAlive: false
})
this.server = target
target.on('error', error => {
@ -50,7 +48,39 @@ export class Instance {
}
private async _importModules (): Promise<void> {
for await (const module of importModulesGenerator(MODULE_DIR_PATH, 'global.js')) {
console.group('Loading modules... (global)')
const start = performance.now()
let moduleStart = NaN
for await (const module of importModulesGenerator(
MODULE_DIR_PATH,
'global.js',
{
pre (entry) {
const now = performance.now()
moduleStart = now
const module = entry.name
console.group(`Loading ${module}...`)
},
post (entry) {
const now = performance.now()
const delta = now - moduleStart
console.groupEnd()
console.info(`took ${delta.toPrecision(2)}ms`)
},
error (error, entry) {
const module = entry.name
error.stack += `\n while loading module ${JSON.stringify(module)} (local)`
console.error(chalk.red(error.stack))
}
}
)) {
if (module === null) throw new Error('Expected module not to be null')
if (typeof module !== 'object') throw new Error('Expected module to be an object')
@ -62,6 +92,13 @@ export class Instance {
await (f as (instance: Instance) => Promise<void>)(this)
}
const end = performance.now()
const delta = end - start
console.groupEnd()
console.info(`took ${delta.toFixed(2)}ms`)
}
protected postMessage (channel: string, data: any): void {

View file

@ -9,57 +9,52 @@ export type PacketEventMap = Record<string, (packet: Packet) => AsyncVoid>
// ? Should I export the channel
export const channel = createChannel<Message>('proxy')
export class Proxy {
public readonly client = new PublicEventHandler<PacketEventMap>()
public readonly server = new PublicEventHandler<PacketEventMap>()
function write (side: Side, packet: RawPacket): void {
channel.write({
side,
packet
})
}
constructor () {
channel.subscribe(({ side, packet: raw }: Message) => {
void (async () => {
const emitter = this[side]
export const proxy = {
client: new PublicEventHandler<PacketEventMap>(),
server: new PublicEventHandler<PacketEventMap>(),
const packet = new Packet(raw.name, raw.data)
await emitter.emit('packet', packet)
await emitter.emit(packet.name, packet)
if (packet.canceled) return
switch (side) {
case 'client':
side = 'server'
break
case 'server':
side = 'client'
break
default:
throw new Error(`Invalid side: ${side as any}`)
}
channel.write({
side,
packet
})
})()
})
writeClient (name: string, data: unknown): void {
write('client', { name, data })
},
writeServer (name: string, data: unknown): void {
write('server', { name, data })
}
} as const
channel.subscribe(({ side, packet: raw }: Message) => {
void (async () => {
const emitter = proxy[side]
const packet = new Packet(raw.name, raw.data)
await emitter.emit('packet', packet)
await emitter.emit(packet.name, packet)
if (packet.canceled) return
switch (side) {
case 'client':
side = 'server'
break
case 'server':
side = 'client'
break
default:
throw new Error(`Invalid side: ${side as any}`)
}
protected write (side: Side, packet: RawPacket): void {
channel.write({
side,
packet
})
}
public writeClient (name: string, data: unknown): void {
this.write('client', { name, data })
}
public writeServer (name: string, data: unknown): void {
this.write('server', { name, data })
}
}
export const proxy = new Proxy()
})()
})
export default proxy

21
src/settings.ts Normal file
View file

@ -0,0 +1,21 @@
import { type ClientOptions, type ServerOptions } from 'minecraft-protocol'
export const VERSION = '1.19.4'
interface TargetOptions extends Omit<ClientOptions, 'username'> {
username?: ClientOptions['username']
}
export const TARGET_OPTIONS: TargetOptions = {
host: 'kaboom.pw',
port: 25565,
// username: 'RealDinhero21',
// auth: 'microsoft',
version: VERSION
}
export const SERVER_OPTIONS: ServerOptions = {
host: '127.0.0.1',
port: 25565,
version: VERSION
}

View file

@ -24,6 +24,17 @@ export class EventHandler<T extends EventMap<T>> {
set.add(callback)
}
public once<E extends keyof T>(name: E, callback: T[E]): void {
const original = callback
callback = function (this: ThisParameterType<T[E]>, ...args: Parameters<T[E]>): ReturnType<T[E]> {
const output = original.apply(this, args)
return output as ReturnType<T[E]>
} as unknown as T[E]
this.on(name, callback)
}
public off<E extends keyof T> (name: E, callback: T[E]): void {
const map = this.map

View file

@ -5,22 +5,46 @@
// }
import { exists } from './file.js'
import { type AsyncVoid } from './types.js'
import { type Dirent } from 'fs'
import { readdir } from 'fs/promises'
import { resolve } from 'path'
export async function * importModulesGenerator (directory: string, index: string): AsyncGenerator<unknown> {
export interface Callbacks {
pre?: (entry: Dirent) => AsyncVoid
post?: (entry: Dirent) => AsyncVoid
error?: (error: Error, entry: Dirent) => AsyncVoid
}
export async function * importModulesGenerator (directory: string, index: string, callbacks?: Callbacks): AsyncGenerator<unknown> {
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = resolve(entry.path, entry.name, index)
if (!entry.isDirectory()) throw new Error(`Expected ${path} to be a directory`)
if (!entry.isDirectory()) console.warn(`Expected ${entry.name} to be a directory (located at ${entry.path})`)
if (!await exists(path)) continue
yield await import(path)
try {
const preCallback = callbacks?.pre
if (preCallback !== undefined) await preCallback(entry)
yield await import(path)
const postCallback = callbacks?.post
if (postCallback !== undefined) await postCallback(entry)
} catch (error) {
const errorCallback = callbacks?.error
if (errorCallback === undefined) throw error
await errorCallback(error as Error, entry)
}
}
}
export async function importModules (directory: string, index: string): Promise<void> {
export async function importModules (directory: string, index: string, callbacks?: Callbacks): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for await (const _ of importModulesGenerator(directory, index));
for await (const _ of importModulesGenerator(directory, index, callbacks));
}

View file

@ -1,6 +1,7 @@
import { importModules } from '../util/import-modules.js'
import { dirname, resolve } from 'path'
import { fileURLToPath } from 'url'
import chalk from 'chalk'
if (!('require' in globalThis)) {
globalThis.__filename = fileURLToPath(import.meta.url)
@ -9,4 +10,43 @@ if (!('require' in globalThis)) {
const MODULE_DIR_PATH = resolve(__dirname, '../module')
await importModules(MODULE_DIR_PATH, 'local.js')
console.group('Loading modules... (local)')
const start = performance.now()
let moduleStart = NaN
await importModules(
MODULE_DIR_PATH,
'local.js',
{
pre (entry) {
const module = entry.name
console.group(`Loading ${module}...`)
const now = performance.now()
moduleStart = now
},
post (entry) {
const now = performance.now()
const delta = now - moduleStart
console.groupEnd()
console.info(`took ${delta.toFixed(2)}ms`)
},
error (error, entry) {
const module = entry.name
error.stack += `\n while loading module ${JSON.stringify(module)} (local)`
console.error(chalk.red(error.stack))
}
}
)
const end = performance.now()
const delta = end - start
console.groupEnd()
console.info(`took ${delta.toFixed(2)}ms`)