-
Notifications
You must be signed in to change notification settings - Fork 154
feat: add Renegade X support #643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
b18c535
Add support for Renegade X, querying master server
RattleSN4K3 200ce16
Fix game id being compliant to naming rule
RattleSN4K3 4cd87fb
docs: update CHANGELOG and GAMES_LIST for Renegade X
RattleSN4K3 cfa743b
Pass state.password as raw + move to separate method for subclassing
RattleSN4K3 9478204
Define json response via schema, optional data validation with Ajv (c…
RattleSN4K3 83f0099
Remove virtualized player/bot list in results
RattleSN4K3 9939ee6
Provide full server response as raw data
RattleSN4K3 14e8f3e
Add support for Renegade X master query through separate protocol
RattleSN4K3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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'] || '' | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
}) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.