@hytaleone/query 1.0.0 → 1.1.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/README.md +110 -2
- package/dist/index.cjs +409 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +101 -1
- package/dist/index.d.ts +101 -1
- package/dist/index.js +403 -11
- package/dist/index.js.map +1 -1
- package/package.json +12 -4
package/dist/index.js
CHANGED
|
@@ -6,6 +6,8 @@ var REQUEST_MAGIC = Buffer.from("HYQUERY\0", "ascii");
|
|
|
6
6
|
var RESPONSE_MAGIC = Buffer.from("HYREPLY\0", "ascii");
|
|
7
7
|
var TYPE_BASIC = 0;
|
|
8
8
|
var TYPE_FULL = 1;
|
|
9
|
+
var CAP_V2_PROTOCOL = 1;
|
|
10
|
+
var CAP_NETWORK_MODE = 2;
|
|
9
11
|
function buildRequest(type) {
|
|
10
12
|
const buf = Buffer.alloc(REQUEST_MAGIC.length + 1);
|
|
11
13
|
REQUEST_MAGIC.copy(buf, 0);
|
|
@@ -17,31 +19,58 @@ var BufferReader = class {
|
|
|
17
19
|
this.buf = buf;
|
|
18
20
|
}
|
|
19
21
|
offset = 0;
|
|
22
|
+
checkBounds(needed) {
|
|
23
|
+
if (this.offset + needed > this.buf.length) {
|
|
24
|
+
throw new RangeError(
|
|
25
|
+
`Buffer underflow: need ${needed} bytes at offset ${this.offset}, but buffer is ${this.buf.length} bytes`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
20
29
|
readBytes(length) {
|
|
30
|
+
this.checkBounds(length);
|
|
21
31
|
const slice = this.buf.subarray(this.offset, this.offset + length);
|
|
22
32
|
this.offset += length;
|
|
23
33
|
return slice;
|
|
24
34
|
}
|
|
35
|
+
readUInt8() {
|
|
36
|
+
this.checkBounds(1);
|
|
37
|
+
return this.buf[this.offset++];
|
|
38
|
+
}
|
|
25
39
|
readUInt16LE() {
|
|
40
|
+
this.checkBounds(2);
|
|
26
41
|
const value = this.buf.readUInt16LE(this.offset);
|
|
27
42
|
this.offset += 2;
|
|
28
43
|
return value;
|
|
29
44
|
}
|
|
45
|
+
readUInt32LE() {
|
|
46
|
+
this.checkBounds(4);
|
|
47
|
+
const value = this.buf.readUInt32LE(this.offset);
|
|
48
|
+
this.offset += 4;
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
30
51
|
readInt32LE() {
|
|
52
|
+
this.checkBounds(4);
|
|
31
53
|
const value = this.buf.readInt32LE(this.offset);
|
|
32
54
|
this.offset += 4;
|
|
33
55
|
return value;
|
|
34
56
|
}
|
|
35
57
|
readBigInt64BE() {
|
|
58
|
+
this.checkBounds(8);
|
|
36
59
|
const value = this.buf.readBigInt64BE(this.offset);
|
|
37
60
|
this.offset += 8;
|
|
38
61
|
return value;
|
|
39
62
|
}
|
|
40
63
|
readBoolean() {
|
|
64
|
+
this.checkBounds(1);
|
|
41
65
|
return this.buf[this.offset++] !== 0;
|
|
42
66
|
}
|
|
43
67
|
readString() {
|
|
44
68
|
const length = this.readUInt16LE();
|
|
69
|
+
if (length > this.remaining) {
|
|
70
|
+
throw new RangeError(
|
|
71
|
+
`Invalid string length ${length} at offset ${this.offset - 2}, only ${this.remaining} bytes remaining`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
45
74
|
const bytes = this.readBytes(length);
|
|
46
75
|
return bytes.toString("utf8");
|
|
47
76
|
}
|
|
@@ -50,9 +79,23 @@ var BufferReader = class {
|
|
|
50
79
|
const lsb = this.readBigInt64BE();
|
|
51
80
|
return formatUUID(msb, lsb);
|
|
52
81
|
}
|
|
82
|
+
readBigInt64LE() {
|
|
83
|
+
this.checkBounds(8);
|
|
84
|
+
const value = this.buf.readBigInt64LE(this.offset);
|
|
85
|
+
this.offset += 8;
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
readUUIDLE() {
|
|
89
|
+
const msb = this.readBigInt64LE();
|
|
90
|
+
const lsb = this.readBigInt64LE();
|
|
91
|
+
return formatUUID(msb, lsb);
|
|
92
|
+
}
|
|
53
93
|
get remaining() {
|
|
54
94
|
return this.buf.length - this.offset;
|
|
55
95
|
}
|
|
96
|
+
get position() {
|
|
97
|
+
return this.offset;
|
|
98
|
+
}
|
|
56
99
|
};
|
|
57
100
|
function formatUUID(msb, lsb) {
|
|
58
101
|
const toHex = (n) => {
|
|
@@ -70,6 +113,18 @@ function validateResponse(buf) {
|
|
|
70
113
|
}
|
|
71
114
|
return buf.subarray(0, RESPONSE_MAGIC.length).equals(RESPONSE_MAGIC);
|
|
72
115
|
}
|
|
116
|
+
function parseCapabilities(reader) {
|
|
117
|
+
if (reader.remaining >= 3) {
|
|
118
|
+
const caps = reader.readUInt16LE();
|
|
119
|
+
const v2Version = reader.readUInt8();
|
|
120
|
+
return {
|
|
121
|
+
supportsV2: (caps & CAP_V2_PROTOCOL) !== 0,
|
|
122
|
+
isNetworkMode: (caps & CAP_NETWORK_MODE) !== 0,
|
|
123
|
+
v2Version
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return { supportsV2: false, isNetworkMode: false, v2Version: 0 };
|
|
127
|
+
}
|
|
73
128
|
function parseBasicResponse(buf) {
|
|
74
129
|
if (!validateResponse(buf)) {
|
|
75
130
|
throw new Error("Invalid response: magic mismatch");
|
|
@@ -77,15 +132,25 @@ function parseBasicResponse(buf) {
|
|
|
77
132
|
const reader = new BufferReader(buf);
|
|
78
133
|
reader.readBytes(RESPONSE_MAGIC.length);
|
|
79
134
|
reader.readBytes(1);
|
|
135
|
+
const serverName = reader.readString();
|
|
136
|
+
const motd = reader.readString();
|
|
137
|
+
const currentPlayers = reader.readInt32LE();
|
|
138
|
+
const maxPlayers = reader.readInt32LE();
|
|
139
|
+
const hostPort = reader.readUInt16LE();
|
|
140
|
+
const version = reader.readString();
|
|
141
|
+
const protocolVersion = reader.readInt32LE();
|
|
142
|
+
const protocolHash = reader.readString();
|
|
143
|
+
const capabilities = parseCapabilities(reader);
|
|
80
144
|
return {
|
|
81
|
-
serverName
|
|
82
|
-
motd
|
|
83
|
-
currentPlayers
|
|
84
|
-
maxPlayers
|
|
85
|
-
hostPort
|
|
86
|
-
version
|
|
87
|
-
protocolVersion
|
|
88
|
-
protocolHash
|
|
145
|
+
serverName,
|
|
146
|
+
motd,
|
|
147
|
+
currentPlayers,
|
|
148
|
+
maxPlayers,
|
|
149
|
+
hostPort,
|
|
150
|
+
version,
|
|
151
|
+
protocolVersion,
|
|
152
|
+
protocolHash,
|
|
153
|
+
...capabilities
|
|
89
154
|
};
|
|
90
155
|
}
|
|
91
156
|
function parseFullResponse(buf) {
|
|
@@ -120,6 +185,7 @@ function parseFullResponse(buf) {
|
|
|
120
185
|
enabled: reader.readBoolean()
|
|
121
186
|
});
|
|
122
187
|
}
|
|
188
|
+
const capabilities = parseCapabilities(reader);
|
|
123
189
|
return {
|
|
124
190
|
serverName,
|
|
125
191
|
motd,
|
|
@@ -130,7 +196,8 @@ function parseFullResponse(buf) {
|
|
|
130
196
|
protocolVersion,
|
|
131
197
|
protocolHash,
|
|
132
198
|
players,
|
|
133
|
-
plugins
|
|
199
|
+
plugins,
|
|
200
|
+
...capabilities
|
|
134
201
|
};
|
|
135
202
|
}
|
|
136
203
|
|
|
@@ -140,9 +207,15 @@ function sendQuery(host, port, type, timeout) {
|
|
|
140
207
|
return new Promise((resolve, reject) => {
|
|
141
208
|
const socket = dgram.createSocket("udp4");
|
|
142
209
|
let timeoutHandle;
|
|
210
|
+
let closed = false;
|
|
143
211
|
const cleanup = () => {
|
|
212
|
+
if (closed) return;
|
|
213
|
+
closed = true;
|
|
144
214
|
clearTimeout(timeoutHandle);
|
|
145
|
-
|
|
215
|
+
try {
|
|
216
|
+
socket.close();
|
|
217
|
+
} catch {
|
|
218
|
+
}
|
|
146
219
|
};
|
|
147
220
|
socket.on("message", (msg) => {
|
|
148
221
|
cleanup();
|
|
@@ -174,7 +247,326 @@ async function query(host, port = 5520, options = {}) {
|
|
|
174
247
|
}
|
|
175
248
|
return parseBasicResponse(response);
|
|
176
249
|
}
|
|
250
|
+
|
|
251
|
+
// src/query-v2.ts
|
|
252
|
+
import dgram2 from "dgram";
|
|
253
|
+
|
|
254
|
+
// src/types-v2.ts
|
|
255
|
+
var V2QueryType = {
|
|
256
|
+
CHALLENGE: 0,
|
|
257
|
+
BASIC: 1,
|
|
258
|
+
PLAYERS: 2
|
|
259
|
+
};
|
|
260
|
+
var V2ResponseFlag = {
|
|
261
|
+
HAS_MORE_PLAYERS: 1,
|
|
262
|
+
AUTH_REQUIRED: 2,
|
|
263
|
+
IS_NETWORK: 16,
|
|
264
|
+
HAS_ADDRESS: 32
|
|
265
|
+
};
|
|
266
|
+
var V2TLVType = {
|
|
267
|
+
SERVER_INFO: 1,
|
|
268
|
+
PLAYER_LIST: 2
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// src/protocol-v2.ts
|
|
272
|
+
var V2_REQUEST_MAGIC = Buffer.from("ONEQUERY", "ascii");
|
|
273
|
+
var V2_RESPONSE_MAGIC = Buffer.from("ONEREPLY", "ascii");
|
|
274
|
+
var CHALLENGE_TOKEN_SIZE = 32;
|
|
275
|
+
function buildChallengeRequest() {
|
|
276
|
+
const buf = Buffer.alloc(V2_REQUEST_MAGIC.length + 1);
|
|
277
|
+
V2_REQUEST_MAGIC.copy(buf, 0);
|
|
278
|
+
buf[V2_REQUEST_MAGIC.length] = V2QueryType.CHALLENGE;
|
|
279
|
+
return buf;
|
|
280
|
+
}
|
|
281
|
+
function buildV2Request(type, token, requestId, flags, offset, authToken) {
|
|
282
|
+
const authBuf = authToken ? Buffer.from(authToken, "utf8") : null;
|
|
283
|
+
const authLength = authBuf ? 2 + authBuf.length : 0;
|
|
284
|
+
const totalLength = V2_REQUEST_MAGIC.length + 1 + 32 + 4 + 2 + 4 + authLength;
|
|
285
|
+
const buf = Buffer.alloc(totalLength);
|
|
286
|
+
let pos = 0;
|
|
287
|
+
V2_REQUEST_MAGIC.copy(buf, pos);
|
|
288
|
+
pos += V2_REQUEST_MAGIC.length;
|
|
289
|
+
buf[pos++] = type;
|
|
290
|
+
token.copy(buf, pos);
|
|
291
|
+
pos += 32;
|
|
292
|
+
buf.writeUInt32LE(requestId, pos);
|
|
293
|
+
pos += 4;
|
|
294
|
+
buf.writeUInt16LE(flags, pos);
|
|
295
|
+
pos += 2;
|
|
296
|
+
buf.writeUInt32LE(offset, pos);
|
|
297
|
+
pos += 4;
|
|
298
|
+
if (authBuf) {
|
|
299
|
+
buf.writeUInt16LE(authBuf.length, pos);
|
|
300
|
+
pos += 2;
|
|
301
|
+
authBuf.copy(buf, pos);
|
|
302
|
+
}
|
|
303
|
+
return buf;
|
|
304
|
+
}
|
|
305
|
+
function validateV2Response(buf) {
|
|
306
|
+
if (buf.length < V2_RESPONSE_MAGIC.length) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
return buf.subarray(0, V2_RESPONSE_MAGIC.length).equals(V2_RESPONSE_MAGIC);
|
|
310
|
+
}
|
|
311
|
+
function parseChallengeResponse(buf) {
|
|
312
|
+
if (!validateV2Response(buf)) {
|
|
313
|
+
throw new Error("Invalid V2 response: magic mismatch");
|
|
314
|
+
}
|
|
315
|
+
const tokenOffset = V2_RESPONSE_MAGIC.length + 1;
|
|
316
|
+
if (buf.length < tokenOffset + CHALLENGE_TOKEN_SIZE) {
|
|
317
|
+
throw new Error("Invalid challenge response: too short");
|
|
318
|
+
}
|
|
319
|
+
return Buffer.from(buf.subarray(tokenOffset, tokenOffset + CHALLENGE_TOKEN_SIZE));
|
|
320
|
+
}
|
|
321
|
+
function parseV2ResponseHeader(buf) {
|
|
322
|
+
if (!validateV2Response(buf)) {
|
|
323
|
+
throw new Error("Invalid V2 response: magic mismatch");
|
|
324
|
+
}
|
|
325
|
+
const reader = new BufferReader(buf);
|
|
326
|
+
reader.readBytes(V2_RESPONSE_MAGIC.length);
|
|
327
|
+
const protocolVersion = reader.readUInt8();
|
|
328
|
+
const flags = reader.readUInt16LE();
|
|
329
|
+
const requestId = reader.readUInt32LE();
|
|
330
|
+
const payloadLength = reader.readUInt16LE();
|
|
331
|
+
return { protocolVersion, flags, requestId, payloadLength };
|
|
332
|
+
}
|
|
333
|
+
function parseTLVEntries(payload) {
|
|
334
|
+
const entries = [];
|
|
335
|
+
const reader = new BufferReader(payload);
|
|
336
|
+
while (reader.remaining >= 4) {
|
|
337
|
+
const type = reader.readUInt16LE();
|
|
338
|
+
const length = reader.readUInt16LE();
|
|
339
|
+
if (reader.remaining < length) {
|
|
340
|
+
throw new Error("Invalid TLV: truncated value");
|
|
341
|
+
}
|
|
342
|
+
const value = reader.readBytes(length);
|
|
343
|
+
entries.push({ type, value });
|
|
344
|
+
}
|
|
345
|
+
return entries;
|
|
346
|
+
}
|
|
347
|
+
function parseV2Response(buf) {
|
|
348
|
+
const header = parseV2ResponseHeader(buf);
|
|
349
|
+
const headerSize = V2_RESPONSE_MAGIC.length + 1 + 2 + 4 + 2;
|
|
350
|
+
const payload = buf.subarray(headerSize, headerSize + header.payloadLength);
|
|
351
|
+
const entries = parseTLVEntries(payload);
|
|
352
|
+
return { header, entries };
|
|
353
|
+
}
|
|
354
|
+
function parseTLVServerInfo(entry, hasAddress, isNetwork) {
|
|
355
|
+
if (entry.type !== V2TLVType.SERVER_INFO) {
|
|
356
|
+
throw new Error(`Expected SERVER_INFO TLV (${V2TLVType.SERVER_INFO}), got ${entry.type}`);
|
|
357
|
+
}
|
|
358
|
+
const reader = new BufferReader(entry.value);
|
|
359
|
+
const serverName = reader.readString();
|
|
360
|
+
const motd = reader.readString();
|
|
361
|
+
const currentPlayers = reader.readInt32LE();
|
|
362
|
+
const maxPlayers = reader.readInt32LE();
|
|
363
|
+
const version = reader.readString();
|
|
364
|
+
const protocolVersion = reader.readInt32LE();
|
|
365
|
+
const protocolHash = reader.readString();
|
|
366
|
+
const result = {
|
|
367
|
+
serverName,
|
|
368
|
+
motd,
|
|
369
|
+
currentPlayers,
|
|
370
|
+
maxPlayers,
|
|
371
|
+
version,
|
|
372
|
+
protocolVersion,
|
|
373
|
+
protocolHash,
|
|
374
|
+
isNetwork
|
|
375
|
+
};
|
|
376
|
+
if (hasAddress && reader.remaining >= 2) {
|
|
377
|
+
result.host = reader.readString();
|
|
378
|
+
if (reader.remaining >= 2) {
|
|
379
|
+
result.hostPort = reader.readUInt16LE();
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return result;
|
|
383
|
+
}
|
|
384
|
+
function parseTLVPlayerList(entry) {
|
|
385
|
+
if (entry.type !== V2TLVType.PLAYER_LIST) {
|
|
386
|
+
throw new Error(`Expected PLAYER_LIST TLV (${V2TLVType.PLAYER_LIST}), got ${entry.type}`);
|
|
387
|
+
}
|
|
388
|
+
const reader = new BufferReader(entry.value);
|
|
389
|
+
const totalPlayers = reader.readInt32LE();
|
|
390
|
+
const playersInResponse = reader.readInt32LE();
|
|
391
|
+
const offset = reader.readInt32LE();
|
|
392
|
+
const players = [];
|
|
393
|
+
for (let i = 0; i < playersInResponse; i++) {
|
|
394
|
+
const name = reader.readString();
|
|
395
|
+
const uuid = reader.readUUIDLE();
|
|
396
|
+
players.push({ name, uuid });
|
|
397
|
+
}
|
|
398
|
+
return { players, totalPlayers, offset };
|
|
399
|
+
}
|
|
400
|
+
function findTLVEntry(entries, type) {
|
|
401
|
+
return entries.find((e) => e.type === type);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/query-v2.ts
|
|
405
|
+
var DEFAULT_TIMEOUT2 = 5e3;
|
|
406
|
+
var TOKEN_TTL_MS = 3e4;
|
|
407
|
+
var TOKEN_REFRESH_BUFFER_MS = 5e3;
|
|
408
|
+
var CLEANUP_INTERVAL_MS = 6e4;
|
|
409
|
+
var challengeCache = /* @__PURE__ */ new Map();
|
|
410
|
+
var cleanupScheduled = false;
|
|
411
|
+
function scheduleCleanup() {
|
|
412
|
+
if (cleanupScheduled) return;
|
|
413
|
+
cleanupScheduled = true;
|
|
414
|
+
setTimeout(() => {
|
|
415
|
+
const now = Date.now();
|
|
416
|
+
for (const [key, entry] of challengeCache) {
|
|
417
|
+
if (now >= entry.expiresAt) {
|
|
418
|
+
challengeCache.delete(key);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
cleanupScheduled = false;
|
|
422
|
+
if (challengeCache.size > 0) {
|
|
423
|
+
scheduleCleanup();
|
|
424
|
+
}
|
|
425
|
+
}, CLEANUP_INTERVAL_MS).unref();
|
|
426
|
+
}
|
|
427
|
+
function clearChallengeCache() {
|
|
428
|
+
challengeCache.clear();
|
|
429
|
+
}
|
|
430
|
+
function sendUDP(host, port, data, timeout) {
|
|
431
|
+
return new Promise((resolve, reject) => {
|
|
432
|
+
const socket = dgram2.createSocket("udp4");
|
|
433
|
+
let timeoutHandle;
|
|
434
|
+
let closed = false;
|
|
435
|
+
const cleanup = () => {
|
|
436
|
+
if (closed) return;
|
|
437
|
+
closed = true;
|
|
438
|
+
clearTimeout(timeoutHandle);
|
|
439
|
+
try {
|
|
440
|
+
socket.close();
|
|
441
|
+
} catch {
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
socket.on("message", (msg) => {
|
|
445
|
+
cleanup();
|
|
446
|
+
resolve(msg);
|
|
447
|
+
});
|
|
448
|
+
socket.on("error", (err) => {
|
|
449
|
+
cleanup();
|
|
450
|
+
reject(err);
|
|
451
|
+
});
|
|
452
|
+
timeoutHandle = setTimeout(() => {
|
|
453
|
+
cleanup();
|
|
454
|
+
reject(new Error(`Query timeout after ${timeout}ms`));
|
|
455
|
+
}, timeout);
|
|
456
|
+
socket.send(data, port, host, (err) => {
|
|
457
|
+
if (err) {
|
|
458
|
+
cleanup();
|
|
459
|
+
reject(err);
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
async function fetchChallenge(host, port, timeout) {
|
|
465
|
+
const request = buildChallengeRequest();
|
|
466
|
+
const response = await sendUDP(host, port, request, timeout);
|
|
467
|
+
return parseChallengeResponse(response);
|
|
468
|
+
}
|
|
469
|
+
async function ensureChallenge(host, port, timeout) {
|
|
470
|
+
const key = `${host}:${port}`;
|
|
471
|
+
const cached = challengeCache.get(key);
|
|
472
|
+
const now = Date.now();
|
|
473
|
+
if (cached && now < cached.expiresAt - TOKEN_REFRESH_BUFFER_MS) {
|
|
474
|
+
cached.requestId++;
|
|
475
|
+
return cached;
|
|
476
|
+
}
|
|
477
|
+
const token = await fetchChallenge(host, port, timeout);
|
|
478
|
+
const entry = {
|
|
479
|
+
token,
|
|
480
|
+
expiresAt: now + TOKEN_TTL_MS,
|
|
481
|
+
requestId: 1
|
|
482
|
+
};
|
|
483
|
+
challengeCache.set(key, entry);
|
|
484
|
+
scheduleCleanup();
|
|
485
|
+
return entry;
|
|
486
|
+
}
|
|
487
|
+
async function performV2Query(host, port, type, offset, timeout, authToken) {
|
|
488
|
+
const challenge = await ensureChallenge(host, port, timeout);
|
|
489
|
+
const request = buildV2Request(type, challenge.token, challenge.requestId, 0, offset, authToken);
|
|
490
|
+
const response = await sendUDP(host, port, request, timeout);
|
|
491
|
+
return parseV2Response(response);
|
|
492
|
+
}
|
|
493
|
+
async function queryV2(host, port = 5520, options = {}) {
|
|
494
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT2;
|
|
495
|
+
const wantPlayers = options.players === true || options.players === "all";
|
|
496
|
+
const offset = options.playerOffset ?? 0;
|
|
497
|
+
const { header: basicHeader, entries: basicEntries } = await performV2Query(
|
|
498
|
+
host,
|
|
499
|
+
port,
|
|
500
|
+
V2QueryType.BASIC,
|
|
501
|
+
0,
|
|
502
|
+
timeout,
|
|
503
|
+
options.authToken
|
|
504
|
+
);
|
|
505
|
+
const hasAddress = (basicHeader.flags & V2ResponseFlag.HAS_ADDRESS) !== 0;
|
|
506
|
+
const isNetwork = (basicHeader.flags & V2ResponseFlag.IS_NETWORK) !== 0;
|
|
507
|
+
const serverInfoEntry = findTLVEntry(basicEntries, V2TLVType.SERVER_INFO);
|
|
508
|
+
if (!serverInfoEntry) {
|
|
509
|
+
throw new Error("Response missing SERVER_INFO TLV");
|
|
510
|
+
}
|
|
511
|
+
const serverInfo = parseTLVServerInfo(serverInfoEntry, hasAddress, isNetwork);
|
|
512
|
+
if (!wantPlayers) {
|
|
513
|
+
return serverInfo;
|
|
514
|
+
}
|
|
515
|
+
const { header: playersHeader, entries: playersEntries } = await performV2Query(
|
|
516
|
+
host,
|
|
517
|
+
port,
|
|
518
|
+
V2QueryType.PLAYERS,
|
|
519
|
+
offset,
|
|
520
|
+
timeout,
|
|
521
|
+
options.authToken
|
|
522
|
+
);
|
|
523
|
+
const hasMorePlayers = (playersHeader.flags & V2ResponseFlag.HAS_MORE_PLAYERS) !== 0;
|
|
524
|
+
const playerListEntry = findTLVEntry(playersEntries, V2TLVType.PLAYER_LIST);
|
|
525
|
+
if (!playerListEntry) {
|
|
526
|
+
throw new Error("Response missing PLAYER_LIST TLV");
|
|
527
|
+
}
|
|
528
|
+
const playerList = parseTLVPlayerList(playerListEntry);
|
|
529
|
+
let players = playerList.players;
|
|
530
|
+
let currentOffset = playerList.offset + players.length;
|
|
531
|
+
let hasMore = hasMorePlayers;
|
|
532
|
+
if (options.players === "all" && hasMore) {
|
|
533
|
+
const allPlayers = [...players];
|
|
534
|
+
while (hasMore) {
|
|
535
|
+
const nextResult = await performV2Query(
|
|
536
|
+
host,
|
|
537
|
+
port,
|
|
538
|
+
V2QueryType.PLAYERS,
|
|
539
|
+
currentOffset,
|
|
540
|
+
timeout,
|
|
541
|
+
options.authToken
|
|
542
|
+
);
|
|
543
|
+
const nextHasMore = (nextResult.header.flags & V2ResponseFlag.HAS_MORE_PLAYERS) !== 0;
|
|
544
|
+
const nextPlayerEntry = findTLVEntry(nextResult.entries, V2TLVType.PLAYER_LIST);
|
|
545
|
+
if (!nextPlayerEntry) {
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
const nextPlayerList = parseTLVPlayerList(nextPlayerEntry);
|
|
549
|
+
allPlayers.push(...nextPlayerList.players);
|
|
550
|
+
currentOffset = nextPlayerList.offset + nextPlayerList.players.length;
|
|
551
|
+
hasMore = nextHasMore;
|
|
552
|
+
}
|
|
553
|
+
players = allPlayers;
|
|
554
|
+
hasMore = false;
|
|
555
|
+
}
|
|
556
|
+
return {
|
|
557
|
+
...serverInfo,
|
|
558
|
+
players,
|
|
559
|
+
totalPlayers: playerList.totalPlayers,
|
|
560
|
+
offset: playerList.offset,
|
|
561
|
+
hasMore
|
|
562
|
+
};
|
|
563
|
+
}
|
|
177
564
|
export {
|
|
178
|
-
|
|
565
|
+
V2QueryType,
|
|
566
|
+
V2ResponseFlag,
|
|
567
|
+
V2TLVType,
|
|
568
|
+
clearChallengeCache,
|
|
569
|
+
query,
|
|
570
|
+
queryV2
|
|
179
571
|
};
|
|
180
572
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/query.ts","../src/protocol.ts"],"sourcesContent":["import dgram from 'node:dgram';\nimport {\n buildRequest,\n parseBasicResponse,\n parseFullResponse,\n TYPE_BASIC,\n TYPE_FULL,\n} from './protocol.js';\nimport type { QueryOptions, ServerInfo, ServerInfoFull } from './types.js';\n\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * Send a UDP query and wait for response.\n */\nfunction sendQuery(\n host: string,\n port: number,\n type: number,\n timeout: number\n): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const socket = dgram.createSocket('udp4');\n let timeoutHandle: NodeJS.Timeout;\n\n const cleanup = () => {\n clearTimeout(timeoutHandle);\n socket.close();\n };\n\n socket.on('message', (msg) => {\n cleanup();\n resolve(msg);\n });\n\n socket.on('error', (err) => {\n cleanup();\n reject(err);\n });\n\n timeoutHandle = setTimeout(() => {\n cleanup();\n reject(new Error(`Query timeout after ${timeout}ms`));\n }, timeout);\n\n const request = buildRequest(type);\n socket.send(request, port, host, (err) => {\n if (err) {\n cleanup();\n reject(err);\n }\n });\n });\n}\n\n/**\n * Query a Hytale server for information.\n *\n * @param host - Server hostname or IP address\n * @param port - Server port (default: 5520)\n * @param options - Query options\n * @returns Server information (full if options.full is true)\n *\n * @example\n * ```typescript\n * // Basic query\n * const info = await query('play.example.com', 5520);\n * console.log(`${info.serverName}: ${info.currentPlayers}/${info.maxPlayers}`);\n *\n * // Full query (includes players + plugins)\n * const full = await query('play.example.com', 5520, { full: true });\n * console.log('Players:', full.players.map(p => p.name).join(', '));\n * ```\n */\nexport async function query(\n host: string,\n port?: number,\n options?: QueryOptions & { full?: false }\n): Promise<ServerInfo>;\nexport async function query(\n host: string,\n port: number,\n options: QueryOptions & { full: true }\n): Promise<ServerInfoFull>;\nexport async function query(\n host: string,\n port = 5520,\n options: QueryOptions & { full?: boolean } = {}\n): Promise<ServerInfo | ServerInfoFull> {\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n const type = options.full ? TYPE_FULL : TYPE_BASIC;\n const response = await sendQuery(host, port, type, timeout);\n\n if (options.full) {\n return parseFullResponse(response);\n }\n return parseBasicResponse(response);\n}\n","import type { Player, Plugin, ServerInfo, ServerInfoFull } from './types.js';\n\n// Protocol constants\nexport const REQUEST_MAGIC = Buffer.from('HYQUERY\\0', 'ascii');\nexport const RESPONSE_MAGIC = Buffer.from('HYREPLY\\0', 'ascii');\nexport const TYPE_BASIC = 0x00;\nexport const TYPE_FULL = 0x01;\n\n/**\n * Build a query request packet.\n */\nexport function buildRequest(type: number): Buffer {\n const buf = Buffer.alloc(REQUEST_MAGIC.length + 1);\n REQUEST_MAGIC.copy(buf, 0);\n buf[REQUEST_MAGIC.length] = type;\n return buf;\n}\n\n/**\n * Buffer reader helper for parsing responses.\n */\nclass BufferReader {\n private offset = 0;\n\n constructor(private buf: Buffer) {}\n\n readBytes(length: number): Buffer {\n const slice = this.buf.subarray(this.offset, this.offset + length);\n this.offset += length;\n return slice;\n }\n\n readUInt16LE(): number {\n const value = this.buf.readUInt16LE(this.offset);\n this.offset += 2;\n return value;\n }\n\n readInt32LE(): number {\n const value = this.buf.readInt32LE(this.offset);\n this.offset += 4;\n return value;\n }\n\n readBigInt64BE(): bigint {\n const value = this.buf.readBigInt64BE(this.offset);\n this.offset += 8;\n return value;\n }\n\n readBoolean(): boolean {\n return this.buf[this.offset++] !== 0;\n }\n\n readString(): string {\n const length = this.readUInt16LE();\n const bytes = this.readBytes(length);\n return bytes.toString('utf8');\n }\n\n readUUID(): string {\n const msb = this.readBigInt64BE();\n const lsb = this.readBigInt64BE();\n return formatUUID(msb, lsb);\n }\n\n get remaining(): number {\n return this.buf.length - this.offset;\n }\n}\n\n/**\n * Format UUID from most/least significant bits.\n */\nfunction formatUUID(msb: bigint, lsb: bigint): string {\n const toHex = (n: bigint): string => {\n if (n < 0n) {\n n = BigInt.asUintN(64, n);\n }\n return n.toString(16).padStart(16, '0');\n };\n\n const hex = toHex(msb) + toHex(lsb);\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * Validate response magic bytes.\n */\nexport function validateResponse(buf: Buffer): boolean {\n if (buf.length < RESPONSE_MAGIC.length + 1) {\n return false;\n }\n return buf.subarray(0, RESPONSE_MAGIC.length).equals(RESPONSE_MAGIC);\n}\n\n/**\n * Parse a basic query response.\n */\nexport function parseBasicResponse(buf: Buffer): ServerInfo {\n if (!validateResponse(buf)) {\n throw new Error('Invalid response: magic mismatch');\n }\n\n const reader = new BufferReader(buf);\n\n // Skip magic\n reader.readBytes(RESPONSE_MAGIC.length);\n\n // Skip type\n reader.readBytes(1);\n\n return {\n serverName: reader.readString(),\n motd: reader.readString(),\n currentPlayers: reader.readInt32LE(),\n maxPlayers: reader.readInt32LE(),\n hostPort: reader.readUInt16LE(),\n version: reader.readString(),\n protocolVersion: reader.readInt32LE(),\n protocolHash: reader.readString(),\n };\n}\n\n/**\n * Parse a full query response.\n */\nexport function parseFullResponse(buf: Buffer): ServerInfoFull {\n if (!validateResponse(buf)) {\n throw new Error('Invalid response: magic mismatch');\n }\n\n const reader = new BufferReader(buf);\n\n // Skip magic\n reader.readBytes(RESPONSE_MAGIC.length);\n\n // Skip type\n reader.readBytes(1);\n\n // Base info\n const serverName = reader.readString();\n const motd = reader.readString();\n const currentPlayers = reader.readInt32LE();\n const maxPlayers = reader.readInt32LE();\n const hostPort = reader.readUInt16LE();\n const version = reader.readString();\n const protocolVersion = reader.readInt32LE();\n const protocolHash = reader.readString();\n\n // Player list\n const playerCount = reader.readInt32LE();\n const players: Player[] = [];\n for (let i = 0; i < playerCount; i++) {\n players.push({\n name: reader.readString(),\n uuid: reader.readUUID(),\n });\n }\n\n // Plugin list\n const pluginCount = reader.readInt32LE();\n const plugins: Plugin[] = [];\n for (let i = 0; i < pluginCount; i++) {\n plugins.push({\n id: reader.readString(),\n version: reader.readString(),\n enabled: reader.readBoolean(),\n });\n }\n\n return {\n serverName,\n motd,\n currentPlayers,\n maxPlayers,\n hostPort,\n version,\n protocolVersion,\n protocolHash,\n players,\n plugins,\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/query.ts","../src/protocol.ts","../src/query-v2.ts","../src/types-v2.ts","../src/protocol-v2.ts"],"sourcesContent":["import dgram from 'node:dgram';\nimport {\n buildRequest,\n parseBasicResponse,\n parseFullResponse,\n TYPE_BASIC,\n TYPE_FULL,\n} from './protocol.js';\nimport type { QueryOptions, ServerInfo, ServerInfoFull } from './types.js';\n\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * Send a UDP query and wait for response.\n */\nfunction sendQuery(\n host: string,\n port: number,\n type: number,\n timeout: number\n): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const socket = dgram.createSocket('udp4');\n let timeoutHandle: NodeJS.Timeout;\n let closed = false;\n\n const cleanup = () => {\n if (closed) return;\n closed = true;\n clearTimeout(timeoutHandle);\n try {\n socket.close();\n } catch {\n // Already closed\n }\n };\n\n socket.on('message', (msg) => {\n cleanup();\n resolve(msg);\n });\n\n socket.on('error', (err) => {\n cleanup();\n reject(err);\n });\n\n timeoutHandle = setTimeout(() => {\n cleanup();\n reject(new Error(`Query timeout after ${timeout}ms`));\n }, timeout);\n\n const request = buildRequest(type);\n socket.send(request, port, host, (err) => {\n if (err) {\n cleanup();\n reject(err);\n }\n });\n });\n}\n\n/**\n * Query a Hytale server for information.\n *\n * @param host - Server hostname or IP address\n * @param port - Server port (default: 5520)\n * @param options - Query options\n * @returns Server information (full if options.full is true)\n *\n * @example\n * ```typescript\n * // Basic query\n * const info = await query('play.example.com', 5520);\n * console.log(`${info.serverName}: ${info.currentPlayers}/${info.maxPlayers}`);\n *\n * // Full query (includes players + plugins)\n * const full = await query('play.example.com', 5520, { full: true });\n * console.log('Players:', full.players.map(p => p.name).join(', '));\n * ```\n */\nexport async function query(\n host: string,\n port?: number,\n options?: QueryOptions & { full?: false }\n): Promise<ServerInfo>;\nexport async function query(\n host: string,\n port: number,\n options: QueryOptions & { full: true }\n): Promise<ServerInfoFull>;\nexport async function query(\n host: string,\n port = 5520,\n options: QueryOptions & { full?: boolean } = {}\n): Promise<ServerInfo | ServerInfoFull> {\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n const type = options.full ? TYPE_FULL : TYPE_BASIC;\n const response = await sendQuery(host, port, type, timeout);\n\n if (options.full) {\n return parseFullResponse(response);\n }\n return parseBasicResponse(response);\n}\n","import type { Player, Plugin, ServerInfo, ServerInfoFull } from './types.js';\n\n// Protocol constants\nexport const REQUEST_MAGIC = Buffer.from('HYQUERY\\0', 'ascii');\nexport const RESPONSE_MAGIC = Buffer.from('HYREPLY\\0', 'ascii');\nexport const TYPE_BASIC = 0x00;\nexport const TYPE_FULL = 0x01;\n\n// Capability flags (V1 extension)\nexport const CAP_V2_PROTOCOL = 0x01;\nexport const CAP_NETWORK_MODE = 0x02;\n\n/**\n * Build a query request packet.\n */\nexport function buildRequest(type: number): Buffer {\n const buf = Buffer.alloc(REQUEST_MAGIC.length + 1);\n REQUEST_MAGIC.copy(buf, 0);\n buf[REQUEST_MAGIC.length] = type;\n return buf;\n}\n\n/**\n * Buffer reader helper for parsing responses.\n */\nexport class BufferReader {\n private offset = 0;\n\n constructor(private buf: Buffer) {}\n\n private checkBounds(needed: number): void {\n if (this.offset + needed > this.buf.length) {\n throw new RangeError(\n `Buffer underflow: need ${needed} bytes at offset ${this.offset}, but buffer is ${this.buf.length} bytes`\n );\n }\n }\n\n readBytes(length: number): Buffer {\n this.checkBounds(length);\n const slice = this.buf.subarray(this.offset, this.offset + length);\n this.offset += length;\n return slice;\n }\n\n readUInt8(): number {\n this.checkBounds(1);\n return this.buf[this.offset++];\n }\n\n readUInt16LE(): number {\n this.checkBounds(2);\n const value = this.buf.readUInt16LE(this.offset);\n this.offset += 2;\n return value;\n }\n\n readUInt32LE(): number {\n this.checkBounds(4);\n const value = this.buf.readUInt32LE(this.offset);\n this.offset += 4;\n return value;\n }\n\n readInt32LE(): number {\n this.checkBounds(4);\n const value = this.buf.readInt32LE(this.offset);\n this.offset += 4;\n return value;\n }\n\n readBigInt64BE(): bigint {\n this.checkBounds(8);\n const value = this.buf.readBigInt64BE(this.offset);\n this.offset += 8;\n return value;\n }\n\n readBoolean(): boolean {\n this.checkBounds(1);\n return this.buf[this.offset++] !== 0;\n }\n\n readString(): string {\n const length = this.readUInt16LE();\n if (length > this.remaining) {\n throw new RangeError(\n `Invalid string length ${length} at offset ${this.offset - 2}, only ${this.remaining} bytes remaining`\n );\n }\n const bytes = this.readBytes(length);\n return bytes.toString('utf8');\n }\n\n readUUID(): string {\n const msb = this.readBigInt64BE();\n const lsb = this.readBigInt64BE();\n return formatUUID(msb, lsb);\n }\n\n readBigInt64LE(): bigint {\n this.checkBounds(8);\n const value = this.buf.readBigInt64LE(this.offset);\n this.offset += 8;\n return value;\n }\n\n readUUIDLE(): string {\n const msb = this.readBigInt64LE();\n const lsb = this.readBigInt64LE();\n return formatUUID(msb, lsb);\n }\n\n get remaining(): number {\n return this.buf.length - this.offset;\n }\n\n get position(): number {\n return this.offset;\n }\n}\n\n/**\n * Format UUID from most/least significant bits.\n */\nfunction formatUUID(msb: bigint, lsb: bigint): string {\n const toHex = (n: bigint): string => {\n if (n < 0n) {\n n = BigInt.asUintN(64, n);\n }\n return n.toString(16).padStart(16, '0');\n };\n\n const hex = toHex(msb) + toHex(lsb);\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * Validate response magic bytes.\n */\nexport function validateResponse(buf: Buffer): boolean {\n if (buf.length < RESPONSE_MAGIC.length + 1) {\n return false;\n }\n return buf.subarray(0, RESPONSE_MAGIC.length).equals(RESPONSE_MAGIC);\n}\n\n/**\n * Parse capability extension from remaining bytes (3 bytes: 2 caps + 1 version).\n */\nfunction parseCapabilities(reader: BufferReader): {\n supportsV2: boolean;\n isNetworkMode: boolean;\n v2Version: number;\n} {\n if (reader.remaining >= 3) {\n const caps = reader.readUInt16LE();\n const v2Version = reader.readUInt8();\n return {\n supportsV2: (caps & CAP_V2_PROTOCOL) !== 0,\n isNetworkMode: (caps & CAP_NETWORK_MODE) !== 0,\n v2Version,\n };\n }\n return { supportsV2: false, isNetworkMode: false, v2Version: 0 };\n}\n\n/**\n * Parse a basic query response.\n */\nexport function parseBasicResponse(buf: Buffer): ServerInfo {\n if (!validateResponse(buf)) {\n throw new Error('Invalid response: magic mismatch');\n }\n\n const reader = new BufferReader(buf);\n\n // Skip magic\n reader.readBytes(RESPONSE_MAGIC.length);\n\n // Skip type\n reader.readBytes(1);\n\n const serverName = reader.readString();\n const motd = reader.readString();\n const currentPlayers = reader.readInt32LE();\n const maxPlayers = reader.readInt32LE();\n const hostPort = reader.readUInt16LE();\n const version = reader.readString();\n const protocolVersion = reader.readInt32LE();\n const protocolHash = reader.readString();\n\n const capabilities = parseCapabilities(reader);\n\n return {\n serverName,\n motd,\n currentPlayers,\n maxPlayers,\n hostPort,\n version,\n protocolVersion,\n protocolHash,\n ...capabilities,\n };\n}\n\n/**\n * Parse a full query response.\n */\nexport function parseFullResponse(buf: Buffer): ServerInfoFull {\n if (!validateResponse(buf)) {\n throw new Error('Invalid response: magic mismatch');\n }\n\n const reader = new BufferReader(buf);\n\n // Skip magic\n reader.readBytes(RESPONSE_MAGIC.length);\n\n // Skip type\n reader.readBytes(1);\n\n // Base info\n const serverName = reader.readString();\n const motd = reader.readString();\n const currentPlayers = reader.readInt32LE();\n const maxPlayers = reader.readInt32LE();\n const hostPort = reader.readUInt16LE();\n const version = reader.readString();\n const protocolVersion = reader.readInt32LE();\n const protocolHash = reader.readString();\n\n // Player list\n const playerCount = reader.readInt32LE();\n const players: Player[] = [];\n for (let i = 0; i < playerCount; i++) {\n players.push({\n name: reader.readString(),\n uuid: reader.readUUID(),\n });\n }\n\n // Plugin list\n const pluginCount = reader.readInt32LE();\n const plugins: Plugin[] = [];\n for (let i = 0; i < pluginCount; i++) {\n plugins.push({\n id: reader.readString(),\n version: reader.readString(),\n enabled: reader.readBoolean(),\n });\n }\n\n const capabilities = parseCapabilities(reader);\n\n return {\n serverName,\n motd,\n currentPlayers,\n maxPlayers,\n hostPort,\n version,\n protocolVersion,\n protocolHash,\n players,\n plugins,\n ...capabilities,\n };\n}\n","import dgram from 'node:dgram';\nimport type { Player } from './types.js';\nimport type { QueryV2Options, ServerInfoV2, ServerInfoV2WithPlayers } from './types-v2.js';\nimport { V2QueryType, V2ResponseFlag, V2TLVType } from './types-v2.js';\nimport {\n buildChallengeRequest,\n buildV2Request,\n findTLVEntry,\n parseChallengeResponse,\n parseTLVPlayerList,\n parseTLVServerInfo,\n parseV2Response,\n} from './protocol-v2.js';\n\nconst DEFAULT_TIMEOUT = 5000;\n\n// Challenge token cache configuration\nconst TOKEN_TTL_MS = 30_000;\nconst TOKEN_REFRESH_BUFFER_MS = 5_000;\nconst CLEANUP_INTERVAL_MS = 60_000;\n\ninterface CachedChallenge {\n token: Buffer;\n expiresAt: number;\n requestId: number;\n}\n\n// Module-level cache\nconst challengeCache = new Map<string, CachedChallenge>();\nlet cleanupScheduled = false;\n\nfunction scheduleCleanup(): void {\n if (cleanupScheduled) return;\n cleanupScheduled = true;\n\n setTimeout(() => {\n const now = Date.now();\n for (const [key, entry] of challengeCache) {\n if (now >= entry.expiresAt) {\n challengeCache.delete(key);\n }\n }\n cleanupScheduled = false;\n if (challengeCache.size > 0) {\n scheduleCleanup();\n }\n }, CLEANUP_INTERVAL_MS).unref();\n}\n\n/**\n * Clear the challenge token cache.\n * Useful for testing or forcing fresh challenges.\n */\nexport function clearChallengeCache(): void {\n challengeCache.clear();\n}\n\n/**\n * Send a UDP packet and wait for response.\n */\nfunction sendUDP(host: string, port: number, data: Buffer, timeout: number): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const socket = dgram.createSocket('udp4');\n let timeoutHandle: NodeJS.Timeout;\n let closed = false;\n\n const cleanup = () => {\n if (closed) return;\n closed = true;\n clearTimeout(timeoutHandle);\n try {\n socket.close();\n } catch {\n // Already closed\n }\n };\n\n socket.on('message', (msg) => {\n cleanup();\n resolve(msg);\n });\n\n socket.on('error', (err) => {\n cleanup();\n reject(err);\n });\n\n timeoutHandle = setTimeout(() => {\n cleanup();\n reject(new Error(`Query timeout after ${timeout}ms`));\n }, timeout);\n\n socket.send(data, port, host, (err) => {\n if (err) {\n cleanup();\n reject(err);\n }\n });\n });\n}\n\n/**\n * Fetch a new challenge token from the server.\n */\nasync function fetchChallenge(host: string, port: number, timeout: number): Promise<Buffer> {\n const request = buildChallengeRequest();\n const response = await sendUDP(host, port, request, timeout);\n return parseChallengeResponse(response);\n}\n\n/**\n * Get or refresh a challenge token for a host:port.\n */\nasync function ensureChallenge(\n host: string,\n port: number,\n timeout: number\n): Promise<CachedChallenge> {\n const key = `${host}:${port}`;\n const cached = challengeCache.get(key);\n const now = Date.now();\n\n // Return cached if still valid (with buffer time)\n if (cached && now < cached.expiresAt - TOKEN_REFRESH_BUFFER_MS) {\n cached.requestId++;\n return cached;\n }\n\n // Fetch new challenge token\n const token = await fetchChallenge(host, port, timeout);\n const entry: CachedChallenge = {\n token,\n expiresAt: now + TOKEN_TTL_MS,\n requestId: 1,\n };\n\n challengeCache.set(key, entry);\n scheduleCleanup();\n\n return entry;\n}\n\n/**\n * Perform a V2 query request.\n */\nasync function performV2Query(\n host: string,\n port: number,\n type: number,\n offset: number,\n timeout: number,\n authToken?: string\n): Promise<{ header: ReturnType<typeof parseV2Response>['header']; entries: ReturnType<typeof parseV2Response>['entries'] }> {\n const challenge = await ensureChallenge(host, port, timeout);\n\n const request = buildV2Request(type, challenge.token, challenge.requestId, 0, offset, authToken);\n const response = await sendUDP(host, port, request, timeout);\n\n return parseV2Response(response);\n}\n\n/**\n * Query a Hytale server using V2 protocol (basic info only).\n */\nexport async function queryV2(\n host: string,\n port?: number,\n options?: QueryV2Options & { players?: false }\n): Promise<ServerInfoV2>;\n\n/**\n * Query a Hytale server using V2 protocol with player list.\n */\nexport async function queryV2(\n host: string,\n port: number,\n options: QueryV2Options & { players: true | 'all' }\n): Promise<ServerInfoV2WithPlayers>;\n\n/**\n * Query a Hytale server using V2 protocol.\n *\n * @param host - Server hostname or IP address\n * @param port - Server port (default: 5520)\n * @param options - Query options\n * @returns Server information (with players if requested)\n *\n * @example\n * ```typescript\n * // Basic V2 query\n * const info = await queryV2('play.example.com', 5520);\n * console.log(`${info.serverName}: ${info.currentPlayers}/${info.maxPlayers}`);\n *\n * // V2 query with players\n * const full = await queryV2('play.example.com', 5520, { players: true });\n * console.log('Players:', full.players.map(p => p.name).join(', '));\n *\n * // V2 query with all players (auto-pagination)\n * const all = await queryV2('play.example.com', 5520, { players: 'all' });\n * console.log(`All ${all.totalPlayers} players:`, all.players.map(p => p.name).join(', '));\n * ```\n */\nexport async function queryV2(\n host: string,\n port = 5520,\n options: QueryV2Options = {}\n): Promise<ServerInfoV2 | ServerInfoV2WithPlayers> {\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n const wantPlayers = options.players === true || options.players === 'all';\n const offset = options.playerOffset ?? 0;\n\n // First, always do a BASIC query to get server info\n const { header: basicHeader, entries: basicEntries } = await performV2Query(\n host,\n port,\n V2QueryType.BASIC,\n 0,\n timeout,\n options.authToken\n );\n\n const hasAddress = (basicHeader.flags & V2ResponseFlag.HAS_ADDRESS) !== 0;\n const isNetwork = (basicHeader.flags & V2ResponseFlag.IS_NETWORK) !== 0;\n\n // Parse server info\n const serverInfoEntry = findTLVEntry(basicEntries, V2TLVType.SERVER_INFO);\n if (!serverInfoEntry) {\n throw new Error('Response missing SERVER_INFO TLV');\n }\n const serverInfo = parseTLVServerInfo(serverInfoEntry, hasAddress, isNetwork);\n\n // If players not requested, return basic info\n if (!wantPlayers) {\n return serverInfo;\n }\n\n // Do a PLAYERS query to get player list\n const { header: playersHeader, entries: playersEntries } = await performV2Query(\n host,\n port,\n V2QueryType.PLAYERS,\n offset,\n timeout,\n options.authToken\n );\n\n const hasMorePlayers = (playersHeader.flags & V2ResponseFlag.HAS_MORE_PLAYERS) !== 0;\n\n // Parse player list\n const playerListEntry = findTLVEntry(playersEntries, V2TLVType.PLAYER_LIST);\n if (!playerListEntry) {\n throw new Error('Response missing PLAYER_LIST TLV');\n }\n const playerList = parseTLVPlayerList(playerListEntry);\n\n let players = playerList.players;\n let currentOffset = playerList.offset + players.length;\n let hasMore = hasMorePlayers;\n\n // If 'all' mode, paginate through remaining players\n if (options.players === 'all' && hasMore) {\n const allPlayers: Player[] = [...players];\n\n while (hasMore) {\n const nextResult = await performV2Query(\n host,\n port,\n V2QueryType.PLAYERS,\n currentOffset,\n timeout,\n options.authToken\n );\n\n const nextHasMore = (nextResult.header.flags & V2ResponseFlag.HAS_MORE_PLAYERS) !== 0;\n const nextPlayerEntry = findTLVEntry(nextResult.entries, V2TLVType.PLAYER_LIST);\n\n if (!nextPlayerEntry) {\n break;\n }\n\n const nextPlayerList = parseTLVPlayerList(nextPlayerEntry);\n allPlayers.push(...nextPlayerList.players);\n currentOffset = nextPlayerList.offset + nextPlayerList.players.length;\n hasMore = nextHasMore;\n }\n\n players = allPlayers;\n hasMore = false;\n }\n\n return {\n ...serverInfo,\n players,\n totalPlayers: playerList.totalPlayers,\n offset: playerList.offset,\n hasMore,\n };\n}\n","import type { Player } from './types.js';\n\n/**\n * V2 query type constants.\n */\nexport const V2QueryType = {\n CHALLENGE: 0x00,\n BASIC: 0x01,\n PLAYERS: 0x02,\n} as const;\n\n/**\n * V2 response flag constants.\n */\nexport const V2ResponseFlag = {\n HAS_MORE_PLAYERS: 0x0001,\n AUTH_REQUIRED: 0x0002,\n IS_NETWORK: 0x0010,\n HAS_ADDRESS: 0x0020,\n} as const;\n\n/**\n * V2 TLV type constants.\n */\nexport const V2TLVType = {\n SERVER_INFO: 0x0001,\n PLAYER_LIST: 0x0002,\n} as const;\n\n/**\n * Basic server information from V2 protocol.\n */\nexport interface ServerInfoV2 {\n /** Server display name */\n serverName: string;\n /** Message of the day */\n motd: string;\n /** Current number of players online */\n currentPlayers: number;\n /** Maximum player capacity */\n maxPlayers: number;\n /** Server version string */\n version: string;\n /** Protocol version number */\n protocolVersion: number;\n /** Protocol hash string */\n protocolHash: string;\n /** Server host (if FLAG_RESPONSE_HAS_ADDRESS) */\n host?: string;\n /** Server port (if FLAG_RESPONSE_HAS_ADDRESS) */\n hostPort?: number;\n /** Whether the server is in network aggregation mode */\n isNetwork: boolean;\n}\n\n/**\n * V2 server info combined with player list.\n */\nexport interface ServerInfoV2WithPlayers extends ServerInfoV2 {\n /** List of online players */\n players: Player[];\n /** Total players on server */\n totalPlayers: number;\n /** Offset used for this response */\n offset: number;\n /** Whether more players are available */\n hasMore: boolean;\n}\n\n/**\n * Options for V2 query function.\n */\nexport interface QueryV2Options {\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Request player list: false=basic only, true=single page, 'all'=paginate all */\n players?: boolean | 'all';\n /** Starting offset for player pagination */\n playerOffset?: number;\n /** Optional auth token for private servers */\n authToken?: string;\n}\n","import type { Player } from './types.js';\nimport type { ServerInfoV2 } from './types-v2.js';\nimport { V2QueryType, V2ResponseFlag, V2TLVType } from './types-v2.js';\nimport { BufferReader } from './protocol.js';\n\n// Magic bytes for V2 protocol\nexport const V2_REQUEST_MAGIC = Buffer.from('ONEQUERY', 'ascii');\nexport const V2_RESPONSE_MAGIC = Buffer.from('ONEREPLY', 'ascii');\n\n// Challenge token size\nexport const CHALLENGE_TOKEN_SIZE = 32;\n\n/**\n * Build a challenge request packet.\n * Format: Magic (8) + Type (1)\n */\nexport function buildChallengeRequest(): Buffer {\n const buf = Buffer.alloc(V2_REQUEST_MAGIC.length + 1);\n V2_REQUEST_MAGIC.copy(buf, 0);\n buf[V2_REQUEST_MAGIC.length] = V2QueryType.CHALLENGE;\n return buf;\n}\n\n/**\n * Build a V2 request packet.\n * Format: Magic (8) + Type (1) + Token (32) + RequestID (4) + Flags (2) + Offset (4) + [AuthToken]\n */\nexport function buildV2Request(\n type: number,\n token: Buffer,\n requestId: number,\n flags: number,\n offset: number,\n authToken?: string\n): Buffer {\n const authBuf = authToken ? Buffer.from(authToken, 'utf8') : null;\n const authLength = authBuf ? 2 + authBuf.length : 0;\n const totalLength = V2_REQUEST_MAGIC.length + 1 + 32 + 4 + 2 + 4 + authLength;\n\n const buf = Buffer.alloc(totalLength);\n let pos = 0;\n\n // Magic\n V2_REQUEST_MAGIC.copy(buf, pos);\n pos += V2_REQUEST_MAGIC.length;\n\n // Type\n buf[pos++] = type;\n\n // Token\n token.copy(buf, pos);\n pos += 32;\n\n // Request ID (LE)\n buf.writeUInt32LE(requestId, pos);\n pos += 4;\n\n // Flags (LE)\n buf.writeUInt16LE(flags, pos);\n pos += 2;\n\n // Offset (LE)\n buf.writeUInt32LE(offset, pos);\n pos += 4;\n\n // Auth token (optional)\n if (authBuf) {\n buf.writeUInt16LE(authBuf.length, pos);\n pos += 2;\n authBuf.copy(buf, pos);\n }\n\n return buf;\n}\n\n/**\n * Parsed TLV entry.\n */\nexport interface TLVEntry {\n type: number;\n value: Buffer;\n}\n\n/**\n * Parsed V2 response header.\n */\nexport interface V2ResponseHeader {\n protocolVersion: number;\n flags: number;\n requestId: number;\n payloadLength: number;\n}\n\n/**\n * Validate V2 response magic bytes.\n */\nexport function validateV2Response(buf: Buffer): boolean {\n if (buf.length < V2_RESPONSE_MAGIC.length) {\n return false;\n }\n return buf.subarray(0, V2_RESPONSE_MAGIC.length).equals(V2_RESPONSE_MAGIC);\n}\n\n/**\n * Parse challenge response to extract 32-byte token.\n * Format: Magic (8) + Status (1) + Token (32) + [padding]\n */\nexport function parseChallengeResponse(buf: Buffer): Buffer {\n if (!validateV2Response(buf)) {\n throw new Error('Invalid V2 response: magic mismatch');\n }\n\n // Challenge response: Magic (8) + Status (1) + Token (32)\n const tokenOffset = V2_RESPONSE_MAGIC.length + 1; // Skip magic and status byte\n if (buf.length < tokenOffset + CHALLENGE_TOKEN_SIZE) {\n throw new Error('Invalid challenge response: too short');\n }\n\n return Buffer.from(buf.subarray(tokenOffset, tokenOffset + CHALLENGE_TOKEN_SIZE));\n}\n\n/**\n * Parse V2 response header.\n */\nexport function parseV2ResponseHeader(buf: Buffer): V2ResponseHeader {\n if (!validateV2Response(buf)) {\n throw new Error('Invalid V2 response: magic mismatch');\n }\n\n const reader = new BufferReader(buf);\n\n // Skip magic\n reader.readBytes(V2_RESPONSE_MAGIC.length);\n\n const protocolVersion = reader.readUInt8();\n const flags = reader.readUInt16LE();\n const requestId = reader.readUInt32LE();\n const payloadLength = reader.readUInt16LE();\n\n return { protocolVersion, flags, requestId, payloadLength };\n}\n\n/**\n * Parse TLV entries from payload.\n */\nexport function parseTLVEntries(payload: Buffer): TLVEntry[] {\n const entries: TLVEntry[] = [];\n const reader = new BufferReader(payload);\n\n while (reader.remaining >= 4) {\n const type = reader.readUInt16LE();\n const length = reader.readUInt16LE();\n\n if (reader.remaining < length) {\n throw new Error('Invalid TLV: truncated value');\n }\n\n const value = reader.readBytes(length);\n entries.push({ type, value });\n }\n\n return entries;\n}\n\n/**\n * Parse full V2 response (header + TLV payload).\n */\nexport function parseV2Response(buf: Buffer): { header: V2ResponseHeader; entries: TLVEntry[] } {\n const header = parseV2ResponseHeader(buf);\n\n // Header is 17 bytes: Magic (8) + Version (1) + Flags (2) + RequestID (4) + PayloadLength (2)\n const headerSize = V2_RESPONSE_MAGIC.length + 1 + 2 + 4 + 2;\n const payload = buf.subarray(headerSize, headerSize + header.payloadLength);\n\n const entries = parseTLVEntries(payload);\n\n return { header, entries };\n}\n\n/**\n * Parse SERVER_INFO TLV into ServerInfoV2.\n * Note: isNetwork is determined from response flags, not from the TLV data.\n */\nexport function parseTLVServerInfo(entry: TLVEntry, hasAddress: boolean, isNetwork: boolean): ServerInfoV2 {\n if (entry.type !== V2TLVType.SERVER_INFO) {\n throw new Error(`Expected SERVER_INFO TLV (${V2TLVType.SERVER_INFO}), got ${entry.type}`);\n }\n\n const reader = new BufferReader(entry.value);\n\n const serverName = reader.readString();\n const motd = reader.readString();\n const currentPlayers = reader.readInt32LE();\n const maxPlayers = reader.readInt32LE();\n const version = reader.readString();\n const protocolVersion = reader.readInt32LE();\n const protocolHash = reader.readString();\n\n const result: ServerInfoV2 = {\n serverName,\n motd,\n currentPlayers,\n maxPlayers,\n version,\n protocolVersion,\n protocolHash,\n isNetwork,\n };\n\n if (hasAddress && reader.remaining >= 2) {\n result.host = reader.readString();\n if (reader.remaining >= 2) {\n result.hostPort = reader.readUInt16LE();\n }\n }\n\n return result;\n}\n\n/**\n * Parsed player list from TLV.\n */\nexport interface ParsedPlayerList {\n players: Player[];\n totalPlayers: number;\n offset: number;\n}\n\n/**\n * Parse PLAYER_LIST TLV.\n * Format: totalPlayers (4) + playersInResponse (4) + offset (4) + player entries\n */\nexport function parseTLVPlayerList(entry: TLVEntry): ParsedPlayerList {\n if (entry.type !== V2TLVType.PLAYER_LIST) {\n throw new Error(`Expected PLAYER_LIST TLV (${V2TLVType.PLAYER_LIST}), got ${entry.type}`);\n }\n\n const reader = new BufferReader(entry.value);\n\n const totalPlayers = reader.readInt32LE();\n const playersInResponse = reader.readInt32LE();\n const offset = reader.readInt32LE();\n\n const players: Player[] = [];\n for (let i = 0; i < playersInResponse; i++) {\n const name = reader.readString();\n const uuid = reader.readUUIDLE(); // UUIDs are little-endian in V2\n players.push({ name, uuid });\n }\n\n return { players, totalPlayers, offset };\n}\n\n/**\n * Find a TLV entry by type.\n */\nexport function findTLVEntry(entries: TLVEntry[], type: number): TLVEntry | undefined {\n return entries.find((e) => e.type === type);\n}\n"],"mappings":"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","names":["dgram","DEFAULT_TIMEOUT","dgram"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hytaleone/query",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "TypeScript/JavaScript client for querying Hytale servers. Get server status, player count, player list, and plugin information via UDP protocol.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -27,11 +27,19 @@
|
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"hytale",
|
|
30
|
+
"hytale-server",
|
|
30
31
|
"query",
|
|
31
|
-
"server",
|
|
32
|
+
"server-query",
|
|
33
|
+
"server-status",
|
|
34
|
+
"server-list",
|
|
32
35
|
"udp",
|
|
33
36
|
"status",
|
|
34
|
-
"ping"
|
|
37
|
+
"ping",
|
|
38
|
+
"player-list",
|
|
39
|
+
"game-server",
|
|
40
|
+
"typescript",
|
|
41
|
+
"nodejs",
|
|
42
|
+
"motd"
|
|
35
43
|
],
|
|
36
44
|
"author": "HytaleOne",
|
|
37
45
|
"license": "MIT",
|