Repository files navigation

tcprcon

A fully native RCON client implementation, zero deps.

Remote Console (RCON) is a protocol that allows remote administration of game servers. Essentially, it's an agreement on how a client can send commands to a game server and receive responses over a standard TCP connection. This enables developers and administrators to manage server settings, execute commands, and monitor game events without direct access to the server console.

This library provides a client-side implementation of the RCON protocol, based on the Source RCON Protocol.

Installation

To install tcprcon, use go get:

go get github.com/UltimateForm/tcprcon

Using as a Library

The RCON client can be used as a library in your own Go projects:

import (
"github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, err:=rcon.New("192.168.1.100:7778")
iferr!=nil {
panic(err)
}
deferclient.Close()
// Authenticatesuccess, err:=common_rcon.Authenticate(client, "your_password")
iferr!=nil||!success {
panic("auth failed")
}
// Send commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("playerlist"))
client.Write(execPacket.Serialize())
// Read responseresponse, err:=packet.Read(client)
iferr!=nil {
panic(err)
}
fmt.Println(response.BodyStr())
}

Streaming Responses

For continuous listening (e.g., server broadcasts or multiple responses), use CreateResponseChannel:

usually you will want a more ellegant way of handling the concurrent nature of this, this example is just for illustration

import (
"context""fmt""io""github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, _:=rcon.New("192.168.1.100:7778")
deferclient.Close()
common_rcon.Authenticate(client, "your_password")
ctx, cancel:=context.WithCancel(context.Background())
defercancel()
// Create a channel that streams incoming packetspacketChan:=packet.CreateResponseChannel(client, ctx)
// Send a commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("listen event"))
client.Write(execPacket.Serialize())
// Listen for responsesforpkt:=rangepacketChan {
ifpkt.Error!=nil {
ifpkt.Error==io.EOF {
fmt.Println("Connection closed")
break
}
continue// Timeout or other non-fatal error
}
fmt.Printf("Received: %s\n", pkt.BodyStr())
}
}

Examples

The /examples directory contains production-ready patterns for common use cases:

Controlled Client

ControlledClient wraps the base Client with mutex protection and a simplified Execute() method for synchronous command execution. Use this when you need a single-connection client in a concurrent context.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationclient, _:=examples.NewControlledClient("192.168.1.100:7778")
deferclient.Close()
client.Authenticate("password")
response, _:=client.Execute("status")
fmt.Println(response)

Connection Pool

ConnectionPool manages a pool of reusable connections, automatically creating and discarding clients as needed. Use this for high-concurrency scenarios where multiple commands run in parallel.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationpool:=examples.NewConnectionPool("192.168.1.100:7778", "password", 5, time.Minute)
deferpool.Close()
// Option 1: Use WithClient for automatic release/discarderr:=pool.WithClient(context.Background(), func(client*examples.ControlledClient) error {
response, err:=client.Execute("playerlist")
fmt.Println(response)
returnerr
})
// Option 2: Manually manage client lifecycleclient, err:=pool.Get(context.Background())
iferr!=nil {
panic(err)
}
response, err:=client.Execute("status")
iferr!=nil {
pool.Discard(client) // Mark as bad and remove from pool
} else {
pool.Release(client) // Return to idle pool
}
fmt.Println(response)

Event Listener

EventListener demonstrates streaming server events using CreateResponseChannel, with automatic reconnection and keepalive. Use this to listen for asynchronous server broadcasts (player logins, chat, killfeed, etc.).

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationlistener, _:=examples.NewEventListener("192.168.1.100:7778", "password")
deferlistener.Close()
ctx:=context.Background()
listener.Run(ctx)
forevent:=rangelistener.Events {
fmt.Printf("Event: %s\n", event)
}

Real-World Application

For a complete, production-ready application using these patterns, see mh-gobot — a game server bot that demonstrates:

  • Connection pooling for concurrent command execution
  • Event streaming with custom parsing
  • Integration with external services
  • Error handling and reconnection strategies

tcprcon-cli

https://github.com/UltimateForm/tcprcon-cli

Caveats

Handling Server Broadcasts

Servers can (and will) often broadcast events over the TCP connection in an asynchronous manner. These are typically game events like killfeed messages, player logins, chat, etc. Some servers operate on an opt-in basis, requiring the RCON client to signal its interest in receiving these broadcasts, while others broadcast them by default.

What this means in practice:

Let's say you send a command packet (e.g., "status" with ID 54) and then immediately try to read its response. It's possible you might first receive a broadcast packet with a body like "Login: player B just joined game" instead of your expected status response. This highlights the importance of checking the ID field of incoming packets.

Generally, the best practice is to decouple your command writes from your response reads. The example under Using as a Library demonstrates a synchronous request-response pattern for a playerlist command, which can be unoptimal in such scenarios. For a more robust approach, you should handle your writes (commands) and reads (responses and broadcasts) in parallel, as shown in the Streaming Responses section.

Server Protocol Compliance

Ideally, all RCON servers would consistently follow the Valve protocol defined at https://developer.valvesoftware.com/wiki/Source_RCON_Protocol, eliminating surprises. However, in reality, some server implementations—such as that of Rust—exhibit unorthodox behavior.

The Rust game server commits the following notable violations of the RCON protocol:

  • Initial Logging Packet (ID 0, Type 4): After a client sends a SERVERDATA_EXECCOMMAND (e.g., info), the server typically responds with an immediate SERVERDATA_RESPONSE_VALUE packet that has an ID of 0 and often a Type of 4 (which is not a standard RCON packet type). The Body of this packet usually contains a server-side log message echoing the received command (e.g., [RCON][<client_ip>:<client_port>] <command>). The ID 0 is non-compliant, as the server should echo the client's original ID.
  • Repeated Command Output: The actual command output (e.g., hostname: LinuxGSM...) is often sent twice: once with the correct echoed client ID, and again with an ID of 0. This is redundant and non-compliant.
  • Misuse of ID -1: The server uses ID -1 (0xFF FF FF FF) as a general "end of response stream" or broadcast indicator following command output. According to the Source RCON Protocol, ID -1 is specifically reserved to indicate an authentication failure within a SERVERDATA_AUTH_RESPONSE packet. Its use in the context of command responses is a significant deviation.

These are the most prominent violations; other quirks might exist with greater room for nuanced interpretation, which are not listed here.

The concluding point is that you should anticipate such cases. In general, this library will function—even with servers like Rust—because it provides the fundamental tools for writing and reading data according to Valve's protocol over a TCP socket. However, depending on these aforementioned server-specific behaviors, you might need to adapt how and when you send commands and process responses in your application.

Specifically for Rust servers, you might implement simple checks to filter out extraneous packets. For example, you could ignore all SERVERDATA_RESPONSE_VALUE packets with ID -1 (after successful authentication) or ID 0, or filter out any packet with a Type value greater than 3 (as types 0-3 cover standard RCON messages). This allows your application to focus on the actual command responses while gracefully discarding server-initiated noise.

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE for details.

About

my very own rcon tcp implementation, aint much, but it's honest work

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

tcprcon

