Configuration Files

Detailed specifications of variables and settings inside the plugin configuration files.

Main Configuration File (config.yml)

Located at plugins/ShyamDuels/config.yml. This file manages module toggles, world structures, command limitations, and party and duel matchmaking metrics.

Module Toggles

Disable specific sub-modules server-wide:

yamlLicense: ""                    # BuiltByBit license key (injected at build time)
debug: false                    # Verbose display/logging output

server:
  name: Practice Server         # Shown in messages and display configs

modules:
  queue: true
  ffa: true
  party: true
  spectate: true
  friends: true
  duels: true
  custom-kits: true
  killstreaks: true
  auto-gg: true
  action-bar: true
  tab-list: true
  scoreboard: true
  practice-kit-editor: true
  practice-duel-command: true
  practice-right-click-duel: true
  practice-death-messages: true
  practice-golden-head: true
  practice-totem-practice: true
  armor-trimmer: true
  enchant-table: true
  shield-customizer: true
  kit-editor-armor-trimmer: true
  kit-room-shulker-editor: true

See Practice Features Country Flags for practice module details.

World and Arena Configurations

yamlarenas:
  world: Arenas               # Base void world; clone worlds become Arenas1, Arenas2, …
  difficulty: HARD              # Applied to base + clone worlds (HARD for crystal/anchor damage)
  cloning: 0                  # Extra world copies for duel capacity (0 = off; FFA never cloned)
  reset-clones-on-startup: true # Delete and re-clone Arenas1/2/… from base world each boot
  reset-on-startup: true    # Sanitize the base arena world and FAWE-reset template arenas on boot
  build-height-limit-offset: 2 # Height allowance above the arena top where placing blocks is still allowed
  reset-queue:
    max-concurrent: 3         # Max simultaneous FAWE arena restorations
    ffa-one-at-a-time: true   # Only one FFA reset at a time (duel resets unaffected)
  clear-entities-on-startup: true # Remove dropped items/non-player entities in loaded worlds
  sanitize-clone-worlds: true # Strip entity data from copied world folders
  gamerules:                  # Applied to base + clone worlds on startup/load (all optional)
    daylight-cycle: false     # freeze the time of day
    weather-cycle: false      # freeze the weather
    mob-spawning: false       # doMobSpawning (passive)
    monster-spawning: false   # hostile monsters
    mob-griefing: false       # mobGriefing
    show-advancement-messages: false
    locator-bar: false        # player locator HUD
    show-death-messages: false
    natural-regeneration: true
    spawn-phantoms: false
    spawn-patrols: false
    spawn-wandering-traders: false
    spawn-wardens: false
    raids: false              # false = raids disabled
    random-tick-speed: 0      # stop block/crop ticking
    fire-spread: 0            # no fire spread

See the Arena Setup: Arena Cloning guide for how cloning works. Build template arenas only in the base Arenas world, not in Arenas1, Arenas2, or other auto-generated clone worlds.

Lobby World Protection

The lobby location and optional world protection are configured under lobby:. When protect-world is enabled, the plugin disables PvP, mob spawning, mob griefing, and freezes time/weather in the lobby world via gamerules, applied on startup and whenever the lobby world loads. adventure-mode puts players into Adventure mode whenever they are sent to spawn (admins with shyamduels.admin.bypass and Creative/Spectator players are left untouched).

yamllobby:
  world: spawn
  x: 0.0
  # ... y, z, yaw, pitch ...
  adventure-mode: true        # Force Adventure mode in the lobby
  protect-world: true         # Apply the protective gamerules below to the lobby world
  gamerules:                  # Each value is optional and falls back to the shown default
    mob-spawning: false       # doMobSpawning (passive)
    monster-spawning: false   # hostile monsters
    mob-griefing: false       # mobGriefing
    daylight-cycle: false     # freeze the time of day
    weather-cycle: false      # freeze the weather
    show-death-messages: false
    random-tick-speed: 0      # stop block/crop ticking

Enabled Worlds

Restrict certain features to specific lobby/world names:

yamlenabled-worlds:
  kit-editor:
    - spawn                   # Worlds where /kiteditor is allowed
  right-click-duel:
    - spawn                   # Worlds where right-click player interaction is active

Command Restrictions

Configure command rules inside matches to block cheating or bypass attempts:

