Перейти к содержимому
Mineforgian

XGamemode

You're not switching modes, you're switching dimensions.

Загрузки
2K
Подписчики
4
Обновлён
29 апреля 2026 г.
Лицензия
CC-BY-NC-SA-4.0

Опубликован 23 января 2025 г.

XGamemode Logo

You're not switching modes, you're switching dimensions.

2000 Downloads?! Thanks!

1 Feature

1.1 Offline Player

You can change the gamemode of a player, even he (or she) was offline.

1.2 Permissions

Allow permission group plugins (like Luckperms) to manage which gamemodes can be switched to and which gamemodes cannot be switched to.

e.g. Allow to switch to survival mode but not to any other mode.

1.3 Alias

/gamemode is simplified to /gm and /g.

(of course you can still use /gamemode)

1.4 Cross-Server Storage

Support MySQL and SQLite storage backends. Player gamemodes are saved and synchronized across multiple servers in a network.

1.5 Folia Support

Fully compatible with Folia servers — using region-based scheduling for optimal performance.

1.6 Lightweight

The Plugin size is only ~25 KB and has almost no impact on server performance!

2 Caution

2.1 Shortcut Keys

If you need to use shortcut keys such as F3+F4, please install another plugin F3NPerm.

2.2 Versions

1.8.X +

All Bukkit based server (Paper, Spigot, Purpur, Folia, etc.)

3 Commands

- /xgamemode
- /xgamemode reload
- /gm <mode>
- /gm <mode> <player>
- /gm <mode> -all
- /gm <mode> -online
- /gm <mode> -offline

/g = /gm = /gamemode
/xg = /xgm = /xgamemode

4 Permission Node

- xgamemode.survival
- xgamemode.creative
- xgamemode.adventure
- xgamemode.spectator
- xgamemode.others.survival
- xgamemode.others.creative
- xgamemode.others.adventure
- xgamemode.others.spectator
- xgamemode.reload
- xgamemode.admin

5 Custom Config

5.1 Basic Settings

lang: en-US

Language setting

default_gamemode: survival

If a new player joined your server, which gamemode should he (she) be?

survival, creative, adventure, spectator

show_logo: true

Show logo or not when plugin enabled.

true, false

debug: false

Enable debug logging for troubleshooting.

true, false

5.2 Storage Configuration

storage:
  type: yaml

Storage backend type: yaml, sqlite, mysql

SQLite Example

storage:
  type: sqlite
  # No additional config needed — data.db will be created automatically

MySQL Example

storage:
  type: mysql
  mysql:
    host: localhost
    port: 3306
    database: minecraft
    username: root
    password: your_password

5.3 Custom Messages

messages: 
  en-US: # Original en-US Language by Wind_Blood
    reloaded: "&aXGamemode Reloaded!"
    # ......
  custom_lang: # Add Your Custom Language!
    reloaded: "Thank you for using XGamemode!"
    # ......

Customizable Messages. You can add your own new language here!

5.4 An Example config.yml

lang: msgForMyServer
default_gamemode: survival
show_logo: false
debug: false
storage:
  type: yaml
messages: 
  msgForMyServer:
    reloaded: "很明显作者是中国人 会说中文很正常 然后我也不知道为什么我要在一堆英文里写一段中文 挺好笑的 哈哈哈 对了 看到我就是缘分 XGamemode 官方 QQ 反馈交流群:点击链接加入群聊【XGamemode 反馈群】:https://qm.qq.com/q/rpTk5LK7w4 欢迎加"
    usage: "&cUsage: /gm <mode> [player/-all/-online/-offline]"
    console_usage: "&cUsage: /gm <mode> <player/-all/-online/-offline>"
    invalid_gamemode: "&cInvalid Gamemode!"
    invalid_player: "&cInvalid Player!"
    gamemode_self: "&aYour Gamemode Changed to {mode}!"
    gamemode_all: "&aChanged All Player's Gamemode to {mode}!"
    gamemode_online: "&aAll Online Player's Gamemode Changed to {mode}!"
    gamemode_offline: "&aAll Offline Player's Gamemode Changed to {mode}!"
    no_permission: "&cYOU ARE NOT ALLOWED TO DO THAT!"
    gamemode_other: "&a{player}'s Gamemode Changed to {mode}!"
    gamemode_other_offline: "&a{player} (Offline)'s Gamemode Changed to {mode}!"

