workers-rcon 1.0.0 → 1.0.2

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 CHANGED
@@ -1,27 +1,20 @@
1
1
  {
2
2
  "name": "workers-rcon",
3
- "version": "1.0.0",
4
- "description": "Minecraft RCON client for Cloudflare Workers via cloudflare:sockets",
5
- "main": "./dist/index.js",
6
- "module": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "files": [
9
- "dist"
10
- ],
11
- "scripts": {
12
- "build": "tsc"
3
+ "version": "1.0.2",
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,119 @@
1
+ import { connect } from 'cloudflare:sockets';
2
+
3
+ const SERVERDATA_AUTH = 3;
4
+ const SERVERDATA_EXECCOMMAND = 2;
5
+ const SERVERDATA_RESPONSE_VALUE = 0;
6
+ const SERVERDATA_AUTH_RESPONSE = 2;
7
+
8
+ export interface RCONConfig {
9
+ host: string;
10
+ port: number;
11
+ password: string;
12
+ }
13
+
14
+ interface RCONPacket {
15
+ id: number;
16
+ type: number;
17
+ body: string;
18
+ }
19
+
20
+ export class MinecraftRCON {
21
+ private host: string;
22
+ private port: number;
23
+ private password: string;
24
+ private requestId = 1;
25
+ private readBuffer = new Uint8Array(0);
26
+
27
+ constructor(config: RCONConfig) {
28
+ this.host = config.host;
29
+ this.port = config.port;
30
+ this.password = config.password;
31
+ }
32
+
33
+ async run(command: string): Promise<string> {
34
+ const socket = connect({ hostname: this.host, port: this.port });
35
+ const writer = socket.writable.getWriter();
36
+ const reader = socket.readable.getReader();
37
+
38
+ try {
39
+ await this.writePacket(writer, SERVERDATA_AUTH, this.password);
40
+
41
+ let authRes1 = await this.readPacket(reader);
42
+ let authRes2: RCONPacket | null = null;
43
+
44
+ if (authRes1.type === SERVERDATA_RESPONSE_VALUE) {
45
+ authRes2 = await this.readPacket(reader);
46
+ }
47
+
48
+ const finalAuth = authRes2 || authRes1;
49
+ if (finalAuth.id === -1) {
50
+ throw new Error('RCON Authentication failed: Invalid password');
51
+ }
52
+
53
+ await this.writePacket(writer, SERVERDATA_EXECCOMMAND, command);
54
+ const cmdRes = await this.readPacket(reader);
55
+
56
+ return cmdRes.body;
57
+ } finally {
58
+ writer.releaseLock();
59
+ reader.releaseLock();
60
+ await socket.close();
61
+ }
62
+ }
63
+
64
+ private async writePacket(writer: WritableStreamDefaultWriter, type: number, body: string) {
65
+ const encoder = new TextEncoder();
66
+ const bodyBytes = encoder.encode(body);
67
+ const packetLength = 10 + bodyBytes.length;
68
+
69
+ const buffer = new ArrayBuffer(packetLength + 4);
70
+ const view = new DataView(buffer);
71
+
72
+ view.setInt32(0, packetLength, true);
73
+ view.setInt32(4, this.requestId, true);
74
+ view.setInt32(8, type, true);
75
+
76
+ const uint8 = new Uint8Array(buffer);
77
+ uint8.set(bodyBytes, 12);
78
+ uint8[buffer.byteLength - 2] = 0;
79
+ uint8[buffer.byteLength - 1] = 0;
80
+
81
+ await writer.write(buffer);
82
+ }
83
+
84
+ private async readPacket(reader: ReadableStreamDefaultReader<Uint8Array>): Promise<RCONPacket> {
85
+ const decoder = new TextDecoder();
86
+
87
+ while (true) {
88
+ if (this.readBuffer.length >= 4) {
89
+ const view = new DataView(this.readBuffer.buffer, this.readBuffer.byteOffset, this.readBuffer.byteLength);
90
+ const length = view.getInt32(0, true);
91
+ const totalPacketLength = length + 4;
92
+
93
+ if (this.readBuffer.length >= totalPacketLength) {
94
+ const id = view.getInt32(4, true);
95
+ const type = view.getInt32(8, true);
96
+
97
+ const bodyBytes = this.readBuffer.subarray(12, totalPacketLength - 2);
98
+ const body = decoder.decode(bodyBytes);
99
+
100
+ this.readBuffer = this.readBuffer.slice(totalPacketLength);
101
+
102
+ return { id, type, body };
103
+ }
104
+ }
105
+
106
+ const { value, done } = await reader.read();
107
+ if (done) {
108
+ throw new Error('Connection closed by RCON server unexpectedly');
109
+ }
110
+
111
+ if (value) {
112
+ const newBuffer = new Uint8Array(this.readBuffer.length + value.length);
113
+ newBuffer.set(this.readBuffer);
114
+ newBuffer.set(value, this.readBuffer.length);
115
+ this.readBuffer = newBuffer;
116
+ }
117
+ }
118
+ }
119
+ }
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
+ }
package/dist/index.d.ts DELETED
@@ -1,15 +0,0 @@
1
- export interface RCONConfig {
2
- host: string;
3
- port: number;
4
- password: string;
5
- }
6
- export declare class MinecraftRCON {
7
- private host;
8
- private port;
9
- private password;
10
- private requestId;
11
- constructor(config: RCONConfig);
12
- run(command: string): Promise<string>;
13
- private writePacket;
14
- private readPacket;
15
- }
package/dist/index.js DELETED
@@ -1,67 +0,0 @@
1
- import { connect } from 'cloudflare:sockets';
2
- // Типы пакетов RCON
3
- const SERVERDATA_AUTH = 3;
4
- const SERVERDATA_EXECCOMMAND = 2;
5
- const SERVERDATA_RESPONSE_VALUE = 0;
6
- const SERVERDATA_AUTH_RESPONSE = 2;
7
- export class MinecraftRCON {
8
- host;
9
- port;
10
- password;
11
- requestId = 1;
12
- constructor(config) {
13
- this.host = config.host;
14
- this.port = config.port;
15
- this.password = config.password;
16
- }
17
- async run(command) {
18
- const socket = connect({ hostname: this.host, port: this.port });
19
- const writer = socket.writable.getWriter();
20
- const reader = socket.readable.getReader();
21
- try {
22
- // 1. Авторизация
23
- await this.writePacket(writer, SERVERDATA_AUTH, this.password);
24
- // Читаем ответ авторизации
25
- let authRes = await this.readPacket(reader);
26
- if (authRes.id === -1) {
27
- throw new Error('RCON Authentication failed: Invalid password');
28
- }
29
- // 2. Выполнение команды
30
- await this.writePacket(writer, SERVERDATA_EXECCOMMAND, command);
31
- const cmdRes = await this.readPacket(reader);
32
- return cmdRes.body;
33
- }
34
- finally {
35
- await writer.close();
36
- socket.close();
37
- }
38
- }
39
- async writePacket(writer, type, body) {
40
- const encoder = new TextEncoder();
41
- const bodyBytes = encoder.encode(body);
42
- // Длина: 4(id) + 4(type) + body + 2(null terminators)
43
- const packetLength = 10 + bodyBytes.length;
44
- const buffer = new ArrayBuffer(packetLength + 4); // +4 для самого поля длины
45
- const view = new DataView(buffer);
46
- view.setInt32(0, packetLength, true);
47
- view.setInt32(4, this.requestId, true);
48
- view.setInt32(8, type, true);
49
- const uint8 = new Uint8Array(buffer);
50
- uint8.set(bodyBytes, 12);
51
- uint8[buffer.byteLength - 2] = 0;
52
- uint8[buffer.byteLength - 1] = 0;
53
- await writer.write(buffer);
54
- }
55
- async readPacket(reader) {
56
- const { value, done } = await reader.read();
57
- if (done || !value)
58
- throw new Error('Connection closed by server');
59
- const view = new DataView(value.buffer, value.byteOffset, value.byteLength);
60
- const length = view.getInt32(0, true);
61
- const id = view.getInt32(4, true);
62
- const type = view.getInt32(8, true);
63
- const bodyBytes = value.slice(12, 12 + (length - 10));
64
- const body = new TextDecoder().decode(bodyBytes);
65
- return { id, type, body };
66
- }
67
- }