4.1.9 • Published 1 year ago

@fabricio-191/valve-server-query v4.1.9

Weekly downloads
2
License
Apache License 2....
Repository
github
Last release
1 year ago

Discord

An implementation of valve protocols

const { Server, RCON, MasterServer } = require('@fabricio-191/valve-server-query');

This module allows you to:

  • Easily make queries to servers running valve games
  • Make queries to valve master servers
  • Use a remote console to control your server remotely

Some of the games where it works with are:

  • Counter-Strike
  • Counter-Strike: Global Offensive
  • Garry's Mod
  • Half-Life
  • Team Fortress 2
  • Day of Defeat
  • The ship

These are the default values

{
  ip: 'localhost', //in MasterServer is 'hl2master.steampowered.com'
  port: 27015, //in MasterServer is 27011
  
  timeout: 2000,
  debug: false,
  enableWarns: true,

  //RCON
  password: 'The RCON password', // hasn't a default value
  
  //Master server
  quantity: 200,
  region: 'OTHER',
}

Server

const server = await Server({
  ip: '0.0.0.0',
  port: 27015,
  timeout: 3000,
});

const info = await server.getInfo();
console.log(info);

const players = await server.getPlayers();
console.log(players);

const rules = await server.getRules();
console.log(rules);

const ping = server.lastPing;
console.log(ping);

/*
You can also do:

const [info, players, rules] = await Promise.all([
  server.getInfo(),
  server.getPlayers().catch(e => {}),
  server.getRules().catch(e => {})
]);

This make all queries in parallel
*/

Performs an A2S_INFO query to the server

server.getInfo()
  .then(info => {
    console.log(info);
  })
  .catch(err => {
    //...
  });

Info can be something like this:

{
  address: '192.223.30.25:27015',
  ping: 222,
  protocol: 17,
  goldSource: false,
  name: '[Raidboss] Private KZ/Climb [GOKZ |Global | VIP/Whitelist Only]',
  map: 'kz_frozen_go',
  folder: 'csgo',
  game: 'Counter-Strike: Global Offensive',
  appID: 730n,
  players: { online: 3, max: 10, bots: 0 },
  type: 'dedicated',
  OS: 'linux',
  visibility: 'public',
  VAC: true,
  version: '1.37.6.9',
  //data after this may not be present
  port: 27015,
  steamID: 85568392922144671n,
  tv: {
    port: 27020,
    name: 'RaidbossTV'
  },
  keywords: [
    'empty',     '5v5',
    'boss',    'casual',
    'climb',     'comp',
    'competitive', 'esea',
    'faceit',    'gloves',
    'knife',     'kreedz',
    'kz',      'ladder',
    'priz',    'pub',
    'pug',     'raid',
    'raidboss',  'rank',
    'ranks',     'st'
  ],
  gameID: 730n
}

Performs an A2S_PLAYER query to get the list of players in the server

Some servers may have disable this query (very rare).

server.getPlayers()
  .then(players => {
    console.log(`There are ${players.length} in the server`);

    const list = players
      .sort((a, b) => b.timeOnline - a.timeOnline)
      .map((player, index) => `${index + 1}. ${player.name} ${player.timeOnline}`)
      .join('\n');

    console.log(list);
  })
  .catch(console.error);

The time class has an personalized toString() and @@toPrimitive() methods

Example:

