Documentation
¶
Index ¶
- Variables
- func FormatText(input string) c.Component
- func ReplacePlaceholders(template string, ctx *PlaceholderContext) string
- type Config
- type ConfigStore
- type Group
- type PlaceholderContext
- type PlayerStateManager
- func (sm *PlayerStateManager) GetCustomName(id uuid.UUID) (string, bool)
- func (sm *PlayerStateManager) IsTabDisabled(id uuid.UUID) bool
- func (sm *PlayerStateManager) RemovePlayer(id uuid.UUID)
- func (sm *PlayerStateManager) ResetCustomName(id uuid.UUID) bool
- func (sm *PlayerStateManager) SetCustomName(id uuid.UUID, name string)
- func (sm *PlayerStateManager) ToggleTab(id uuid.UUID) bool
- type Role
- type TabManager
Constants ¶
This section is empty.
Variables ¶
var Plugin = proxy.Plugin{ Name: "BetterTab", Init: func(ctx context.Context, p *proxy.Proxy) error { log := logr.FromContextOrDiscard(ctx) configPath := filepath.Join("config", "bettertab.toml") store := NewConfigStore(configPath) if _, err := store.Load(); err != nil { log.Error(err, "Failed to load bettertab configuration") return err } states := NewPlayerStateManager() tabMgr := NewTabManager(p, store, states, log) tabMgr.Start(ctx) event.Subscribe(p.Event(), 0, func(e *proxy.ServerPostConnectEvent) { sName := "unknown" if e.Player().CurrentServer() != nil { sName = e.Player().CurrentServer().Server().ServerInfo().Name() } tabMgr.HandleServerSwitch(e.Player(), sName) }) event.Subscribe(p.Event(), 0, func(e *proxy.DisconnectEvent) { tabMgr.HandlePlayerDisconnect(e.Player()) }) buildCommand := func(name string) brigodier.LiteralNodeBuilder { return brigodier.Literal(name). Executes(command.Command(func(cmdCtx *command.Context) error { return cmdCtx.Source.SendMessage(&c.Text{ Content: "§b[BetterTab for Gate] §7by Andrei\n" + "§7Commands: §f/" + name + " reload§7, §f/" + name + " name <name>§7, §f/" + name + " reset§7, §f/" + name + " toggle§7, §f/" + name + " info", }) })). Then(brigodier.Literal("reload"). Requires(command.Requires(func(c *command.RequiresContext) bool { return c.Source.HasPermission("bettertab.reload") || c.Source.HasPermission("bettertab.admin") || c.Source.HasPermission("*") })). Executes(command.Command(func(cmdCtx *command.Context) error { if _, err := store.Load(); err != nil { return cmdCtx.Source.SendMessage(&c.Text{Content: fmt.Sprintf("§c[BetterTab] Reload failed: %v", err)}) } tabMgr.UpdateAll() return cmdCtx.Source.SendMessage(&c.Text{Content: "§a[BetterTab] Configuration reloaded successfully!"}) })), ). Then(brigodier.Literal("reset"). Requires(command.Requires(func(c *command.RequiresContext) bool { return c.Source.HasPermission("bettertab.reset") || c.Source.HasPermission("bettertab.name") || c.Source.HasPermission("bettertab.admin") || c.Source.HasPermission("*") })). Executes(command.Command(func(cmdCtx *command.Context) error { player, ok := cmdCtx.Source.(proxy.Player) if !ok { return cmdCtx.Source.SendMessage(&c.Text{Content: "§cOnly players can reset their TAB name."}) } if states.ResetCustomName(player.ID()) { tabMgr.UpdateAll() return cmdCtx.Source.SendMessage(&c.Text{Content: "§a[BetterTab] Your TAB display name has been reset."}) } return cmdCtx.Source.SendMessage(&c.Text{Content: "§e[BetterTab] You do not have a custom TAB name set."}) })), ). Then(brigodier.Literal("toggle"). Executes(command.Command(func(cmdCtx *command.Context) error { player, ok := cmdCtx.Source.(proxy.Player) if !ok { return cmdCtx.Source.SendMessage(&c.Text{Content: "§cOnly players can toggle their TAB list."}) } enabled := states.ToggleTab(player.ID()) tabMgr.UpdateAll() if enabled { return cmdCtx.Source.SendMessage(&c.Text{Content: "§a[BetterTab] Custom TAB list enabled."}) } return cmdCtx.Source.SendMessage(&c.Text{Content: "§e[BetterTab] Custom TAB list disabled."}) })), ). Then(brigodier.Literal("info"). Executes(command.Command(func(cmdCtx *command.Context) error { player, ok := cmdCtx.Source.(proxy.Player) if !ok { cfg := store.GetConfig() return cmdCtx.Source.SendMessage(&c.Text{ Content: fmt.Sprintf("§b[BetterTab Status] §7Groups: %d, Online: %d", len(cfg.Groups), len(p.Players())), }) } sName := "none" if cs := player.CurrentServer(); cs != nil { sName = cs.Server().ServerInfo().Name() } group := store.GetGroupForServer(sName) gName := "none" if group != nil { gName = group.Name } _, rName := store.GetRoleWithInfo(player.Username()) return cmdCtx.Source.SendMessage(&c.Text{ Content: fmt.Sprintf("§b[BetterTab Info]\n§7Server: §f%s\n§7Group: §f%s\n§7Role: §f%s\n§7Ping: §f%dms", sName, gName, rName, player.Ping().Milliseconds()), }) })), ). Then(brigodier.Literal("name"). Requires(command.Requires(func(c *command.RequiresContext) bool { return c.Source.HasPermission("bettertab.name") || c.Source.HasPermission("bettertab.admin") || c.Source.HasPermission("*") })). Then(brigodier.Argument("new_name", brigodier.String). Executes(command.Command(func(cmdCtx *command.Context) error { player, ok := cmdCtx.Source.(proxy.Player) if !ok { return cmdCtx.Source.SendMessage(&c.Text{Content: "§cOnly players can set their TAB name."}) } newName := cmdCtx.String("new_name") if strings.TrimSpace(newName) == "" { return cmdCtx.Source.SendMessage(&c.Text{Content: "§cName cannot be empty."}) } if (strings.Contains(newName, "&") || strings.Contains(newName, "§") || strings.Contains(newName, "<")) && !player.HasPermission("bettertab.name.color") && !player.HasPermission("bettertab.admin") && !player.HasPermission("*") { return cmdCtx.Source.SendMessage(&c.Text{Content: "§cYou do not have permission to use color or formatting tags in your TAB name (bettertab.name.color)."}) } states.SetCustomName(player.ID(), newName) tabMgr.UpdateAll() return cmdCtx.Source.SendMessage(&c.Text{ Content: fmt.Sprintf("§a[BetterTab] Your TAB display name is now: §f%s", newName), }) })), ), ) } p.Command().Register(buildCommand("bettertab")) p.Command().Register(buildCommand("btab")) p.Command().Register( brigodier.Literal("gatetab"). Executes(command.Command(func(cmdCtx *command.Context) error { return cmdCtx.Source.SendMessage(&c.Text{ Content: "§bUse §f/bettertab §b(or §f/btab§b) for tab list commands.", }) })), ) log.Info("BetterTab loaded (custom tablist formatting, animations & groups active)") return nil }, }
Plugin is the Gate BetterTab tablist & player list customization plugin.
Functions ¶
func FormatText ¶
FormatText parses MiniMessage, MineDown, and Legacy color syntax into a native Minecraft Component.
func ReplacePlaceholders ¶
func ReplacePlaceholders(template string, ctx *PlaceholderContext) string
ReplacePlaceholders evaluates and replaces all built-in, contextual, and conditional placeholders.
Types ¶
type Config ¶
type Config struct {
UpdateIntervalMs int `toml:"update_interval_ms"`
SortPlayers bool `toml:"sort_players"`
DefaultGroup string `toml:"default_group"`
Roles map[string]Role `toml:"roles"`
PlayerRoles map[string]string `toml:"player_roles"`
Groups []Group `toml:"groups"`
}
Config represents the complete Velocitab configuration.
type ConfigStore ¶
type ConfigStore struct {
// contains filtered or unexported fields
}
ConfigStore provides thread-safe configuration management.
func NewConfigStore ¶
func NewConfigStore(path string) *ConfigStore
NewConfigStore creates a new ConfigStore.
func (*ConfigStore) GetConfig ¶
func (cs *ConfigStore) GetConfig() *Config
GetConfig returns the current Config snapshot.
func (*ConfigStore) GetGroupForServer ¶
func (cs *ConfigStore) GetGroupForServer(serverName string) *Group
GetGroupForServer finds the matching Group for a given server name.
func (*ConfigStore) GetRoleForPlayer ¶
func (cs *ConfigStore) GetRoleForPlayer(username string) Role
GetRoleForPlayer returns the role associated with a player name.
func (*ConfigStore) GetRoleWithInfo ¶
func (cs *ConfigStore) GetRoleWithInfo(username string) (Role, string)
GetRoleWithInfo returns the role and its role key name.
func (*ConfigStore) Load ¶
func (cs *ConfigStore) Load() (*Config, error)
Load loads or creates the configuration file.
type Group ¶
type Group struct {
Name string `toml:"name"`
Servers []string `toml:"servers"`
Headers []string `toml:"headers"`
Format string `toml:"format"`
OnlyListPlayersInSameServer bool `toml:"only_list_players_in_same_server"`
SortingPlaceholders []string `toml:"sorting_placeholders"`
PlaceholderReplacements map[string]map[string]string `toml:"placeholder_replacements"`
// contains filtered or unexported fields
}
Group represents a server group tablist definition.
func (*Group) MatchesServer ¶
MatchesServer checks if a server matches any of the group's server patterns.
type PlaceholderContext ¶
type PlaceholderContext struct {
Player proxy.Player
CustomName string
ServerName string
ProxyPlayerCount int
MaxPlayers int
LocalPlayerCount int
GroupPlayerCount int
ServerCounts map[string]int
GroupCounts map[string]int
Role Role
RoleName string
Group *Group
}
PlaceholderContext provides metadata for placeholder evaluation.
type PlayerStateManager ¶
type PlayerStateManager struct {
// contains filtered or unexported fields
}
PlayerStateManager tracks in-memory custom names and preferences for players.
func NewPlayerStateManager ¶
func NewPlayerStateManager() *PlayerStateManager
NewPlayerStateManager creates a new PlayerStateManager.
func (*PlayerStateManager) GetCustomName ¶
func (sm *PlayerStateManager) GetCustomName(id uuid.UUID) (string, bool)
GetCustomName returns the custom nickname if present.
func (*PlayerStateManager) IsTabDisabled ¶
func (sm *PlayerStateManager) IsTabDisabled(id uuid.UUID) bool
IsTabDisabled checks if custom tab formatting is disabled for a player.
func (*PlayerStateManager) RemovePlayer ¶
func (sm *PlayerStateManager) RemovePlayer(id uuid.UUID)
RemovePlayer cleans up player state on disconnect.
func (*PlayerStateManager) ResetCustomName ¶
func (sm *PlayerStateManager) ResetCustomName(id uuid.UUID) bool
ResetCustomName clears any custom nickname for a player.
func (*PlayerStateManager) SetCustomName ¶
func (sm *PlayerStateManager) SetCustomName(id uuid.UUID, name string)
SetCustomName sets a custom display nickname for a player.
type Role ¶
type Role struct {
Prefix string `toml:"prefix"`
Suffix string `toml:"suffix"`
Weight int `toml:"weight"`
}
Role represents prefix, suffix, and sorting weight.
type TabManager ¶
type TabManager struct {
// contains filtered or unexported fields
}
TabManager manages the live state, animation ticks, and tablist synchronization for all players.
func NewTabManager ¶
func NewTabManager(p *proxy.Proxy, store *ConfigStore, states *PlayerStateManager, log logr.Logger) *TabManager
NewTabManager creates a new TabManager instance.
func (*TabManager) HandlePlayerDisconnect ¶
func (tm *TabManager) HandlePlayerDisconnect(player proxy.Player)
HandlePlayerDisconnect cleans up state and updates remaining players upon logout.
func (*TabManager) HandleServerSwitch ¶
func (tm *TabManager) HandleServerSwitch(player proxy.Player, targetServer string)
HandleServerSwitch immediately updates tablist state upon switching servers.
func (*TabManager) Start ¶
func (tm *TabManager) Start(ctx context.Context)
Start begins the background update loop.
func (*TabManager) UpdateAll ¶
func (tm *TabManager) UpdateAll()
UpdateAll synchronizes headers, footers, display names, and list ordering across all active connections.