From 1b2345fa0214c2b991bb6bd1dd191a3fbc608491 Mon Sep 17 00:00:00 2001 From: Thomas Koetsier Date: Mon, 2 Feb 2026 22:57:27 +0000 Subject: [PATCH] feat(protocol/hytale): add support for Hytale servers (#750) Add protocol implementation for querying Hytale game servers using the Nitrado Query API endpoint. The protocol uses HTTPS with HTTP fallback to fetch server information including name, version, player count, and map. Example: $ gamedig --type hytale play.hyfyve.net:5523 { "name": "Hytale Server", "map": "default", "version": "2026.01.13-dcad8778f", "maxplayers": 100, "numplayers": 1, "connect": "play.hyfyve.net:5523" } Co-authored-by: Thomas Koetsier --- lib/games.js | 8 +++++ protocols/hytale.js | 71 +++++++++++++++++++++++++++++++++++++++++++++ protocols/index.js | 3 +- 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 protocols/hytale.js diff --git a/lib/games.js b/lib/games.js index bf6649d..ebc99b7 100644 --- a/lib/games.js +++ b/lib/games.js @@ -1586,6 +1586,14 @@ export const games = { protocol: 'valve' } }, + hytale: { + name: 'Hytale', + release_year: 2026, + options: { + port: 5523, + protocol: 'hytale' + } + }, i2cs: { name: 'IGI 2: Covert Strike', release_year: 2003, diff --git a/protocols/hytale.js b/protocols/hytale.js new file mode 100644 index 0000000..9c669f1 --- /dev/null +++ b/protocols/hytale.js @@ -0,0 +1,71 @@ +import Core from './core.js' + +export default class hytale extends Core { + async run (state) { + this.usedTcp = true + + // Hytale servers commonly use self-signed certificates for HTTPS. + // We default rejectUnauthorized to false unless explicitly set. + if (this.options.rejectUnauthorized === undefined) { + this.options.rejectUnauthorized = false + } + + let response + // Try HTTPS first (most common), fall back to HTTP + try { + response = await this.queryEndpoint('https') + } catch (e) { + this.logger.debug('HTTPS query failed, trying HTTP') + this.logger.debug(e) + response = await this.queryEndpoint('http') + } + + if (response.Server) { + state.name = response.Server.Name + state.version = response.Server.Version + state.maxplayers = response.Server.MaxPlayers + state.raw.server = response.Server + } + + if (response.Universe) { + state.numplayers = response.Universe.CurrentPlayers + state.map = response.Universe.DefaultWorld + state.raw.universe = response.Universe + } + + if (response.Players) { + state.players = response.Players.map(player => ({ + name: player.Name, + raw: { + uuid: player.UUID, + world: player.World + } + })) + } + + if (response.Plugins) { + state.raw.plugins = response.Plugins + } + } + + async queryEndpoint (protocol) { + const url = `${protocol}://${this.options.host}:${this.options.port}/Nitrado/Query` + + const requestOptions = { + url, + headers: { + Accept: 'application/json' + }, + responseType: 'json' + } + + if (protocol === 'https') { + requestOptions.https = { + minVersion: 'TLSv1.2', + rejectUnauthorized: this.options.rejectUnauthorized + } + } + + return await this.request(requestOptions) + } +} diff --git a/protocols/index.js b/protocols/index.js index 66efb97..be4fb31 100644 --- a/protocols/index.js +++ b/protocols/index.js @@ -25,6 +25,7 @@ import gtasao from './gtasao.js' import hawakening from './hawakening.js' import hawakeningmaster from './hawakeningmaster.js' import hexen2 from './hexen2.js' +import hytale from './hytale.js' import jc2mp from './jc2mp.js' import kspdmp from './kspdmp.js' import mafia2mp from './mafia2mp.js' @@ -78,7 +79,7 @@ import scpsl from './scpsl.js' export { armagetron, ase, asa, assettocorsa, battlefield, brokeprotocol, brokeprotocolmaster, buildandshoot, cs2d, discord, doom3, eco, epic, factorio, farmingsimulator, ffow, - fivem, gamespy1, gamespy2, gamespy3, geneshift, goldsrc, gtasao, hawakening, hawakeningmaster, hexen2, jc2mp, kspdmp, mafia2mp, mafia2online, minecraft, + fivem, gamespy1, gamespy2, gamespy3, geneshift, goldsrc, gtasao, hawakening, hawakeningmaster, hexen2, hytale, jc2mp, kspdmp, mafia2mp, mafia2online, minecraft, minecraftbedrock, minecraftvanilla, minetest, mumble, mumbleping, nadeo, openttd, palworld, quake1, quake2, quake3, renegadex, renegadexmaster, renown, rfactor, ragemp, samp, satisfactory, soldat, savage2, squad, starmade, starsiege, teamspeak2, teamspeak3, terraria, toxikk, tribes1, tribes1master, unreal2, ut3, valve, vcmp, ventrilo, warsow, eldewrito, beammpmaster, beammp, dayz, theisleevrima, xonotic, altvmp, vintagestorymaster, vintagestory, sdtd, scpsl