A fully native RCON client implementation, zero deps.

Remote Console (RCON) is a protocol that allows remote administration of game servers. Essentially, it's an agreement on how a client can send commands to a game server and receive responses over a standard TCP connection. This enables developers and administrators to manage server settings, execute commands, and monitor game events without direct access to the server console.

This library provides a client-side implementation of the RCON protocol, based on the Source RCON Protocol.

Installation

To install tcprcon, use go get:

go get github.com/UltimateForm/tcprcon

Using as a Library

The RCON client can be used as a library in your own Go projects:

import (
"github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, err:=rcon.New("192.168.1.100:7778")
iferr!=nil {
panic(err)
}
deferclient.Close()
// Authenticatesuccess, err:=common_rcon.Authenticate(client, "your_password")
iferr!=nil||!success {
panic("auth failed")
}
// Send commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("playerlist"))
client.Write(execPacket.Serialize())
// Read responseresponse, err:=packet.Read(client)
iferr!=nil {
panic(err)
}
fmt.Println(response.BodyStr())
}

Streaming Responses

For continuous listening (e.g., server broadcasts or multiple responses), use CreateResponseChannel:

usually you will want a more ellegant way of handling the concurrent nature of this, this example is just for illustration

import (
"context""fmt""io""github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, _:=rcon.New("192.168.1.100:7778")
deferclient.Close()
common_rcon.Authenticate(client, "your_password")
ctx, cancel:=context.WithCancel(context.Background())
defercancel()
// Create a channel that streams incoming packetspacketChan:=packet.CreateResponseChannel(client, ctx)
// Send a commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("listen event"))
client.Write(execPacket.Serialize())
// Listen for responsesforpkt:=rangepacketChan {
ifpkt.Error!=nil {
ifpkt.Error==io.EOF {
fmt.Println("Connection closed")
break
}
continue// Timeout or other non-fatal error
}
fmt.Printf("Received: %s\n", pkt.BodyStr())
}
}

Examples

The /examples directory contains production-ready patterns for common use cases:

Controlled Client

ControlledClient wraps the base Client with mutex protection and a simplified Execute() method for synchronous command execution. Use this when you need a single-connection client in a concurrent context.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationclient, _:=examples.NewControlledClient("192.168.1.100:7778")
deferclient.Close()
client.Authenticate("password")
response, _:=client.Execute("status")
fmt.Println(response)

Connection Pool

ConnectionPool manages a pool of reusable connections, automatically creating and discarding clients as needed. Use this for high-concurrency scenarios where multiple commands run in parallel.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationpool:=examples.NewConnectionPool("192.168.1.100:7778", "password", 5, time.Minute)
deferpool.Close()
// Option 1: Use WithClient for automatic release/discarderr:=pool.WithClient(context.Background(), func(client*examples.ControlledClient) error {
response, err:=client.Execute("playerlist")
fmt.Println(response)
returnerr
})
// Option 2: Manually manage client lifecycleclient, err:=pool.Get(context.Background())
iferr!=nil {
panic(err)
}
response, err:=client.Execute("status")
iferr!=nil {
pool.Discard(client) // Mark as bad and remove from pool
} else {
pool.Release(client) // Return to idle pool
}
fmt.Println(response)

Event Listener

EventListener demonstrates streaming server events using CreateResponseChannel, with automatic reconnection and keepalive. Use this to listen for asynchronous server broadcasts (player logins, chat, killfeed, etc.).

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationlistener, _:=examples.NewEventListener("192.168.1.100:7778", "password")
deferlistener.Close()
ctx:=context.Background()
listener.Run(ctx)
forevent:=rangelistener.Events {
fmt.Printf("Event: %s\n", event)
}

Real-World Application

For a complete, production-ready application using these patterns, see mh-gobot — a game server bot that demonstrates:

  • Connection pooling for concurrent command execution
  • Event streaming with custom parsing
  • Integration with external services
  • Error handling and reconnection strategies

tcprcon-cli

https://github.com/UltimateForm/tcprcon-cli

Caveats

Handling Server Broadcasts

Servers can (and will) often broadcast events over the TCP connection in an asynchronous manner. These are typically game events like killfeed messages, player logins, chat, etc. Some servers operate on an opt-in basis, requiring the RCON client to signal its interest in receiving these broadcasts, while others broadcast them by default.

What this means in practice:

Let's say you send a command packet (e.g., "status" with ID 54) and then immediately try to read its response. It's possible you might first receive a broadcast packet with a body like "Login: player B just joined game" instead of your expected status response. This highlights the importance of checking the ID field of incoming packets.

Generally, the best practice is to decouple your command writes from your response reads. The example under Using as a Library demonstrates a synchronous request-response pattern for a playerlist command, which can be unoptimal in such scenarios. For a more robust approach, you should handle your writes (commands) and reads (responses and broadcasts) in parallel, as shown in the Streaming Responses section.

Server Protocol Compliance

Ideally, all RCON servers would consistently follow the Valve protocol defined at https://developer.valvesoftware.com/wiki/Source_RCON_Protocol, eliminating surprises. However, in reality, some server implementations—such as that of Rust—exhibit unorthodox behavior.

The Rust game server commits the following notable violations of the RCON protocol:

  • Initial Logging Packet (ID 0, Type 4): After a client sends a SERVERDATA_EXECCOMMAND (e.g., info), the server typically responds with an immediate SERVERDATA_RESPONSE_VALUE packet that has an ID of 0 and often a Type of 4 (which is not a standard RCON packet type). The Body of this packet usually contains a server-side log message echoing the received command (e.g., [RCON][<client_ip>:<client_port>] <command>). The ID 0 is non-compliant, as the server should echo the client's original ID.
  • Repeated Command Output: The actual command output (e.g., hostname: LinuxGSM...) is often sent twice: once with the correct echoed client ID, and again with an ID of 0. This is redundant and non-compliant.
  • Misuse of ID -1: The server uses ID -1 (0xFF FF FF FF) as a general "end of response stream" or broadcast indicator following command output. According to the Source RCON Protocol, ID -1 is specifically reserved to indicate an authentication failure within a SERVERDATA_AUTH_RESPONSE packet. Its use in the context of command responses is a significant deviation.

These are the most prominent violations; other quirks might exist with greater room for nuanced interpretation, which are not listed here.

The concluding point is that you should anticipate such cases. In general, this library will function—even with servers like Rust—because it provides the fundamental tools for writing and reading data according to Valve's protocol over a TCP socket. However, depending on these aforementioned server-specific behaviors, you might need to adapt how and when you send commands and process responses in your application.

Specifically for Rust servers, you might implement simple checks to filter out extraneous packets. For example, you could ignore all SERVERDATA_RESPONSE_VALUE packets with ID -1 (after successful authentication) or ID 0, or filter out any packet with a Type value greater than 3 (as types 0-3 cover standard RCON messages). This allows your application to focus on the actual command responses while gracefully discarding server-initiated noise.

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE for details.

About

my very own rcon tcp implementation, aint much, but it's honest work

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

tcprcon

A fully native RCON client implementation, zero deps.