[
  {
    index: 0,
    name: 'kritikal',
    score: 0,
    timeOnline: Time {
      hours: 0,
      minutes: 10,
      seconds: 25,
      start: 2021-03-20T02:46:28.267Z, //Date
      raw: 625.2186279296875
    }
  },
  {
    index: 0,
    name: 'dmx;',
    score: 0,
    timeOnline: Time {
      hours: 0,
      minutes: 10,
      seconds: 25,
      start: 2021-03-20T02:46:28.267Z,
      raw: 625.0791625976562
    }
  },
  {
    index: 0,
    name: 'fenakz',
    score: 0,
    timeOnline: Time {
      hours: 0,
      minutes: 10,
      seconds: 21,
      start: 2021-03-20T02:46:28.271Z,
      raw: 621.9488525390625
    }
  },
  {
    index: 0,
    name: '[JC] Master-cba',
    score: 0,
    timeOnline: Time {
      hours: 0,
      minutes: 10,
      seconds: 15,
      start: 2021-03-20T02:46:28.278Z,
      raw: 615.4395751953125
    }
  },
  {
    index: 0,
    name: 'sAIONARAH34! [pw] ⚓✵♣',
    score: 1,
    timeOnline: Time {
      hours: 0,
      minutes: 10,
      seconds: 1,
      start: 2021-03-20T02:46:28.292Z,
      raw: 601.7681274414062
    }
  },
  {
    index: 0,
    name: 'INFRA-',
    score: 0,
    timeOnline: Time {
      hours: 0,
      minutes: 9,
      seconds: 50,
      start: 2021-03-20T02:46:28.303Z,
      raw: 590.30859375
    }
  },
  {
    index: 0,
    name: 'Agente86',
    score: 1,
    timeOnline: Time {
      hours: 0,
      minutes: 9,
      seconds: 0,
      start: 2021-03-20T02:46:28.353Z,
      raw: 540.4190063476562
    }
  }
]

If the game is The Ship every player will have 2 extra properties deaths and money

Makes an A2S_RULES query to the server

Some servers may have disable this query, so you should expect an error.

server.getRules()
  .then(console.log)
  .catch(() => {});

The response can change a lot between servers

Example:

{
  bot_quota: 0,
  coop: 0,
  cssdm_enabled: 0,
  cssdm_ffa_enabled: 0,
  cssdm_version: '2.1.6-dev',
  deathmatch: 1,
  decalfrequency: 10,
  gungame_enabled: 1,
  metamod_version: '1.10.7-devV',
  mp_allowNPCs: 1,
  mp_autocrosshair: 1,
  mp_autoteambalance: 1,
  mp_c4timer: 35,
  mp_disable_respawn_times: 0,
  mp_fadetoblack: 0,
  mp_falldamage: 0,
  mp_flashlight: 1,
  mp_footsteps: 1,
  mp_forceautoteam: 0,
  mp_forcerespawn: 1,
  mp_fraglimit: 0,
  mp_freezetime: 1,
  mp_friendlyfire: 0,
  mp_holiday_nogifts: 0,
  mp_hostagepenalty: 13,
  mp_limitteams: 2,
  mp_match_end_at_timelimit: 0,
  mp_maxrounds: 0,
  mp_respawnwavetime: 10,
  mp_roundtime: 9,
  mp_scrambleteams_auto: 1,
  mp_scrambleteams_auto_windifference: 2,
  mp_stalemate_enable: 0,
  mp_stalemate_meleeonly: 0,
  mp_startmoney: 800,
  mp_teamlist: 'hgrunt;scientist',
  mp_teamplay: 0,
  mp_timelimit: 18,
  mp_tournament: 0,
  mp_weaponstay: 0,
  mp_winlimit: 0,
  nextlevel: '',
  r_AirboatViewDampenDamp: 1,
  r_AirboatViewDampenFreq: 7,
  r_AirboatViewZHeight: 0,
  r_JeepViewDampenDamp: 1,
  r_JeepViewDampenFreq: 7,
  r_JeepViewZHeight: 10,
  r_VehicleViewDampen: 1,
  scc_version: '2.0.0',
  sm_advertisements_version: 0.6,
  sm_allchat_version: '1.1.1',
  sm_cannounce_version: 1.8,
  sm_ggdm_version: '1.8.0',
  sm_gungamesm_version: '1.2.16.0',
  sm_nextmap: 'gg_toon_poolday',
  sm_noblock: 1,
  sm_playersvotes_version: '1.5.0',
  sm_quakesounds_version: 1.8,
  sm_resetscore_version: '2.6.0',
  sm_show_damage_version: '1.0.7',
  sm_vbping_version: 1.4,
  sourcemod_version: '1.10.0.6482',
  sv_accelerate: 5,
  sv_airaccelerate: 10,
  sv_allowminmodels: 1,
  sv_alltalk: 1,
  sv_bounce: 0,
  sv_cheats: 0,
  sv_competitive_minspec: 0,
  sv_contact: 'linkinaz0@vtr.net',
  sv_enableboost: 0,
  sv_enablebunnyhopping: 0,
  sv_footsteps: 1,
  sv_friction: 4,
  sv_gravity: 800,
  sv_maxspeed: 320,
  sv_maxusrcmdprocessticks: 24,
  sv_noclipaccelerate: 5,
  sv_noclipspeed: 5,
  sv_nostats: 0,
  sv_password: 0,
  sv_pausable: 0,
  sv_rollangle: 0,
  sv_rollspeed: 200,
  sv_specaccelerate: 5,
  sv_specnoclip: 1,
  sv_specspeed: 3,
  sv_steamgroup: '',
  sv_stepsize: 18,
  sv_stopspeed: 75,
  sv_tags: 'alltalk',
  sv_voiceenable: 1,
  sv_vote_quorum_ratio: 0.6,
  sv_wateraccelerate: 10,
  sv_waterfriction: 1,
  tf_arena_max_streak: 3,
  tf_arena_preround_time: 10,
  tf_arena_round_time: 0,
  tf_arena_use_queue: 1,
  tv_enable: 0,
  tv_password: 0,
  tv_relaypassword: 0
  }

Performs an A2A_PING query into the server

This is a deprecated feature of source servers, may not work. The server.lastPing property contains the server ping, so this is not necessary

A warn in console will be shown (you can disable it by using { enableWarns: false }, see Options)

It will return -1 if the server did not respond to the query

server.ping()
  .then(ping => {
    console.log(ping); // 214
  })
  .catch(console.error)

Disconnect the server and destroy the socket

server.disconnect();

Use example

Server(...)
  .then(async server => {
    const players = await server.getPlayers();

    server.disconnect();

    //...
  })
  .catch(console.error);

Returns a promise that is resolved in an object with the server information, example:

const { Server } = require('@fabricio-191/valve-server-query');

Server.getInfo({
  ip: '0.0.0.0',
  port: 27015,
})
  .then(console.log)
  .catch(console.error);

MasterServer

Warns:

  • Gold source master servers may not work. The reason is unknown, the servers simply do not respond to queries
  • If the quantity is Infinity or 'all', it will try to get all the servers, but most likely it will end up giving an warning (it will as many servers as possible). The master server stops responding at a certain point.
MasterServer({
  quantity: 1000, // or Infinity or 'all'
  region: 'US_EAST',
  timeout: 3000,
})
  .then(servers => {
    //do something...

    //servers is an array if 'ip:port' strings, see below
  })
  .catch(console.error);

Valid regions are

  • US_EAST
  • US_WEST
  • SOUTH_AMERICA
  • EUROPE
  • ASIA
  • AUSTRALIA
  • MIDDLE_EAST
  • AFRICA
  • OTHER
