@hytaleone/query 1.1.0 → 1.1.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/dist/index.cjs CHANGED
@@ -155,7 +155,7 @@ function validateResponse(buf) {
155
155
  return buf.subarray(0, RESPONSE_MAGIC.length).equals(RESPONSE_MAGIC);
156
156
  }
157
157
  function parseCapabilities(reader) {
158
- if (reader.remaining >= 3) {
158
+ if (reader.remaining === 3) {
159
159
  const caps = reader.readUInt16LE();
160
160
  const v2Version = reader.readUInt8();
161
161
  return {
@@ -166,21 +166,27 @@ function parseCapabilities(reader) {
166
166
  }
167
167
  return { supportsV2: false, isNetworkMode: false, v2Version: 0 };
168
168
  }
169
+ function isLegacyPortFormat(buf, offsetAfterMaxPlayers) {
170
+ const versionLenOffset = offsetAfterMaxPlayers + 2;
171
+ if (versionLenOffset + 2 > buf.length) return true;
172
+ const versionLen = buf.readUInt16LE(versionLenOffset);
173
+ return versionLen === 0 || versionLen > 500;
174
+ }
169
175
  function parseBasicResponse(buf) {
170
176
  if (!validateResponse(buf)) {
171
177
  throw new Error("Invalid response: magic mismatch");
172
178
  }
173
179
  const reader = new BufferReader(buf);
174
- reader.readBytes(RESPONSE_MAGIC.length);
175
- reader.readBytes(1);
180
+ reader.readBytes(RESPONSE_MAGIC.length + 1);
176
181
  const serverName = reader.readString();
177
182
  const motd = reader.readString();
178
183
  const currentPlayers = reader.readInt32LE();
179
184
  const maxPlayers = reader.readInt32LE();
180
- const hostPort = reader.readUInt16LE();
185
+ const legacy = isLegacyPortFormat(buf, reader.position);
186
+ const hostPort = legacy ? reader.readInt32LE() : reader.readUInt16LE();
181
187
  const version = reader.readString();
182
- const protocolVersion = reader.readInt32LE();
183
- const protocolHash = reader.readString();
188
+ const protocolVersion = legacy ? 0 : reader.readInt32LE();
189
+ const protocolHash = legacy ? "" : reader.readString();
184
190
  const capabilities = parseCapabilities(reader);
185
191
  return {
186
192
  serverName,
@@ -199,16 +205,16 @@ function parseFullResponse(buf) {
199
205
  throw new Error("Invalid response: magic mismatch");
200
206
  }
201
207
  const reader = new BufferReader(buf);
202
- reader.readBytes(RESPONSE_MAGIC.length);
203
- reader.readBytes(1);
208
+ reader.readBytes(RESPONSE_MAGIC.length + 1);
204
209
  const serverName = reader.readString();
205
210
  const motd = reader.readString();
206
211
  const currentPlayers = reader.readInt32LE();
207
212
  const maxPlayers = reader.readInt32LE();
208
- const hostPort = reader.readUInt16LE();
213
+ const legacy = isLegacyPortFormat(buf, reader.position);
214
+ const hostPort = legacy ? reader.readInt32LE() : reader.readUInt16LE();
209
215
  const version = reader.readString();
210
- const protocolVersion = reader.readInt32LE();
211
- const protocolHash = reader.readString();
216
+ const protocolVersion = legacy ? 0 : reader.readInt32LE();
217
+ const protocolHash = legacy ? "" : reader.readString();
212
218
  const playerCount = reader.readInt32LE();
213
219
  const players = [];
214
220
  for (let i = 0; i < playerCount; i++) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/query.ts","../src/protocol.ts","../src/query-v2.ts","../src/types-v2.ts","../src/protocol-v2.ts"],"sourcesContent":["export { query } from './query.js';\nexport { queryV2, clearChallengeCache } from './query-v2.js';\nexport type {\n ServerInfo,\n ServerInfoFull,\n Player,\n Plugin,\n QueryOptions,\n} from './types.js';\nexport type {\n ServerInfoV2,\n ServerInfoV2WithPlayers,\n QueryV2Options,\n} from './types-v2.js';\nexport { V2QueryType, V2ResponseFlag, V2TLVType } from './types-v2.js';\n","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","import_node_dgram","DEFAULT_TIMEOUT","dgram"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/query.ts","../src/protocol.ts","../src/query-v2.ts","../src/types-v2.ts","../src/protocol-v2.ts"],"sourcesContent":["export { query } from './query.js';\nexport { queryV2, clearChallengeCache } from './query-v2.js';\nexport type {\n ServerInfo,\n ServerInfoFull,\n Player,\n Plugin,\n QueryOptions,\n} from './types.js';\nexport type {\n ServerInfoV2,\n ServerInfoV2WithPlayers,\n QueryV2Options,\n} from './types-v2.js';\nexport { V2QueryType, V2ResponseFlag, V2TLVType } from './types-v2.js';\n","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 * Only parses if exactly 3 bytes remain to avoid misinterpreting other data.\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 * Check if hostPort is encoded as int32 (legacy) vs uint16 (extended).\n * Returns true if int32, false if uint16.\n */\nfunction isLegacyPortFormat(buf: Buffer, offsetAfterMaxPlayers: number): boolean {\n const versionLenOffset = offsetAfterMaxPlayers + 2; // assuming uint16 port\n if (versionLenOffset + 2 > buf.length) return true;\n\n const versionLen = buf.readUInt16LE(versionLenOffset);\n // If version length is 0 or invalid, port is likely int32 (legacy format)\n return versionLen === 0 || versionLen > 500;\n}\n\n/**\n * Parse a basic query response.\n * Supports both extended format (uint16 port + protocolVersion/Hash) and legacy format (int32 port).\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 + type\n reader.readBytes(RESPONSE_MAGIC.length + 1);\n\n const serverName = reader.readString();\n const motd = reader.readString();\n const currentPlayers = reader.readInt32LE();\n const maxPlayers = reader.readInt32LE();\n\n // Detect format: legacy uses int32 for port, extended uses uint16\n const legacy = isLegacyPortFormat(buf, reader.position);\n\n const hostPort = legacy ? reader.readInt32LE() : reader.readUInt16LE();\n const version = reader.readString();\n const protocolVersion = legacy ? 0 : reader.readInt32LE();\n const protocolHash = legacy ? '' : 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 * Supports both extended and legacy formats.\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 + type\n reader.readBytes(RESPONSE_MAGIC.length + 1);\n\n const serverName = reader.readString();\n const motd = reader.readString();\n const currentPlayers = reader.readInt32LE();\n const maxPlayers = reader.readInt32LE();\n\n // Detect format\n const legacy = isLegacyPortFormat(buf, reader.position);\n\n const hostPort = legacy ? reader.readInt32LE() : reader.readUInt16LE();\n const version = reader.readString();\n const protocolVersion = legacy ? 0 : reader.readInt32LE();\n const protocolHash = legacy ? '' : 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","import_node_dgram","DEFAULT_TIMEOUT","dgram"]}
package/dist/index.js CHANGED
@@ -114,7 +114,7 @@ function validateResponse(buf) {
114
114
  return buf.subarray(0, RESPONSE_MAGIC.length).equals(RESPONSE_MAGIC);
115
115
  }
116
116
  function parseCapabilities(reader) {
117
- if (reader.remaining >= 3) {
117
+ if (reader.remaining === 3) {
118
118
  const caps = reader.readUInt16LE();
119
119
  const v2Version = reader.readUInt8();
120
120
  return {
@@ -125,21 +125,27 @@ function parseCapabilities(reader) {
125
125
  }
126
126
  return { supportsV2: false, isNetworkMode: false, v2Version: 0 };
127
127
  }
128
+ function isLegacyPortFormat(buf, offsetAfterMaxPlayers) {
129
+ const versionLenOffset = offsetAfterMaxPlayers + 2;
130
+ if (versionLenOffset + 2 > buf.length) return true;
131
+ const versionLen = buf.readUInt16LE(versionLenOffset);
132
+ return versionLen === 0 || versionLen > 500;
133
+ }
128
134
  function parseBasicResponse(buf) {
129
135
  if (!validateResponse(buf)) {
130
136
  throw new Error("Invalid response: magic mismatch");
131
137
  }
132
138
  const reader = new BufferReader(buf);
133
- reader.readBytes(RESPONSE_MAGIC.length);
134
- reader.readBytes(1);
139
+ reader.readBytes(RESPONSE_MAGIC.length + 1);
135
140
  const serverName = reader.readString();
136
141
  const motd = reader.readString();
137
142
  const currentPlayers = reader.readInt32LE();
138
143
  const maxPlayers = reader.readInt32LE();
139
- const hostPort = reader.readUInt16LE();
144
+ const legacy = isLegacyPortFormat(buf, reader.position);
145
+ const hostPort = legacy ? reader.readInt32LE() : reader.readUInt16LE();
140
146
  const version = reader.readString();
141
- const protocolVersion = reader.readInt32LE();
142
- const protocolHash = reader.readString();
147
+ const protocolVersion = legacy ? 0 : reader.readInt32LE();
148
+ const protocolHash = legacy ? "" : reader.readString();
143
149
  const capabilities = parseCapabilities(reader);
144
150
  return {
145
151
  serverName,
@@ -158,16 +164,16 @@ function parseFullResponse(buf) {
158
164
  throw new Error("Invalid response: magic mismatch");
159
165
  }
160
166
  const reader = new BufferReader(buf);
161
- reader.readBytes(RESPONSE_MAGIC.length);
162
- reader.readBytes(1);
167
+ reader.readBytes(RESPONSE_MAGIC.length + 1);
163
168
  const serverName = reader.readString();
164
169
  const motd = reader.readString();
165
170
  const currentPlayers = reader.readInt32LE();
166
171
  const maxPlayers = reader.readInt32LE();
167
- const hostPort = reader.readUInt16LE();
172
+ const legacy = isLegacyPortFormat(buf, reader.position);
173
+ const hostPort = legacy ? reader.readInt32LE() : reader.readUInt16LE();
168
174
  const version = reader.readString();
169
- const protocolVersion = reader.readInt32LE();
170
- const protocolHash = reader.readString();
175
+ const protocolVersion = legacy ? 0 : reader.readInt32LE();
176
+ const protocolHash = legacy ? "" : reader.readString();
171
177
  const playerCount = reader.readInt32LE();
172
178
  const players = [];
173
179
  for (let i = 0; i < playerCount; i++) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
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"]}
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 * Only parses if exactly 3 bytes remain to avoid misinterpreting other data.\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 * Check if hostPort is encoded as int32 (legacy) vs uint16 (extended).\n * Returns true if int32, false if uint16.\n */\nfunction isLegacyPortFormat(buf: Buffer, offsetAfterMaxPlayers: number): boolean {\n const versionLenOffset = offsetAfterMaxPlayers + 2; // assuming uint16 port\n if (versionLenOffset + 2 > buf.length) return true;\n\n const versionLen = buf.readUInt16LE(versionLenOffset);\n // If version length is 0 or invalid, port is likely int32 (legacy format)\n return versionLen === 0 || versionLen > 500;\n}\n\n/**\n * Parse a basic query response.\n * Supports both extended format (uint16 port + protocolVersion/Hash) and legacy format (int32 port).\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 + type\n reader.readBytes(RESPONSE_MAGIC.length + 1);\n\n const serverName = reader.readString();\n const motd = reader.readString();\n const currentPlayers = reader.readInt32LE();\n const maxPlayers = reader.readInt32LE();\n\n // Detect format: legacy uses int32 for port, extended uses uint16\n const legacy = isLegacyPortFormat(buf, reader.position);\n\n const hostPort = legacy ? reader.readInt32LE() : reader.readUInt16LE();\n const version = reader.readString();\n const protocolVersion = legacy ? 0 : reader.readInt32LE();\n const protocolHash = legacy ? '' : 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 * Supports both extended and legacy formats.\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 + type\n reader.readBytes(RESPONSE_MAGIC.length + 1);\n\n const serverName = reader.readString();\n const motd = reader.readString();\n const currentPlayers = reader.readInt32LE();\n const maxPlayers = reader.readInt32LE();\n\n // Detect format\n const legacy = isLegacyPortFormat(buf, reader.position);\n\n const hostPort = legacy ? reader.readInt32LE() : reader.readUInt16LE();\n const version = reader.readString();\n const protocolVersion = legacy ? 0 : reader.readInt32LE();\n const protocolHash = legacy ? '' : 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,6 +1,6 @@
1
1
  {
2
2
  "name": "@hytaleone/query",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
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",