Remote Console (RCON) is a protocol that allows remote administration of game servers. Essentially, it's an agreement on how a client can send commands to a game server and receive responses over a standard TCP connection. This enables developers and administrators to manage server settings, execute commands, and monitor game events without direct access to the server console.

This library provides a client-side implementation of the RCON protocol, based on the Source RCON Protocol.

Installation

To install tcprcon, use go get:

go get github.com/UltimateForm/tcprcon

Using as a Library

The RCON client can be used as a library in your own Go projects:

import (
"github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, err:=rcon.New("192.168.1.100:7778")
iferr!=nil {
panic(err)
}
deferclient.Close()
// Authenticatesuccess, err:=common_rcon.Authenticate(client, "your_password")
iferr!=nil||!success {
panic("auth failed")
}
// Send commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("playerlist"))
client.Write(execPacket.Serialize())
// Read responseresponse, err:=packet.Read(client)
iferr!=nil {
panic(err)
}
fmt.Println(response.BodyStr())
}

Streaming Responses

For continuous listening (e.g., server broadcasts or multiple responses), use CreateResponseChannel:

usually you will want a more ellegant way of handling the concurrent nature of this, this example is just for illustration

import (
"context""fmt""io""github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, _:=rcon.New("192.168.1.100:7778")
deferclient.Close()
common_rcon.Authenticate(client, "your_password")
ctx, cancel:=context.WithCancel(context.Background())
defercancel()
// Create a channel that streams incoming packetspacketChan:=packet.CreateResponseChannel(client, ctx)
// Send a commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("listen event"))
client.Write(execPacket.Serialize())
// Listen for responsesforpkt:=rangepacketChan {
ifpkt.Error!=nil {
ifpkt.Error==io.EOF {
fmt.Println("Connection closed")
break
}
continue// Timeout or other non-fatal error
}
fmt.Printf("Received: %s\n", pkt.BodyStr())
}
}

Examples

The /examples directory contains production-ready patterns for common use cases:

Controlled Client

ControlledClient wraps the base Client with mutex protection and a simplified Execute() method for synchronous command execution. Use this when you need a single-connection client in a concurrent context.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationclient, _:=examples.NewControlledClient("192.168.1.100:7778")
deferclient.Close()
client.Authenticate("password")
response, _:=client.Execute("status")
fmt.Println(response)

Connection Pool

ConnectionPool manages a pool of reusable connections, automatically creating and discarding clients as needed. Use this for high-concurrency scenarios where multiple commands run in parallel.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationpool:=examples.NewConnectionPool("192.168.1.100:7778", "password", 5, time.Minute)
deferpool.Close()
// Option 1: Use WithClient for automatic release/discarderr:=pool.WithClient(context.Background(), func(client*examples.ControlledClient) error {
response, err:=client.Execute("playerlist")
fmt.Println(response)
returnerr
})
// Option 2: Manually manage client lifecycleclient, err:=pool.Get(context.Background())
iferr!=nil {
panic(err)
}
response, err:=client.Execute("status")
iferr!=nil {
pool.Discard(client) // Mark as bad and remove from pool
} else {
pool.Release(client) // Return to idle pool
}
fmt.Println(response)

Event Listener

EventListener demonstrates streaming server events using CreateResponseChannel, with automatic reconnection and keepalive. Use this to listen for asynchronous server broadcasts (player logins, chat, killfeed, etc.).

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationlistener, _:=examples.NewEventListener("192.168.1.100:7778", "password")
deferlistener.Close()
ctx:=context.Background()
listener.Run(ctx)
forevent:=rangelistener.Events {
fmt.Printf("Event: %s\n", event)
}

Real-World Application

For a complete, production-ready application using these patterns, see mh-gobot — a game server bot that demonstrates:

  • Connection pooling for concurrent command execution
  • Event streaming with custom parsing
  • Integration with external services
  • Error handling and reconnection strategies

tcprcon-cli

https://github.com/UltimateForm/tcprcon-cli

Caveats

Handling Server Broadcasts

Servers can (and will) often broadcast events over the TCP connection in an asynchronous manner. These are typically game events like killfeed messages, player logins, chat, etc. Some servers operate on an opt-in basis, requiring the RCON client to signal its interest in receiving these broadcasts, while others broadcast them by default.

What this means in practice:

Let's say you send a command packet (e.g., "status" with ID 54) and then immediately try to read its response. It's possible you might first receive a broadcast packet with a body like "Login: player B just joined game" instead of your expected status response. This highlights the importance of checking the ID field of incoming packets.

Generally, the best practice is to decouple your command writes from your response reads. The example under Using as a Library demonstrates a synchronous request-response pattern for a playerlist command, which can be unoptimal in such scenarios. For a more robust approach, you should handle your writes (commands) and reads (responses and broadcasts) in parallel, as shown in the Streaming Responses section.

Server Protocol Compliance

Ideally, all RCON servers would consistently follow the Valve protocol defined at https://developer.valvesoftware.com/wiki/Source_RCON_Protocol, eliminating surprises. However, in reality, some server implementations—such as that of Rust—exhibit unorthodox behavior.

The Rust game server commits the following notable violations of the RCON protocol:

  • Initial Logging Packet (ID 0, Type 4): After a client sends a SERVERDATA_EXECCOMMAND (e.g., info), the server typically responds with an immediate SERVERDATA_RESPONSE_VALUE packet that has an ID of 0 and often a Type of 4 (which is not a standard RCON packet type). The Body of this packet usually contains a server-side log message echoing the received command (e.g., [RCON][<client_ip>:<client_port>] <command>). The ID 0 is non-compliant, as the server should echo the client's original ID.
  • Repeated Command Output: The actual command output (e.g., hostname: LinuxGSM...) is often sent twice: once with the correct echoed client ID, and again with an ID of 0. This is redundant and non-compliant.
  • Misuse of ID -1: The server uses ID -1 (0xFF FF FF FF) as a general "end of response stream" or broadcast indicator following command output. According to the Source RCON Protocol, ID -1 is specifically reserved to indicate an authentication failure within a SERVERDATA_AUTH_RESPONSE packet. Its use in the context of command responses is a significant deviation.

These are the most prominent violations; other quirks might exist with greater room for nuanced interpretation, which are not listed here.

The concluding point is that you should anticipate such cases. In general, this library will function—even with servers like Rust—because it provides the fundamental tools for writing and reading data according to Valve's protocol over a TCP socket. However, depending on these aforementioned server-specific behaviors, you might need to adapt how and when you send commands and process responses in your application.

Specifically for Rust servers, you might implement simple checks to filter out extraneous packets. For example, you could ignore all SERVERDATA_RESPONSE_VALUE packets with ID -1 (after successful authentication) or ID 0, or filter out any packet with a Type value greater than 3 (as types 0-3 cover standard RCON messages). This allows your application to focus on the actual command responses while gracefully discarding server-initiated noise.

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE for details.

About

my very own rcon tcp implementation, aint much, but it's honest work

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

tcprcon

A fully native RCON client implementation, zero deps.