yamlcommand-restrictions:
  allowed:                    # Always permitted (plus admin bypass)
    - leave
    - leavefight
    - spawn
    - spectate
  in-party:                   # Blocked while in a party lobby
    - kiteditor
    - duel
    - ffa
    - spectate
  in-match-global:            # Blocked during any active match
    - kiteditor
    - duel
    - queue
    - play
    - ffa
    - party
  in-duel:
    mode: block-all
    commands: []
  in-ffa:
    mode: block-all
    commands: []
  in-spectate:
    mode: block-all
    commands: []

Queue and Duels

yamlqueue:
  multiple-kits: true         # Allow queuing multiple kits at once
  max-multiple-kits: 3        # Max kits queued at once (minimum 1; values <1 clamp to 1; needs multiple-kits)
  random-queue-enabled: true  # Random kit queue option
  override-conflicting-commands: true # Reroute /queue to ShyamDuels over other plugins
  modes:                      # Disable individual queue sizes
    1v1: true
    2v2: true
    3v3: true
    4v4: true
  match-found-animation:
    enabled: true
    blindness: true           # Apply blindness for the duration of the match-found animation
    blindness-duration-ticks: 200 # Safety cap (removed early by animation callback; should exceed animation length)
    totem-pop: true           # Client totem-pop on match start (bundles item overlay + particles + sound)
    totem-sound: false        # false suppresses the local vanilla pop sound; custom sound below can replace it
    sound: ""                 # Optional extra sound key; empty = no sound
    sound-volume: 1.0
    sound-pitch: 1.2
    fallback-item: DIAMOND_SWORD  # Item model shown in the pop when a kit has no icon (totem-pop only)

  indicator:
    enabled: true
    height-offset: 1.15       # Vertical offset above nametag passenger mount
    scale: 0.60               # ItemDisplay scale (0.60 balanced with nametag)
    rotation-speed-ticks: 5   # Ticks per 45° step (5 = 2s per full rotation)
    billboard: FIXED          # FIXED (world-space), VERTICAL, or CENTER
    show-to-self: false       # Show indicator to the queued player in F5

duels:
  invite-expiration: 60       # Seconds before duel invites expire
  invite-resend-cooldown: 10  # Seconds before the same sender can re-invite the same player (0 disables)
  start-countdown: 5          # Countdown before round starts
  round-system: BEST_OF       # BEST_OF (majority of rounds) or FIRST_TO (exact win count)
  end-commands: []            # Console commands run when a duel ends

leavefight:
  delay: 1                      # Teleport delay in seconds after /leavefight (0 disables the delay)

spawn:
  delay: 1                      # Teleport delay for /spawn (falls back to leavefight.delay)

Arena World Borders

Per-player, client-side red borders drawn around arenas during duels, FFA, and spectating. They are sent as packets and never modify the real server world border. See Arena Setup: Fake World Borders for the full explanation.

yamlworld-border:
  enabled: true               # Master switch; false sends no arena borders at all
  padding: 0.0                # Extra blocks on every side (0.0 hugs the corner blocks exactly)
  warning-blocks: 0           # Red "approaching border" overlay distance in blocks (0 = off)
  warning-time: 0             # Red warning overlay duration in seconds (0 = off)

world-border-shrink:
  enabled: false              # Timed per-duel border shrink (PacketWorldBorderManager + MatchBorderShrinkSession)
  kits: ["*"]                 # Kit filter ("*" = all kits)
  interval: 1m                # Shrink step interval (ms/s/m/h/d/t units, bare number = seconds)
  shrink:
    blocks-per-interval: 10.0
    duration: null             # Optional lerp duration (same units); null = instant step
  minimum-distance-from-center: 5.0  # Minimum radius; minimum diameter = 2x this value
  damage:
    enabled: true
    amount: 2.0
    interval: 1s
    buffer-blocks: 0.0

Advancements

Challenge/achievement subsystem (AdvancementManager, 36 trigger types). Full key reference lives in advancement.yml - settings, defaults, per-category tabs, per-advancement triggers/conditions/rewards. See the Advancements guide for every key, all trigger and condition types, and a step-by-step example. Key commands: /advancements (aliases /adv, /achievements, /sdadv), admin grant/revoke/reset/resetall/reload (shyamduels.advancements.admin). Event: AdvancementGrantEvent (see Events Reference). Persistence: player_advancements table.

GUI Mode (Normal vs Dialog)

