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()) } }