Skip to content

Commit

Permalink
feat: add Toxikk support (#641)
Browse files Browse the repository at this point in the history
* Add support for Toxikk, using Valve protocol plus UnrealEngine3 rules parsing

* docs: update CHANGELOG for Toxikk

* docs: update GAMES_LIST for Toxikk
  • Loading branch information
RattleSN4K3 authored Sep 27, 2024
1 parent c77ca29 commit 39d7f06
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Feat: Replaced `punycode` package usage with `url.domainToASCII` (#630).
* Feat: World of Padman (2007) - Added support (#636)
* Feat: Update Soldat protocol (#642)
* Feat: TOXIKK (2016) - Added support (#641)

## 5.1.3
* Fix: `Deus Ex` using the wrong protocol (#621)
Expand Down
1 change: 1 addition & 0 deletions GAMES_LIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@
| toh | Take On Helicopters | |
| tonolf | The Operative: No One Lives Forever | |
| towerunite | Tower Unite | [Valve Protocol](#valve) |
| toxikk | TOXIKK | |
| trackmania2 | Trackmania 2 | [Notes](#nadeo) |
| trackmaniaforever | Trackmania Forever | [Notes](#nadeo) |
| tremulous | Tremulous | |
Expand Down
9 changes: 9 additions & 0 deletions lib/games.js
Original file line number Diff line number Diff line change
Expand Up @@ -3063,6 +3063,15 @@ export const games = {
protocol: 'valve'
}
},
toxikk: {
name: 'TOXIKK',
release_year: 2016,
options: {
port: 7777,
port_query: 27015,
protocol: 'toxikk'
}
},
turok2: {
name: 'Turok 2',
release_year: 1998,
Expand Down
3 changes: 2 additions & 1 deletion protocols/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import starsiege from './starsiege.js'
import teamspeak2 from './teamspeak2.js'
import teamspeak3 from './teamspeak3.js'
import terraria from './terraria.js'
import toxikk from './toxikk.js'
import tribes1 from './tribes1.js'
import tribes1master from './tribes1master.js'
import unreal2 from './unreal2.js'
Expand All @@ -68,7 +69,7 @@ export {
armagetron, ase, asa, assettocorsa, battlefield, buildandshoot, cs2d, discord, doom3, eco, epic, factorio, farmingsimulator, ffow,
fivem, gamespy1, gamespy2, gamespy3, geneshift, goldsrc, gtasao, hexen2, jc2mp, kspdmp, mafia2mp, mafia2online, minecraft,
minecraftbedrock, minecraftvanilla, minetest, mumble, mumbleping, nadeo, openttd, palworld, quake1, quake2, quake3, rfactor, ragemp, samp,
savage2, starmade, starsiege, teamspeak2, teamspeak3, terraria, tribes1, tribes1master, unreal2, ut3, valve,
savage2, starmade, starsiege, teamspeak2, teamspeak3, terraria, toxikk, tribes1, tribes1master, unreal2, ut3, valve,
vcmp, ventrilo, warsow, eldewrito, beammpmaster, beammp, dayz, theisleevrima, xonotic, altvmp, vintagestorymaster,
vintagestory, soldat
}
122 changes: 122 additions & 0 deletions protocols/toxikk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import valve from './valve.js'
// import { TranslateMapUT3 } from './ut3.js'

export const TranslateMapToxikk = Object.freeze({
// add UT3 values
// Toxikk is using UDK which includes basic implementation of UT3
// ...TranslateMapUT3,

// Old UT3/UDK properties
p1073741825: 'map', // UC Source='CRZMapName'
p1073741826: 'game', // UC Source='CustomGameMode'
p268435704: 'frag_limit', // UC Source='TimeLimit'
p268435705: 'time_limit', // UC Source='TimeLimit'
p268435703: 'numbots',
p1073741827: 'servername', // UC Source='ServerDescription'
p268435717: false, // 'stock_mutators' // UC Source='OfficialMutators' // Note: "EpicMutators" are bit-masked and require Full UE3 license (C++) to flag mutators, stock mutators could be always 0, ignore for now
p1073741828: 'custom_mutators', // UC Source='CustomMutators'

// Toxikk specific localized settings (commented name is based on source code)
s32779: 'GameMode', // 8=Custom, anything else is UT3/UDK
s0: 'bot_skill', // UC Source='BotSkill', // 0=Ridiculous 1=Novice 2=Average 3=Experienced 4=Adept 5=Masterful 6=Inhuman 7=Godlike
s1: false, // UC Source='Map' // Note: set as '0' mostly, generally the index will state the official map, ignore for now
s6: 'pure_server', // UC Source='PureServer', // bool
s7: 'password', // UC Source='LockedServer', // bool
s8: 'vs_bots', // UC Source='VsBots', // 0=Disabled 1="2:1" 2="1:1" 3="2:3" 4="1:2" 5="1:3" 6="1:4"
s9: 'Campaign', // bool
s10: 'force_respawn', // UC Source='ForceRespawn', // bool
s11: 'AllowKeyboard', // bool
s12: 'IsFullServer', // bool
s13: 'IsEmptyServer', // bool
s14: 'IsDedicated', // bool
s15: 'RankedServer', // 0=UnRanked 1=Ranked
s16: 'OnlyFullGamePlayers', // bool
s17: 'IgnoredByMatchmaking', // bool
s18: 'OfficialServer', // 0=Community 1=Official
s19: 'ModdingLevel', // 0=Unmodded 1=Server Modded 2=Client Modded

// Toxikk properties
p268435706: 'MaxPlayers',
p268435707: 'MinNetPlayers',
p268435708: 'MinSkillClass',
p268435709: 'MaxSkillClass',
p1073741829: 'PLAYERIDS1',
p1073741830: 'PLAYERIDS2',
p1073741831: 'PLAYERIDS3',
p1073741832: 'PLAYERNAMES1',
p1073741833: 'PLAYERNAMES2',
p1073741834: 'PLAYERNAMES3',
p1073741837: 'PLAYERSCS',
p1073741838: 'PLAYERBadgeRanks',
p1073741839: 'GameVersion',
p1073741840: 'GameVoteList'
})

/**
* Implements the protocol for Toxikk, an UnrealEngine3 based game,
* using Valve protocol for query with additional UE3 properties/settings parsing
*/
export default class toxikk extends valve {
async run (state) {
if (!this.options.port) this.options.port = 27015
await this.queryInfo(state)
await this.queryChallenge()
await this.queryPlayers(state)
await this.queryRules(state)

this.processQueryInfo(state)
await this.cleanup(state)
}

/** @override */
async cleanup (state) {
// valve protocol attempts to put "hidden players" into player/bot array
// the bot data is not properly queried, therefore prevent push players into bots-array
const originalNumBots = state.raw.numbots
state.raw.numbots = null
super.cleanup(state)
state.raw.numbots = originalNumBots
}

async queryRules (state) {
if (!this.options.requestRules) {
return
}

const rules = {}
this.logger.debug('Requesting rules ...')

const b = await this.sendPacket(0x56, null, 0x45, true)
if (b === null && !this.options.requestRulesRequired) return // timed out - the server probably has rules disabled

const reader = this.reader(b)
const num = reader.uint(2)
for (let i = num - 1; i > 0 && !reader.done(); i--) {
const key = reader.string()
const value = reader.string()
if (reader.remaining() <= 0) {
// data might be corrupt in this case, keep existing rules
break
}

rules[key] = value
}

state.raw.rules = rules
}

processQueryInfo (state) {
// move raw rules into root-raw object and attempt to translate properties
Object.assign(state.raw, state.raw.rules)
this.translate(state.raw, TranslateMapToxikk)

const split = (a) => {
let s = a.split('\x1c')
s = s.filter((e) => { return e })
return s
}
if ('custom_mutators' in state.raw) state.raw.custom_mutators = split(state.raw.custom_mutators)
if ('stock_mutators' in state.raw) state.raw.stock_mutators = split(state.raw.stock_mutators)
if ('map' in state.raw) state.map ??= state.raw.map
}
}

0 comments on commit 39d7f06

Please sign in to comment.