smartlimbo

package module
v0.0.0-...-6a6dde3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

README

Gate SmartLimbo

Go Reference

A smart Limbo failover, per-server auto-reconnect queue, and protected sandbox extension for the Gate Minecraft proxy.

Keeps your players connected, calm, and informed when backend servers restart, crash, or enter maintenance.


✨ Features

  • 🌀 Seamless Limbo Failover: When backend servers restart or crash, players are moved to a lightweight Limbo server (e.g. nanolimbo, picolimbo, limbo) instead of being disconnected to the title screen.
  • 🚦 Per-Server Smart Queue: Players wait specifically for the server they were playing on (e.g., smp, survival), without getting stuck behind players waiting for different servers.
  • 🔄 Automatic Backend Ping & Reconnect: Checks backend server TCP ports in the background. As soon as the server boots up, players are automatically reconnected in controlled batches (reconnect_batch_size) to prevent server lag spikes.
  • 📢 Live Queue Updates: Action bar and chat notifications keep players informed about their queue position, total waiting, and server status.
  • 🔒 Protected Limbo Sandbox: Blocks unauthorized commands (e.g. /server, /msg, /pay) while in Limbo with custom messages and whitelist control.
  • 🎮 Interactive Player Commands:
    • /reconnect (alias /rc): Try reconnecting immediately.
    • /queue leave (alias /leave, /hub): Leave the queue and return to the main lobby server.
    • /queue info: View your queue status and position.
  • 🛠️ Admin Controls: /smartlimbo status (view all queues in real-time) and /smartlimbo reload.

📦 Installation

In Pelican / Pterodactyl Panel

Add the plugin package to your Gate Go Plugins (List) setting:

github.com/andreisugu/gate-smartlimbo

(Combine with other plugins: github.com/andreisugu/gate-simplewhitelist, github.com/andreisugu/gate-hybridforwarding, github.com/andreisugu/gate-bettertab, github.com/andreisugu/gate-smartlimbo)

In Go Code
go get github.com/andreisugu/gate-smartlimbo
package main

import (
	"github.com/andreisugu/gate-smartlimbo"
	"go.minekube.com/gate/cmd/gate"
	"go.minekube.com/gate/pkg/edition/java/proxy"
)

func main() {
	proxy.Plugins = append(proxy.Plugins,
		smartlimbo.Plugin,
	)
	gate.Execute()
}

⚙️ Configuration (config/smartlimbo.toml)

# The backend server name used as Limbo (e.g. nanolimbo, picolimbo, limbo)
limbo_server = "nanolimbo"

# Where to send direct connections if target server is down
direct_connect_server = "lobby"

# Fallback servers when a player leaves the queue with /leave
fallback_servers = ["lobby"]

# Queue checks interval in milliseconds
task_interval_ms = 3000

# Queue position notifications interval
queue_notify_interval_ms = 3000

# Where queue notifications are displayed: "action_bar", "chat", "both", or "none"
notify_mode = "action_bar"

# How many players to reconnect per tick
reconnect_batch_size = 3
reconnect_delay_ms = 500
ping_timeout_ms = 1500

# Protected Limbo sandbox
protected_limbo = true
allowed_commands = ["reconnect", "rc", "queue", "leave", "hub", "help", "msg"]

[messages]
kicked_to_limbo = "<yellow>⚠ <aqua>%server%</aqua> went offline or restarted.\n<gray>You were moved to Limbo and placed in queue <gold>#%position%</gold>.</gray></yellow>"
queue_actionbar = "<yellow>⏳ Waiting for <aqua>%server%</aqua> <dark_gray>•</dark_gray> Position: <gold>#%position%</gold>/<gold>%total%</gold></yellow>"
server_online = "<green>✔ <aqua>%server%</aqua> is back online! Reconnecting now...</green>"
reconnect_success = "<green>✔ Reconnected to <aqua>%server%</aqua>!</green>"
reconnect_failed = "<red>✖ <aqua>%server%</aqua> is not ready yet. Still waiting in queue (#%position%)...</red>"
left_queue = "<yellow>You left the queue and returned to <aqua>%server%</aqua>.</yellow>"
not_in_queue = "<gray>You are not currently in any reconnect queue.</gray>"
queue_info = "<aqua>════ SmartLimbo Queue Info ════</aqua>\n<gray>Target Server:</gray> <yellow>%server%</yellow>\n<gray>Your Position:</gray> <gold>#%position%</gold> <gray>of</gray> <gold>%total%</gold>\n<gray>Server Status:</gray> <yellow>%status%</yellow>"
command_blocked = "<red>✖ Commands are blocked in Limbo.\n<gray>Use <yellow>/leave</yellow> to exit or <yellow>/reconnect</yellow> to retry.</gray></red>"

🎮 In-Game Commands

Command Permission Description
/reconnect (or /rc) All Force an immediate reconnect attempt to your target server.
/queue info (or /queue) All Shows your target server, queue position, total waiting, and server status.
/queue leave (or /leave, /hub) All Leaves the reconnection queue and redirects you to the lobby.
/smartlimbo status Op / Console Displays real-time counts of all active queues across servers.
/smartlimbo reload Op / Console Hot-reloads config/smartlimbo.toml with zero downtime.

📄 License

Apache 2.0 © Andrei

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Plugin = proxy.Plugin{
	Name: "SmartLimbo",
	Init: func(ctx context.Context, p *proxy.Proxy) error {
		log := logr.FromContextOrDiscard(ctx)
		configPath := filepath.Join("config", "smartlimbo.toml")

		store := NewConfigStore(configPath)
		if _, err := store.Load(); err != nil {
			log.Error(err, "Failed to load smartlimbo configuration")
			return err
		}

		queue := NewQueueManager()
		handler := NewLimboHandler(p, store, queue, log)
		handler.Start(ctx)

		event.Subscribe(p.Event(), 0, func(e *proxy.KickedFromServerEvent) {
			handler.HandleKick(e)
		})

		event.Subscribe(p.Event(), 0, func(e *proxy.PlayerChooseInitialServerEvent) {
			handler.HandleInitialServer(e)
		})

		event.Subscribe(p.Event(), 0, func(e *proxy.CommandExecuteEvent) {
			handler.HandleCommand(e)
		})

		event.Subscribe(p.Event(), 0, func(e *proxy.DisconnectEvent) {
			handler.HandleDisconnect(e)
		})

		RegisterCommands(p, store, queue)

		log.Info("SmartLimbo loaded (smart failover, per-server queue & protected limbo active)")
		return nil
	},
}

Plugin is the Gate SmartLimbo failover and reconnection queue plugin.

Functions

func FormatText

func FormatText(input string) c.Component

FormatText parses MiniMessage, RGB, Legacy color tags, and Click/Hover tags into a Minecraft Component.

func RegisterCommands

func RegisterCommands(p *proxy.Proxy, store *ConfigStore, queue *QueueManager)

RegisterCommands registers /reconnect, /rc, /queue, /leave, and /smartlimbo commands.

Types

type Config

type Config struct {
	LimboServer           string   `toml:"limbo_server"`
	DirectConnectServer   string   `toml:"direct_connect_server"`
	FallbackServers       []string `toml:"fallback_servers"`
	TaskIntervalMs        int      `toml:"task_interval_ms"`
	QueueNotifyIntervalMs int      `toml:"queue_notify_interval_ms"`
	NotifyMode            string   `toml:"notify_mode"`
	ReconnectBatchSize    int      `toml:"reconnect_batch_size"`
	ReconnectDelayMs      int      `toml:"reconnect_delay_ms"`
	PingTimeoutMs         int      `toml:"ping_timeout_ms"`
	ProtectedLimbo        bool     `toml:"protected_limbo"`
	AllowedCommands       []string `toml:"allowed_commands"`
	Messages              Messages `toml:"messages"`
}

Config represents the complete SmartLimbo configuration.

type ConfigStore

type ConfigStore struct {
	// contains filtered or unexported fields
}

ConfigStore manages thread-safe configuration reading and updating.

func NewConfigStore

func NewConfigStore(path string) *ConfigStore

NewConfigStore creates a new ConfigStore instance.

func (*ConfigStore) Get

func (cs *ConfigStore) Get() *Config

Get returns the current Config snapshot.

func (*ConfigStore) IsCommandAllowed

func (cs *ConfigStore) IsCommandAllowed(cmd string) bool

IsCommandAllowed checks if a command name is allowed in Limbo.

func (*ConfigStore) Load