[
  '190.195.150.143:27015', '143.255.142.150:27015', '177.54.144.122:27523',
  '189.1.173.26:27015',  '177.144.128.13:27015',  '177.66.222.92:27015',
  '196.28.69.113:27065',   '144.48.37.119:27015',   '139.180.174.191:27051',
  '108.61.168.31:27050',   '108.61.168.31:27015',   '108.61.168.31:27051',
  '139.180.174.191:27052', '139.180.174.191:27053', '139.99.173.74:27015',
  '45.121.210.91:27550',   '139.99.131.105:27015',  '139.99.144.39:27015',
  '34.87.217.246:27015',   '108.61.227.50:27025',   '108.61.227.12:27035',
  '220.240.1.134:27023',   '221.121.159.236:35240', '221.121.159.236:35260',
  '221.121.159.236:35250', '221.121.149.12:32860',  '121.74.206.225:27015',
  '41.190.141.250:27015',  '111.221.44.137:27018',  '111.221.44.137:27024',
  '111.221.44.137:27023',  '49.245.116.134:27017',  '49.245.116.134:27025',
  '49.245.116.134:27027',  '49.245.116.134:27015',  '49.245.116.134:27016',
  '49.245.116.134:27026',  '223.25.71.43:27015',  '49.245.116.134:27115',
  '49.245.116.134:27118',  '49.245.116.134:27117',  '49.245.116.134:27116',
  '13.229.55.66:24000',  '49.245.116.134:27215',  '103.9.159.78:27065',
  '75.85.184.227:27031',   '75.85.184.227:27034',   '168.235.81.229:27015',
  '66.55.74.111:27015',  '66.55.74.100:27015',  '66.55.74.82:27015',  
  '66.55.74.38:27015',   '66.55.74.103:27015',  '66.55.68.38:27015',  
  '66.55.74.65:27015',   '66.55.74.105:27015',  '66.55.74.104:27015',
  '66.55.68.36:27015',   '66.55.70.53:27015',   '64.111.99.165:27020',
  '66.55.70.177:27115',  '66.55.70.177:27315',  '104.153.109.22:27015',
  '104.153.109.26:27015',  '47.153.235.28:27015',   '173.199.84.186:27015',
  '64.190.203.117:27015',  '103.214.108.12:27105',  '173.199.87.235:27025',
  '8.3.6.148:27015',     '66.75.2.253:27015',   '172.107.198.173:27075',
  '172.107.2.177:27035',   '198.12.71.30:27015',  '206.251.72.62:27017',
  '104.207.148.159:27045', '92.38.148.25:27015',  '159.89.142.219:27016',
  '159.89.142.219:27015',  '159.89.142.219:27017',  '74.91.118.231:27015',
  '108.61.124.77:27065',   '192.53.126.95:27015',   '108.61.124.78:27980',
  '108.61.235.138:27015',  '108.61.124.72:27045',   '104.206.244.2:19001',
  '198.24.171.83:27185',   '131.153.29.243:27035',  '173.27.92.73:27016',
  '162.248.90.33:27015',   '162.248.90.38:27015',   '162.248.90.19:27015',
  '66.58.130.164:27015',   '71.193.199.206:27015',  '71.193.199.206:27017',
  '54.202.134.208:27015',  '64.42.176.58:27015',  '104.192.227.146:17741',
  '74.201.72.18:27015',
  ...1051 more items
]
const filter = new MasterServer.Filter()
  .addFlag('dedicated')
  .add('map', 'cs_italy')
  .addNOR(
    new MasterServer.Filter()
      .addFlag('secure')
  );

MasterServer(
  quantity: 1000,
  region: 'EUROPE',
  timeout: 3000,
  filter,
})
  .then(console.log)
  .catch(console.error)

// Will return a list of ips that are dedicated servers, in the cs_italy map and that do not have an anti-cheat enabled

add(key, value) adds a condition to the filter. addFlag(flag) adds a flag to the filter. addFlags([flag, flag, ...]) adds multiple flags to the filter. addNOR(filter) a special condition, specifies that servers matching any of the filter conditions should not be returned. addNAND(filter) a special condition, specifies that servers matching all of the filter conditions should not be returned.

See https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol#Filter

ParameterValuesDescription
name_matchstringServers with their hostname matching that hostname (can use * as a wildcard)
version_matchstringServers running version that version (can use * as a wildcard)
gameaddrstringReturn only servers on the specified IP address (port supported and optional)
gamedirstringServers running the specified modification (ex. cstrike)
mapstringServers running the specified map (ex. cs_italy)
appidnumberServers that are running game with that appid
nappnumberServers that are NOT running game with that appid
gametypearrayServers with all of the given tag(s) in sv_tags
gamedataarrayServers with all of the given tag(s) in their 'hidden' tags (only in L4D2)
gamedataorarrayServers with any of the given tag(s) in their 'hidden' tags (only in L4D2)