Remote Console (RCON) is a protocol that allows remote administration of game servers. Essentially, it's an agreement on how a client can send commands to a game server and receive responses over a standard TCP connection. This enables developers and administrators to manage server settings, execute commands, and monitor game events without direct access to the server console.

This library provides a client-side implementation of the RCON protocol, based on the Source RCON Protocol.

Installation

To install tcprcon, use go get:

go get github.com/UltimateForm/tcprcon

Using as a Library

The RCON client can be used as a library in your own Go projects:

import (
"github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, err:=rcon.New("192.168.1.100:7778")
iferr!=nil {
panic(err)
}
deferclient.Close()
// Authenticatesuccess, err:=common_rcon.Authenticate(client, "your_password")
iferr!=nil||!success {
panic("auth failed")
}
// Send commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("playerlist"))
client.Write(execPacket.Serialize())
// Read responseresponse, err:=packet.Read(client)
iferr!=nil {
panic(err)
}
fmt.Println(response.BodyStr())
}

Streaming Responses

For continuous listening (e.g., server broadcasts or multiple responses), use CreateResponseChannel:

usually you will want a more ellegant way of handling the concurrent nature of this, this example is just for illustration

import (
"context""fmt""io""github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, _:=rcon.New("192.168.1.100:7778")
deferclient.Close()
common_rcon.Authenticate(client, "your_password")
ctx, cancel:=context.WithCancel(context.Background())
defercancel()
// Create a channel that streams incoming packetspacketChan:=packet.CreateResponseChannel(client, ctx)
// Send a commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("listen event"))
client.Write(execPacket.Serialize())
// Listen for responsesforpkt:=rangepacketChan {
ifpkt.Error!=nil {
ifpkt.Error==io.EOF {
fmt.Println("Connection closed")
break
}
continue// Timeout or other non-fatal error
}
fmt.Printf("Received: %s\n", pkt.BodyStr())
}
}

Examples

The /examples directory contains production-ready patterns for common use cases:

Controlled Client

ControlledClient wraps the base Client with mutex protection and a simplified Execute() method for synchronous command execution. Use this when you need a single-connection client in a concurrent context.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationclient, _:=examples.NewControlledClient("192.168.1.100:7778")
deferclient.Close()
client.Authenticate("password")
response, _:=client.Execute("status")
fmt.Println(response)

Connection Pool

ConnectionPool manages a pool of reusable connections, automatically creating and discarding clients as needed. Use this for high-concurrency scenarios where multiple commands run in parallel.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationpool:=examples.NewConnectionPool("192.168.1.100:7778", "password", 5, time.Minute)
deferpool.Close()
// Option 1: Use WithClient for automatic release/discarderr:=pool.WithClient(context.Background(), func(client*examples.ControlledClient) error {
response, err:=client.Execute("playerlist")
fmt.Println(response)
returnerr
})
// Option 2: Manually manage client lifecycleclient, err:=pool.Get(context.Background())
iferr!=nil {
panic(err)
}
response, err:=client.Execute("status")
iferr!=nil {
pool.Discard(client) // Mark as bad and remove from pool
} else {
pool.Release(client) // Return to idle pool
}
fmt.Println(response)

Event Listener

EventListener demonstrates streaming server events using CreateResponseChannel, with automatic reconnection and keepalive. Use this to listen for asynchronous server broadcasts (player logins, chat, killfeed, etc.).

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationlistener, _:=examples.NewEventListener("192.168.1.100:7778", "password")
deferlistener.Close()
ctx:=context.Background()
listener.Run(ctx)
forevent:=rangelistener.Events {
fmt.Printf("Event: %s\n", event)
}

Real-World Application

For a complete, production-ready application using these patterns, see mh-gobot — a game server bot that demonstrates:

  • Connection pooling for concurrent command execution
  • Event streaming with custom parsing
  • Integration with external services
  • Error handling and reconnection strategies

tcprcon-cli

https://github.com/UltimateForm/tcprcon-cli

Caveats

Handling Server Broadcasts

Servers can (and will) often broadcast events over the TCP connection in an asynchronous manner. These are typically game events like killfeed messages, player logins, chat, etc. Some servers operate on an opt-in basis, requiring the RCON client to signal its interest in receiving these broadcasts, while others broadcast them by default.

What this means in practice:

Let's say you send a command packet (e.g., "status" with ID 54) and then immediately try to read its response. It's possible you might first receive a broadcast packet with a body like "Login: player B just joined game" instead of your expected status response. This highlights the importance of checking the ID field of incoming packets.

Generally, the best practice is to decouple your command writes from your response reads. The example under Using as a Library demonstrates a synchronous request-response pattern for a playerlist command, which can be unoptimal in such scenarios. For a more robust approach, you should handle your writes (commands) and reads (responses and broadcasts) in parallel, as shown in the Streaming Responses section.

Server Protocol Compliance

Ideally, all RCON servers would consistently follow the Valve protocol defined at https://developer.valvesoftware.com/wiki/Source_RCON_Protocol, eliminating surprises. However, in reality, some server implementations—such as that of Rust—exhibit unorthodox behavior.

The Rust game server commits the following notable violations of the RCON protocol:

  • Initial Logging Packet (ID 0, Type 4): After a client sends a SERVERDATA_EXECCOMMAND (e.g., info), the server typically responds with an immediate SERVERDATA_RESPONSE_VALUE packet that has an ID of 0 and often a Type of 4 (which is not a standard RCON packet type). The Body of this packet usually contains a server-side log message echoing the received command (e.g., [RCON][<client_ip>:<client_port>] <command>). The ID 0 is non-compliant, as the server should echo the client's original ID.
  • Repeated Command Output: The actual command output (e.g., hostname: LinuxGSM...) is often sent twice: once with the correct echoed client ID, and again with an ID of 0. This is redundant and non-compliant.
  • Misuse of ID -1: The server uses ID -1 (0xFF FF FF FF) as a general "end of response stream" or broadcast indicator following command output. According to the Source RCON Protocol, ID -1 is specifically reserved to indicate an authentication failure within a SERVERDATA_AUTH_RESPONSE packet. Its use in the context of command responses is a significant deviation.

These are the most prominent violations; other quirks might exist with greater room for nuanced interpretation, which are not listed here.

The concluding point is that you should anticipate such cases. In general, this library will function—even with servers like Rust—because it provides the fundamental tools for writing and reading data according to Valve's protocol over a TCP socket. However, depending on these aforementioned server-specific behaviors, you might need to adapt how and when you send commands and process responses in your application.

Specifically for Rust servers, you might implement simple checks to filter out extraneous packets. For example, you could ignore all SERVERDATA_RESPONSE_VALUE packets with ID -1 (after successful authentication) or ID 0, or filter out any packet with a Type value greater than 3 (as types 0-3 cover standard RCON messages). This allows your application to focus on the actual command responses while gracefully discarding server-initiated noise.

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE for details.

About

my very own rcon tcp implementation, aint much, but it's honest work

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

tcprcon

A fully native RCON client implementation, zero deps.

Remote Console (RCON) is a protocol that allows remote administration of game servers. Essentially, it's an agreement on how a client can send commands to a game server and receive responses over a standard TCP connection. This enables developers and administrators to manage server settings, execute commands, and monitor game events without direct access to the server console.

This library provides a client-side implementation of the RCON protocol, based on the Source RCON Protocol.

Installation

To install tcprcon, use go get:

go get github.com/UltimateForm/tcprcon

Using as a Library

The RCON client can be used as a library in your own Go projects:

import (
"github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, err:=rcon.New("192.168.1.100:7778")
iferr!=nil {
panic(err)
}
deferclient.Close()
// Authenticatesuccess, err:=common_rcon.Authenticate(client, "your_password")
iferr!=nil||!success {
panic("auth failed")
}
// Send commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("playerlist"))
client.Write(execPacket.Serialize())
// Read responseresponse, err:=packet.Read(client)
iferr!=nil {
panic(err)
}
fmt.Println(response.BodyStr())
}

Streaming Responses

For continuous listening (e.g., server broadcasts or multiple responses), use CreateResponseChannel:

usually you will want a more ellegant way of handling the concurrent nature of this, this example is just for illustration

import (
"context""fmt""io""github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, _:=rcon.New("192.168.1.100:7778")
deferclient.Close()
common_rcon.Authenticate(client, "your_password")
ctx, cancel:=context.WithCancel(context.Background())
defercancel()
// Create a channel that streams incoming packetspacketChan:=packet.CreateResponseChannel(client, ctx)
// Send a commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("listen event"))
client.Write(execPacket.Serialize())
// Listen for responsesforpkt:=rangepacketChan {
ifpkt.Error!=nil {
ifpkt.Error==io.EOF {
fmt.Println("Connection closed")
break
}
continue// Timeout or other non-fatal error
}
fmt.Printf("Received: %s\n", pkt.BodyStr())
}
}

Examples

The /examples directory contains production-ready patterns for common use cases:

Controlled Client

ControlledClient wraps the base Client with mutex protection and a simplified Execute() method for synchronous command execution. Use this when you need a single-connection client in a concurrent context.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationclient, _:=examples.NewControlledClient("192.168.1.100:7778")
deferclient.Close()
client.Authenticate("password")
response, _:=client.Execute("status")
fmt.Println(response)

Connection Pool

ConnectionPool manages a pool of reusable connections, automatically creating and discarding clients as needed. Use this for high-concurrency scenarios where multiple commands run in parallel.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationpool:=examples.NewConnectionPool("192.168.1.100:7778", "password", 5, time.Minute)
deferpool.Close()
// Option 1: Use WithClient for automatic release/discarderr:=pool.WithClient(context.Background(), func(client*examples.ControlledClient) error {
response, err:=client.Execute("playerlist")
fmt.Println(response)
returnerr
})
// Option 2: Manually manage client lifecycleclient, err:=pool.Get(context.Background())
iferr!=nil {
panic(err)
}
response, err:=client.Execute("status")
iferr!=nil {
pool.Discard(client) // Mark as bad and remove from pool
} else {
pool.Release(client) // Return to idle pool
}
fmt.Println(response)

Event Listener

EventListener demonstrates streaming server events using CreateResponseChannel, with automatic reconnection and keepalive. Use this to listen for asynchronous server broadcasts (player logins, chat, killfeed, etc.).

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationlistener, _:=examples.NewEventListener("192.168.1.100:7778", "password")
deferlistener.Close()
ctx:=context.Background()
listener.Run(ctx)
forevent:=rangelistener.Events {
fmt.Printf("Event: %s\n", event)
}

Real-World Application

For a complete, production-ready application using these patterns, see mh-gobot — a game server bot that demonstrates:

  • Connection pooling for concurrent command execution
  • Event streaming with custom parsing
  • Integration with external services
  • Error handling and reconnection strategies

tcprcon-cli

https://github.com/UltimateForm/tcprcon-cli

Caveats

Handling Server Broadcasts

Servers can (and will) often broadcast events over the TCP connection in an asynchronous manner. These are typically game events like killfeed messages, player logins, chat, etc. Some servers operate on an opt-in basis, requiring the RCON client to signal its interest in receiving these broadcasts, while others broadcast them by default.

What this means in practice:

Let's say you send a command packet (e.g., "status" with ID 54) and then immediately try to read its response. It's possible you might first receive a broadcast packet with a body like "Login: player B just joined game" instead of your expected status response. This highlights the importance of checking the ID field of incoming packets.

Generally, the best practice is to decouple your command writes from your response reads. The example under Using as a Library demonstrates a synchronous request-response pattern for a playerlist command, which can be unoptimal in such scenarios. For a more robust approach, you should handle your writes (commands) and reads (responses and broadcasts) in parallel, as shown in the Streaming Responses section.

Server Protocol Compliance

Ideally, all RCON servers would consistently follow the Valve protocol defined at https://developer.valvesoftware.com/wiki/Source_RCON_Protocol, eliminating surprises. However, in reality, some server implementations—such as that of Rust—exhibit unorthodox behavior.

The Rust game server commits the following notable violations of the RCON protocol:

  • Initial Logging Packet (ID 0, Type 4): After a client sends a SERVERDATA_EXECCOMMAND (e.g., info), the server typically responds with an immediate SERVERDATA_RESPONSE_VALUE packet that has an ID of 0 and often a Type of 4 (which is not a standard RCON packet type). The Body of this packet usually contains a server-side log message echoing the received command (e.g., [RCON][<client_ip>:<client_port>] <command>). The ID 0 is non-compliant, as the server should echo the client's original ID.
  • Repeated Command Output: The actual command output (e.g., hostname: LinuxGSM...) is often sent twice: once with the correct echoed client ID, and again with an ID of 0. This is redundant and non-compliant.
  • Misuse of ID -1: The server uses ID -1 (0xFF FF FF FF) as a general "end of response stream" or broadcast indicator following command output. According to the Source RCON Protocol, ID -1 is specifically reserved to indicate an authentication failure within a SERVERDATA_AUTH_RESPONSE packet. Its use in the context of command responses is a significant deviation.

These are the most prominent violations; other quirks might exist with greater room for nuanced interpretation, which are not listed here.

The concluding point is that you should anticipate such cases. In general, this library will function—even with servers like Rust—because it provides the fundamental tools for writing and reading data according to Valve's protocol over a TCP socket. However, depending on these aforementioned server-specific behaviors, you might need to adapt how and when you send commands and process responses in your application.

Specifically for Rust servers, you might implement simple checks to filter out extraneous packets. For example, you could ignore all SERVERDATA_RESPONSE_VALUE packets with ID -1 (after successful authentication) or ID 0, or filter out any packet with a Type value greater than 3 (as types 0-3 cover standard RCON messages). This allows your application to focus on the actual command responses while gracefully discarding server-initiated noise.

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE for details.

About

my very own rcon tcp implementation, aint much, but it's honest work

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

tcprcon

A fully native RCON client implementation, zero deps.

Remote Console (RCON) is a protocol that allows remote administration of game servers. Essentially, it's an agreement on how a client can send commands to a game server and receive responses over a standard TCP connection. This enables developers and administrators to manage server settings, execute commands, and monitor game events without direct access to the server console.

This library provides a client-side implementation of the RCON protocol, based on the Source RCON Protocol.

Installation

To install tcprcon, use go get:

go get github.com/UltimateForm/tcprcon

Using as a Library

The RCON client can be used as a library in your own Go projects:

import (
"github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, err:=rcon.New("192.168.1.100:7778")
iferr!=nil {
panic(err)
}
deferclient.Close()
// Authenticatesuccess, err:=common_rcon.Authenticate(client, "your_password")
iferr!=nil||!success {
panic("auth failed")
}
// Send commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("playerlist"))
client.Write(execPacket.Serialize())
// Read responseresponse, err:=packet.Read(client)
iferr!=nil {
panic(err)
}
fmt.Println(response.BodyStr())
}

Streaming Responses

For continuous listening (e.g., server broadcasts or multiple responses), use CreateResponseChannel:

usually you will want a more ellegant way of handling the concurrent nature of this, this example is just for illustration

import (
"context""fmt""io""github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, _:=rcon.New("192.168.1.100:7778")
deferclient.Close()
common_rcon.Authenticate(client, "your_password")
ctx, cancel:=context.WithCancel(context.Background())
defercancel()
// Create a channel that streams incoming packetspacketChan:=packet.CreateResponseChannel(client, ctx)
// Send a commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("listen event"))
client.Write(execPacket.Serialize())
// Listen for responsesforpkt:=rangepacketChan {
ifpkt.Error!=nil {
ifpkt.Error==io.EOF {
fmt.Println("Connection closed")
break
}
continue// Timeout or other non-fatal error
}
fmt.Printf("Received: %s\n", pkt.BodyStr())
}
}

Examples

The /examples directory contains production-ready patterns for common use cases:

Controlled Client

ControlledClient wraps the base Client with mutex protection and a simplified Execute() method for synchronous command execution. Use this when you need a single-connection client in a concurrent context.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationclient, _:=examples.NewControlledClient("192.168.1.100:7778")
deferclient.Close()
client.Authenticate("password")
response, _:=client.Execute("status")
fmt.Println(response)

Connection Pool

ConnectionPool manages a pool of reusable connections, automatically creating and discarding clients as needed. Use this for high-concurrency scenarios where multiple commands run in parallel.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationpool:=examples.NewConnectionPool("192.168.1.100:7778", "password", 5, time.Minute)
deferpool.Close()
// Option 1: Use WithClient for automatic release/discarderr:=pool.WithClient(context.Background(), func(client*examples.ControlledClient) error {
response, err:=client.Execute("playerlist")
fmt.Println(response)
returnerr
})
// Option 2: Manually manage client lifecycleclient, err:=pool.Get(context.Background())
iferr!=nil {
panic(err)
}
response, err:=client.Execute("status")
iferr!=nil {
pool.Discard(client) // Mark as bad and remove from pool
} else {
pool.Release(client) // Return to idle pool
}
fmt.Println(response)

Event Listener

EventListener demonstrates streaming server events using CreateResponseChannel, with automatic reconnection and keepalive. Use this to listen for asynchronous server broadcasts (player logins, chat, killfeed, etc.).

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationlistener, _:=examples.NewEventListener("192.168.1.100:7778", "password")
deferlistener.Close()
ctx:=context.Background()
listener.Run(ctx)
forevent:=rangelistener.Events {
fmt.Printf("Event: %s\n", event)
}

Real-World Application

For a complete, production-ready application using these patterns, see mh-gobot — a game server bot that demonstrates:

  • Connection pooling for concurrent command execution
  • Event streaming with custom parsing
  • Integration with external services
  • Error handling and reconnection strategies

tcprcon-cli

https://github.com/UltimateForm/tcprcon-cli

Caveats

Handling Server Broadcasts

Servers can (and will) often broadcast events over the TCP connection in an asynchronous manner. These are typically game events like killfeed messages, player logins, chat, etc. Some servers operate on an opt-in basis, requiring the RCON client to signal its interest in receiving these broadcasts, while others broadcast them by default.

What this means in practice:

Let's say you send a command packet (e.g., "status" with ID 54) and then immediately try to read its response. It's possible you might first receive a broadcast packet with a body like "Login: player B just joined game" instead of your expected status response. This highlights the importance of checking the ID field of incoming packets.

Generally, the best practice is to decouple your command writes from your response reads. The example under Using as a Library demonstrates a synchronous request-response pattern for a playerlist command, which can be unoptimal in such scenarios. For a more robust approach, you should handle your writes (commands) and reads (responses and broadcasts) in parallel, as shown in the Streaming Responses section.

Server Protocol Compliance

Ideally, all RCON servers would consistently follow the Valve protocol defined at https://developer.valvesoftware.com/wiki/Source_RCON_Protocol, eliminating surprises. However, in reality, some server implementations—such as that of Rust—exhibit unorthodox behavior.

The Rust game server commits the following notable violations of the RCON protocol:

  • Initial Logging Packet (ID 0, Type 4): After a client sends a SERVERDATA_EXECCOMMAND (e.g., info), the server typically responds with an immediate SERVERDATA_RESPONSE_VALUE packet that has an ID of 0 and often a Type of 4 (which is not a standard RCON packet type). The Body of this packet usually contains a server-side log message echoing the received command (e.g., [RCON][<client_ip>:<client_port>] <command>). The ID 0 is non-compliant, as the server should echo the client's original ID.
  • Repeated Command Output: The actual command output (e.g., hostname: LinuxGSM...) is often sent twice: once with the correct echoed client ID, and again with an ID of 0. This is redundant and non-compliant.
  • Misuse of ID -1: The server uses ID -1 (0xFF FF FF FF) as a general "end of response stream" or broadcast indicator following command output. According to the Source RCON Protocol, ID -1 is specifically reserved to indicate an authentication failure within a SERVERDATA_AUTH_RESPONSE packet. Its use in the context of command responses is a significant deviation.

These are the most prominent violations; other quirks might exist with greater room for nuanced interpretation, which are not listed here.

The concluding point is that you should anticipate such cases. In general, this library will function—even with servers like Rust—because it provides the fundamental tools for writing and reading data according to Valve's protocol over a TCP socket. However, depending on these aforementioned server-specific behaviors, you might need to adapt how and when you send commands and process responses in your application.

Specifically for Rust servers, you might implement simple checks to filter out extraneous packets. For example, you could ignore all SERVERDATA_RESPONSE_VALUE packets with ID -1 (after successful authentication) or ID 0, or filter out any packet with a Type value greater than 3 (as types 0-3 cover standard RCON messages). This allows your application to focus on the actual command responses while gracefully discarding server-initiated noise.

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE for details.

About

my very own rcon tcp implementation, aint much, but it's honest work

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

tcprcon

A fully native RCON client implementation, zero deps.

Remote Console (RCON) is a protocol that allows remote administration of game servers. Essentially, it's an agreement on how a client can send commands to a game server and receive responses over a standard TCP connection. This enables developers and administrators to manage server settings, execute commands, and monitor game events without direct access to the server console.

This library provides a client-side implementation of the RCON protocol, based on the Source RCON Protocol.

Installation

To install tcprcon, use go get:

go get github.com/UltimateForm/tcprcon

Using as a Library

The RCON client can be used as a library in your own Go projects:

import (
"github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, err:=rcon.New("192.168.1.100:7778")
iferr!=nil {
panic(err)
}
deferclient.Close()
// Authenticatesuccess, err:=common_rcon.Authenticate(client, "your_password")
iferr!=nil||!success {
panic("auth failed")
}
// Send commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("playerlist"))
client.Write(execPacket.Serialize())
// Read responseresponse, err:=packet.Read(client)
iferr!=nil {
panic(err)
}
fmt.Println(response.BodyStr())
}

Streaming Responses

For continuous listening (e.g., server broadcasts or multiple responses), use CreateResponseChannel:

usually you will want a more ellegant way of handling the concurrent nature of this, this example is just for illustration

import (
"context""fmt""io""github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, _:=rcon.New("192.168.1.100:7778")
deferclient.Close()
common_rcon.Authenticate(client, "your_password")
ctx, cancel:=context.WithCancel(context.Background())
defercancel()
// Create a channel that streams incoming packetspacketChan:=packet.CreateResponseChannel(client, ctx)
// Send a commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("listen event"))
client.Write(execPacket.Serialize())
// Listen for responsesforpkt:=rangepacketChan {
ifpkt.Error!=nil {
ifpkt.Error==io.EOF {
fmt.Println("Connection closed")
break
}
continue// Timeout or other non-fatal error
}
fmt.Printf("Received: %s\n", pkt.BodyStr())
}
}

Examples

The /examples directory contains production-ready patterns for common use cases:

Controlled Client

ControlledClient wraps the base Client with mutex protection and a simplified Execute() method for synchronous command execution. Use this when you need a single-connection client in a concurrent context.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationclient, _:=examples.NewControlledClient("192.168.1.100:7778")
deferclient.Close()
client.Authenticate("password")
response, _:=client.Execute("status")
fmt.Println(response)

Connection Pool

ConnectionPool manages a pool of reusable connections, automatically creating and discarding clients as needed. Use this for high-concurrency scenarios where multiple commands run in parallel.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationpool:=examples.NewConnectionPool("192.168.1.100:7778", "password", 5, time.Minute)
deferpool.Close()
// Option 1: Use WithClient for automatic release/discarderr:=pool.WithClient(context.Background(), func(client*examples.ControlledClient) error {
response, err:=client.Execute("playerlist")
fmt.Println(response)
returnerr
})
// Option 2: Manually manage client lifecycleclient, err:=pool.Get(context.Background())
iferr!=nil {
panic(err)
}
response, err:=client.Execute("status")
iferr!=nil {
pool.Discard(client) // Mark as bad and remove from pool
} else {
pool.Release(client) // Return to idle pool
}
fmt.Println(response)

Event Listener

EventListener demonstrates streaming server events using CreateResponseChannel, with automatic reconnection and keepalive. Use this to listen for asynchronous server broadcasts (player logins, chat, killfeed, etc.).

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationlistener, _:=examples.NewEventListener("192.168.1.100:7778", "password")
deferlistener.Close()
ctx:=context.Background()
listener.Run(ctx)
forevent:=rangelistener.Events {
fmt.Printf("Event: %s\n", event)
}

Real-World Application

For a complete, production-ready application using these patterns, see mh-gobot — a game server bot that demonstrates:

  • Connection pooling for concurrent command execution
  • Event streaming with custom parsing
  • Integration with external services
  • Error handling and reconnection strategies

tcprcon-cli

https://github.com/UltimateForm/tcprcon-cli

Caveats

Handling Server Broadcasts

Servers can (and will) often broadcast events over the TCP connection in an asynchronous manner. These are typically game events like killfeed messages, player logins, chat, etc. Some servers operate on an opt-in basis, requiring the RCON client to signal its interest in receiving these broadcasts, while others broadcast them by default.

What this means in practice:

Let's say you send a command packet (e.g., "status" with ID 54) and then immediately try to read its response. It's possible you might first receive a broadcast packet with a body like "Login: player B just joined game" instead of your expected status response. This highlights the importance of checking the ID field of incoming packets.

Generally, the best practice is to decouple your command writes from your response reads. The example under Using as a Library demonstrates a synchronous request-response pattern for a playerlist command, which can be unoptimal in such scenarios. For a more robust approach, you should handle your writes (commands) and reads (responses and broadcasts) in parallel, as shown in the Streaming Responses section.

Server Protocol Compliance

Ideally, all RCON servers would consistently follow the Valve protocol defined at https://developer.valvesoftware.com/wiki/Source_RCON_Protocol, eliminating surprises. However, in reality, some server implementations—such as that of Rust—exhibit unorthodox behavior.

The Rust game server commits the following notable violations of the RCON protocol:

  • Initial Logging Packet (ID 0, Type 4): After a client sends a SERVERDATA_EXECCOMMAND (e.g., info), the server typically responds with an immediate SERVERDATA_RESPONSE_VALUE packet that has an ID of 0 and often a Type of 4 (which is not a standard RCON packet type). The Body of this packet usually contains a server-side log message echoing the received command (e.g., [RCON][<client_ip>:<client_port>] <command>). The ID 0 is non-compliant, as the server should echo the client's original ID.
  • Repeated Command Output: The actual command output (e.g., hostname: LinuxGSM...) is often sent twice: once with the correct echoed client ID, and again with an ID of 0. This is redundant and non-compliant.
  • Misuse of ID -1: The server uses ID -1 (0xFF FF FF FF) as a general "end of response stream" or broadcast indicator following command output. According to the Source RCON Protocol, ID -1 is specifically reserved to indicate an authentication failure within a SERVERDATA_AUTH_RESPONSE packet. Its use in the context of command responses is a significant deviation.

These are the most prominent violations; other quirks might exist with greater room for nuanced interpretation, which are not listed here.

The concluding point is that you should anticipate such cases. In general, this library will function—even with servers like Rust—because it provides the fundamental tools for writing and reading data according to Valve's protocol over a TCP socket. However, depending on these aforementioned server-specific behaviors, you might need to adapt how and when you send commands and process responses in your application.

Specifically for Rust servers, you might implement simple checks to filter out extraneous packets. For example, you could ignore all SERVERDATA_RESPONSE_VALUE packets with ID -1 (after successful authentication) or ID 0, or filter out any packet with a Type value greater than 3 (as types 0-3 cover standard RCON messages). This allows your application to focus on the actual command responses while gracefully discarding server-initiated noise.

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE for details.

About

my very own rcon tcp implementation, aint much, but it's honest work

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

tcprcon

A fully native RCON client implementation, zero deps.

Remote Console (RCON) is a protocol that allows remote administration of game servers. Essentially, it's an agreement on how a client can send commands to a game server and receive responses over a standard TCP connection. This enables developers and administrators to manage server settings, execute commands, and monitor game events without direct access to the server console.