6 Developer API (Async)

Get Instance

Main plugin = Main.getInstance();

Get Player Gamemode

plugin.getPlayerMode(playerUUID).thenAccept(mode -> {
    if (mode != null) {
        // mode is GameMode.SURVIVAL, CREATIVE, ADVENTURE, or SPECTATOR
        // Player is online or has saved data
    } else {
        // Player not found or no saved gamemode yet
    }
});

Set Player Gamemode

plugin.setPlayerMode(playerUUID, GameMode.SURVIVAL).thenAccept(success -> {
    if (success) {
        // Gamemode changed and saved successfully
        // Works for both online and offline players
    } else {
        // Failed to save (database error, etc.)
    }
});

Listen to Gamemode Changes

@EventHandler
public void onGamemodeChange(GamemodeChangeEvent event) {
    Player player = event.getPlayer();      // Online player only
    GameMode oldMode = event.getOldMode();
    GameMode newMode = event.getNewMode();
}

Note: All APIs are fully async and thread-safe. The GamemodeChangeEvent is only fired for online players when their mode is actually changed. The getPlayerMode() method returns null if the player has no saved gamemode data.

7 Reprint Post

www.minebbs.com/resources/xgamemode.11041

hangar.papermc.io/Wind_Blood/XGamemode

modrinth.com/plugin/xgamemode

8 Feedback

Just e-mail me with [email protected]. Thanks!

Ченджлог

4.6-betaБета26.1, 26.1.1, 26.1.2 · 29 апреля 2026 г.

Added

  • API security control: allow_to_use_api config option (default: true) to prevent other plugins from accessing the API when set to false
  • IllegalStateException with clear message when API access is disabled

Fixed

  • None

Changed

  • None

Removed

  • None
4.6-alphaАльфа26.1, 26.1.1, 26.1.2 · 24 апреля 2026 г.

Added

  • Storage type change detection on reload - warns admin to restart server instead of loading mismatched data

Fixed

  • MAX_MAPPINGS limit now trims oldest entries instead of only warning
  • YAML name fallback uses full UUID string instead of substring(0, 8) to prevent potential boundary issues

Changed

  • lastStorageType field tracks storage type across reloads for change detection
4.5-rlsРелиз26.1, 26.1.1, 26.1.2 · 23 апреля 2026 г.

Added

  • Folia scheduler support with automatic fallback to Bukkit scheduler
  • SQLite ON CONFLICT detection for better UPSERT performance
  • UUID-based write locks for thread-safe player data operations
  • Player save queue (pendingSaves) to serialize async database writes per UUID
  • Offline player name caching to reduce I/O operations during batch processing
  • YAML save debouncing with resettable timer (scheduleYamlSave)
  • Atomic reference for nameToUUIDs and uuidToName maps for safe reload
  • Legacy YAML format detection and migration support (uuid: GAMEMODE)
  • Support for setting gamemode for players who have never joined the server
  • GamemodeChangeEvent custom event for API integration
  • -all, -online, -offline batch operation flags
  • Database transaction support for batch offline player updates
  • Automatic config.yml key merging with missing defaults preservation
  • Connection retry mechanism with configurable attempts and delay
  • Debug logging controlled by config option

Fixed

  • NPE in onDisable() when storageType is not "yaml" and yamlCache is null
  • Race condition in onPlayerJoin where async save could overwrite recently set gamemode
  • SQL operation silent failure - now logs error after max retry attempts
  • Write lock timeout now logs debug message instead of failing silently
  • Skipped offline players during batch operations now logged with warning count
  • Config merge now properly adds missing keys without overwriting existing values
  • Migration now handles both legacy (flat) and modern (nested) YAML formats