Notes:

  • all array's are of strings.
  • flags are not booleans. if you want to get the inverse of any of these flags, you should use addNOR or addNAND.

Flags

NameDescription
dedicatedServers that are dedicated servers
secureServers using anti-cheat technology (VAC, but potentially others as well)
linuxServers running on a Linux platform
passwordServers that are not password protected
noplayersServers that are empty
emptyServers that are not empty
fullServers that are not full
proxyServers that are spectator proxies
whiteServers that are whitelisted
collapse_addr_hashReturn only one server for each unique IP address matched
MasterServer.getIPS()
  .then(console.log)
  .catch(console.error)

/*
Returns an object with the master servers ips, like this: 
{
  goldSource: [ '208.64.200.118', '208.64.200.117' ],  
  source: [ '208.64.200.65', '208.64.200.39', '208.64.200.52' ]
}
*/

See https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol#Master_servers

The port used in hl2master.steampowered.com (source) ip's is 27011 but one of them is using a different port: 27015
The port numbers used by hl1master.steampowered.com (goldSource) can be anything between 27010 and 27013.

RCON

Some commands will cause the server to disconnect, for example sv_gravity 0 in some cases or rcon_password newPassword as it changes de password and needs to authenticate again.

Commands list

const rcon = await RCON({
  ip: '0.0.0.0',
  port: 27015, //RCON port
  password: 'your RCON password'
});

rcon.on('disconnect', async (reason) => {
  console.log('disconnected', reason);
  try{
    await rcon.reconnect();
  }catch(e){
    console.log('reconnect failed', e.message);
  }
});

rcon.on('passwordChanged', async () => {
  const password = await getNewPasswordSomehow();
  try{
    await rcon.authenticate(password);
  }catch(e){
    console.error('Failed to authenticate with new password', e.message);
  }
});

const response = await rcon.exec('sv_gravity 1000');
// Response is always a string that is some kind of log of the server or it can be empty

This will work well with server.getRules()

// gravity will change randomly every 5 seconds

setInterval(() => {
  const value = Math.floor(Math.random() * 10000) - 3000;
  //value will be a number between -3000 and 6999

  rcon.exec(`sv_gravity ${value}`)
    .catch(console.error);

}, 5000);

Destroys the RCON connection, this will not fire the disconnect event

rcon.destroy();
4.1.9

1 year ago

4.1.8

1 year ago

4.1.7

1 year ago

4.1.4

2 years ago

4.1.3

2 years ago

4.1.6

1 year ago

4.1.5

1 year ago

4.1.2

2 years ago

4.1.0

2 years ago

4.0.1

2 years ago

4.0.0

2 years ago

4.1.1

2 years ago

3.0.0-6

3 years ago

3.0.0-3

3 years ago

3.0.0-5

3 years ago

3.0.0-4

3 years ago

3.0.0-1

3 years ago

3.0.0-0

3 years ago

3.0.0-2

3 years ago

2.4.0-3

3 years ago

2.4.0-2

3 years ago

2.4.0-1

3 years ago

2.4.0-0

3 years ago

2.0.1-0

4 years ago

2.0.0-2

4 years ago

2.0.0-1

4 years ago

2.0.0-0

4 years ago

1.6.0-5

4 years ago

1.6.0-3

4 years ago

1.6.0-2

4 years ago

1.6.0-1

4 years ago

1.6.0-0

4 years ago

1.5.3-5

4 years ago

1.5.3-201

4 years ago

1.5.3-200

4 years ago

1.5.3-400

4 years ago

1.5.3-0

4 years ago

1.5.2-0

4 years ago

1.5.100

4 years ago

1.5.0

4 years ago

1.3.2

4 years ago

1.3.1

4 years ago

1.3.0

4 years ago

1.2.0

4 years ago

1.1.1

4 years ago

1.1.0

4 years ago

1.2.1

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago