skinschanger

package module
v0.0.0-...-fdf3d38 Latest Latest
Warning

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

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

README

🎭 Gate SkinsChanger

A high-performance, resilient, zero-fluff skin management extension built natively for the Minekube Gate Minecraft proxy.


✨ Features

  • 🌐 Multi-Provider Resilient Skin Resolution:
    • Automatically queries official Mojang Sessionserver, falling back to Ashcon API and PlayerDB API to prevent Mojang rate limits.
  • 🖼️ Custom URL Skins via Mineskin:
    • Convert any direct PNG image URL into a signed Mojang skin using /skin url <url> [classic|slim].
  • ⚡ In-Game Live Skin Refresh:
    • Changing skin in-game seamlessly refreshes the player's connection to the current backend server so their new skin renders immediately without proxy disconnection.
  • 🔓 Offline Mode Auto-Skin:
    • Automatically fetches and applies genuine Java Edition skins for cracked / offline players who join with real Minecraft usernames.
  • 💾 Fast & Safe Storage:
    • Atomic JSON persistence for player skin assignments (users_skins.json).
    • TTL-based in-memory and on-disk texture cache (skins_cache.json) to minimize redundant network lookups.
  • ⏱️ Anti-Spam Cooldowns:
    • Configurable cooldown timer with permission bypass for staff and VIPs.
  • 🛡️ Granular Brigadier Permissions:
    • Every single command and subcommand has its own permission node.

⌨️ Commands

Command Permission Description
/skin set <target> skinschanger.set Set your skin to any Minecraft player or skin name.
/skin setfor <player> <target> skinschanger.setfor Set another player's skin (Admin).
/skin url <url> [classic|slim] skinschanger.url Set your skin from a direct image URL.
/skin urlfor <player> <url> [model] skinschanger.urlfor Set another player's skin from a URL (Admin).
/skin clear (or /skin reset) skinschanger.clear Reset your skin back to your default/Mojang skin.
/skin clearfor <player> skinschanger.clearfor Reset another player's skin.
/skin update skinschanger.update Force re-fetch the latest skin from Mojang session servers.
/skin info [player] skinschanger.info View active skin details, arm model, and texture URL.
/skin reload skinschanger.reload Reload skins.toml and skin caches.

Aliases: /skin, /skins, /skinchange, /skinchanger, /skinschanger


🛡️ Permissions

  • skinschanger.admin: Full access to all skinschanger features.
  • skinschanger.use: Access to basic /skin help and info.
  • skinschanger.set: Set your own skin via player name.
  • skinschanger.setfor: Set skins for other players.
  • skinschanger.url: Set your skin from image URLs.
  • skinschanger.urlfor: Set image URL skins for other players.
  • skinschanger.clear: Reset your own skin.
  • skinschanger.clearfor: Reset other players' skins.
  • skinschanger.update: Re-fetch your active skin.
  • skinschanger.info: View skin diagnostics.
  • skinschanger.reload: Reload configuration.
  • skinschanger.bypass.cooldown: Bypass cooldown between skin changes.

⚙️ Configuration (config/skins.toml)

enabled = true
auto_skin_offline = true
default_skin = ""
cache_ttl = "24h"
cooldown = "10s"
mineskin_api_key = ""
refresh_on_change = true

[custom_skins]
# [custom_skins.herobrine]
# value = "eyJ0..."
# signature = "..."
# model = "classic"

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Plugin = proxy.Plugin{
	Name: "SkinsChanger",
	Init: func(ctx context.Context, p *proxy.Proxy) error {
		log := logr.FromContextOrDiscard(ctx)

		cfgStore := NewConfigStore("config")
		cfg, err := cfgStore.Load()
		if err != nil {
			log.Error(err, "Failed to load skins.toml")
			return err
		}

		store := storage.NewStorage("config", cfg.ParsedCacheTTL)
		if err := store.Load(); err != nil {
			log.Error(err, "Failed to initialize skins storage")
			return err
		}

		skinFetcher := fetcher.NewMultiProviderFetcher(cfg.MineskinAPIKey, 0)

		cfgSnapshotProvider := func() *manager.ConfigSnapshot {
			current := cfgStore.Get()
			if current == nil {
				return nil
			}
			customMap := make(map[string]struct {
				Value     string
				Signature string
				Model     string
			}, len(current.CustomSkins))
			for k, v := range current.CustomSkins {
				customMap[k] = struct {
					Value     string
					Signature string
					Model     string
				}{
					Value:     v.Value,
					Signature: v.Signature,
					Model:     v.Model,
				}
			}

			return &manager.ConfigSnapshot{
				Enabled:         current.Enabled,
				AutoSkinOffline: current.AutoSkinOffline,
				DefaultSkin:     current.DefaultSkin,
				CacheTTL:        current.ParsedCacheTTL,
				Cooldown:        current.ParsedCooldown,
				RefreshOnChange: current.RefreshOnChange,
				CustomSkins:     customMap,
			}
		}

		mgr := manager.NewManager(p, skinFetcher, store, cfgSnapshotProvider)

		event.Subscribe(p.Event(), 0, func(e *proxy.GameProfileRequestEvent) {
			newProf := mgr.ProcessProfileRequest(context.Background(), e.Original(), e.OnlineMode())
			e.SetGameProfile(newProf)
		})

		event.Subscribe(p.Event(), 0, func(e *proxy.ServerPostConnectEvent) {
			mgr.OnServerPostConnect(e.Player())
		})

		reloadFn := func() error {
			newCfg, err := cfgStore.Load()
			if err != nil {
				return err
			}
			_ = store.SaveCache()
			_ = store.Load()
			log.Info("SkinsChanger configuration reloaded", "enabled", newCfg.Enabled)
			return nil
		}
		command.RegisterCommands(p, mgr, reloadFn)

		log.Info("SkinsChanger loaded successfully (Mojang, Ashcon, PlayerDB, Mineskin active)")
		return nil
	},
}

Plugin is the SkinsRestorer-like skin management extension for Minekube Gate.

Functions

This section is empty.

Types

type Config

type Config struct {
	Enabled         bool                  `toml:"enabled"`
	AutoSkinOffline bool                  `toml:"auto_skin_offline"`
	DefaultSkin     string                `toml:"default_skin"`
	CacheTTLStr     string                `toml:"cache_ttl"`
	CooldownStr     string                `toml:"cooldown"`
	MineskinAPIKey  string                `toml:"mineskin_api_key"`
	RefreshOnChange bool                  `toml:"refresh_on_change"`
	CustomSkins     map[string]CustomSkin `toml:"custom_skins"`

	ParsedCacheTTL time.Duration `toml:"-"`
	ParsedCooldown time.Duration `toml:"-"`
}

Config represents the TOML configuration file for gate-skinschanger.

type ConfigStore

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

ConfigStore manages loading and thread-safe access to Config.

func NewConfigStore

func NewConfigStore(configDir string) *ConfigStore

NewConfigStore initializes a ConfigStore for the given config directory.

func (*ConfigStore) Get

func (s *ConfigStore) Get() *Config

Get returns the current active configuration snapshot.

func (*ConfigStore) Load

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

Load loads or creates skins.toml with defaults.

type CustomSkin

type CustomSkin struct {
	Value     string `toml:"value"`
	Signature string `toml:"signature"`
	Model     string `toml:"model"`
}

CustomSkin represents a pre-signed alias skin.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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