Changed

  • Replaced pendingSaves Set<String> with Map<UUID, CompletableFuture<Void>> for per-UUID queuing
  • Refactored tryWriteLock pattern into withWriteLock helper method
  • Optimized processOfflinePlayersData with name cache and early empty check
  • Cached UPSERT SQL generation to avoid repeated ON CONFLICT detection
  • Message placeholder replacement now uses Matcher.quoteReplacement() to escape $ and \
  • Folia reflection now uses Consumer.class with proper generic erasure handling
  • savePlayerModeDirect now updates memory cache immediately before async DB write
  • Batch migration now uses incremental executeBatch() to avoid memory issues

Removed

  • Legacy yamlCache.set(uuid.toString(), mode.name()) flat format write
  • Redundant saveYamlCache() call when using database storage
  • Unused SAVE_DELAY field from earlier versions
4.5-betaБета26.1, 26.1.1, 26.1.2 · 20 апреля 2026 г.

Added

  • Added database connection retry mechanism with MAX_RETRY_ATTEMPTS (3) and RETRY_DELAY_MS (1000ms) for MySQL/SQLite.
  • Added ensureConnection() and reconnect() methods for automatic database connection recovery.
  • Added executeWithRetry(), executeUuidQueryWithRetry(), and executeVoidWithRetry() helper methods to wrap SQL operations with retry logic.
  • Added cross-server compatibility for offline player targeting: When a player name is not found in the local memory cache (nameToUUIDs), the plugin now queries the database directly via queryUuidByName().
  • Added -offline, -online, and -all flags to batch target processing.
  • Added PlayerQuitEvent handler to clean up playerLocks when a player disconnects, preventing potential memory leaks.
  • Added proper index creation handling (createIndexSafe) with fallback checks for MySQL vs SQLite metadata differences.
  • Added GamemodeChangeEvent custom event that is called when a player's gamemode is changed by the plugin.

Fixed

  • Fixed database index creation to check for existing indexes before attempting CREATE INDEX to prevent SQL errors on MySQL.
  • Fixed onPlayerJoin event to use Optional for loadPlayerMode and properly handle null stored gamemodes without triggering unnecessary errors.
  • Fixed savePlayerMode to ensure player name is correctly updated in the database when saving gamemode.
  • Fixed setPlayerMode API method to handle exceptions and return false on failure rather than always returning true.
  • Fixed YAML caching to properly wrap saves in synchronized blocks to prevent concurrent modification issues.
  • Fixed loadData to properly populate nameToUUIDs and uuidToName mappings from both YAML and SQL storage.
  • Fixed handleSinglePlayer to check for player name in database when local cache misses, improving cross-server or bungeecord network support.
  • Fixed processOfflinePlayersData to use database batch operations for SQL storage instead of locking each player individually, greatly improving performance.

Changed

  • Refactored database initialization to use a dedicated dbUrl, dbUser, and dbPass fields instead of constructing the connection string inline.
  • Updated loadPlayerMode to use executeWithRetry for robust SQL error handling and Optional<GameMode> return type.
  • Improved setPlayerGamemodeLocked logic to check player.isOnline() before scheduling tasks.
  • Enhanced handleGamemodeTarget to use async() for both batch and single player operations to avoid blocking the main thread.
  • Replaced explicit try-catch blocks with executeWithRetry wrappers in database operations for cleaner code.
  • Changed handleBatchPlayers and handleSinglePlayer to use sync() for sending messages to ensure thread safety.
  • Improved migrateFromYamlIfNeeded to handle ON DUPLICATE KEY UPDATE properly on MySQL.
  • Optimized processOfflinePlayersData to use batch SQL execution (executeBatch()) with BATCH_SIZE instead of individual updates.

