mirror of
https://github.com/DinheroDevelopmentGroup/modular-minecraft-proxy.git
synced 2024-11-27 01:25:57 -05:00
refactor
I actually wrote a package manager but then my shit got fucked and I lost motivation to do it again but now I have motivation again (thanks modern medicine)
This commit is contained in:
parent
1a58d1c126
commit
cc9c274325
30 changed files with 1569 additions and 2604 deletions
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"standard-with-typescript",
|
||||
"plugin:require-extensions/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"require-extensions"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module",
|
||||
"project": "tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
"groups": [
|
||||
"type",
|
||||
"index",
|
||||
"sibling",
|
||||
"parent",
|
||||
"internal",
|
||||
"builtin",
|
||||
"external",
|
||||
"object"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-case-declarations": "off"
|
||||
}
|
||||
}
|
3
.prettierrc
Normal file
3
.prettierrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"singleQuote": true
|
||||
}
|
8
.swcrc
8
.swcrc
|
@ -14,13 +14,9 @@
|
|||
"target": "esnext",
|
||||
"loose": true
|
||||
},
|
||||
"exclude": [
|
||||
"\\.js$",
|
||||
"\\.d\\.ts$",
|
||||
"node_modules"
|
||||
],
|
||||
"exclude": ["\\.js$", "\\.d\\.ts$", "node_modules"],
|
||||
"module": {
|
||||
"type": "es6"
|
||||
},
|
||||
"minify": false
|
||||
}
|
||||
}
|
||||
|
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"files.exclude": {
|
||||
"src/module/*": false
|
||||
}
|
||||
}
|
21
LICENSE
21
LICENSE
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Dinhero21
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,4 +1,5 @@
|
|||
# MMP
|
||||
|
||||
- Modular Minecraft Proxy
|
||||
|
||||
# How does this differ from other proxies (such as SMP)?
|
||||
|
@ -13,7 +14,7 @@ No more cleint, sever or posiotin!
|
|||
|
||||
For every client that connects a new thread is created.
|
||||
|
||||
While also offering minimal performance gains (as there probably isn't (and shouldn't) be ever more then one client) makes it so that program instances client-based instead of being shared between all clients.
|
||||
While also offering minimal performance gains (as there probably isn't (and shouldn't) be ever more then one client) makes it so that program instances client-based instead of being shared between all clients.
|
||||
|
||||
This makes a Singleton-like architecture possible without needing to worry about multiple instances of the same plugin and explicit declaration of dependencies.
|
||||
|
||||
|
|
6
TODO.md
Normal file
6
TODO.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
- [x] TypeScript
|
||||
- [x] Client
|
||||
- [x] Server
|
||||
- [x] Packet Forwarding
|
||||
- [x] Module Loading
|
||||
- [ ] Module Manager
|
46
eslint.config.mjs
Normal file
46
eslint.config.mjs
Normal file
|
@ -0,0 +1,46 @@
|
|||
// @ts-check
|
||||
|
||||
import eslint from '@eslint/js';
|
||||
import prettier from 'eslint-config-prettier';
|
||||
import simpleImportSort from 'eslint-plugin-simple-import-sort';
|
||||
import globals from 'globals';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
{ languageOptions: { globals: globals.browser } },
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
plugins: {
|
||||
'simple-import-sort': simpleImportSort,
|
||||
},
|
||||
rules: {
|
||||
'simple-import-sort/exports': 'off',
|
||||
'simple-import-sort/imports': 'error',
|
||||
'no-case-declarations': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'no-constant-condition': ['error', { checkLoops: false }],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
args: 'all',
|
||||
argsIgnorePattern: '^_',
|
||||
caughtErrors: 'all',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
destructuredArrayIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-misused-promises': 'error',
|
||||
'object-shorthand': 'error',
|
||||
'@typescript-eslint/consistent-type-imports': 'error',
|
||||
},
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
},
|
||||
},
|
||||
prettier,
|
||||
);
|
3176
package-lock.json
generated
3176
package-lock.json
generated
File diff suppressed because it is too large
Load diff
43
package.json
43
package.json
|
@ -1,47 +1,44 @@
|
|||
{
|
||||
"name": "modular-minecraft-proxy",
|
||||
"version": "1.0.0",
|
||||
"description": "Lego Minecraft",
|
||||
"description": "Modular Minecraft Proxy",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "scripty",
|
||||
"watch:build": "scripty",
|
||||
"copy": "scripty",
|
||||
"watch:copy": "scripty",
|
||||
"lint": "scripty",
|
||||
"run": "scripty",
|
||||
"watch:run": "scripty",
|
||||
"watch": "scripty"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"author": "Dinhero Development Group",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"config": {
|
||||
"scripty": {
|
||||
"parallel": true,
|
||||
"path": "script"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@swc/cli": "^0.1.63",
|
||||
"@swc/core": "^1.3.102",
|
||||
"@types/node": "^20.16.2",
|
||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||
"@typescript-eslint/parser": "^6.17.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-standard-with-typescript": "^43.0.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-n": "^16.6.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-require-extensions": "^0.1.3",
|
||||
"nodemon": "^3.0.2",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"minecraft-protocol": "^1.47.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.9.1",
|
||||
"@swc/cli": "^0.4.0",
|
||||
"@swc/core": "^1.7.22",
|
||||
"@swc/helpers": "^0.5.12",
|
||||
"chalk": "^5.3.0",
|
||||
"minecraft-protocol": "^1.26.5",
|
||||
"scripty": "^2.1.1"
|
||||
"@types/node": "^22.5.1",
|
||||
"eslint": "^9.9.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"globals": "^15.9.0",
|
||||
"nodemon": "^3.1.4",
|
||||
"prettier": "^3.3.3",
|
||||
"scripty": "^2.1.1",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.3.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
swc src -d dist $@
|
||||
# --delete-dir-on-start breaks nodemon
|
||||
swc src -d dist --strip-leading-paths $@
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
SOURCE="$1"
|
||||
|
||||
: ${SOURCE:=src}
|
||||
|
||||
TARGET="$2"
|
||||
|
||||
: ${TARGET:=dist}
|
||||
|
||||
rsync -rav --exclude="*.ts" $SOURCE/ $TARGET/
|
4
script/lint.sh
Executable file
4
script/lint.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
eslint --fix src
|
||||
prettier --write src
|
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
node dist/index.js
|
||||
node dist/index.js $@
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
./script/copy.sh
|
||||
|
||||
nodemon --watch src --exec "./script/copy.sh $@"
|
22
src/index.ts
22
src/index.ts
|
@ -1,19 +1,19 @@
|
|||
import Instance from './instance/index.js'
|
||||
import { SERVER_OPTIONS } from './settings.js'
|
||||
import { createServer } from 'minecraft-protocol'
|
||||
import { createServer } from 'minecraft-protocol';
|
||||
|
||||
import Instance from './instance/index.js';
|
||||
import { SERVER_OPTIONS } from './settings.js';
|
||||
|
||||
// See: https://nodejs.org/api/worker_threads.html#considerations-when-transferring-typedarrays-and-buffers
|
||||
Object.assign(Uint8Array.prototype, Buffer.prototype)
|
||||
Object.assign(Uint8Array.prototype, Buffer.prototype);
|
||||
|
||||
export const server = createServer({
|
||||
'online-mode': false,
|
||||
...SERVER_OPTIONS,
|
||||
keepAlive: false
|
||||
})
|
||||
keepAlive: false,
|
||||
});
|
||||
|
||||
server.on('login', client => {
|
||||
console.info(`${client.username} has connected!`)
|
||||
server.on('login', (client) => {
|
||||
console.info(`${client.username} has connected!`);
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Instance(client)
|
||||
})
|
||||
new Instance(client);
|
||||
});
|
||||
|
|
|
@ -1,140 +1,147 @@
|
|||
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'
|
||||
import chalk from 'chalk';
|
||||
import { createClient, type ServerClient } from 'minecraft-protocol';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { Worker } from 'worker_threads';
|
||||
|
||||
import { TARGET_OPTIONS } from '../settings.js';
|
||||
import { Channel } from '../util/channel.js';
|
||||
import { importModulesGenerator } from '../util/import-modules.js';
|
||||
import { type Message } from '../worker/parent.js';
|
||||
|
||||
if (!('require' in globalThis)) {
|
||||
globalThis.__filename = fileURLToPath(import.meta.url)
|
||||
globalThis.__dirname = dirname(__filename)
|
||||
globalThis.__filename = fileURLToPath(import.meta.url);
|
||||
globalThis.__dirname = dirname(__filename);
|
||||
}
|
||||
|
||||
const WORKER_PATH = resolve(__dirname, '../worker')
|
||||
const MODULE_DIR_PATH = resolve(__dirname, '../module')
|
||||
const WORKER_PATH = resolve(__dirname, '../worker');
|
||||
const MODULE_DIR_PATH = resolve(__dirname, '../module');
|
||||
|
||||
export class Instance {
|
||||
public readonly client
|
||||
public readonly server
|
||||
public readonly worker
|
||||
public readonly client;
|
||||
public readonly server;
|
||||
public readonly worker;
|
||||
|
||||
constructor (client: ServerClient) {
|
||||
this.client = client
|
||||
constructor(client: ServerClient) {
|
||||
this.client = client;
|
||||
|
||||
const target = createClient({
|
||||
auth: 'offline',
|
||||
username: client.username,
|
||||
...TARGET_OPTIONS,
|
||||
keepAlive: false
|
||||
})
|
||||
keepAlive: false,
|
||||
});
|
||||
|
||||
this.server = target
|
||||
this.server = target;
|
||||
|
||||
target.on('error', error => {
|
||||
console.error('Target error:', error)
|
||||
})
|
||||
target.on('error', (error) => {
|
||||
console.error('Target error:', error);
|
||||
});
|
||||
|
||||
console.info('Initializing worker... (local context)')
|
||||
console.info('Initializing worker... (local context)');
|
||||
|
||||
const start = performance.now()
|
||||
const start = performance.now();
|
||||
|
||||
const worker = new Worker(WORKER_PATH)
|
||||
this.worker = worker
|
||||
const worker = new Worker(WORKER_PATH);
|
||||
this.worker = worker;
|
||||
|
||||
worker.on('online', () => {
|
||||
const end = performance.now()
|
||||
const end = performance.now();
|
||||
|
||||
const delta = end - start
|
||||
const delta = end - start;
|
||||
|
||||
console.info(`Worker online! took ${delta.toFixed(2)}ms`)
|
||||
})
|
||||
console.info(`Worker online! took ${delta.toFixed(2)}ms`);
|
||||
});
|
||||
|
||||
worker.on('error', error => {
|
||||
console.error('Worker error:', error)
|
||||
})
|
||||
worker.on('error', (error) => {
|
||||
console.error('Worker error:', error);
|
||||
});
|
||||
|
||||
void this._importModules()
|
||||
void this._importModules();
|
||||
}
|
||||
|
||||
private async _importModules (): Promise<void> {
|
||||
console.group('Loading modules... (global)')
|
||||
private async _importModules(): Promise<void> {
|
||||
console.group('Loading modules... (global)');
|
||||
|
||||
const start = performance.now()
|
||||
const start = performance.now();
|
||||
|
||||
let moduleStart = NaN
|
||||
let moduleStart = NaN;
|
||||
|
||||
for await (const module of importModulesGenerator(
|
||||
MODULE_DIR_PATH,
|
||||
'global.js',
|
||||
{
|
||||
pre (entry) {
|
||||
const now = performance.now()
|
||||
moduleStart = now
|
||||
pre(entry) {
|
||||
const now = performance.now();
|
||||
moduleStart = now;
|
||||
|
||||
const module = entry.name
|
||||
console.group(`Loading ${module}...`)
|
||||
const module = entry.name;
|
||||
console.group(`Loading ${module}...`);
|
||||
},
|
||||
post (entry) {
|
||||
const now = performance.now()
|
||||
const delta = now - moduleStart
|
||||
post(_entry) {
|
||||
const now = performance.now();
|
||||
const delta = now - moduleStart;
|
||||
|
||||
console.groupEnd()
|
||||
console.info(`took ${delta.toPrecision(2)}ms`)
|
||||
console.groupEnd();
|
||||
console.info(`took ${delta.toPrecision(2)}ms`);
|
||||
},
|
||||
error (error, entry) {
|
||||
const module = entry.name
|
||||
error(error, entry) {
|
||||
const module = entry.name;
|
||||
|
||||
error.stack += `\n while loading module ${JSON.stringify(module)} (local)`
|
||||
error.stack += `\n while loading module ${JSON.stringify(
|
||||
module,
|
||||
)} (local)`;
|
||||
|
||||
console.error(chalk.red(error.stack))
|
||||
}
|
||||
}
|
||||
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')
|
||||
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');
|
||||
|
||||
if (!('default' in module)) throw new Error('Expected default export')
|
||||
if (!('default' in module)) throw new Error('Expected default export');
|
||||
|
||||
const f = module.default
|
||||
const f = module.default;
|
||||
|
||||
if (typeof f !== 'function') throw new Error('Expected default export to be a function')
|
||||
if (typeof f !== 'function')
|
||||
throw new Error('Expected default export to be a function');
|
||||
|
||||
await (f as (instance: Instance) => Promise<void>)(this)
|
||||
await (f as (instance: Instance) => Promise<void>)(this);
|
||||
}
|
||||
|
||||
const end = performance.now()
|
||||
const end = performance.now();
|
||||
|
||||
const delta = end - start
|
||||
const delta = end - start;
|
||||
|
||||
console.groupEnd()
|
||||
console.info(`took ${delta.toFixed(2)}ms`)
|
||||
console.groupEnd();
|
||||
console.info(`took ${delta.toFixed(2)}ms`);
|
||||
}
|
||||
|
||||
protected postMessage (channel: string, data: any): void {
|
||||
protected postMessage(channel: string, data: any): void {
|
||||
this.worker.postMessage({
|
||||
channel,
|
||||
data
|
||||
} satisfies Message)
|
||||
data,
|
||||
} satisfies Message);
|
||||
}
|
||||
|
||||
public createChannel<TSend, TReceive = TSend> (id: string): Channel<TSend, TReceive> {
|
||||
const channel = new Channel<TSend, TReceive>(id)
|
||||
public createChannel<TSend, TReceive = TSend>(
|
||||
id: string,
|
||||
): Channel<TSend, TReceive> {
|
||||
const channel = new Channel<TSend, TReceive>(id);
|
||||
|
||||
channel._subscribe(data => {
|
||||
this.postMessage(id, data)
|
||||
})
|
||||
channel._subscribe((data) => {
|
||||
this.postMessage(id, data);
|
||||
});
|
||||
|
||||
this.worker.on('message', (message: Message<TReceive>) => {
|
||||
if (message.channel !== id) return
|
||||
if (message.channel !== id) return;
|
||||
|
||||
channel._write(message.data)
|
||||
})
|
||||
channel._write(message.data);
|
||||
});
|
||||
|
||||
return channel
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
|
||||
export default Instance
|
||||
export default Instance;
|
||||
|
|
|
@ -1,122 +1,125 @@
|
|||
import type Instance from '../../instance/index.js'
|
||||
import { type Message } from './shared.js'
|
||||
import { type RawPacket } from '../../util/packet.js'
|
||||
import { states } from 'minecraft-protocol'
|
||||
import { states } from 'minecraft-protocol';
|
||||
|
||||
import type Instance from '../../instance/index.js';
|
||||
import { type RawPacket } from '../../util/packet.js';
|
||||
import { type Message } from './shared.js';
|
||||
|
||||
export default async function (instance: Instance): Promise<void> {
|
||||
const clientQueue: RawPacket[] = []
|
||||
const serverQueue: RawPacket[] = []
|
||||
const clientQueue: RawPacket[] = [];
|
||||
const serverQueue: RawPacket[] = [];
|
||||
|
||||
const channel = instance.createChannel<Message>('proxy')
|
||||
const channel = instance.createChannel<Message>('proxy');
|
||||
|
||||
const client = instance.client
|
||||
const server = instance.server
|
||||
const worker = instance.worker
|
||||
const client = instance.client;
|
||||
const server = instance.server;
|
||||
const worker = instance.worker;
|
||||
|
||||
client.on('end', reason => {
|
||||
console.info('Client disconnected:', reason)
|
||||
client.on('end', (reason) => {
|
||||
console.info('Client disconnected:', reason);
|
||||
|
||||
server.end(reason)
|
||||
server.end(reason);
|
||||
|
||||
void worker.terminate()
|
||||
})
|
||||
void worker.terminate();
|
||||
});
|
||||
|
||||
server.on('end', reason => {
|
||||
console.info('Target disconnected:', reason)
|
||||
server.on('end', (reason) => {
|
||||
console.info('Server disconnected:', reason);
|
||||
|
||||
client.end(reason)
|
||||
client.end(reason);
|
||||
|
||||
void worker.terminate()
|
||||
})
|
||||
void worker.terminate();
|
||||
});
|
||||
|
||||
client.on('error', error => {
|
||||
console.error('Client error:', error)
|
||||
})
|
||||
client.on('error', (error) => {
|
||||
console.error('Client error:', error);
|
||||
});
|
||||
|
||||
server.on('error', error => {
|
||||
console.error('Target error:', error)
|
||||
})
|
||||
server.on('error', (error) => {
|
||||
console.error('Server error:', error);
|
||||
});
|
||||
|
||||
client.on('packet', (data, meta) => {
|
||||
if (meta.state !== states.PLAY) return
|
||||
// if (meta.state !== states.PLAY) return;
|
||||
|
||||
channel.write({
|
||||
side: 'client',
|
||||
direction: 'downstream',
|
||||
packet: {
|
||||
name: meta.name,
|
||||
data
|
||||
}
|
||||
})
|
||||
})
|
||||
...meta,
|
||||
data,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
server.on('packet', (data, meta) => {
|
||||
if (meta.state !== states.PLAY) return
|
||||
// if (meta.state !== states.PLAY) return;
|
||||
|
||||
channel.write({
|
||||
side: 'server',
|
||||
direction: 'upstream',
|
||||
packet: {
|
||||
name: meta.name,
|
||||
data
|
||||
}
|
||||
})
|
||||
})
|
||||
...meta,
|
||||
data,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
client.on('state', state => {
|
||||
if (state !== states.PLAY) return
|
||||
// Flush packet queue on play state
|
||||
|
||||
const queue = clientQueue
|
||||
client.on('state', (state) => {
|
||||
if (state !== states.PLAY) return;
|
||||
|
||||
for (const packet of queue) client.write(packet.name, packet.data)
|
||||
const queue = clientQueue;
|
||||
|
||||
queue.length = 0
|
||||
})
|
||||
for (const packet of queue) client.write(packet.name, packet.data);
|
||||
|
||||
server.on('state', state => {
|
||||
if (state !== states.PLAY) return
|
||||
queue.length = 0;
|
||||
});
|
||||
|
||||
const queue = serverQueue
|
||||
server.on('state', (state) => {
|
||||
if (state !== states.PLAY) return;
|
||||
|
||||
for (const packet of queue) server.write(packet.name, packet.data)
|
||||
const queue = serverQueue;
|
||||
|
||||
queue.length = 0
|
||||
})
|
||||
for (const packet of queue) server.write(packet.name, packet.data);
|
||||
|
||||
channel.subscribe(({ side, packet }) => {
|
||||
queue.length = 0;
|
||||
});
|
||||
|
||||
channel.subscribe(({ direction: side, packet }) => {
|
||||
switch (side) {
|
||||
case 'client':
|
||||
writeClientPacket(packet)
|
||||
break
|
||||
case 'server':
|
||||
writeServerPacket(packet)
|
||||
break
|
||||
case 'downstream':
|
||||
writeClientPacket(packet);
|
||||
break;
|
||||
case 'upstream':
|
||||
writeServerPacket(packet);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid side: ${side as any}`)
|
||||
throw new Error(`Invalid side: ${side as any}`);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function writeClientPacket (packet: RawPacket): void {
|
||||
function writeClientPacket(packet: RawPacket): void {
|
||||
// wait until play state
|
||||
if (client.state !== states.PLAY) {
|
||||
clientQueue.push(packet)
|
||||
clientQueue.push(packet);
|
||||
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
client.write(
|
||||
packet.name,
|
||||
packet.data
|
||||
)
|
||||
if (packet.state !== states.PLAY) return;
|
||||
|
||||
client.write(packet.name, packet.data);
|
||||
}
|
||||
|
||||
function writeServerPacket (packet: RawPacket): void {
|
||||
function writeServerPacket(packet: RawPacket): void {
|
||||
// wait until play state
|
||||
if (server.state !== states.PLAY) {
|
||||
serverQueue.push(packet)
|
||||
serverQueue.push(packet);
|
||||
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
server.write(
|
||||
packet.name,
|
||||
packet.data
|
||||
)
|
||||
if (packet.state !== states.PLAY) return;
|
||||
|
||||
server.write(packet.name, packet.data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,60 +1,75 @@
|
|||
import { type Side, type Message } from './shared.js'
|
||||
import { PublicEventHandler } from '../../util/events.js'
|
||||
import { createChannel } from '../../worker/parent.js'
|
||||
import { Packet, type RawPacket } from '../../util/packet.js'
|
||||
import { type AsyncVoid } from '../../util/types.js'
|
||||
import type { States } from 'minecraft-protocol';
|
||||
import { states } from 'minecraft-protocol';
|
||||
|
||||
export type PacketEventMap = Record<string, (packet: Packet) => AsyncVoid>
|
||||
import { PublicEventHandler } from '../../util/events.js';
|
||||
import { Packet, type RawPacket } from '../../util/packet.js';
|
||||
import { type AsyncVoid } from '../../util/types.js';
|
||||
import { createChannel } from '../../worker/parent.js';
|
||||
import { type Direction as Direction, type Message } from './shared.js';
|
||||
|
||||
export type PacketEventMap = Record<string, (packet: Packet) => AsyncVoid>;
|
||||
|
||||
// ? Should I export the channel
|
||||
export const channel = createChannel<Message>('proxy')
|
||||
export const channel = createChannel<Message>('proxy');
|
||||
|
||||
function write (side: Side, packet: RawPacket): void {
|
||||
function write(direction: Direction, packet: RawPacket): void {
|
||||
channel.write({
|
||||
side,
|
||||
packet
|
||||
})
|
||||
direction,
|
||||
packet,
|
||||
});
|
||||
}
|
||||
|
||||
export const proxy = {
|
||||
client: new PublicEventHandler<PacketEventMap>(),
|
||||
server: new PublicEventHandler<PacketEventMap>(),
|
||||
|
||||
writeClient (name: string, data: unknown): void {
|
||||
write('client', { name, data })
|
||||
writeDownstream(
|
||||
name: string,
|
||||
data: unknown,
|
||||
state: States = states.PLAY,
|
||||
): void {
|
||||
write('downstream', { name, data, state });
|
||||
},
|
||||
writeServer (name: string, data: unknown): void {
|
||||
write('server', { name, data })
|
||||
}
|
||||
} as const
|
||||
writeUpstream(
|
||||
name: string,
|
||||
data: unknown,
|
||||
state: States = states.PLAY,
|
||||
): void {
|
||||
write('upstream', { name, data, state });
|
||||
},
|
||||
} as const;
|
||||
|
||||
channel.subscribe(({ side, packet: raw }: Message) => {
|
||||
channel.subscribe(({ direction, packet: raw }: Message) => {
|
||||
void (async () => {
|
||||
const emitter = proxy[side]
|
||||
const sourceHandler = {
|
||||
downstream: proxy.server,
|
||||
upstream: proxy.client,
|
||||
}[direction];
|
||||
|
||||
const packet = new Packet(raw.name, raw.data)
|
||||
const packet = new Packet(raw.name, raw.data);
|
||||
|
||||
await emitter.emit('packet', packet)
|
||||
await emitter.emit(packet.name, packet)
|
||||
await sourceHandler.emit('packet', packet);
|
||||
await sourceHandler.emit(packet.name, packet);
|
||||
|
||||
if (packet.canceled) return
|
||||
if (packet.canceled) return;
|
||||
|
||||
switch (side) {
|
||||
case 'client':
|
||||
side = 'server'
|
||||
break
|
||||
case 'server':
|
||||
side = 'client'
|
||||
break
|
||||
switch (direction) {
|
||||
case 'downstream':
|
||||
direction = 'upstream';
|
||||
break;
|
||||
case 'upstream':
|
||||
direction = 'downstream';
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid side: ${side as any}`)
|
||||
throw new Error(`Invalid direction: ${direction as any}`);
|
||||
}
|
||||
|
||||
// Forward packet
|
||||
channel.write({
|
||||
side,
|
||||
packet
|
||||
})
|
||||
})()
|
||||
})
|
||||
direction,
|
||||
packet,
|
||||
});
|
||||
})();
|
||||
});
|
||||
|
||||
export default proxy
|
||||
export default proxy;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { type RawPacket } from '../../util/packet.js'
|
||||
import { type RawPacket } from '../../util/packet.js';
|
||||
|
||||
export type Side = 'client' | 'server'
|
||||
export type Direction = 'upstream' | 'downstream';
|
||||
|
||||
export interface Message {
|
||||
side: Side
|
||||
packet: RawPacket
|
||||
direction: Direction;
|
||||
packet: RawPacket;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
import { type ClientOptions, type ServerOptions } from 'minecraft-protocol'
|
||||
import { type ClientOptions, type ServerOptions } from 'minecraft-protocol';
|
||||
|
||||
export const VERSION = '1.19.4'
|
||||
export const VERSION = '1.19.4';
|
||||
|
||||
export const TARGET_HOST = process.env.HOST;
|
||||
export const TARGET_PORT = parseInt(process.env.PORT ?? '25565');
|
||||
|
||||
interface TargetOptions extends Omit<ClientOptions, 'username'> {
|
||||
username?: ClientOptions['username']
|
||||
username?: ClientOptions['username'];
|
||||
}
|
||||
|
||||
export const TARGET_OPTIONS: TargetOptions = {
|
||||
host: 'kaboom.pw',
|
||||
port: 25565,
|
||||
host: TARGET_HOST,
|
||||
port: TARGET_PORT,
|
||||
// username: 'RealDinhero21',
|
||||
// auth: 'microsoft',
|
||||
version: VERSION
|
||||
}
|
||||
version: VERSION,
|
||||
};
|
||||
|
||||
export const SERVER_OPTIONS: ServerOptions = {
|
||||
host: '127.0.0.1',
|
||||
port: 25565,
|
||||
version: VERSION
|
||||
}
|
||||
version: VERSION,
|
||||
};
|
||||
|
|
|
@ -1,43 +1,48 @@
|
|||
export type Listener<T> = (data: T) => void
|
||||
// ==============================================================
|
||||
// # This looks more and more like rx with each day that passes #
|
||||
// ==============================================================
|
||||
// https://rxjs.dev/
|
||||
|
||||
export type Listener<T> = (data: T) => void;
|
||||
|
||||
// _x -> x
|
||||
// x -> _x
|
||||
export class Channel<TSend, TReceive = TSend> {
|
||||
public readonly id
|
||||
public readonly id;
|
||||
|
||||
constructor (id: string) {
|
||||
this.id = id
|
||||
constructor(id: string) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private readonly listeners = new Set<Listener<TReceive>>()
|
||||
private readonly listeners = new Set<Listener<TReceive>>();
|
||||
|
||||
public subscribe (listener: Listener<TReceive>): void {
|
||||
this.listeners.add(listener)
|
||||
public subscribe(listener: Listener<TReceive>): void {
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public unsubscribe (listener: Listener<TReceive>): void {
|
||||
this.listeners.delete(listener)
|
||||
public unsubscribe(listener: Listener<TReceive>): void {
|
||||
this.listeners.delete(listener);
|
||||
}
|
||||
|
||||
public write (data: TSend): void {
|
||||
public write(data: TSend): void {
|
||||
for (const listener of this._listeners) {
|
||||
listener(data)
|
||||
listener(data);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly _listeners = new Set<Listener<TSend>>()
|
||||
private readonly _listeners = new Set<Listener<TSend>>();
|
||||
|
||||
public _subscribe (listener: Listener<TSend>): void {
|
||||
this._listeners.add(listener)
|
||||
public _subscribe(listener: Listener<TSend>): void {
|
||||
this._listeners.add(listener);
|
||||
}
|
||||
|
||||
public _unsubscribe (listener: Listener<TSend>): void {
|
||||
this._listeners.delete(listener)
|
||||
public _unsubscribe(listener: Listener<TSend>): void {
|
||||
this._listeners.delete(listener);
|
||||
}
|
||||
|
||||
public _write (data: TReceive): void {
|
||||
public _write(data: TReceive): void {
|
||||
for (const listener of this.listeners) {
|
||||
listener(data)
|
||||
listener(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +1,73 @@
|
|||
import { type AsyncVoid } from './types.js'
|
||||
// ==============================================================
|
||||
// # This looks more and more like rx with each day that passes #
|
||||
// ==============================================================
|
||||
// https://rxjs.dev/
|
||||
|
||||
export type EventMap<T> = { [K in keyof T]: (...args: any[]) => AsyncVoid }
|
||||
import { type AsyncVoid } from './types.js';
|
||||
|
||||
export type EventMap<T> = { [K in keyof T]: (...args: any[]) => AsyncVoid };
|
||||
|
||||
export class EventHandler<T extends EventMap<T>> {
|
||||
protected map = new Map<keyof T, Set<T[keyof T]>>()
|
||||
protected map = new Map<keyof T, Set<T[keyof T]>>();
|
||||
|
||||
protected async _emit<E extends keyof T> (name: E, ...data: Parameters<T[E]>): Promise<void> {
|
||||
const map = this.map
|
||||
protected async _emit<E extends keyof T>(
|
||||
name: E,
|
||||
...data: Parameters<T[E]>
|
||||
): Promise<void> {
|
||||
const map = this.map;
|
||||
|
||||
const set = map.get(name)
|
||||
const set = map.get(name);
|
||||
|
||||
if (set === undefined) return
|
||||
if (set === undefined) return;
|
||||
|
||||
for (const listener of set) await listener(...data)
|
||||
for (const listener of set) await listener(...data);
|
||||
}
|
||||
|
||||
public on<E extends keyof T> (name: E, callback: T[E]): void {
|
||||
const map = this.map
|
||||
public on<E extends keyof T>(name: E, callback: T[E]): void {
|
||||
const map = this.map;
|
||||
|
||||
const set = map.get(name) ?? new Set()
|
||||
map.set(name, set)
|
||||
const set = map.get(name) ?? new Set();
|
||||
map.set(name, set);
|
||||
|
||||
set.add(callback)
|
||||
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)
|
||||
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]
|
||||
return output as ReturnType<T[E]>;
|
||||
} as unknown as T[E];
|
||||
|
||||
this.on(name, callback)
|
||||
this.on(name, callback);
|
||||
}
|
||||
|
||||
public off<E extends keyof T> (name: E, callback: T[E]): void {
|
||||
const map = this.map
|
||||
public off<E extends keyof T>(name: E, callback: T[E]): void {
|
||||
const map = this.map;
|
||||
|
||||
const set = map.get(name)
|
||||
const set = map.get(name);
|
||||
|
||||
if (set === undefined) return
|
||||
if (set === undefined) return;
|
||||
|
||||
set.delete(callback)
|
||||
set.delete(callback);
|
||||
}
|
||||
|
||||
public clear (): void {
|
||||
const map = this.map
|
||||
public clear(): void {
|
||||
const map = this.map;
|
||||
|
||||
map.clear()
|
||||
map.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export class PublicEventHandler<T extends EventMap<T>> extends EventHandler<T> {
|
||||
public async emit<E extends keyof T> (name: E, ...data: Parameters<T[E]>): Promise<void> {
|
||||
await this._emit(name, ...data)
|
||||
public async emit<E extends keyof T>(
|
||||
name: E,
|
||||
...data: Parameters<T[E]>
|
||||
): Promise<void> {
|
||||
await this._emit(name, ...data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
import fs, { access, constants } from 'fs/promises'
|
||||
import { resolve } from 'path'
|
||||
import fs, { access, constants } from 'fs/promises';
|
||||
import { resolve } from 'path';
|
||||
|
||||
export async function * getAllFiles (dir: string): AsyncIterable<string> {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true })
|
||||
export async function* getAllFiles(dir: string): AsyncIterable<string> {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const res = resolve(dir, entry.name)
|
||||
const res = resolve(dir, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
yield * getAllFiles(res)
|
||||
yield* getAllFiles(res);
|
||||
} else {
|
||||
yield res
|
||||
yield res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function exists (path: string): Promise<boolean> {
|
||||
export async function exists(path: string): Promise<boolean> {
|
||||
return await access(path, constants.F_OK)
|
||||
.then(() => true)
|
||||
.catch(() => false)
|
||||
.catch(() => false);
|
||||
}
|
||||
|
|
|
@ -4,47 +4,60 @@
|
|||
// }
|
||||
// }
|
||||
|
||||
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'
|
||||
import { type Dirent } from 'fs';
|
||||
import { readdir } from 'fs/promises';
|
||||
import { resolve } from 'path';
|
||||
|
||||
import { exists } from './file.js';
|
||||
import { type AsyncVoid } from './types.js';
|
||||
|
||||
export interface Callbacks {
|
||||
pre?: (entry: Dirent) => AsyncVoid
|
||||
post?: (entry: Dirent) => AsyncVoid
|
||||
error?: (error: Error, entry: Dirent) => AsyncVoid
|
||||
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> {
|
||||
// TODO: listr2?
|
||||
// https://listr2.kilic.dev/
|
||||
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)
|
||||
const path = resolve(entry.path, entry.name, index);
|
||||
|
||||
if (!entry.isDirectory()) console.warn(`Expected ${entry.name} to be a directory (located at ${entry.path})`)
|
||||
if (!entry.isDirectory())
|
||||
console.warn(
|
||||
`Expected ${entry.name} to be a directory (located at ${entry.path})`,
|
||||
);
|
||||
|
||||
if (!await exists(path)) continue
|
||||
if (!(await exists(path))) continue;
|
||||
|
||||
try {
|
||||
const preCallback = callbacks?.pre
|
||||
const preCallback = callbacks?.pre;
|
||||
|
||||
if (preCallback !== undefined) await preCallback(entry)
|
||||
if (preCallback !== undefined) await preCallback(entry);
|
||||
|
||||
yield await import(path)
|
||||
yield await import(path);
|
||||
|
||||
const postCallback = callbacks?.post
|
||||
const postCallback = callbacks?.post;
|
||||
|
||||
if (postCallback !== undefined) await postCallback(entry)
|
||||
if (postCallback !== undefined) await postCallback(entry);
|
||||
} catch (error) {
|
||||
const errorCallback = callbacks?.error
|
||||
const errorCallback = callbacks?.error;
|
||||
|
||||
if (errorCallback === undefined) throw error
|
||||
if (errorCallback === undefined) throw error;
|
||||
|
||||
await errorCallback(error as Error, entry)
|
||||
await errorCallback(error as Error, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function importModules (directory: string, index: string, callbacks?: Callbacks): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export async function importModules(
|
||||
directory: string,
|
||||
index: string,
|
||||
callbacks?: Callbacks,
|
||||
): Promise<void> {
|
||||
for await (const _ of importModulesGenerator(directory, index, callbacks));
|
||||
}
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
export interface RawPacket {
|
||||
name: string
|
||||
data: unknown
|
||||
import type { PacketMeta, States } from 'minecraft-protocol';
|
||||
import { states } from 'minecraft-protocol';
|
||||
|
||||
export interface RawPacket extends PacketMeta {
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
export class Packet<Data = unknown> {
|
||||
public name
|
||||
public data
|
||||
public name;
|
||||
public state: States = states.PLAY;
|
||||
public data;
|
||||
|
||||
public canceled: boolean = false
|
||||
public canceled: boolean = false;
|
||||
|
||||
constructor (name: string, data: Data) {
|
||||
this.name = name
|
||||
this.data = data
|
||||
constructor(name: string, data: Data) {
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
export type AsyncVoid = void | Promise<void>
|
||||
export type AsyncVoid = void | Promise<void>;
|
||||
|
|
|
@ -1,52 +1,51 @@
|
|||
import { importModules } from '../util/import-modules.js'
|
||||
import { dirname, resolve } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import chalk from 'chalk'
|
||||
import chalk from 'chalk';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { importModules } from '../util/import-modules.js';
|
||||
|
||||
if (!('require' in globalThis)) {
|
||||
globalThis.__filename = fileURLToPath(import.meta.url)
|
||||
globalThis.__dirname = dirname(__filename)
|
||||
globalThis.__filename = fileURLToPath(import.meta.url);
|
||||
globalThis.__dirname = dirname(__filename);
|
||||
}
|
||||
|
||||
const MODULE_DIR_PATH = resolve(__dirname, '../module')
|
||||
const MODULE_DIR_PATH = resolve(__dirname, '../module');
|
||||
|
||||
console.group('Loading modules... (local)')
|
||||
console.group('Loading modules... (local)');
|
||||
|
||||
const start = performance.now()
|
||||
const start = performance.now();
|
||||
|
||||
let moduleStart = NaN
|
||||
let moduleStart = NaN;
|
||||
|
||||
await importModules(
|
||||
MODULE_DIR_PATH,
|
||||
'local.js',
|
||||
{
|
||||
pre (entry) {
|
||||
const module = entry.name
|
||||
console.group(`Loading ${module}...`)
|
||||
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
|
||||
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
|
||||
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)`
|
||||
error.stack += `\n while loading module ${JSON.stringify(
|
||||
module,
|
||||
)} (local)`;
|
||||
|
||||
console.error(chalk.red(error.stack))
|
||||
}
|
||||
}
|
||||
)
|
||||
console.error(chalk.red(error.stack));
|
||||
},
|
||||
});
|
||||
|
||||
const end = performance.now()
|
||||
const end = performance.now();
|
||||
|
||||
const delta = end - start
|
||||
const delta = end - start;
|
||||
|
||||
console.groupEnd()
|
||||
console.info(`took ${delta.toFixed(2)}ms`)
|
||||
console.groupEnd();
|
||||
console.info(`took ${delta.toFixed(2)}ms`);
|
||||
|
|
|
@ -1,34 +1,37 @@
|
|||
import { Channel } from '../util/channel.js'
|
||||
import { parentPort } from 'worker_threads'
|
||||
import { parentPort } from 'worker_threads';
|
||||
|
||||
if (parentPort === null) throw new Error('Must run in worker thread')
|
||||
import { Channel } from '../util/channel.js';
|
||||
|
||||
const port = parentPort
|
||||
if (parentPort === null) throw new Error('Must run in worker thread');
|
||||
|
||||
const port = parentPort;
|
||||
|
||||
export interface Message<T = any> {
|
||||
channel: string
|
||||
data: T
|
||||
channel: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
function postMessage (channel: string, data: any): void {
|
||||
function postMessage(channel: string, data: any): void {
|
||||
port.postMessage({
|
||||
channel,
|
||||
data
|
||||
})
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function createChannel<TSend, TReceive = TSend> (id: string): Channel<TSend, TReceive> {
|
||||
const channel = new Channel<TSend, TReceive>(id)
|
||||
export function createChannel<TSend, TReceive = TSend>(
|
||||
id: string,
|
||||
): Channel<TSend, TReceive> {
|
||||
const channel = new Channel<TSend, TReceive>(id);
|
||||
|
||||
channel._subscribe(message => {
|
||||
postMessage(id, message)
|
||||
})
|
||||
channel._subscribe((message) => {
|
||||
postMessage(id, message);
|
||||
});
|
||||
|
||||
port.on('message', (message: Message<TReceive>) => {
|
||||
if (message.channel !== id) return
|
||||
if (message.channel !== id) return;
|
||||
|
||||
channel._write(message.data)
|
||||
})
|
||||
channel._write(message.data);
|
||||
});
|
||||
|
||||
return channel
|
||||
return channel;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
"target": "esnext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
|
@ -25,9 +25,9 @@
|
|||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "nodenext", /* Specify what module code is generated. */
|
||||
"module": "nodenext" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
"moduleResolution": "nodenext" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
|
@ -77,12 +77,12 @@
|
|||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
|
@ -104,12 +104,8 @@
|
|||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue