quake3 server connector | quake3 server status | | Search

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.

Run example

npm run import -- "spectate q3 server"

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

What the code could have been:

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

Importing Modules

The code starts by importing various modules using the importer object. The imported modules are:

The imported functions are:

Defining the spectateServer Function

The 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.

Authorization

The function first calls the authorizeGateway function to authorize the Discord gateway. This is likely done to establish a connection with the Discord API.

Generating a Random Challenge

A random challenge is generated using a Uint32Array and assigned to the challenge variable.

Getting Server Info

The function calls getInfo and waits for the nextInfoResponse to retrieve information about the server.

Connecting to 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.

Retrieving Game State and Team Information

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.

Sending Team Information

The function calls sendReliable to send a "team s" message to the server.

Establishing a Chat Listener

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.

Notes