From ec4860cffcbf6dad625536a883b58c10d2893e47 Mon Sep 17 00:00:00 2001 From: Sam Schumacher <38103916+HerrSammyDE@users.noreply.github.com> Date: Tue, 26 May 2026 21:14:06 +0200 Subject: [PATCH] feat(games): add ATS, The Cenozoic Era, VEIN and Mindustry support (#768) * feat(games): add American Truck Simulator (ats) support Mirrors the Euro Truck Simulator 2 configuration (valve protocol, port 27015 with query port offset +1). * Update .gitignore * feat(games): add The Cenozoic Era, VEIN and Mindustry support - The Cenozoic Era and VEIN use the Valve A2S protocol with a query port offset of +1 (verified live against running servers). - Mindustry is queried via a new protocol module implementing the arc-net discovery ping: a two-byte UDP request (0xFE 0x01) returns the serialized Host struct (name, map, players, wave, version, vertype, gamemode, player limit, description, modeName). * fix(games): rename thecenozoicera to tce Satisfies the MoreThanTwoWordsMakeAcronym naming rule enforced by the id-tests CI check: game names with more than two words require an acronym-style id. * Update .gitignore * meta: restore .gitignore endline --------- Co-authored-by: CosminPerRam --- GAMES_LIST.md | 4 ++++ lib/games.js | 35 +++++++++++++++++++++++++++++++++ protocols/index.js | 3 ++- protocols/mindustry.js | 44 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 protocols/mindustry.js diff --git a/GAMES_LIST.md b/GAMES_LIST.md index 1bacb56..afd68e0 100644 --- a/GAMES_LIST.md +++ b/GAMES_LIST.md @@ -28,6 +28,7 @@ | asr08 | Arca Sim Racing '08 | | | assettocorsa | Assetto Corsa | | | atlas | Atlas | [Valve Protocol](#valve) | +| ats | American Truck Simulator | [Valve Protocol](#valve) | | avorion | Avorion | [Valve Protocol](#valve) | | avp2 | Aliens versus Predator 2 | | | avp2010 | Aliens vs. Predator 2010 | [Valve Protocol](#valve) | @@ -190,6 +191,7 @@ | mbe | Minecraft: Bedrock Edition | | | medievalengineers | Medieval Engineers | [Valve Protocol](#valve) | | mgm | Mumble - GT Murmur | [Notes](#mumble) | +| mindustry | Mindustry | | | minecraft | Minecraft | [Notes](#minecraft) | | minetest | Minetest | | | mnc | Monday Night Combat | [Valve Protocol](#valve) | @@ -303,6 +305,7 @@ | synergy | Synergy | [Valve Protocol](#valve) | | t1s | Tribes 1: Starsiege | | | tacticalops | Tactical Ops | | +| tce | The Cenozoic Era | [Valve Protocol](#valve) | | tcgraw | Tom Clancy's Ghost Recon Advanced Warfighter | | | tcgraw2 | Tom Clancy's Ghost Recon Advanced Warfighter 2 | | | teamfactor | Team Factor | | @@ -345,6 +348,7 @@ | valheim | Valheim | [Notes](#valheim), [Valve Protocol](#valve) | | vampireslayer | Vampire Slayer | [Valve Protocol](#valve) | | vcm | Vice City Multiplayer | | +| vein | VEIN | [Valve Protocol](#valve) | | ventrilo | Ventrilo | | | vietcong | Vietcong | | | vietcong2 | Vietcong 2 | | diff --git a/lib/games.js b/lib/games.js index c8bcda8..8dc5e58 100644 --- a/lib/games.js +++ b/lib/games.js @@ -89,6 +89,15 @@ export const games = { protocol: 'assettocorsa' } }, + ats: { + name: 'American Truck Simulator', + release_year: 2016, + options: { + port: 27015, + port_query_offset: 1, + protocol: 'valve' + } + }, atlas: { name: 'Atlas', release_year: 2018, @@ -1930,6 +1939,14 @@ export const games = { doc_notes: 'minecraft' } }, + mindustry: { + name: 'Mindustry', + release_year: 2017, + options: { + port: 6567, + protocol: 'mindustry' + } + }, minetest: { name: 'Minetest', release_year: 2010, @@ -3057,6 +3074,15 @@ export const games = { doc_notes: 'terraria' } }, + tce: { + name: 'The Cenozoic Era', + release_year: 2024, + options: { + port: 7777, + port_query_offset: 1, + protocol: 'valve' + } + }, theforest: { name: 'The Forest', release_year: 2014, @@ -3309,6 +3335,15 @@ export const games = { doc_notes: 'valheim' } }, + vein: { + name: 'VEIN', + release_year: 2024, + options: { + port: 7777, + port_query_offset: 1, + protocol: 'valve' + } + }, vcm: { name: 'Vice City Multiplayer', release_year: 2015, diff --git a/protocols/index.js b/protocols/index.js index be4fb31..eb01898 100644 --- a/protocols/index.js +++ b/protocols/index.js @@ -33,6 +33,7 @@ import mafia2online from './mafia2online.js' import minecraft from './minecraft.js' import minecraftbedrock from './minecraftbedrock.js' import minecraftvanilla from './minecraftvanilla.js' +import mindustry from './mindustry.js' import minetest from './minetest.js' import mumble from './mumble.js' import mumbleping from './mumbleping.js' @@ -80,7 +81,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, hytale, jc2mp, kspdmp, mafia2mp, mafia2online, minecraft, - minecraftbedrock, minecraftvanilla, minetest, mumble, mumbleping, nadeo, openttd, palworld, quake1, quake2, quake3, renegadex, renegadexmaster, renown, rfactor, ragemp, samp, + minecraftbedrock, minecraftvanilla, mindustry, 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 } diff --git a/protocols/mindustry.js b/protocols/mindustry.js new file mode 100644 index 0000000..34c0400 --- /dev/null +++ b/protocols/mindustry.js @@ -0,0 +1,44 @@ +import Core from './core.js' + +const GAMEMODES = ['survival', 'sandbox', 'attack', 'pvp', 'editor'] + +export default class mindustry extends Core { + constructor () { + super() + this.encoding = 'utf8' + this.byteorder = 'be' + } + + async run (state) { + if (!this.options.port) this.options.port = 6567 + + // arc-net discovery request: triggers the Mindustry server to reply + // with a serialized Host struct (see Mindustry NetworkIO.writeServerData). + const request = Buffer.from([0xfe, 0x01]) + const buffer = await this.udpSend(request, b => b) + + const reader = this.reader(buffer) + const name = reader.pascalString(1) + const map = reader.pascalString(1) + const players = reader.int(4) + const wave = reader.int(4) + const version = reader.int(4) + const vertype = reader.pascalString(1) + const gamemodeId = reader.uint(1) + const playerLimit = reader.int(4) + const description = reader.pascalString(1) + const modeName = reader.pascalString(1) + + state.name = name + state.map = map + state.password = false + state.numplayers = players + state.maxplayers = playerLimit + state.version = String(version) + state.raw.wave = wave + state.raw.vertype = vertype + state.raw.gamemode = GAMEMODES[gamemodeId] ?? String(gamemodeId) + state.raw.description = description + state.raw.modeName = modeName + } +}