func (cs *ConfigStore) Load() (*Config, error)

Load reads and parses the configuration file.

type LimboHandler

type LimboHandler struct {
	// contains filtered or unexported fields
}

LimboHandler manages kicks, failovers, background server pinging, and batch auto-reconnection.

func NewLimboHandler

func NewLimboHandler(p *proxy.Proxy, store *ConfigStore, queue *QueueManager, log logr.Logger) *LimboHandler

NewLimboHandler creates a new LimboHandler instance.

func (*LimboHandler) FindBestFallback

func (h *LimboHandler) FindBestFallback(excludeServer string) proxy.RegisteredServer

FindBestFallback finds the highest priority online fallback server from the configured list.

func (*LimboHandler) HandleCommand

func (h *LimboHandler) HandleCommand(e *proxy.CommandExecuteEvent)

HandleCommand intercepts commands executed while inside the protected Limbo server.

func (*LimboHandler) HandleDisconnect

func (h *LimboHandler) HandleDisconnect(e *proxy.DisconnectEvent)

HandleDisconnect cleans player from all queues upon logout.

func (*LimboHandler) HandleInitialServer

func (h *LimboHandler) HandleInitialServer(e *proxy.PlayerChooseInitialServerEvent)

HandleInitialServer handles failover if the direct connect server is unavailable.

func (*LimboHandler) HandleKick

func (h *LimboHandler) HandleKick(e *proxy.KickedFromServerEvent)

HandleKick intercepts server kicks and moves the player to the best available fallback server + queue.

func (*LimboHandler) Start

func (h *LimboHandler) Start(ctx context.Context)

Start begins the background queue worker and notification routines.

type Messages

type Messages struct {
	KickedToLimbo    string `toml:"kicked_to_limbo"`
	QueueActionbar   string `toml:"queue_actionbar"`
	QueueChat        string `toml:"queue_chat"`
	ServerOnline     string `toml:"server_online"`
	ReconnectSuccess string `toml:"reconnect_success"`
	ReconnectFailed  string `toml:"reconnect_failed"`
	LeftQueue        string `toml:"left_queue"`
	NotInQueue       string `toml:"not_in_queue"`
	QueueInfo        string `toml:"queue_info"`
	CommandBlocked   string `toml:"command_blocked"`
}

Messages holds user-customizable chat and action bar notifications.

type QueueManager

type QueueManager struct {
	// contains filtered or unexported fields
}

QueueManager manages per-server smart queues with thread safety.

func NewQueueManager

func NewQueueManager() *QueueManager

NewQueueManager creates a new QueueManager instance.

func (*QueueManager) Enqueue

func (qm *QueueManager) Enqueue(targetServer string, player proxy.Player) int

Enqueue adds a player to the queue for targetServer and returns their 1-indexed position.

func (*QueueManager) GetActiveTargetServers

func (qm *QueueManager) GetActiveTargetServers() []string

GetActiveTargetServers returns all server names currently having waiting players.

func (*QueueManager) GetAllQueuesSummary

func (qm *QueueManager) GetAllQueuesSummary() map[string]int

GetAllQueuesSummary returns a summary map of server name to queue length.

func (*QueueManager) GetNextBatch

func (qm *QueueManager) GetNextBatch(targetServer string, limit int) []*QueuedPlayer

GetNextBatch pops up to limit players from targetServer's queue.

func (*QueueManager) GetPosition

func (qm *QueueManager) GetPosition(id uuid.UUID) (targetServer string, position int, total int, found bool)

GetPosition returns the target server, 1-indexed position, total players, and whether the player is queued.

func (*QueueManager) GetQueueCount

func (qm *QueueManager) GetQueueCount(targetServer string) int

GetQueueCount returns the count of players in a specific queue.

func (*QueueManager) Remove

func (qm *QueueManager) Remove(id uuid.UUID) (string, bool)

Remove removes a player from any queue they are in.

func (*QueueManager) TotalQueuedPlayers

func (qm *QueueManager) TotalQueuedPlayers() int

TotalQueuedPlayers returns the total count of queued players across all servers.

type QueuedPlayer

type QueuedPlayer struct {
	Player       proxy.Player
	TargetServer string
	EnqueuedAt   time.Time
}

QueuedPlayer stores a player waiting in a reconnect queue.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL