@callowayisweird/source-rcon
v0.1.0
Published
Production-grade Source Engine RCON client for Node.js/Bun. Zero runtime dependencies.
Maintainers
Readme
@callowayisweird/source-rcon
Production-grade Source Engine RCON client for Node.js and Bun. Zero runtime dependencies, full TypeScript support.
Install
npm install @callowayisweird/source-rconUsage
Basic Connection
import { Rcon } from '@callowayisweird/source-rcon'
const rcon = new Rcon({
host: '127.0.0.1',
port: 27015,
password: 'your_rcon_password',
})
await rcon.connect()
const status = await rcon.execute('status')
console.log(status)
await rcon.disconnect()Events
const rcon = new Rcon({
host: '127.0.0.1',
port: 27015,
password: 'secret',
})
rcon.on('connect', () => console.log('Connected'))
rcon.on('disconnect', () => console.log('Disconnected'))
rcon.on('error', (err) => console.error('Error:', err))
await rcon.connect()Auto-Reconnection
const rcon = new Rcon({
host: '127.0.0.1',
port: 27015,
password: 'secret',
autoReconnect: true,
maxReconnectAttempts: 5,
reconnectDelay: 3000,
})
rcon.on('reconnecting', (attempt) => {
console.log(`Reconnecting... attempt ${attempt}`)
})
await rcon.connect()Multi-Server Pool
import { RconPool } from '@callowayisweird/source-rcon'
const pool = new RconPool([
{ host: '10.0.0.1', port: 27015, password: 'pass1' },
{ host: '10.0.0.2', port: 27015, password: 'pass2' },
{ host: '10.0.0.3', port: 27015, password: 'pass3' },
])
await pool.connectAll()
// Execute on a specific server
const status = await pool.execute('10.0.0.1:27015', 'status')
// Execute on all servers
const results = await pool.executeAll('status')
for (const [server, response] of results) {
console.log(`${server}: ${response}`)
}
await pool.disconnectAll()Permission-Gated RCON
import { Rcon, GatedRcon } from '@callowayisweird/source-rcon'
const rcon = new Rcon({
host: '127.0.0.1',
port: 27015,
password: 'secret',
})
await rcon.connect()
const gated = new GatedRcon(rcon, {
allowedUsers: ['STEAM_0:1:12345678', 'STEAM_0:1:87654321'],
permissions: {
'STEAM_0:1:12345678': ['say *', 'status', 'changelevel *'], // admin
'STEAM_0:1:87654321': ['say *', 'status'], // moderator
},
rateLimit: {
windowMs: 60000, // 1 minute
maxCommands: 10, // max 10 commands per minute
},
onExecute: (steamId, command, response) => {
console.log(`[RCON] ${steamId} executed: ${command}`)
},
onBlocked: (steamId, command, reason) => {
console.warn(`[RCON] Blocked ${steamId}: ${command} (${reason})`)
},
})
// Will succeed for allowed users with matching permissions
await gated.execute('STEAM_0:1:12345678', 'say Hello everyone!')
// Will throw PermissionDeniedError
await gated.execute('STEAM_0:1:87654321', 'changelevel gm_flatgrass')Error Handling
import {
Rcon,
AuthFailedError,
ConnectionRefusedError,
TimeoutError,
DisconnectedError,
} from '@callowayisweird/source-rcon'
const rcon = new Rcon({
host: '127.0.0.1',
port: 27015,
password: 'secret',
timeout: 5000,
})
try {
await rcon.connect()
} catch (err) {
if (err instanceof AuthFailedError) {
console.error('Wrong RCON password')
} else if (err instanceof ConnectionRefusedError) {
console.error('Server refused connection')
} else if (err instanceof TimeoutError) {
console.error('Connection timed out')
}
}
try {
await rcon.execute('status')
} catch (err) {
if (err instanceof DisconnectedError) {
console.error('Not connected to server')
} else if (err instanceof TimeoutError) {
console.error('Command timed out')
}
}API Reference
Rcon
| Method | Description |
|---|---|
| new Rcon(options) | Create a new RCON client |
| connect() | Connect and authenticate |
| execute(command) | Execute a command (queued, sequential) |
| disconnect() | Disconnect from the server |
| isConnected | Whether the client is authenticated |
RconOptions
| Option | Type | Default | Description |
|---|---|---|---|
| host | string | required | Server hostname or IP |
| port | number | required | Server RCON port |
| password | string | required | RCON password |
| timeout | number | 5000 | Timeout in milliseconds |
| autoReconnect | boolean | false | Auto-reconnect on disconnect |
| maxReconnectAttempts | number | 5 | Max reconnection attempts |
| reconnectDelay | number | 3000 | Delay between reconnect attempts (ms) |
Events
| Event | Arguments | Description |
|---|---|---|
| connect | none | Successfully connected and authenticated |
| disconnect | none | Disconnected from server |
| error | Error | An error occurred |
| reconnecting | number | Attempting to reconnect (attempt number) |
RconPool
| Method | Description |
|---|---|
| new RconPool(servers) | Create pool from array of server options |
| connectAll() | Connect to all servers |
| execute(key, command) | Execute on a specific server (host:port) |
| executeAll(command) | Execute on all servers |
| disconnectAll() | Disconnect all servers |
| get(key) | Get the Rcon instance for a server |
| size | Number of servers in the pool |
GatedRcon
| Method | Description |
|---|---|
| new GatedRcon(rcon, options) | Create gated wrapper around an Rcon instance |
| execute(steamId, command) | Execute if permitted, throws PermissionDeniedError if not |
Errors
| Error | Code | Description |
|---|---|---|
| RconError | varies | Base error class |
| AuthFailedError | AUTH_FAILED | Wrong RCON password |
| ConnectionError | CONNECTION_FAILED | Cannot connect to server |
| ConnectionRefusedError | CONNECTION_REFUSED | Connection actively refused |
| TimeoutError | TIMEOUT | Operation timed out |
| DisconnectedError | DISCONNECTED | Not connected to server |
| BannedError | BANNED | IP banned after failed auth attempts |
| PermissionDeniedError | PERMISSION_DENIED | GatedRcon blocked the command |
License
MIT
