mirror of
https://github.com/tribufu/node-gamedig
synced 2026-05-06 07:07:33 +00:00
feat: add Renegade X support (#643)
This commit is contained in:
parent
b3947164d3
commit
fe9d4f2cfc
7 changed files with 266 additions and 2 deletions
|
|
@ -6,6 +6,7 @@
|
||||||
* Feat: Satisfactory - Added support (By @Smidy13 #645)
|
* Feat: Satisfactory - Added support (By @Smidy13 #645)
|
||||||
* Feat: Update Soldat protocol (#642)
|
* Feat: Update Soldat protocol (#642)
|
||||||
* Feat: TOXIKK (2016) - Added support (#641)
|
* Feat: TOXIKK (2016) - Added support (#641)
|
||||||
|
* Feat: Renegade X (2014) - Added support (#643)
|
||||||
|
|
||||||
## 5.1.3
|
## 5.1.3
|
||||||
* Fix: `Deus Ex` using the wrong protocol (#621)
|
* Fix: `Deus Ex` using the wrong protocol (#621)
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,7 @@
|
||||||
| redline | Redline | |
|
| redline | Redline | |
|
||||||
| redorchestra | Red Orchestra | |
|
| redorchestra | Red Orchestra | |
|
||||||
| redorchestra2 | Red Orchestra 2 | [Valve Protocol](#valve) |
|
| redorchestra2 | Red Orchestra 2 | [Valve Protocol](#valve) |
|
||||||
|
| renegade10 | Renegade X | |
|
||||||
| rfactor | rFactor | |
|
| rfactor | rFactor | |
|
||||||
| rfactor2 | rFactor 2 | [Valve Protocol](#valve) |
|
| rfactor2 | rFactor 2 | [Valve Protocol](#valve) |
|
||||||
| ricochet | Ricochet | [Valve Protocol](#valve) |
|
| ricochet | Ricochet | [Valve Protocol](#valve) |
|
||||||
|
|
|
||||||
|
|
@ -2400,6 +2400,13 @@ export const games = {
|
||||||
protocol: 'gamespy1'
|
protocol: 'gamespy1'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
renegade10: {
|
||||||
|
name: 'Renegade X',
|
||||||
|
release_year: 2014,
|
||||||
|
options: {
|
||||||
|
protocol: 'renegadex'
|
||||||
|
}
|
||||||
|
},
|
||||||
rdr2r: {
|
rdr2r: {
|
||||||
name: 'Red Dead Redemption 2 - RedM',
|
name: 'Red Dead Redemption 2 - RedM',
|
||||||
release_year: 2018,
|
release_year: 2018,
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ import palworld from './palworld.js'
|
||||||
import quake1 from './quake1.js'
|
import quake1 from './quake1.js'
|
||||||
import quake2 from './quake2.js'
|
import quake2 from './quake2.js'
|
||||||
import quake3 from './quake3.js'
|
import quake3 from './quake3.js'
|
||||||
|
import renegadex from './renegadex.js'
|
||||||
|
import renegadexmaster from './renegadexmaster.js'
|
||||||
import rfactor from './rfactor.js'
|
import rfactor from './rfactor.js'
|
||||||
import samp from './samp.js'
|
import samp from './samp.js'
|
||||||
import satisfactory from './satisfactory.js'
|
import satisfactory from './satisfactory.js'
|
||||||
|
|
@ -69,7 +71,7 @@ import vintagestory from './vintagestory.js'
|
||||||
export {
|
export {
|
||||||
armagetron, ase, asa, assettocorsa, battlefield, buildandshoot, cs2d, discord, doom3, eco, epic, factorio, farmingsimulator, ffow,
|
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,
|
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,
|
minecraftbedrock, minecraftvanilla, minetest, mumble, mumbleping, nadeo, openttd, palworld, quake1, quake2, quake3, renegadex, renegadexmaster, rfactor, ragemp, samp,
|
||||||
satisfactory, soldat, savage2, starmade, starsiege, teamspeak2, teamspeak3, terraria, toxikk, tribes1, tribes1master, unreal2, ut3, valve,
|
satisfactory, soldat, savage2, starmade, starsiege, teamspeak2, teamspeak3, terraria, toxikk, tribes1, tribes1master, unreal2, ut3, valve,
|
||||||
vcmp, ventrilo, warsow, eldewrito, beammpmaster, beammp, dayz, theisleevrima, xonotic, altvmp, vintagestorymaster, vintagestory
|
vcmp, ventrilo, warsow, eldewrito, beammpmaster, beammp, dayz, theisleevrima, xonotic, altvmp, vintagestorymaster, vintagestory
|
||||||
}
|
}
|
||||||
|
|
|
||||||
232
protocols/renegadex.js
Normal file
232
protocols/renegadex.js
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
import Core from './core.js'
|
||||||
|
// import Ajv from 'ajv'
|
||||||
|
// const ajv = new Ajv()
|
||||||
|
|
||||||
|
export const MasterServerServerInfoSchema = {
|
||||||
|
type: 'object',
|
||||||
|
required: [
|
||||||
|
'IP',
|
||||||
|
'Port',
|
||||||
|
'Name',
|
||||||
|
'Current Map',
|
||||||
|
'Bots',
|
||||||
|
'Players',
|
||||||
|
'Game Version',
|
||||||
|
'Variables'
|
||||||
|
],
|
||||||
|
properties: {
|
||||||
|
IP: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'ipv4',
|
||||||
|
description: 'IP of the server'
|
||||||
|
},
|
||||||
|
Port: {
|
||||||
|
type: 'integer',
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 65535,
|
||||||
|
description: 'The port of the server instance to connect to for joining'
|
||||||
|
},
|
||||||
|
Name: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Name of the server, i.e.: Bob\'s Server.'
|
||||||
|
},
|
||||||
|
NamePrefix: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'A prefix of the server'
|
||||||
|
},
|
||||||
|
'Current Map': {
|
||||||
|
type: 'string',
|
||||||
|
description: 'The current map\'s name the server is running is running'
|
||||||
|
},
|
||||||
|
Players: {
|
||||||
|
type: 'integer',
|
||||||
|
description: 'The number of players connected to the server',
|
||||||
|
minimum: 0
|
||||||
|
},
|
||||||
|
Bots: {
|
||||||
|
type: 'integer',
|
||||||
|
minimum: 0,
|
||||||
|
description: 'The number of bots'
|
||||||
|
},
|
||||||
|
'Game Version': {
|
||||||
|
type: 'string',
|
||||||
|
pattern: '^Open Beta (.*?)?$',
|
||||||
|
description: 'Version of the build of the server'
|
||||||
|
},
|
||||||
|
Variables: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
'Player Limit': {
|
||||||
|
type: 'integer',
|
||||||
|
minimum: 0,
|
||||||
|
description: 'Maximum number of players allowed by this server'
|
||||||
|
},
|
||||||
|
'Time Limit': {
|
||||||
|
type: 'integer',
|
||||||
|
minimum: 0,
|
||||||
|
description: 'time limit in minutes'
|
||||||
|
},
|
||||||
|
'Team Mode': {
|
||||||
|
type: 'integer',
|
||||||
|
description: 'Determines how teams are organized between matches.',
|
||||||
|
enum: [
|
||||||
|
0, // static,
|
||||||
|
1, // swap
|
||||||
|
2, // random swap
|
||||||
|
3, // shuffle
|
||||||
|
4, // traditional (assign as players connect)
|
||||||
|
5, // traditional + free swap
|
||||||
|
6 // ladder rank
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'Game Type': {
|
||||||
|
type: 'integer',
|
||||||
|
description: 'Type of the game the server is running',
|
||||||
|
enum: [
|
||||||
|
0, // Rx_Game_MainMenu
|
||||||
|
1, // Rx_Game
|
||||||
|
2, // TS_Game
|
||||||
|
3 // SP_Game
|
||||||
|
// < 3 x < 1000 = RenX Unused/Reserved
|
||||||
|
// < 1000 < x < 2^31 - 1 = Unassigned / Mod space
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'Vehicle Limit': {
|
||||||
|
type: 'integer',
|
||||||
|
minimum: 0,
|
||||||
|
description: 'Maximum number of vehicles allowed by this server'
|
||||||
|
},
|
||||||
|
'Mine Limit': {
|
||||||
|
type: 'integer',
|
||||||
|
minimum: 0,
|
||||||
|
description: 'Maximum number of mines allowed by this server'
|
||||||
|
},
|
||||||
|
bPassworded: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether a password is required to enter the game'
|
||||||
|
},
|
||||||
|
bSteamRequired: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether clients required to be logged into Steam to play on this server'
|
||||||
|
},
|
||||||
|
bRanked: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether the serer is ranked/official'
|
||||||
|
},
|
||||||
|
bAllowPrivateMessaging: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether the server allows non-admin clients to PM each other'
|
||||||
|
},
|
||||||
|
bPrivateMessageTeamOnly: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'whether private messaging is restricted to just teammates'
|
||||||
|
},
|
||||||
|
bAutoBalanceTeams: { // alias of 'bSpawnCrates'
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether the server will spawn crates in this game for balancing'
|
||||||
|
},
|
||||||
|
bSpawnCrates: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether the server will spawn crates in this game for balancing'
|
||||||
|
},
|
||||||
|
CrateRespawnAfterPickup: {
|
||||||
|
type: 'integer',
|
||||||
|
minimum: 0,
|
||||||
|
description: 'interval for crate respawn (after pickup)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: [
|
||||||
|
'Player Limit',
|
||||||
|
'Time Limit',
|
||||||
|
'Team Mode',
|
||||||
|
'Game Type',
|
||||||
|
'Vehicle Limit',
|
||||||
|
'Mine Limit'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const MasterServerResponseSchema = {
|
||||||
|
type: 'array',
|
||||||
|
items: { $ref: '#/$defs/server' },
|
||||||
|
$defs: {
|
||||||
|
server: MasterServerServerInfoSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the protocol for Renegade X, an UnrealEngine3 based game, using a custom master server
|
||||||
|
*/
|
||||||
|
export default class renegadex extends Core {
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
this.usedTcp = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async run (state) {
|
||||||
|
// query master list and find specific server
|
||||||
|
const servers = await this.getMasterServerList()
|
||||||
|
const serverInfo = servers.find((server) => {
|
||||||
|
return server.IP === this.options.address && server.Port === this.options.port
|
||||||
|
})
|
||||||
|
|
||||||
|
if (serverInfo == null) {
|
||||||
|
throw new Error('Server not found in master server list')
|
||||||
|
}
|
||||||
|
|
||||||
|
// set state properties based on received server info
|
||||||
|
this.populateProperties(state, serverInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves server list from master server
|
||||||
|
* @throws {Error} Will throw error when no master list was received
|
||||||
|
* @returns a list of servers as raw data
|
||||||
|
*/
|
||||||
|
async getMasterServerList () {
|
||||||
|
const servers = await this.request({
|
||||||
|
url: 'https://serverlist-rx.totemarts.services/servers.jsp',
|
||||||
|
responseType: 'json'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (servers == null) {
|
||||||
|
throw new Error('Unable to retrieve master server list')
|
||||||
|
}
|
||||||
|
if (!Array.isArray(servers)) {
|
||||||
|
throw new Error('Invalid data received from master server. Expecting list of data')
|
||||||
|
}
|
||||||
|
if (servers.length === 0) {
|
||||||
|
throw new Error('No data received from master server.')
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Ajv response validation
|
||||||
|
// const isDataValid = ajv.validate(MasterServerResponseSchema, servers)
|
||||||
|
// if (!isDataValid) {
|
||||||
|
// throw new Error(`Received master server data is unknown/invalid: ${ajv.errorsText(ajv.errors)}`)
|
||||||
|
// }
|
||||||
|
|
||||||
|
return servers
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates raw properties into known properties
|
||||||
|
* @param {Object} state Parsed data
|
||||||
|
*/
|
||||||
|
populateProperties (state, serverInfo) {
|
||||||
|
let emptyPrefix = ''
|
||||||
|
if (serverInfo.NamePrefix) emptyPrefix = serverInfo.NamePrefix + ' '
|
||||||
|
const servername = `${emptyPrefix}${serverInfo.Name || ''}`
|
||||||
|
const numplayers = serverInfo.Players || 0
|
||||||
|
const variables = serverInfo.Variables || {}
|
||||||
|
|
||||||
|
state.name = servername
|
||||||
|
state.map = serverInfo['Current Map'] || ''
|
||||||
|
state.password = !!variables.bPassworded
|
||||||
|
|
||||||
|
state.numplayers = numplayers
|
||||||
|
state.maxplayers = variables['Player Limit'] || 0
|
||||||
|
|
||||||
|
state.raw = serverInfo
|
||||||
|
state.version = serverInfo['Game Version'] || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
21
protocols/renegadexmaster.js
Normal file
21
protocols/renegadexmaster.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import renegadex from './renegadex.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the protocol for retrieving a master list for Renegade X, an UnrealEngine3 based game
|
||||||
|
*/
|
||||||
|
export default class renegadexmaster extends renegadex {
|
||||||
|
async run (state) {
|
||||||
|
const servers = await this.getMasterServerList()
|
||||||
|
|
||||||
|
// pass processed servers as raw list
|
||||||
|
state.raw.servers = servers.map((serverInfo) => {
|
||||||
|
// TODO: may use any other deep-copy method like structuredClone() (in Node.js 17+)
|
||||||
|
// or use a method of Core to retrieve a clean state
|
||||||
|
const serverState = JSON.parse(JSON.stringify(state))
|
||||||
|
|
||||||
|
// set state properties based on received server info
|
||||||
|
this.populateProperties(serverState, serverInfo)
|
||||||
|
return serverState
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ const gamedig = new GameDig(options)
|
||||||
const protocolList = []
|
const protocolList = []
|
||||||
Object.keys(protocols).forEach((key) => protocolList.push(key))
|
Object.keys(protocols).forEach((key) => protocolList.push(key))
|
||||||
|
|
||||||
const ignoredProtocols = ['discord', 'beammpmaster', 'beammp', 'teamspeak2', 'teamspeak3', 'vintagestorymaster']
|
const ignoredProtocols = ['discord', 'beammpmaster', 'beammp', 'teamspeak2', 'teamspeak3', 'vintagestorymaster', 'renegadexmaster']
|
||||||
const protocolListFiltered = protocolList.filter((protocol) => !ignoredProtocols.includes(protocol))
|
const protocolListFiltered = protocolList.filter((protocol) => !ignoredProtocols.includes(protocol))
|
||||||
|
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue