Files
mc-god/internal/pkg/rcon/rcon.go
2026-02-13 14:14:08 -08:00

178 lines
4.1 KiB
Go

package rcon
import (
"context"
"fmt"
"os"
"time"
"github.com/gorcon/rcon"
)
// Client represents an RCON client for Minecraft server interaction
type Client struct {
conn *rcon.Conn
}
// New creates a new RCON client
func New(address, password string) (*Client, error) {
conn, err := rcon.Dial(address, password)
if err != nil {
return nil, fmt.Errorf("failed to connect to RCON: %w", err)
}
return &Client{
conn: conn,
}, nil
}
// Execute executes a command on the Minecraft server
func (c *Client) Execute(command string) (string, error) {
if c.conn == nil {
return "", fmt.Errorf("RCON connection not established")
}
response, err := c.conn.Execute(command)
if err != nil {
return "", fmt.Errorf("failed to execute command '%s': %w", command, err)
}
return response, nil
}
// Close closes the RCON connection
func (c *Client) Close() error {
if c.conn != nil {
return c.conn.Close()
}
return nil
}
// Ping checks if the RCON connection is alive
func (c *Client) Ping() error {
_, err := c.Execute("help")
return err
}
// SetWeather sets the weather in the Minecraft world
func (c *Client) SetWeather(weather string) error {
return fmt.Errorf("not implemented")
}
// SetTime sets the time in the Minecraft world
func (c *Client) SetTime(timeValue string) error {
return fmt.Errorf("not implemented")
}
// SetDifficulty sets the difficulty level
func (c *Client) SetDifficulty(difficulty string) error {
return fmt.Errorf("not implemented")
}
// GetServerInfo returns basic server information
func (c *Client) GetServerInfo() (string, error) {
return c.Execute("version")
}
// ConnectWithTimeout attempts to connect with a timeout
func ConnectWithTimeout(address, password string, timeout time.Duration) (*Client, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// Create a channel to receive the connection result
connChan := make(chan *Client, 1)
errChan := make(chan error, 1)
go func() {
client, err := New(address, password)
if err != nil {
errChan <- err
return
}
connChan <- client
}()
select {
case client := <-connChan:
return client, nil
case err := <-errChan:
return nil, err
case <-ctx.Done():
return nil, fmt.Errorf("timeout connecting to RCON: %w", ctx.Err())
}
}
// GetEnvCredentials returns RCON address and password from environment variables
func GetEnvCredentials() (string, string, error) {
address := os.Getenv("RCON_ADDRESS")
if address == "" {
return "", "", fmt.Errorf("RCON_ADDRESS environment variable not set")
}
password := os.Getenv("RCON_PASSWORD")
if password == "" {
return "", "", fmt.Errorf("RCON_PASSWORD environment variable not set")
}
return address, password, nil
}
// NewFromEnv creates a new RCON client using environment variables
func NewFromEnv() (*Client, error) {
address, password, err := GetEnvCredentials()
if err != nil {
return nil, err
}
client, err := New(address, password)
if err != nil {
return nil, fmt.Errorf("failed to create RCON client from environment: %w", err)
}
return client, nil
}
// HealthCheck verifies the RCON connection is working properly
func (c *Client) HealthCheck() error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
select {
case <-ctx.Done():
return fmt.Errorf("health check timeout")
default:
if err := c.Ping(); err != nil {
return fmt.Errorf("health check failed: %w", err)
}
return nil
}
}
// ExecuteWithTimeout executes a command with a timeout
func (c *Client) ExecuteWithTimeout(command string, timeout time.Duration) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// Create a channel to receive the response
responseChan := make(chan string, 1)
errChan := make(chan error, 1)
go func() {
response, err := c.Execute(command)
if err != nil {
errChan <- err
return
}
responseChan <- response
}()
select {
case response := <-responseChan:
return response, nil
case err := <-errChan:
return "", err
case <-ctx.Done():
return "", fmt.Errorf("timeout executing command '%s': %w", command, ctx.Err())
}
}