raknet-typeo 1.0.0
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 +36 -0
- package/src/client.js +209 -0
- package/src/compat/bedrock-protocol.js +147 -0
- package/src/compat/index.js +10 -0
- package/src/compat/register.js +62 -0
- package/src/connection.js +394 -0
- package/src/constants.js +87 -0
- package/src/index.js +52 -0
- package/src/protocol/ack.js +47 -0
- package/src/protocol/frame.js +100 -0
- package/src/protocol/offline.js +151 -0
- package/src/server.js +161 -0
- package/src/transport/udp.js +38 -0
- package/src/transport/websocket.js +138 -0
- package/src/utils/binary-stream.js +184 -0
- package/src/ws-client.js +12 -0
- package/src/ws-server.js +13 -0
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "raknet-typeo",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Pure JavaScript RakNet implementation — no C++, works on all platforms",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"raknet",
|
|
7
|
+
"udp",
|
|
8
|
+
"networking",
|
|
9
|
+
"gameserver",
|
|
10
|
+
"minecraft",
|
|
11
|
+
"bedrock",
|
|
12
|
+
"bedrock-protocol"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": "./src/index.js",
|
|
19
|
+
"./compat": "./src/compat/index.js",
|
|
20
|
+
"./compat/register": "./src/compat/register.js",
|
|
21
|
+
"./compat/bedrock-protocol": "./src/compat/bedrock-protocol.js"
|
|
22
|
+
},
|
|
23
|
+
"main": "./src/index.js",
|
|
24
|
+
"directories": {
|
|
25
|
+
"test": "test"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"src"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"test": "node --test test/*.test.js"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"ws": "^8.20.1"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/client.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { Connection } from "./connection.js";
|
|
3
|
+
import {
|
|
4
|
+
writeUnconnectedPing,
|
|
5
|
+
writeOpenConnectionRequest1,
|
|
6
|
+
writeOpenConnectionRequest2,
|
|
7
|
+
} from "./protocol/offline.js";
|
|
8
|
+
import {
|
|
9
|
+
MessageId,
|
|
10
|
+
PacketReliability,
|
|
11
|
+
PacketPriority,
|
|
12
|
+
DATAGRAM_FLAG,
|
|
13
|
+
ACK_FLAG,
|
|
14
|
+
NACK_FLAG,
|
|
15
|
+
MAX_MTU_SIZE,
|
|
16
|
+
MIN_MTU_SIZE,
|
|
17
|
+
} from "./constants.js";
|
|
18
|
+
import { UdpTransport } from "./transport/udp.js";
|
|
19
|
+
|
|
20
|
+
const CONNECT_TIMEOUT_MS = 10_000;
|
|
21
|
+
const HANDSHAKE_RETRY_MS = 1_000;
|
|
22
|
+
|
|
23
|
+
export class RakClient extends EventEmitter {
|
|
24
|
+
constructor(serverAddress, serverPort, guid, options = {}) {
|
|
25
|
+
super();
|
|
26
|
+
this.serverAddress = serverAddress;
|
|
27
|
+
this.serverPort = serverPort;
|
|
28
|
+
this._guid = guid ?? randomBigInt();
|
|
29
|
+
this._transport = options.transport ?? new UdpTransport();
|
|
30
|
+
this._connection = null;
|
|
31
|
+
this._state = "disconnected";
|
|
32
|
+
this._mtu = MAX_MTU_SIZE;
|
|
33
|
+
this._retryTimer = undefined;
|
|
34
|
+
this._connectTimer = undefined;
|
|
35
|
+
this._retryCount = 0;
|
|
36
|
+
|
|
37
|
+
this._transport.onMessage((msg) => this._handleMessage(msg));
|
|
38
|
+
this._transport.onError((err) => this.emit("error", err));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
connect() {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
if (this._state !== "disconnected") { reject(new Error("Already connecting or connected")); return; }
|
|
44
|
+
|
|
45
|
+
this._transport.bind(0).then(() => {
|
|
46
|
+
this.once("connect", resolve);
|
|
47
|
+
|
|
48
|
+
this._connectTimer = setTimeout(() => {
|
|
49
|
+
this._cleanup();
|
|
50
|
+
reject(new Error("Connection timed out"));
|
|
51
|
+
}, CONNECT_TIMEOUT_MS);
|
|
52
|
+
|
|
53
|
+
this._beginHandshake();
|
|
54
|
+
}).catch(reject);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_beginHandshake() {
|
|
59
|
+
this._state = "ocr1";
|
|
60
|
+
this._retryCount = 0;
|
|
61
|
+
this._sendOcr1();
|
|
62
|
+
|
|
63
|
+
this._retryTimer = setInterval(() => {
|
|
64
|
+
this._retryCount++;
|
|
65
|
+
if (this._state === "ocr1") {
|
|
66
|
+
if (this._retryCount > 5) this._mtu = Math.max(this._mtu - 100, MIN_MTU_SIZE);
|
|
67
|
+
this._sendOcr1();
|
|
68
|
+
} else if (this._state === "ocr2") {
|
|
69
|
+
this._sendOcr2();
|
|
70
|
+
}
|
|
71
|
+
}, HANDSHAKE_RETRY_MS);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
_sendOcr1() {
|
|
75
|
+
this._transport.send(writeOpenConnectionRequest1(this._mtu), this.serverAddress, this.serverPort);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_sendOcr2() {
|
|
79
|
+
this._transport.send(
|
|
80
|
+
writeOpenConnectionRequest2(this.serverAddress, this.serverPort, this._mtu, this._guid),
|
|
81
|
+
this.serverAddress, this.serverPort
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_handleMessage(msg) {
|
|
86
|
+
if (msg.length === 0) return;
|
|
87
|
+
const flag = msg[0];
|
|
88
|
+
|
|
89
|
+
if (this._state === "connected" || this._state === "connecting") {
|
|
90
|
+
if (
|
|
91
|
+
(flag & DATAGRAM_FLAG) === DATAGRAM_FLAG ||
|
|
92
|
+
(flag & ACK_FLAG) === ACK_FLAG ||
|
|
93
|
+
(flag & NACK_FLAG) === NACK_FLAG
|
|
94
|
+
) {
|
|
95
|
+
this._connection?.handleDatagram(msg);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
switch (flag) {
|
|
101
|
+
case MessageId.UnconnectedPong:
|
|
102
|
+
this.emit("pong", msg);
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case MessageId.OpenConnectionReply1: {
|
|
106
|
+
// layout: 1(id) + 16(magic) + 8(guid) + 1(security) + 2(mtu) = 28 bytes
|
|
107
|
+
if (this._state !== "ocr1" || msg.length < 28) return;
|
|
108
|
+
this._mtu = Math.min(msg.readUInt16BE(1 + 16 + 8 + 1), MAX_MTU_SIZE);
|
|
109
|
+
this._state = "ocr2";
|
|
110
|
+
this._sendOcr2();
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
case MessageId.OpenConnectionReply2: {
|
|
115
|
+
if (this._state !== "ocr2") return;
|
|
116
|
+
this._mtu = Math.min(msg.readUInt16BE(1 + 16 + 8 + 7), MAX_MTU_SIZE);
|
|
117
|
+
if (this._retryTimer) { clearInterval(this._retryTimer); this._retryTimer = undefined; }
|
|
118
|
+
this._state = "connecting";
|
|
119
|
+
this._setupConnection();
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
case MessageId.IncompatibleProtocolVersion:
|
|
124
|
+
this._cleanup(); this.emit("error", new Error("Incompatible protocol version")); break;
|
|
125
|
+
case MessageId.NoFreeIncomingConnections:
|
|
126
|
+
this._cleanup(); this.emit("error", new Error("No free incoming connections")); break;
|
|
127
|
+
case MessageId.ConnectionBanned:
|
|
128
|
+
this._cleanup(); this.emit("error", new Error("Banned from server")); break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_setupConnection() {
|
|
133
|
+
const sendFn = (buf, a, p) => this._transport.send(buf, a, p);
|
|
134
|
+
|
|
135
|
+
this._connection = new Connection(this.serverAddress, this.serverPort, 0n, sendFn, this._mtu);
|
|
136
|
+
|
|
137
|
+
this._connection.on("connected", () => {
|
|
138
|
+
if (this._connectTimer) { clearTimeout(this._connectTimer); this._connectTimer = undefined; }
|
|
139
|
+
this._state = "connected";
|
|
140
|
+
this.emit("connect");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
this._connection.on("disconnected", (reason) => {
|
|
144
|
+
this._state = "disconnected";
|
|
145
|
+
this._connection = null;
|
|
146
|
+
this.emit("disconnect", reason);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
this._connection.on("timeout", () => {
|
|
150
|
+
this._state = "disconnected";
|
|
151
|
+
this._connection = null;
|
|
152
|
+
this.emit("disconnect", "timeout");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
this._connection.on("encapsulated", (packet) => {
|
|
156
|
+
this.emit("encapsulated", packet);
|
|
157
|
+
this.emit("message", packet);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
this._connection.on("error", (err) => this.emit("error", err));
|
|
161
|
+
|
|
162
|
+
this._connection.start();
|
|
163
|
+
this._connection.sendConnectionRequest(this._guid);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
ping(onPong) {
|
|
167
|
+
if (onPong) {
|
|
168
|
+
this.once("pong", (msg) => {
|
|
169
|
+
try {
|
|
170
|
+
const len = msg.readUInt16BE(33);
|
|
171
|
+
const motd = msg.slice(35, 35 + len).toString("utf8");
|
|
172
|
+
onPong(motd);
|
|
173
|
+
} catch { onPong(""); }
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const buf = writeUnconnectedPing(BigInt(Date.now()), this._guid, false);
|
|
178
|
+
this._transport.bind(0).then(() => {
|
|
179
|
+
this._transport.send(buf, this.serverAddress, this.serverPort);
|
|
180
|
+
}).catch(() => {
|
|
181
|
+
this._transport.send(buf, this.serverAddress, this.serverPort);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
send(buf, priority = PacketPriority.Medium, reliability = PacketReliability.ReliableOrdered, channel = 0) {
|
|
186
|
+
if (!this._connection || this._state !== "connected") throw new Error("Not connected");
|
|
187
|
+
this._connection.sendEncapsulated(buf, priority, reliability, channel);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
close() { this._cleanup(); this.emit("disconnect", "local_closed"); }
|
|
191
|
+
|
|
192
|
+
_cleanup() {
|
|
193
|
+
if (this._retryTimer) { clearInterval(this._retryTimer); this._retryTimer = undefined; }
|
|
194
|
+
if (this._connectTimer) { clearTimeout(this._connectTimer); this._connectTimer = undefined; }
|
|
195
|
+
if (this._connection) { this._connection.close(true); this._connection = null; }
|
|
196
|
+
this._state = "disconnected";
|
|
197
|
+
// Delay socket close by 80ms so the OS has time to flush the disconnect
|
|
198
|
+
// notification packet before the dgram handle is destroyed.
|
|
199
|
+
setTimeout(() => this._transport.close().catch(() => {}), 80);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
get isConnected() { return this._state === "connected"; }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function randomBigInt() {
|
|
206
|
+
const hi = BigInt(Math.floor(Math.random() * 0x7fffffff));
|
|
207
|
+
const lo = BigInt(Math.floor(Math.random() * 0xffffffff));
|
|
208
|
+
return (hi << 32n) | lo;
|
|
209
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bedrock-protocol compatibility shim.
|
|
3
|
+
*
|
|
4
|
+
* Exports Server, Client, PacketPriority, PacketReliability with the exact
|
|
5
|
+
* same shape that bedrock-protocol expects from the `raknet-native` C++ addon.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { patchRequire } from 'raknet-typeo/compat/register'
|
|
9
|
+
* patchRequire() // before requiring bedrock-protocol
|
|
10
|
+
* const { createServer } = require('bedrock-protocol')
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { EventEmitter } from "events";
|
|
14
|
+
import { RakServer as InternalServer } from "../server.js";
|
|
15
|
+
import { RakClient as InternalClient } from "../client.js";
|
|
16
|
+
import { PacketReliability as PktRel, PacketPriority as PktPri } from "../constants.js";
|
|
17
|
+
|
|
18
|
+
export const PacketPriority = Object.freeze({
|
|
19
|
+
IMMEDIATE_PRIORITY : PktPri.Immediate,
|
|
20
|
+
HIGH_PRIORITY : PktPri.High,
|
|
21
|
+
MEDIUM_PRIORITY : PktPri.Medium,
|
|
22
|
+
LOW_PRIORITY : PktPri.Low,
|
|
23
|
+
NUMBER_OF_PRIORITIES : 4,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const PacketReliability = Object.freeze({
|
|
27
|
+
UNRELIABLE : PktRel.Unreliable,
|
|
28
|
+
UNRELIABLE_SEQUENCED : PktRel.UnreliableSequenced,
|
|
29
|
+
RELIABLE : PktRel.Reliable,
|
|
30
|
+
RELIABLE_ORDERED : PktRel.ReliableOrdered,
|
|
31
|
+
RELIABLE_SEQUENCED : PktRel.ReliableSequenced,
|
|
32
|
+
RELIABLE_WITH_ACK_RECEIPT : PktRel.ReliableWithAckReceipt,
|
|
33
|
+
RELIABLE_ORDERED_WITH_ACK_RECEIPT : PktRel.ReliableOrderedWithAckReceipt,
|
|
34
|
+
NUMBER_OF_RELIABILITIES : 7,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function makeAddr(address, port) {
|
|
38
|
+
return { address, port, version: 4, hash: `${address}:${port}` };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function wrapConnection(conn) {
|
|
42
|
+
const addr = makeAddr(conn.address, conn.port);
|
|
43
|
+
const emitter = new EventEmitter();
|
|
44
|
+
|
|
45
|
+
emitter.address = addr;
|
|
46
|
+
emitter.guid = conn.guid;
|
|
47
|
+
|
|
48
|
+
emitter.send = (buffer, priority, reliability, orderingChannel = 0) => {
|
|
49
|
+
conn.sendEncapsulated(buffer, priority, reliability, orderingChannel);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
emitter.sendReliable = (buffer, immediate = false) => {
|
|
53
|
+
conn.sendEncapsulated(
|
|
54
|
+
buffer,
|
|
55
|
+
immediate ? PktPri.Immediate : PktPri.Medium,
|
|
56
|
+
PktRel.ReliableOrdered,
|
|
57
|
+
0
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
emitter.close = () => conn.close(true);
|
|
62
|
+
|
|
63
|
+
conn.on("encapsulated", (buffer) => emitter.emit("encapsulated", { buffer, address: addr }));
|
|
64
|
+
conn.on("disconnected", (reason) => emitter.emit("disconnect", reason));
|
|
65
|
+
|
|
66
|
+
return emitter;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export class Server extends EventEmitter {
|
|
70
|
+
constructor(host, port, options = {}) {
|
|
71
|
+
super();
|
|
72
|
+
this._connections = new Map();
|
|
73
|
+
|
|
74
|
+
this._server = new InternalServer(
|
|
75
|
+
host, port, undefined,
|
|
76
|
+
options.maxConnections ?? 20,
|
|
77
|
+
options.message ? options.message.toString("utf8") : undefined,
|
|
78
|
+
{ transport: options.transport }
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
this._server.on("connect", (conn) => {
|
|
82
|
+
const wrapped = wrapConnection(conn);
|
|
83
|
+
const key = `${conn.address}:${conn.port}`;
|
|
84
|
+
this._connections.set(key, wrapped);
|
|
85
|
+
wrapped.on("disconnect", () => this._connections.delete(key));
|
|
86
|
+
this.emit("openConnection", wrapped);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
this._server.on("disconnect", (conn, reason) => {
|
|
90
|
+
const key = `${conn.address}:${conn.port}`;
|
|
91
|
+
const wrapped = this._connections.get(key);
|
|
92
|
+
if (wrapped) { this._connections.delete(key); this.emit("closeConnection", wrapped, reason); }
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
this._server.on("message", (buffer, conn) => {
|
|
96
|
+
this.emit("encapsulated", { buffer, address: makeAddr(conn.address, conn.port) });
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
this._server.on("error", (err) => this.emit("error", err));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
listen() { return this._server.listen(); }
|
|
103
|
+
setOfflineMessage(message) { this._server.setMotd(message.toString("utf8")); }
|
|
104
|
+
close() { return this._server.close(); }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export class Client extends EventEmitter {
|
|
108
|
+
constructor(host, port, options = {}) {
|
|
109
|
+
super();
|
|
110
|
+
this._connected = false;
|
|
111
|
+
|
|
112
|
+
this._client = new InternalClient(host, port, undefined, { transport: options.transport });
|
|
113
|
+
|
|
114
|
+
this._client.on("connect", () => { this._connected = true; this.emit("connect"); });
|
|
115
|
+
|
|
116
|
+
this._client.on("disconnect", (reason) => {
|
|
117
|
+
this._connected = false;
|
|
118
|
+
this.emit("disconnect", { reason });
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
this._client.on("encapsulated", (buffer) => {
|
|
122
|
+
this.emit("encapsulated", { buffer, address: makeAddr(host, port) });
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
this._client.on("pong", (msg) => {
|
|
126
|
+
try {
|
|
127
|
+
let extra;
|
|
128
|
+
if (msg.length > 33) { const len = msg.readUInt16BE(33); extra = msg.slice(35, 35 + len); }
|
|
129
|
+
this.emit("pong", { extra });
|
|
130
|
+
} catch { this.emit("pong", { extra: Buffer.alloc(0) }); }
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
this._client.on("error", (err) => this.emit("error", err));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
connect() { return this._client.connect(); }
|
|
137
|
+
send(buffer, priority, reliability, orderingChannel = 0) {
|
|
138
|
+
if (!this._connected) return;
|
|
139
|
+
this._client.send(buffer, priority, reliability, orderingChannel);
|
|
140
|
+
}
|
|
141
|
+
sendReliable(buffer, immediate = false) {
|
|
142
|
+
this.send(buffer, immediate ? PktPri.Immediate : PktPri.Medium, PktRel.ReliableOrdered, 0);
|
|
143
|
+
}
|
|
144
|
+
ping() { this._client.ping(); }
|
|
145
|
+
close() { this._connected = false; this._client.close(); }
|
|
146
|
+
get isConnected() { return this._connected; }
|
|
147
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports for `raknet-native` drop-in compatibility (via raknet-typeo).
|
|
3
|
+
*
|
|
4
|
+
* When used as `raknet-native` via module patching (see register.js), this
|
|
5
|
+
* module exposes the exact same surface as the native C++ addon so that
|
|
6
|
+
* bedrock-protocol can use it transparently.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { Server, Client, PacketPriority, PacketReliability } from "./bedrock-protocol.js";
|
|
10
|
+
export { patchRequire, configureBedrockServer } from "./register.js";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module patcher — call `patchRequire()` once at the very top of your entry
|
|
3
|
+
* point, before any `require('raknet-native')` or `require('bedrock-protocol')`
|
|
4
|
+
* calls. Afterwards, every `require('raknet-native')` in the process will
|
|
5
|
+
* resolve to this pure-JS implementation instead of the native addon.
|
|
6
|
+
*
|
|
7
|
+
* Example (CommonJS):
|
|
8
|
+
* require('raknet-typeo/compat/register').patchRequire()
|
|
9
|
+
* const { createServer } = require('bedrock-protocol')
|
|
10
|
+
*
|
|
11
|
+
* Example (ESM):
|
|
12
|
+
* import { patchRequire } from 'raknet-typeo/compat/register'
|
|
13
|
+
* patchRequire()
|
|
14
|
+
* const bp = await import('bedrock-protocol')
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { createRequire } from "module";
|
|
18
|
+
import path from "path";
|
|
19
|
+
import { fileURLToPath } from "url";
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = path.dirname(__filename);
|
|
23
|
+
|
|
24
|
+
let patched = false;
|
|
25
|
+
|
|
26
|
+
export function patchRequire() {
|
|
27
|
+
if (patched) return;
|
|
28
|
+
patched = true;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const req = createRequire(import.meta.url);
|
|
32
|
+
const NodeModule = req("module");
|
|
33
|
+
const originalResolve = NodeModule._resolveFilename.bind(NodeModule);
|
|
34
|
+
const compatPath = path.resolve(__dirname, "bedrock-protocol.js");
|
|
35
|
+
|
|
36
|
+
NodeModule._resolveFilename = function (request, ...rest) {
|
|
37
|
+
if (request === "raknet-native") return compatPath;
|
|
38
|
+
return originalResolve(request, ...rest);
|
|
39
|
+
};
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.warn(
|
|
42
|
+
"[raknet-typeo] patchRequire() failed — module hooking unavailable in this environment:",
|
|
43
|
+
err
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Alternative: patch a specific bedrock-protocol server instance.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* import { configureBedrockServer } from 'raknet-typeo/compat/register'
|
|
53
|
+
* import { createServer } from 'bedrock-protocol'
|
|
54
|
+
*
|
|
55
|
+
* const server = createServer({ host: '0.0.0.0', port: 19132, version: '1.21.0' })
|
|
56
|
+
* configureBedrockServer(server)
|
|
57
|
+
*/
|
|
58
|
+
export function configureBedrockServer(server) {
|
|
59
|
+
const req = createRequire(import.meta.url);
|
|
60
|
+
const compat = req(path.resolve(__dirname, "bedrock-protocol.js"));
|
|
61
|
+
server.RakServer = compat.Server;
|
|
62
|
+
}
|