Menu routing is controlled by gui.mode in config.yml (NORMAL = FastInv inventories, DIALOG = Paper 1.21.7+ dialogs from dialog-menus/*.yml, 57 files). Dialog clicks are debounced (150ms) with single-use 5-minute callbacks.

Friends

yamlfriends:
  # How long a pending friend request stays valid before it is auto-removed, in seconds.
  # Set to 0 (or negative) to disable expiry and keep requests forever.
  request-expiry-seconds: 300

Expired requests are cleaned up by a background task and also pruned whenever a player's requests are read, so a request disappears once request-expiry-seconds has passed.

Party, FFA, and Match Start

yamlparty:
  default-max-size: 8
  create-permission: shyamduels.party.create   # Permission required to run /party create
  public-permission: shyamduels.party.public   # Permission required to switch a party to public
  min-member-limit: 2
  broadcast-cooldown:
    enabled: true
    seconds: 30
  member-limit-input: sign    # chat, sign, or auto
  size-permission-prefix: shyamduels.party.size.
  size-permissions:
    shyamduels.vip: 16
    shyamduels.vipplus: 32
  invite-expiration: 60
  transfer-ownership-on-leave: true   # Present but currently unused; ownership always transfers
  announce-format: "<#FF780C>[Party Announcement] {owner}: <#FFFA45>{message}"
  external-commands:          # Route /party queue|ffa to other commands
    enabled: true
    subcommands:
      queue:
        require-in-party: true
        require-owner: true
        commands:
          - "player:queue"
      ffa:
        require-in-party: false
        commands:
          - "player:ffa"

party-chat:
  format: "7FFFF[Party] &#FFFFFF{player}: &#AAAAAA{message}"

party-ffa:
  spawn-circle-radius: 4.0

ffa:
  reset-interval-seconds: 1800
  use-match-start-options: true
  skip-countdown: false
  countdown-seconds: [5, 4, 3, 2, 1]
  random-spawn:
    enabled: false
    max-attempts: 40          # Columns to test before falling back to configured spawn

match-chat:
  default-enabled: true       # Default state of the Global Chat in Matches player setting
  isolate-match-chat: false   # Force match chat to duel/FFA participants and spectators

kill-regen:
  enabled: true               # Global master toggle
  duels: true                 # 1v1 duels
  team-battle: true           # Team queue duels
  party-vs-party: true
  party-split: true
  party-ffa: true
  ffa: true                   # Global FFA arenas

match-start:
  countdown-seconds: [5, 4, 3, 2, 1]

match-start-potions:
  enabled: true
  delay_seconds: 1
  take-from-inventory: false
  blocked-effects: [poison, slowness, weakness, ...]

Configure match-start potion scanning per kit with /kit matchstartpots. When take-from-inventory is false, buffs are splashed without consuming items from player inventories (MMC/MCPVP style). See Kit Setup.

Killstreaks, Messages, and Practice Items

yamlkillstreaks:
  enabled: true
  broadcast: true
  tiers:
    - kills: 5
      name: "Killing Spree"
    - kills: 10
      name: "Rampage"
    - kills: 15
      name: "Dominating"
    - kills: 20
      name: "Unstoppable"
    - kills: 25
      name: "Godlike"

death-messages:
  enabled: true
  customizable: true
  kill-commands:              # Run commands on kills (rank-based rewards)
    enabled: false
    attacker: []
    victim: []
    ranks: {}

auto-gg:
  enabled: true
  delay-seconds: 2
  default-enabled: true       # Default Auto GG state before a player changes it

combat:
  anti-death-drops: true      # Prevent item drops on death inside matches

cosmetics:
  strip-without-permission:
    enabled: true
    permission: "shyamduels.cosmetics"
    armor-trims: true
    banner-patterns: true

action-bar:
  enabled: true
  interval-ticks: 20
  message: ""                 # MiniMessage; empty = use built-in state message

first-join-commands:
  enabled: false
  commands: []                # Run once on a player's first join

every-join-commands:
  enabled: false
  commands: []                # Run on every join

item-names:
  kit-selector: "7FFFFSelect a Kit"
  arena-selector: "7FFFFSelect an Arena"

golden-head:
  cooldown: 3
  item:
    id: "minecraft:honey_bottle"
    texture-url: "https://textures.minecraft.net/texture/..."
    texture-value: "<base64 texture value>"
    display-name: "..."
    lore: [...]
  effects:
    - "ABSORPTION:120:1:true:false"
    - "REGENERATION:8:2:true:false"
    - "SPEED:15:1:true:false"
  particles:
    enabled: true
    consume-particles: [...]
  advanced:
    drop-if-full: true
    remove-existing-effects: true
    action-bar-countdown: true
    consume-duration: 1.6
    max-stack-size: 16
    prevent-stacking: false

totem-practice:
  command: "totempractice"    # Root command name registered by practice module
  gui: { title, size }
  practice: { score_reset_duration, max_misses }
  titles: { exit: { fadeIn, stay, fadeOut } }

door-animation:
  enabled: true
  animation_ticks: 20
  fence_material: "OAK_FENCE"
  kits: []                    # Empty = all duel kits use gates when they have them
  gate:
    close_delay_seconds: 3
    eject_check_radius: 10
    open_speed_ticks: 20
    close_speed_ticks: 20
    debug: false
    ready_up:
      enabled: true
      prompt: "&#FF780C&l⚡ &#FFFA45Hit the gate to ready up!"
      player_ready: "%F699✔ %F699{player} is ready!"
      all_ready: "7FFFF&l⚡ 7FFFFAll players ready! Opening gates!"
      large_party_threshold: 10
      large_party_actionbar: "%F699✔ &f{player} &7is ready &8({ready}/{total})"
      large_party_bar: "%F699✔ Ready: &f{ready}&7/&f{total}"

bedfight:
  enabled: true
  kits: [bedfight, bedwars]   # On enable these kits get their kits.db bedwars_enabled flag set
  bed-break-commands: []      # Console commands run on bed break (%player%, %team%, %enemyTeam%)
  void-buffer: 5.0
  void-fallback-offset: 10
  void-fallback-y: 0
  respawn-countdown-time: 3
  fireball:
    velocity: 1.0
    lifetime-seconds: 5
    cooldown-seconds: 2
    knockback: { radius: {x: 6, y: 4, z: 6}, multiplier: 2.09, y-force: 0.805 }
  tnt:
    auto-ignite: true
    fuse-ticks: 80
    knockback: { radius: {x: 7, y: 4, z: 7}, multiplier: 1.87, y-force: 0.69 }

bridge:
  enabled: true
  void-buffer: 2.0
  respawn-countdown-time: 3
  lock-to-spawn-during-countdown: true
  spawn-cage: true
  instant-golden-apples: false  # Reserved flag (no reader outside BridgeConfig; verify wiring before relying on it)
  bow-cooldown-seconds: 4.0
  # Note: unlike bedfight, Bridge has no bridge.kits list. A duel becomes a Bridge
  # duel only when the kit has Bridge mode enabled (Kit.bridgeEnabled).

Shulker Lore Previews

Packet-based shulker-box content previews (sprite lore on hover). Requires PacketEvents, Minecraft 1.21.9+ (protocol 773+), and non-Bedrock clients. Players toggle it with /shulkerlore or the SHULKER_LORE setting in /settings (see Player Settings).

yamlshulker-lore:
  enabled: true              # Master switch for the preview system
  default-enabled: true      # Default per-player toggle state
  line-format: "<sprites>"  # Line template (<sprites> is replaced by the item row)
  separator: " "             # Separator between sprites on one line
  max-sprites-per-line: 9    # Sprites per lore line (1-9, 27 slots total over 3 rows)
  lore-position: BOTTOM      # BOTTOM or TOP (where preview lines are inserted)
  header: ""                 # Optional line above the preview
  footer: ""                 # Optional line below the preview
  white-sprite: true         # Use white sprite variant
  empty-slot: "<dark_gray>?</dark_gray>"  # Placeholder for empty slots
  sprite-overrides: {}       # Per-material sprite replacements
  potion-colors: {}          # Per-potion color overrides (default water: #385DC6)
custom-kits: enabled: true base-kit-name: custom default-unlocked-kits: 3 hide-from-public-menus: false play-others-gui: enabled: true show-when-hosted-at-least: 1 auto-join-when-single: false right-click: enabled: true # What right-clicking another player does: # profile -> open the player profile GUI (stats, public kits, send duel) [default] # command -> run the commands listed below (legacy behaviour) action: profile commands: - "duel %clicked_player%" # Kit sharing & player profiles kit-sharing: # Default value of each player's "Share Edited Kits" setting before they change it default-enabled: false # Strip rank-gated cosmetics (trims, banners) from imported kits when the importer lacks the # cosmetics.strip-without-permission.permission, so sharing can't hand out paid cosmetics strip-cosmetics-on-import: true # Cooldown (seconds) between successful kit imports per player (0 = off; anti shift-click spam) import-cooldown-seconds: 30

When action: profile, right-clicking opens the configurable Player Profile GUI (menus/profile.yml) instead of running a command. Worlds are controlled by enabled-worlds.right-click-duel. See Kit Sharing & Profiles for the full system.

Golden Head Item Identity
Keep golden-head.item.id set to minecraft:honey_bottle to retain the intended consumable behaviour. The Golden Head appearance is supplied by item.texture-value; item.texture-url is optional reference data.

Golden head, totem practice, and door animation are toggled via modules.*, not separate enabled keys on those sections.

Tab List and Scoreboard

Tab headers/footers and scoreboard layouts are configured in separate files:

Reload tab config with /tab reload. Players toggle tab/scoreboard visibility in /settings.

In tab/scoreboard.yml, scoreboard.async-placeholders: true (default) resolves PlaceholderAPI off the main thread for performance. Set it to false only if a PlaceholderAPI expansion you use is not thread-safe and errors on the scoreboard.

Other tab/scoreboard.yml keys: scoreboard.update-time, scoreboard.lobby-update-time, scoreboard.max-updates-per-tick, scoreboard.title, scoreboard.disabled-worlds, scoreboard.teleport-update, scoreboard.party-addition, scoreboard.enderpearl-cooldown-*. Add a <mode> section in tab/scoreboard.yml for custom scoreboard modes. Tab keys: tablist.ping-source, tablist.player-sorting, sort-ranks.*, default-player-format, golden-head.item.id, titles.* (title animations), heads.* (head cache).

Tab sort ranks in tab/display.yml use LuckPerms permissions for prefix priority: group.owner, shyamduels.tab.admin, shyamduels.tab.staff, shyamduels.tab.vip.

Nametags and Below-Name Health System

Nametags and above-head health displays are configured in tab/display.yml under nametags: and belowname-objective:.

ShyamDuels v2.0+ supports both modern background-free TextDisplay entities (1.19.4+) and legacy vanilla scoreboard teams:

yaml# tab/display.yml
nametags:
  enabled: true
  # Display mode:
  #   text-display -> Modern (1.19.4+), background-free nametags with crisp text drop shadows (Recommended)
  #   scoreboard   -> Classic vanilla scoreboard teams (has client-side dark translucent background)
  mode: text-display

  # Settings for text-display mode:
  # background-color: 'transparent' (no background box), or hex '#RRGGBB' / '#AARRGGBB', or 'default'
  background-color: transparent
  # Enables crisp text drop shadows for contrast against all blocks and skies
  text-shadow: true
  # Vertical offset above player's head (default: 0.25)
  height-offset: 0.25
  # Whether nametag is visible through solid blocks
  see-through: false

  # Text before player name above head
  tagprefix: '%luckperms_prefix%'
  tagsuffix: ''
  collision: never
  respect-npcs: true

belowname-objective:
  enabled: true
  # In text-display mode, this is automatically merged as Line 2 of the background-free display
  fancy-value: '<#FFFA45>%shyamduels_rank% &r» <#FF780C>%shyamduels_elo% ELO'
  per-world:
    Arenas:
      fancy-value: '<#FF1D54>❤ %player_health_rounded% | <#FFFA45>❤ %player_golden_health%'
      value: '%player_health_rounded%'

In arena worlds the red heart is real HP. After a golden apple or golden head, absorption is shown as a gold heart (for example ❤ 18.0 | ❤ 4.0). Both values are shown to one decimal place. The gold half is omitted when absorption is 0. %player_health_rounded% is HP only; %player_golden_health% is the absorption number.

Highlights of TextDisplay Mode:

GUI Menus and Item Configs

All player-facing menus live under plugins/ShyamDuels/menus/:

Lobby hotbar items and the post-duel rematch button: items/spawn-items.yml. Party and queue hotbar items: items/party-items.yml, items/queue-items.yml.

yaml# items/spawn-items.yml
rematch:
  enabled: true               # Allow rematch after duels
  duration-seconds: 5         # How long the rematch item stays in hotbar
  slot: 5                     # Hotbar slot (0-8)
  material: EMERALD
  name: "&a&lREMATCH"
  lore:
    - " &fQuick rematch vs &e{opponent}"
    - " &7Kit: &b{kit}"

items:
  queue:
    slot: 0
    material: DIAMOND_SWORD
    name: "&b&lRANKED QUEUE"
    commands: ["player:queue"]

Rematch lore supports {opponent} and {kit} placeholders. Reload item configs with /shyamduels reload-items.

Kit and FFA GUI order/icons: data/order.yml. Kit categories: categories/<id>.yml (per-category files, legacy categories.yml supported). Custom kit editor categories: data/customkits/categories.json (runtime, managed by CategoryDataManager).

Custom Item Icons (Custom Model Data & Nexo)

Every item entry in a menu or item config supports two optional fields for swapping the vanilla icon for a resource-pack model or a Nexo custom item. Both sit alongside the normal material, name and lore keys:

yamlsome-item:
  slot: 11
  material: DIAMOND_SWORD
  # Optional custom icon (pick one):
  custom-model-data: 1001     # Applies a CustomModelData value to the vanilla material
  # nexo: my_custom_icon      # OR pull the icon (and its model) from a Nexo item id
  name: "&b&lRANKED QUEUE"
  lore:
    - "&7Click to queue"

Custom Model Data

custom-model-data: <int> stamps a CustomModelData value onto the configured material, so a resource pack can render a custom texture/model for that slot. It is only applied when the value is greater than 0; leave it out (or set 0) to keep the default look. The material must still be a valid Minecraft item.

Item Model

On modern Paper versions, item-model: <namespace:key> applies Minecraft's Item Model component to a vanilla material. This is useful when a resource pack supplies a model without relying on CustomModelData:

yamlsome-item:
  material: HONEY_BOTTLE
  item-model: "minecraft:player_head"

The item remains the configured material; the model changes only its appearance.

Nexo Items

Nexo is an optional soft-dependency. When it is installed, you can render any Nexo custom item as a menu icon using either form:

yaml# Explicit field:
nexo: my_custom_icon

# Or as a material shorthand:
material: "nexo:my_custom_icon"

The Nexo item id (with its own model) takes priority. If Nexo is not installed, or the id does not exist, ShyamDuels falls back to the vanilla material plus any custom-model-data, so menus never break on servers without Nexo. On startup the console logs Nexo hook: enabled when the integration is active.

Precedence
A Nexo id (from the nexo: field or a nexo: material) is resolved first. Only when no Nexo item is found does the vanilla material + custom-model-data apply. You don't need Nexo installed to use custom-model-data.

Runtime Data Files

Messages (lang/messages.yml)

All in-game chat strings, GUI titles, duel titles, party messages, and help text live in plugins/ShyamDuels/lang/messages.yml. Reload with /shyamduels reload-messages.

Database Connection Settings (database.yml)

Database settings are stored in plugins/ShyamDuels/database.yml as a separate editable file. On startup and when you run /shyamduels reload-config, the plugin merges this file into the runtime config under the database.* prefix (e.g. database.type, database.performance.io-threads). Edit database.yml on disk; you do not need a database: block inside config.yml.

yaml# ==============================================================================
# Database Configuration (plugins/ShyamDuels/database.yml)
# ==============================================================================

type: "SQLITE"                  # SQLITE or MYSQL

# Performance settings (tuned for big servers: 300+ players)

sqlite:
  directory: data
  arenas-file: arenas.db      # Local SQLite database files
  kits-file: kits.db
  stats-file: stats.db

mysql:
  host: localhost
  port: 3306
  database: shyamduels
  username: root
  password: ""
  use-ssl: false
  maximum-pool-size: 30       # HikariCP max connections (30 with 10 idle recommended for 400-500 CCU)
  minimum-idle: 10            # Minimum idle connections kept ready
  connection-timeout-ms: 5000 # Max wait for a connection from the pool
  max-lifetime-ms: 1800000    # Max lifetime of a pooled connection (30m)
  idle-timeout-ms: 600000     # Time an idle connection stays in pool (10m)

# Performance settings (tuned for high CCU servers: 400-500+ players)
performance:
  compute-threads: 4          # Scoreboard/Tab diffing & MiniMessage parsing workers (auto: clamp(2,16,cores/2))
  io-threads: 8               # DB queries, GeoIP & file I/O workers (auto: clamp(4,32,cores))
  auto-detect-threads: true   # Auto-detect CPU cores (Pterodactyl/Docker aware) and adjust both pools
  autosave-minutes: 5         # Batch-save all online players' stats every N minutes (0 = off).
                              # Saves everyone in one transaction; limits crash loss to a few minutes.
Scaling to 300+ players
For high player counts, use MySQL (SQLite serializes all writes through a single connection by design). Raise maximum-pool-size / minimum-idle and performance.io-threads together so concurrent queries have both connections and worker threads available. The performance block lives in database.yml and is read under the database. prefix internally. When auto-detect-threads is true, thread counts are derived from detected cores.

ELO and Ranks Database (ranks.yml)

Manages matchmaking ranges and calculations:

yamlelo:
  rank-system: true           # Enable rank progressions
  enabled: true
  custom-kits: false          # Award Win/Loss (and ELO) for custom-kit-system fights.
                              # Official kits (Crystal Sea, Anchor Sea, ...) always count.
  match-types:                # When false, that match type records neither ELO nor Win/Loss.
    duel: true                # Kill/Death still update for every finished match.
    queue: true
    party: false
    party-vs-party: false
    party-split: false
    team-battle: false
    party-ffa: false
    ffa: false
    rematch: false
  starting-elo: 1000          # Base ELO rating for new players
  min-elo: 0                  # Floor a player's ELO can drop to
  base-gain: 25               # ELO added on an even-matchup win (default 25 in EloSettings.java)
  base-loss: 20               # ELO deducted on an even-matchup loss
  kill-gain: 5                # Bonus ELO added per kill

  # Opponent-relative (true Elo) scaling. Beating a much weaker opponent gives almost nothing;
  # beating a stronger one gives more. Set false for flat base-gain / base-loss.
  opponent-based: true
  min-gain: 1                 # Floor for a win (token amount vs far-weaker players)
  max-gain: 40                # Cap for a win (vs far-stronger players)
  min-loss: 1                 # Floor for a loss
  max-loss: 40                # Cap for a loss (vs far-weaker players / prevents extreme swings)

  anti-farm:
    enabled: true             # Block ELO farming exploits
    cooldown-hours: 2         # Cooldown before the same opponent yields ELO again
    max-wins-per-opponent: 5  # Max wins against the same player in cooldown period
    block-same-ip: true       # Block ELO gains between matching IP addresses

  progress-bar:               # %shyamduels_elo_bar% / %shyamduels_elo_progressbar%
    length: 10
    completed-symbol: "|"
    uncompleted-symbol: "|"
    completed-color: "<#00FF88>"
    uncompleted-color: "<#555555>"

Each rank under elo.ranks.* defines name, a colored display tag, an ELO range (min-elo/max-elo) and a loss-multiplier. See ELO & Ranks for the full ladder.

Country Flags & Player Heads Cache

Country display powers /countryflags, tab/nametag flag prefixes, and the %shyamduels_country* placeholders (see the Country Flags guide for the full list of supported countries). It resolves player countries from a MaxMind GeoLite2-Country database:

yamlcountry:
  enabled: true               # Master toggle for the country feature
  update-on-join: true        # Re-check MaxMind on join; persists only when the ISO code changed.
                              # Manual /country flag selections are never overwritten.
  maxmind:
    database: "GeoLite2-Country.mmdb"  # Local file (plugin data folder unless absolute)
    auto-download: true       # Download the database automatically when missing/expired
    download-url: "https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb"

The player's real client IP is taken from Paper's connection address (works behind Velocity/Bungee when Velocity modern forwarding is configured correctly); otherwise it falls back to Player#getAddress(). Without correct proxy forwarding, MaxMind may resolve the proxy host's country instead of the player's.

Player head textures (used by tab lists, scoreboards, and bracket GUIs) are cached with optional Mojang refresh:

yamlheads:
  mojang-enabled: true        # Query Mojang for skin textures
  mojang-interval-ms: 1400    # Rate limit between Mojang requests
  http-timeout-ms: 1500
  positive-ttl-ms: 2592000000 # Successful lookups cached for ~30 days
  failure-ttl-ms: 900000      # Failed lookups retried after ~15 minutes
  max-entries: 20000          # In-memory cache ceiling
  disk-save-debounce-ms: 5000 # Debounce for flushing the disk cache

External Plugin Compatibility

Settings for compatibility with third-party external plugins (such as cosmetic or kill effects plugins):

yamlcompatibility:
  # If enabled, ShyamDuels will fire a fake PlayerDeathEvent during virtual deaths.
  # This allows third-party cosmetic/kill-effects plugins that only listen to the
  # vanilla death event to work seamlessly during virtual duel eliminations.
  fire-fake-death-event: true

Sound Effects Library (sounds.yml)

Maps custom sounds to events, grouped by category (e.g. match-start, duel, queue, party, gui, kiteditor, custom-kits). Each entry has an enabled toggle plus a sound, volume and pitch. Supports standard Minecraft sound keys (e.g. BLOCK_NOTE_BLOCK_PLING):

yamlmatch-start:
  countdown:
    enabled: true
    sound: "BLOCK_NOTE_BLOCK_PLING"
    volume: 1.0
    pitch: 1.0
  fight:
    enabled: true
    sound: "ENTITY_ENDER_DRAGON_GROWL"
    volume: 1.0
    pitch: 1.5

duel:
  invite:
    enabled: true
    sound: "ENTITY_EXPERIENCE_ORB_PICKUP"
    volume: 0.8
    pitch: 1.2

match-result:
  match-win:
    enabled: true
    sound: "UI_TOAST_CHALLENGE_COMPLETE"
    volume: 1.0
    pitch: 1.0

Set enabled: false on any entry to mute just that event.

Kit Categories Configuration (categories/*.yml)

Categories are organized in dedicated files inside plugins/ShyamDuels/categories/<id>.yml (e.g. potpvp.yml, sword.yml). Starts empty by default:

yaml# ==============================================================================
# Category Configuration: potpvp.yml (plugins/ShyamDuels/categories/potpvp.yml)
# ==============================================================================

id: "potpvp"
display-name: "<gradient:#FF1D54:#FF7B00><b>PotPvP Modes</b></gradient>"
slot: 11 # Slot in parent selection GUI (-1 for auto)

icon:
  material: SPLASH_POTION
  custom-model-data: 1001
  # nexo: "potpvp_icon"
  # item-model: "minecraft:custom_potion"

lore:
  - "&7Competitive potion fighting modes."
  - ""
  - "&fKits Included: &e<kits_count>"
  - "&fQueued: &a<queued> &7| &fPlaying: &b<playing>"
  - ""
  - "&eClick to browse category kits!"

kits:
  - "NoDebuff"
  - "Debuff"
  - "Gapple"

# Optional Sub-Menu Layout Overrides:
sub-menu:
  title: "<gradient:#FF1D54:#FF7B00>PotPvP Queue</gradient> &8| &7<mode>"
  size: 54
  border: RED_STAINED_GLASS_PANE
  kit-slots:
    - 10
    - 11
    - 12
    - 13
    - 14
    - 15
    - 16

Tournament Configuration (tournament.yml)

Located at plugins/ShyamDuels/tournament.yml. Controls automated bracket tournaments, match timers, scheduler persistence across restarts, non-strict wave arena scheduling, custom spectator rules, and prize distributions.

yaml# Master toggle (shipped default: false)
enabled: false

schedule:
  enabled: true
  interval: "24h"
  initial-delay: "30m"
  persist-schedule-across-restarts: true

players:
  minimum: 4
  maximum: 16
  require-even: true
  odd-player-handling: "BYE" # "BYE", "WAIT", or "CANCEL"

kit:
  default-kit: "Nethpot"
  random-kit: false
  kit-pool:
    - "axe"
    - "crystal"
    - "BuildUHC"

match:
  time-limit: "5m"
  timeout-behavior: "DISQUALIFY_BOTH" # "DISQUALIFY_BOTH", "HIGHEST_HEALTH", "RANDOM_WINNER"
  rounds: 1
  round-system: "BEST_OF"
  affect-elo: false
  record-stats: false

arenas:
  reserve-before: "5m"
  strict-mode: false
  force-reserve: true
  minimum-required: 1

spectator:
  enabled: true
  auto-spectate-on-elimination: false
  auto-spectate-waiting-players: false
  notify-waiting-players: true

rewards:
  first-place:
    enabled: true
    title: "<gradient:#37F699:#55FFFF><bold>CHAMPION!</bold></gradient>"
    subtitle: "<gray>You won the <aqua>%kit%</aqua> tournament!</gray>"
    commands:
      - "eco give %player% 10000"
      - "give %player% diamond 10"