Skip to content

Commit

Permalink
feat: ✨ add ReadOnlyProxy
Browse files Browse the repository at this point in the history
  • Loading branch information
RichardDorian committed Mar 30, 2022
1 parent 678fd3f commit 1d5d7a1
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 32 deletions.
44 changes: 21 additions & 23 deletions .github/workflow/build.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
# name: Build
name: Build

# on:
# push:
# branches: [main]
# pull_request:
# branches: [main]
on:
push:
branches: [main]
pull_request:
branches: [main]

# jobs:
# build:
# runs-on: ubuntu-latest
# strategy:
# matrix:
# node-version: [16.x]
# steps:
# - uses: actions/checkout@v2
# - name: Node.js
# uses: actions/setup-node@v2
# with:
# node-version: ${{ matrix.node-version }}
# - run: npm install
# - name: Build code
# run: npm run build
# - name: Test code
# run: npm run test
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v2
- name: Node.js
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- name: Build code
run: npm run build
2 changes: 1 addition & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.github
src
test
example
.deepsource.toml
.prettierrc
tsconfig.json
48 changes: 46 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
# MinecraftJS Template
# Proxies

Fill with your own content
Create proxies for Minecraft

# Documentation

## Installation

Install the package:

```bash
$ npm install @minecraft-js/proxies
```

And then import it in your JavaScript/TypeScript file

```ts
const Proxies = require('@minecraft-js/proxies'); // CommonJS

import * as Proxies from '@minecraft-js/proxies'; // ES6
```

## Read only proxies
Read only proxies are proxies where you can only read packets.
Create a read only proxy like so

```js
const proxy = new Proxies.ReadOnlyProxy({
serverHostname: 'localhost', // Target server hostname
port: 8080, // Proxy port
});
```

And then you can use the `ReadOnlyProxy#on` method to listen for different events:
- `raw_listening` - When the TCP server is listening
- `raw_connect` - When a client opens a connection
- `raw_disconnect` - When a client closes a connection
- `raw_close` - When the TCP server is closed
- `error` - When an error occurs
- `outgoing_data` - When data is sent to the target server
- `incoming_data` - When data is received from the target server

# Using TypeScript?
If you are using TypeScript or you want type checking make sure to install the `typed-emitter` package!
```bash
$ npm install typed-emitter
```
15 changes: 15 additions & 0 deletions example/readOnlyProxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { ReadOnlyProxy } = require('../dist');

const proxy = new ReadOnlyProxy({
debug: true,
serverHostname: 'localhost',
port: 8080,
});

proxy.on('incoming_data', (data) => {
console.log('[INCOMING]', data);
});

proxy.on('outgoing_data', (data) => {
console.log('[OUTGOING]', data);
});
14 changes: 8 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
{
"name": "template",
"name": "@minecraft-js/proxies",
"version": "1.0.0",
"description": "todo: change",
"description": "Create proxies for Minecraft",
"main": "dist/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc"
},
"repository": {
"type": "git",
"url": "git+https://github.com/MinecraftJS/template.git"
"url": "git+https://github.com/MinecraftJS/Proxies.git"
},
"keywords": [
"minecraftjs"
],
"author": "todo: change",
"author": "RichardDorian",
"license": "MIT",
"bugs": {
"url": "https://github.com/MinecraftJS/template/issues"
"url": "https://github.com/MinecraftJS/Proxies/issues"
},
"homepage": "https://github.com/MinecraftJS/template#readme",
"homepage": "https://github.com/MinecraftJS/Proxies#readme",
"devDependencies": {
"@types/node": "^17.0.23",
"prettier": "^2.6.1",
"typed-emitter": "^2.1.0",
"typescript": "^4.6.3"
}
}
101 changes: 101 additions & 0 deletions src/ReadOnlyProxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { EventEmitter } from 'node:events';
import TypedEmitter from 'typed-emitter';
import { Server, Socket, createServer } from 'node:net';

export default class ReadOnlyProxy extends (EventEmitter as new () => TypedEmitter<ReadOnlyProxyEvents>) {
private readonly options: ReadOnlyProxyOptions;
private readonly sockets: ReadOnlyProxySocketMap[];
private readonly server: Server;

/**
* Create a new read only proxy. Read only proxies are proxies where you can only read packets
* @param options The options for the proxy
*/
public constructor(options: ReadOnlyProxyOptions = {}) {
super();
this.options = options;

this.sockets = [];

this.server = createServer();
this.server.listen(options.port ?? 25565, options.hostname);

this.server.once('listening', () => {
this.emit('raw_listening', this.server);
this.debug('Listening addr=', this.server.address());
});

this.server.on('connection', (socket: Socket) => {
this.emit('raw_connect', socket);
this.debug('New connection from', socket.remoteAddress);

const internalSocket = new Socket();
this.sockets.push({
socket: socket,
internal: internalSocket,
});

// Handle errors
socket.on('close', (hadError) => {
this.emit('raw_disconnect', socket, hadError);
internalSocket.end();
});
internalSocket.on('close', () => socket.end());

// Connect the internalSocket to the target server
internalSocket.connect(
options.serverPort ?? 25565,
options.serverHostname
);

// Forward data
socket.on('data', (data) => {
internalSocket.write(data);
this.emit('outgoing_data', data, socket, internalSocket);
});
internalSocket.on('data', (data) => {
socket.write(data);
this.emit('incoming_data', data, socket, internalSocket);
});
});

this.server.on('close', () => this.emit('raw_close'));
this.server.on('error', (error) => this.emit('error', 'server', error));
}

private debug(...args) {
if (this.options.debug) console.log('[DEBUG]', ...args);
}
}

type ErrorOrigin = 'server' | 'internal';

type ReadOnlyProxyEvents = {
raw_listening: (server: Server) => void;
raw_connect: (socket: Socket) => void;
raw_disconnect: (socket: Socket, hadError: boolean) => void;
raw_close: () => void;
error: (origin: ErrorOrigin, error: Error) => void;
outgoing_data: (data: Buffer, socket: Socket, internal: Socket) => void;
incoming_data: (data: Buffer, socket: Socket, internal: Socket) => void;
};

type ReadOnlyProxySocketMap = {
/** The real socket (the minecraft client, the user), the one connected to the proxy */
socket: Socket;
/** The proxy socket, the one connected to the target server */
internal: Socket;
};

interface ReadOnlyProxyOptions {
/** Port to listen on, defaults to `25565` */
port?: number;
/** Hostname to bind to */
hostname?: string;
/** Whether or not to enable debugging */
debug?: boolean;
/** Target server port, defaults to `25565` */
serverPort?: number;
/** Target server hostname */
serverHostname?: string;
}
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ReadOnlyProxy from './ReadOnlyProxy';

export { ReadOnlyProxy };

0 comments on commit 1d5d7a1

Please sign in to comment.