This library provides a client-side implementation of the RCON protocol, based on the Source RCON Protocol.

Installation

To install tcprcon, use go get:

go get github.com/UltimateForm/tcprcon

Using as a Library

The RCON client can be used as a library in your own Go projects:

import (
"github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, err:=rcon.New("192.168.1.100:7778")
iferr!=nil {
panic(err)
}
deferclient.Close()
// Authenticatesuccess, err:=common_rcon.Authenticate(client, "your_password")
iferr!=nil||!success {
panic("auth failed")
}
// Send commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("playerlist"))
client.Write(execPacket.Serialize())
// Read responseresponse, err:=packet.Read(client)
iferr!=nil {
panic(err)
}
fmt.Println(response.BodyStr())
}

Streaming Responses

For continuous listening (e.g., server broadcasts or multiple responses), use CreateResponseChannel:

usually you will want a more ellegant way of handling the concurrent nature of this, this example is just for illustration

import (
"context""fmt""io""github.com/UltimateForm/tcprcon/pkg/rcon""github.com/UltimateForm/tcprcon/pkg/common_rcon""github.com/UltimateForm/tcprcon/pkg/packet"
)
funcmain() {
client, _:=rcon.New("192.168.1.100:7778")
deferclient.Close()
common_rcon.Authenticate(client, "your_password")
ctx, cancel:=context.WithCancel(context.Background())
defercancel()
// Create a channel that streams incoming packetspacketChan:=packet.CreateResponseChannel(client, ctx)
// Send a commandexecPacket:=packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte("listen event"))
client.Write(execPacket.Serialize())
// Listen for responsesforpkt:=rangepacketChan {
ifpkt.Error!=nil {
ifpkt.Error==io.EOF {
fmt.Println("Connection closed")
break
}
continue// Timeout or other non-fatal error
}
fmt.Printf("Received: %s\n", pkt.BodyStr())
}
}

Examples

The /examples directory contains production-ready patterns for common use cases:

Controlled Client

ControlledClient wraps the base Client with mutex protection and a simplified Execute() method for synchronous command execution. Use this when you need a single-connection client in a concurrent context.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationclient, _:=examples.NewControlledClient("192.168.1.100:7778")
deferclient.Close()
client.Authenticate("password")
response, _:=client.Execute("status")
fmt.Println(response)

Connection Pool

ConnectionPool manages a pool of reusable connections, automatically creating and discarding clients as needed. Use this for high-concurrency scenarios where multiple commands run in parallel.

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationpool:=examples.NewConnectionPool("192.168.1.100:7778", "password", 5, time.Minute)
deferpool.Close()
// Option 1: Use WithClient for automatic release/discarderr:=pool.WithClient(context.Background(), func(client*examples.ControlledClient) error {
response, err:=client.Execute("playerlist")
fmt.Println(response)
returnerr
})
// Option 2: Manually manage client lifecycleclient, err:=pool.Get(context.Background())
iferr!=nil {
panic(err)
}
response, err:=client.Execute("status")
iferr!=nil {
pool.Discard(client) // Mark as bad and remove from pool
} else {
pool.Release(client) // Return to idle pool
}
fmt.Println(response)

Event Listener

EventListener demonstrates streaming server events using CreateResponseChannel, with automatic reconnection and keepalive. Use this to listen for asynchronous server broadcasts (player logins, chat, killfeed, etc.).

import"github.com/UltimateForm/tcprcon/examples"// replace this with wherever you have your implementationlistener, _:=examples.NewEventListener("192.168.1.100:7778", "password")
deferlistener.Close()
ctx:=context.Background()
listener.Run(ctx)
forevent:=rangelistener.Events {
fmt.Printf("Event: %s\n", event)
}

Real-World Application

For a complete, production-ready application using these patterns, see mh-gobot — a game server bot that demonstrates:

  • Connection pooling for concurrent command execution
  • Event streaming with custom parsing
  • Integration with external services
  • Error handling and reconnection strategies

tcprcon-cli

https://github.com/UltimateForm/tcprcon-cli

Caveats

Handling Server Broadcasts

Servers can (and will) often broadcast events over the TCP connection in an asynchronous manner. These are typically game events like killfeed messages, player logins, chat, etc. Some servers operate on an opt-in basis, requiring the RCON client to signal its interest in receiving these broadcasts, while others broadcast them by default.

What this means in practice:

Let's say you send a command packet (e.g., "status" with ID 54) and then immediately try to read its response. It's possible you might first receive a broadcast packet with a body like "Login: player B just joined game" instead of your expected status response. This highlights the importance of checking the ID field of incoming packets.

Generally, the best practice is to decouple your command writes from your response reads. The example under Using as a Library demonstrates a synchronous request-response pattern for a playerlist command, which can be unoptimal in such scenarios. For a more robust approach, you should handle your writes (commands) and reads (responses and broadcasts) in parallel, as shown in the Streaming Responses section.

Server Protocol Compliance

Ideally, all RCON servers would consistently follow the Valve protocol defined at https://developer.valvesoftware.com/wiki/Source_RCON_Protocol, eliminating surprises. However, in reality, some server implementations—such as that of Rust—exhibit unorthodox behavior.

The Rust game server commits the following notable violations of the RCON protocol:

  • Initial Logging Packet (ID 0, Type 4): After a client sends a SERVERDATA_EXECCOMMAND (e.g., info), the server typically responds with an immediate SERVERDATA_RESPONSE_VALUE packet that has an ID of 0 and often a Type of 4 (which is not a standard RCON packet type). The Body of this packet usually contains a server-side log message echoing the received command (e.g., [RCON][<client_ip>:<client_port>] <command>). The ID 0 is non-compliant, as the server should echo the client's original ID.
  • Repeated Command Output: The actual command output (e.g., hostname: LinuxGSM...) is often sent twice: once with the correct echoed client ID, and again with an ID of 0. This is redundant and non-compliant.
  • Misuse of ID -1: The server uses ID -1 (0xFF FF FF FF) as a general "end of response stream" or broadcast indicator following command output. According to the Source RCON Protocol, ID -1 is specifically reserved to indicate an authentication failure within a SERVERDATA_AUTH_RESPONSE packet. Its use in the context of command responses is a significant deviation.

These are the most prominent violations; other quirks might exist with greater room for nuanced interpretation, which are not listed here.

The concluding point is that you should anticipate such cases. In general, this library will function—even with servers like Rust—because it provides the fundamental tools for writing and reading data according to Valve's protocol over a TCP socket. However, depending on these aforementioned server-specific behaviors, you might need to adapt how and when you send commands and process responses in your application.

Specifically for Rust servers, you might implement simple checks to filter out extraneous packets. For example, you could ignore all SERVERDATA_RESPONSE_VALUE packets with ID -1 (after successful authentication) or ID 0, or filter out any packet with a Type value greater than 3 (as types 0-3 cover standard RCON messages). This allows your application to focus on the actual command responses while gracefully discarding server-initiated noise.

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE for details.

About

my very own rcon tcp implementation, aint much, but it's honest work

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages