diff --git a/protocols/renegadex.js b/protocols/renegadex.js index b2c5007e..4c71a048 100644 --- a/protocols/renegadex.js +++ b/protocols/renegadex.js @@ -1,4 +1,158 @@ 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 @@ -19,6 +173,12 @@ export default class renegadex extends Core { throw new Error('Unable to retrieve master server list') } + // 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)}`) + // } + const serverInfo = servers.find( (server) => server.IP === this.options.address && server.Port === this.options.port