workers-rcon 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -17
- package/src/index.ts +88 -0
- package/tsconfig.json +16 -0
package/package.json
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "workers-rcon",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"files": [
|
|
9
|
-
"dist"
|
|
10
|
-
],
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "tsc"
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"author": "fjk-dev",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/fjk-dev/workers-rcon/"
|
|
13
8
|
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/fjk-dev/workers-rcon/"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/fjk-dev/workers-rcon/",
|
|
14
13
|
"keywords": [
|
|
15
14
|
"cloudflare",
|
|
16
15
|
"workers",
|
|
17
16
|
"minecraft",
|
|
18
17
|
"rcon",
|
|
19
18
|
"tcp"
|
|
20
|
-
]
|
|
21
|
-
"author": "YourName",
|
|
22
|
-
"license": "MIT",
|
|
23
|
-
"devDependencies": {
|
|
24
|
-
"@cloudflare/workers-types": "^4.20240000.0",
|
|
25
|
-
"typescript": "^5.0.0"
|
|
26
|
-
}
|
|
19
|
+
]
|
|
27
20
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { connect } from 'cloudflare:sockets';
|
|
2
|
+
|
|
3
|
+
// Типы пакетов RCON
|
|
4
|
+
const SERVERDATA_AUTH = 3;
|
|
5
|
+
const SERVERDATA_EXECCOMMAND = 2;
|
|
6
|
+
const SERVERDATA_RESPONSE_VALUE = 0;
|
|
7
|
+
const SERVERDATA_AUTH_RESPONSE = 2;
|
|
8
|
+
|
|
9
|
+
export interface RCONConfig {
|
|
10
|
+
host: string;
|
|
11
|
+
port: number;
|
|
12
|
+
password: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class MinecraftRCON {
|
|
16
|
+
private host: string;
|
|
17
|
+
private port: number;
|
|
18
|
+
private password: string;
|
|
19
|
+
private requestId = 1;
|
|
20
|
+
|
|
21
|
+
constructor(config: RCONConfig) {
|
|
22
|
+
this.host = config.host;
|
|
23
|
+
this.port = config.port;
|
|
24
|
+
this.password = config.password;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async run(command: string): Promise<string> {
|
|
28
|
+
const socket = connect({ hostname: this.host, port: this.port });
|
|
29
|
+
const writer = socket.writable.getWriter();
|
|
30
|
+
const reader = socket.readable.getReader();
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// 1. Авторизация
|
|
34
|
+
await this.writePacket(writer, SERVERDATA_AUTH, this.password);
|
|
35
|
+
|
|
36
|
+
// Читаем ответ авторизации
|
|
37
|
+
let authRes = await this.readPacket(reader);
|
|
38
|
+
if (authRes.id === -1) {
|
|
39
|
+
throw new Error('RCON Authentication failed: Invalid password');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 2. Выполнение команды
|
|
43
|
+
await this.writePacket(writer, SERVERDATA_EXECCOMMAND, command);
|
|
44
|
+
const cmdRes = await this.readPacket(reader);
|
|
45
|
+
|
|
46
|
+
return cmdRes.body;
|
|
47
|
+
} finally {
|
|
48
|
+
await writer.close();
|
|
49
|
+
socket.close();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private async writePacket(writer: WritableStreamDefaultWriter, type: number, body: string) {
|
|
54
|
+
const encoder = new TextEncoder();
|
|
55
|
+
const bodyBytes = encoder.encode(body);
|
|
56
|
+
// Длина: 4(id) + 4(type) + body + 2(null terminators)
|
|
57
|
+
const packetLength = 10 + bodyBytes.length;
|
|
58
|
+
|
|
59
|
+
const buffer = new ArrayBuffer(packetLength + 4); // +4 для самого поля длины
|
|
60
|
+
const view = new DataView(buffer);
|
|
61
|
+
|
|
62
|
+
view.setInt32(0, packetLength, true);
|
|
63
|
+
view.setInt32(4, this.requestId, true);
|
|
64
|
+
view.setInt32(8, type, true);
|
|
65
|
+
|
|
66
|
+
const uint8 = new Uint8Array(buffer);
|
|
67
|
+
uint8.set(bodyBytes, 12);
|
|
68
|
+
uint8[buffer.byteLength - 2] = 0;
|
|
69
|
+
uint8[buffer.byteLength - 1] = 0;
|
|
70
|
+
|
|
71
|
+
await writer.write(buffer);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private async readPacket(reader: ReadableStreamDefaultReader) {
|
|
75
|
+
const { value, done } = await reader.read();
|
|
76
|
+
if (done || !value) throw new Error('Connection closed by server');
|
|
77
|
+
|
|
78
|
+
const view = new DataView(value.buffer, value.byteOffset, value.byteLength);
|
|
79
|
+
const length = view.getInt32(0, true);
|
|
80
|
+
const id = view.getInt32(4, true);
|
|
81
|
+
const type = view.getInt32(8, true);
|
|
82
|
+
|
|
83
|
+
const bodyBytes = value.slice(12, 12 + (length - 10));
|
|
84
|
+
const body = new TextDecoder().decode(bodyBytes);
|
|
85
|
+
|
|
86
|
+
return { id, type, body };
|
|
87
|
+
}
|
|
88
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ESNext"],
|
|
7
|
+
"types": ["@cloudflare/workers-types"],
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"rootDir": "./src",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"]
|
|
16
|
+
}
|