RCONServer

Inherits: RefCounted < Object

RCON server for accepting remote management connections to game servers.

Description

RCONServer provides a complete server implementation for accepting RCON connections using either Source RCON (TCP) or BattlEye RCON (UDP) protocols. It handles multiple simultaneous clients, authentication, command registration with callbacks, and protocol-specific features.

Availability: This class is available in:

  • Editor builds (all platforms, for testing/development)

  • Exported games with dedicated_server custom feature tag (Windows and Linux only)

Check with ClassDB.class_exists("RCONServer") before use in templates.

The server runs network operations on a background thread and provides thread-safe access through the poll() method, which must be called regularly (typically in _process) to dispatch events and callbacks.

When you register a command using register_command(), a dynamic signal is automatically created with the name command_<command_name> that emits alongside the callback.

var server = RCONServer.new()

func _ready():
    server.client_authenticated.connect(_on_client_auth)
    server.register_command("status", _handle_status)
    server.register_command("kick", _handle_kick)

    # Dynamic signals are created automatically
    server.command_status.connect(_on_status_cmd)

    server.start_server(27015, "admin123", RCONServer.PROTOCOL_SOURCE)

func _process(_delta):
    server.poll()  # Dispatch events

func _handle_status(client_id: int, args: String, request_id: int):
    server.send_response(client_id, request_id, "Server: Online")

func _on_status_cmd(client_id: int, args: String, request_id: int):
    print("Status command from client %d" % client_id)

Tutorials

Methods

Array

get_connected_clients() const

Protocol

get_protocol() const

State

get_state() const

bool

is_running() const

void

poll()

void

register_command(command: String, callback: Callable)

void

send_raw_packet(client_id: int, packet: PackedByteArray)

void

send_response(client_id: int, request_id: int, response: String)

Error

start_server(port: int, password: String, protocol: Protocol)

void

stop_server()

void

unregister_command(command: String)


Signals

authentication_failed(client_id: int, address: String) 🔗

Emitted when a client fails authentication (incorrect password). The address parameter contains the client's IP address.


client_authenticated(client_id: int) 🔗

Emitted when a client successfully authenticates with the server.


client_connected(client_id: int, address: String) 🔗

Emitted when a new client connects to the server. Authentication may still be in progress.


client_disconnected(client_id: int) 🔗

Emitted when a client disconnects from the server.


client_timeout_warning(client_id: int, seconds_remaining: int) 🔗

Emitted when a BattlEye RCON client is approaching timeout (at 35 seconds, 10 seconds before the 45-second timeout). The seconds_remaining parameter indicates how many seconds until timeout.


command_received(client_id: int, command: String, request_id: int) 🔗

Emitted when a command is received from any client. This is a catch-all signal that fires for all commands, including those with registered handlers.


keep_alive_sent(client_id: int) 🔗

Emitted when a keep-alive packet is received from a BattlEye RCON client (empty command packet).


keep_alive_timeout(client_id: int) 🔗

Emitted when a BattlEye RCON client times out (no packets received for 45 seconds). The client is automatically disconnected.


packet_send_failed(client_id: int, error: String) 🔗

Emitted when sending a packet to a client fails. The error parameter contains a description of the failure.


packet_sent(client_id: int, packet: PackedByteArray) 🔗

Emitted when a packet is successfully sent to a client. Useful for low-level monitoring and debugging.


raw_packet_received(client_id: int, packet: PackedByteArray) 🔗

Emitted when a raw packet is received from a client. Useful for low-level protocol debugging or custom packet handling.


server_error(error: String) 🔗

Emitted when a server error occurs. The error parameter contains a description of the error.


server_started() 🔗

Emitted when the server successfully starts and begins listening for connections.


server_stopped() 🔗

Emitted when the server stops.


Enumerations

enum Protocol: 🔗

Protocol PROTOCOL_SOURCE = 0

Use Source RCON protocol (TCP-based). Default port is 27015.

Protocol PROTOCOL_BATTLEYE = 1

Use BattlEye RCON protocol (UDP-based). Default ports are 2302-2305.


enum State: 🔗

State STATE_STOPPED = 0

Server is not running.

State STATE_STARTING = 1

Server is starting up.

State STATE_LISTENING = 2

Server is listening for client connections.

State STATE_ERROR = 3

An error occurred during server operation.


Method Descriptions

Array get_connected_clients() const 🔗

Returns an Array of client IDs for all currently connected clients.

var clients = server.get_connected_clients()
print("Connected clients: ", clients.size())

Protocol get_protocol() const 🔗

Returns the protocol being used by this server (PROTOCOL_SOURCE or PROTOCOL_BATTLEYE).


State get_state() const 🔗

Returns the current server state. Possible values are:


bool is_running() const 🔗

Returns true if the server is currently running and listening for connections.


void poll() 🔗

Processes pending events from the network thread and dispatches signals and callbacks. This method must be called regularly (typically in _process) for the server to function properly.

func _process(_delta):
    server.poll()

void register_command(command: String, callback: Callable) 🔗

Registers a command handler. When a client sends this command, the callback will be invoked with the signature func(client_id: int, args: String, request_id: int).

Additionally, a dynamic signal named command_<command_name> is automatically created and emitted alongside the callback.

# Register command
server.register_command("kick", _handle_kick)

# Connect to dynamic signal
server.command_kick.connect(_on_kick_command)

func _handle_kick(client_id: int, args: String, request_id: int):
    var player_name = args.strip_edges()
    # Kick logic...
    server.send_response(client_id, request_id, "Kicked: " + player_name)

void send_raw_packet(client_id: int, packet: PackedByteArray) 🔗

Sends a raw packet to a specific client. Use RCONPacket methods to construct packets manually for low-level protocol control.

var packet = RCONPacket.create_source_command(1, "Custom response")
server.send_raw_packet(client_id, packet)

void send_response(client_id: int, request_id: int, response: String) 🔗

Sends a response to a command from a specific client. The request_id should match the request_id provided in the command callback.

func _handle_status(client_id: int, args: String, request_id: int):
    var status = "Server: Online, Players: 5/10"
    server.send_response(client_id, request_id, status)

Error start_server(port: int, password: String, protocol: Protocol) 🔗

Starts the RCON server on the specified port with the given password and protocol.

Returns @GlobalScope.OK if the server started successfully, or an error code otherwise.

Security Warning: RCON passwords are transmitted in plain text. Use strong passwords and consider VPN/SSH tunneling for remote access.

var err = server.start_server(27015, "secure_password", RCONServer.PROTOCOL_SOURCE)
if err != OK:
    print("Failed to start server: ", err)

void stop_server() 🔗

Stops the RCON server and disconnects all clients. Emits the server_stopped signal.

server.stop_server()

void unregister_command(command: String) 🔗

Unregisters a previously registered command handler. The command will no longer trigger callbacks or signals.

server.unregister_command("status")