Removed

  • Removed tryReadLock(), unlockRead(), and withReadLock() methods as they were unused in the new version.
  • Removed saveAllSync() method as it was unused.
  • Removed direct Statement usage in initDatabase() in favor of try-with-resources and prepared statements.
  • Removed redundant player mode saving in onPlayerJoin (no longer forces save if gamemode didn't change).
  • Removed manual thread sleeping in processOfflinePlayersData for SQL storage, as batch processing handles this efficiently.
4.5-alphaАльфа26.1, 26.1.1, 26.1.2 · 18 апреля 2026 г.

Added

  • Multi-storage support with automatic migration (YAML, SQLite, MySQL)
  • Database connection pooling preparation structure
  • Automatic YAML to database migration on first load
  • Fallback mechanism to YAML when database connection fails
  • ReadWriteLock implementation for finer-grained concurrency control
  • Separate read/write lock methods (tryReadLock, tryWriteLock, unlockRead, unlockWrite)
  • withReadLock and withWriteLock helper methods for cleaner lock management
  • Comprehensive API methods with CompletableFuture return types (getPlayerMode, setPlayerMode)
  • Custom GamemodeChangeEvent for third-party plugin integration
  • Static Main.getInstance() API accessor
  • Dedicated savePlayerMode and loadPlayerMode methods with storage abstraction
  • saveAllSync method for batch YAML persistence
  • closeStorage method for proper database connection cleanup
  • Storage type logging on startup when debug mode is enabled
  • Player lock map initialization with ReentrantReadWriteLock
  • Database index creation for player name lookups with fallback for SQLite compatibility

Fixed

  • Lock ownership checking before unlocking to prevent IllegalMonitorStateException
  • Thread interruption handling in lock timeout scenarios with proper flag restoration
  • Concurrent modification safety in batch player processing
  • Data consistency between online player state and persistent storage
  • Proper thread interruption propagation during executor shutdown
  • Offline player name resolution fallback chain
  • UUID validation with proper exception handling for malformed entries
  • Batch processing now includes invalid entry removal and reporting
  • YAML cache synchronization issues with synchronized blocks
  • Pending save deduplication using ConcurrentHashMap.newKeySet()

Changed

  • Storage backend from single YAML file to pluggable architecture (YAML/SQLite/MySQL)
  • Lock mechanism from ReentrantLock to ReentrantReadWriteLock for improved read concurrency
  • Database operations execute asynchronously with prepared statements
  • Player mode saving now includes timestamp field for future audit capabilities
  • Data file renamed from data.yml to structured format with separate name and gamemode fields
  • Configuration now supports storage section with type, MySQL credentials, and connection parameters
  • Migration process creates .bak backup of original YAML file
  • handleGamemodeTarget split into handleBatchPlayers and handleSinglePlayer for clarity
  • processOfflinePlayersData now returns PlayerDataResult with changed and removed counts
  • Lock acquisition timeout reduced from lock-per-operation to centralized timeout constant
  • saveDataAsync and saveDataSync replaced with storage-aware savePlayerMode and savePlayerModeLocked
  • Executor service now uses fixed thread pool of 4 instead of cached pool
  • detectServerVersion method removed as no longer needed for SPECTATOR detection
  • Database connection uses MySQL connection parameters (useSSL=false, serverTimezone=UTC, autoReconnect=true)
  • YAML cache field renamed from dataConfig to yamlCache for clarity

Removed

  • PlayerDataResult inner class (functionality merged into improved batch processing)
  • serverMinorVersion field and version-specific SPECTATOR availability checking
  • saveDataSync direct public method (replaced by saveYamlCache)
  • updateMapping standalone method (merged into player join event handling)
  • processPlayerSet helper method (consolidated into main processing flow)
  • Direct dataConfig field exposure (now accessed through synchronized yamlCache)
  • Inline database connection string building (moved to dedicated initDatabase method)
  • Redundant gamemode name storage in YAML root level (now only stored in structured format)
4.4-rlsРелиз1.21.9, 1.21.10, 1.21.11 · 22 марта 2026 г.

Added

  • Batch processing for offline player data with configurable BATCH_SIZE (default: 100) to prevent memory overflow when handling large datasets
  • Centralized constant declarations (SAVE_DELAY, MAX_MAPPINGS, BATCH_SIZE, LOCK_TIMEOUT, SHUTDOWN_TIMEOUT) for better maintainability

Fixed

  • Potential performance issue where offline player data was processed without pagination, causing high memory usage
  • Redundant BatchResult inner class replaced with local variables for cleaner code structure

Changed

  • Optimized processOfflinePlayersData method to process keys in batches instead of all at once
  • Simplified handleBatchPlayers method by removing unnecessary BatchResult wrapper class
  • Streamlined code formatting: compressed multi-line conditionals into single lines where appropriate
  • Improved code readability with consistent constant usage across all methods

Removed

  • Redundant BatchResult inner class (functionality replaced with local variables)
  • Hardcoded thread pool size value (now using THREAD_POOL_SIZE constant)
4.4-betaБета1.21.9, 1.21.10, 1.21.11 · 27 февраля 2026 г.

Added

  • Added debug logging for lock timeouts and interruptions
  • Added pendingSaves set to prevent multiple concurrent save operations

Changed

  • Improved Folia scheduler fallback logic: sync tasks now use Bukkit scheduler instead of direct run() to prevent main thread blocking
  • Enhanced version detection to properly handle 1.8.8 format

Fixed

  • Fixed potential main thread blocking when Folia scheduling fails
  • Fixed lock timeout handling with proper debug logging for easier troubleshooting
  • Fixed duplicate data saving by implementing pending saves tracking with ConcurrentHashMap.newKeySet()
  • Fixed version parsing for formats like 1.8.8
4.4-alphaАльфа1.21.9, 1.21.10, 1.21.11 · 26 февраля 2026 г.

Added

  • Added comprehensive thread-safe locking mechanism using ReentrantLock for player data
  • Added support for Folia server software with unified scheduling API
  • Added debug mode with detailed logging for troubleshooting
  • Added maximum mappings limit (10,000) to prevent memory leaks
  • Added delayed save system (20 ticks) to batch disk writes
  • Added batch processing commands: /gamemode -all, -online, -offline
  • Added server version detection for spectator mode compatibility
  • Added configuration auto-merge to keep config.yml up-to-date
  • Added proper error handling with stack traces in debug mode
  • Added player quit event handling to clean up locks

Changed

  • Complete architecture rewrite with modular design
  • Migrated from coarse synchronized blocks to fine-grained ReentrantLock
  • Improved thread management: replaced scattered async calls with centralized 4-thread pool
  • Enhanced name-to-UUID mapping with bidirectional cache (uuidToName + nameToUUIDs)
  • Optimized data saving: from immediate writes to delayed batch saving
  • Unified task scheduling for Bukkit and Folia platforms
  • Improved command tab completion with offline player suggestions
  • Refactored permission system with granular control (xgamemode.admin, xgamemode.others.*)
  • Enhanced logging with color-coded console messages
  • Restructured message system with better fallback support

Fixed

  • Fixed thread safety issues in name-to-UUID mappings
  • Fixed memory leak where player data never got cleaned up
  • Fixed data loss risk from frequent uncoordinated saves
  • Fixed incorrect console sender usage in logo display
  • Fixed spectator mode detection on older server versions
  • Fixed race conditions in player join event handling
  • Fixed improper synchronization in data file access
  • Fixed tab completion showing duplicate entries
  • Fixed permission checks for offline player modifications

Removed

  • Removed excessive synchronized blocks causing performance bottlenecks
  • Removed redundant saveData() calls after every player join
  • Removed unsafe HashSet usage in concurrent mappings
  • Removed mixed tab/space indentation (now using consistent spaces)
  • Removed deprecated scheduler implementations

I won't be sad when the final JAR file is big anymore! Stable > Everything! Screw you, JAR size!

Комментарии

Загружаем…