The spectateServer
function is an asynchronous function that establishes a connection with a Quake 3 server, retrieves game state and team information, and sets up a chat listener to forward messages to Discord. The function uses various imported modules to interact with the Quake 3 server, Discord API, and Discord gateway.
npm run import -- "spectate q3 server"
var importer = require('../Core')
var {
getInfo, nextInfoResponse,
getChallenge, nextChallengeResponse,
udpClient, sendConnect, nextConnectResponse,
nextChannelMessage, nextGamestate,
sendPureChecksums, nextSnapshot,
sendReliable, nextChat
} = importer.import("quake 3 server commands")
var discordApi = importer.import("discord api")
var {authorizeGateway} = importer.import("authorize discord")
var removeCtrlChars = importer.import("remove ctrl characters")
async function spectateServer(address = 'localhost', port = 27960) {
// TODO: comment this line out when launched from index? monitor script
await authorizeGateway()
var challenge = new ArrayBuffer(4)
for(var c = 0; c < 4; c++) {
challenge[c] = Math.round(Math.random() * 255)
}
await getInfo(address, port)
var info = await nextInfoResponse(address, port)
if(!info)
return
await getChallenge(address, port, new Uint32Array(challenge)[0], info.gamename || info.game)
var challenge = (await nextChallengeResponse(address, port)).challenge
await sendConnect(address, port, {
qport: udpClient.address().port,
challenge: challenge,
name: 'Orbb-Bot',
protocol: 71,
})
challenge = await nextConnectResponse(address, port)
var gamestate = await nextGamestate(address, port)
console.log('gamestate', server.sv_hostname || server.hostname)
if(!gamestate.channel)
return
if(gamestate.isPure) {
// TODO: send valid "cp" checksums to pure servers
await sendPureChecksums(address, port, gamestate)
}
await nextSnapshot(address, port)
await sendReliable(address, port, 'team s')
// await print commands
info.chatListener = setInterval(async () => {
if(!info.chatWaiting) {
info.chatWaiting = true
var message = await nextChat(address, port)
info.chatWaiting = false
// forward print commands to discord
if(message) {
message = removeCtrlChars((/"([^"]*?)"/).exec(message)[1])
discordApi.createMessage(message, info.channelId)
}
}
}, 100)
}
module.exports = spectateServer
const importer = require('../Core');
const {
getInfo,
nextInfoResponse,
getChallenge,
nextChallengeResponse,
udpClient,
sendConnect,
nextConnectResponse,
nextChannelMessage,
nextGamestate,
sendPureChecksums,
nextSnapshot,
sendReliable,
nextChat,
} = importer.import('quake 3 server commands');
const discordApi = importer.import('discord api');
const { authorizeGateway } = importer.import('authorize discord');
const removeCtrlChars = importer.import('remove ctrl characters');
/**
* Spectates a Quake 3 server and prints chat messages to Discord.
* @param {string} address - The address of the server (default: 'localhost').
* @param {number} port - The port number of the server (default: 27960).
* @returns {Promise<void>}
*/
async function spectateServer(address = 'localhost', port = 27960) {
// Authorize Discord gateway only when not running from index.js
if (process.env.NODE_ENV!== 'development') {
await authorizeGateway();
}
// Generate a random challenge
const challenge = new Uint32Array(4);
for (let i = 0; i < 4; i++) {
challenge[i] = Math.floor(Math.random() * 256);
}
// Get Quake 3 server info
const info = await getInfo(address, port);
if (!info) {
return;
}
// Get challenge response from the server
await getChallenge(address, port, challenge[0], info.gamename || info.game);
const challengeResponse = await nextChallengeResponse(address, port);
const serverChallenge = challengeResponse.challenge;
// Send connect packet to the server
await sendConnect(address, port, {
qport: udpClient.address().port,
challenge: serverChallenge,
name: 'Orbb-Bot',
protocol: 71,
});
// Get connect response from the server
const connectResponse = await nextConnectResponse(address, port);
if (!connectResponse) {
return;
}
// Get Quake 3 game state
const gamestate = await nextGamestate(address, port);
if (!gamestate.channel) {
return;
}
// Send pure checksums to the server if it's a pure server
if (gamestate.isPure) {
await sendPureChecksums(address, port, gamestate);
}
// Get snapshot from the server
await nextSnapshot(address, port);
// Send reliable message to the server
await sendReliable(address, port, 'team s');
// Set up interval to print chat messages to Discord
const printInterval = setInterval(async () => {
const message = await nextChat(address, port);
if (message) {
const cleanedMessage = removeCtrlChars((/"([^"]*?)"/).exec(message)[1]);
await discordApi.createMessage(cleanedMessage, info.channelId);
}
}, 100);
return printInterval;
}
module.exports = spectateServer;
Code Breakdown
The code starts by importing various modules using the importer
object. The imported modules are:
quake 3 server commands
: This module exports several functions related to interacting with a Quake 3 server.discord api
: This module exports functions related to interacting with the Discord API.authorize discord
: This module exports a function to authorize the Discord gateway.remove ctrl characters
: This module is not used in the provided code snippet.The imported functions are:
getInfo
, nextInfoResponse
, getChallenge
, nextChallengeResponse
, etc.spectateServer
FunctionThe spectateServer
function is defined as an asynchronous function that takes two optional arguments: address
and port
. The function is used to spectate a Quake 3 server.
The function first calls the authorizeGateway
function to authorize the Discord gateway. This is likely done to establish a connection with the Discord API.
A random challenge is generated using a Uint32Array
and assigned to the challenge
variable.
The function calls getInfo
and waits for the nextInfoResponse
to retrieve information about the server.
The function calls getChallenge
with the generated random challenge and waits for the nextChallengeResponse
to obtain a challenge response from the server. It then calls sendConnect
with the challenge response and waits for the nextConnectResponse
.
The function calls nextGamestate
to retrieve the game state and logs the server hostname. If the game state has a channel, it calls sendPureChecksums
to send valid "cp" checksums to the server.
The function calls sendReliable
to send a "team s" message to the server.
The function sets up an interval to listen for chat messages using the nextChat
function. When a message is received, it is forwarded to Discord using the info
object.
removeCtrlChars
module is imported but not used in the provided code snippet.server
object is used to log the server hostname, but its definition is not shown in the provided code snippet.