Changelog

Complete version history

Full release history with all features, fixes, and technical changes. For a user-friendly overview, see the Patch Notes or the CurseForge page.

v0.13.3 - NPC Quests & Turn In

NPC Quest System

  • NPC-bound quests - Quests can now be tied to specific NPCs via the npcViewId field. NPC quests are hidden from the main quest log until accepted — players discover them by interacting with NPCs
  • NPC Quest UI - A dedicated two-panel quest page for NPC interactions. The left panel lists available quests with status indicators, the right panel shows quest details including dialogue text, objectives, rewards, and action buttons (Accept, Turn In, Claim, Abandon)
  • NPC accept gating - Quests with npcRequiredToAccept can only be accepted through the NPC quest page, not from the main quest log. Once accepted, NPC quests appear in the normal quest log for tracking
  • Quest dialogue text - Three new markdown fields on quests (incompleteMarkdown, activeMarkdown, completeMarkdown) display contextual NPC dialogue based on quest state. Per-objective dialogue is also supported via detailMarkdown on objectives. Text supports # headers, ## subheaders, bold, and *italic* formatting
  • /mmoquestui command - Admin/console command to open the NPC quest page for any player. Usage: /mmoquestui

Turn In Objective Type

  • TURN_IN objectives - New quest objective type that removes items from the player's inventory. Players click the "Turn In" button in the quest UI to submit items. Supports partial turn-in — if you have 7 of 10 required items, you can turn in what you have and come back for the rest
  • Turn In button in quest log - When the current objective is a TURN_IN type, a "Turn In" button appears alongside the Abandon button in the main quest page
  • Item matching - TURN_IN objectives use the same match modes as other objectives (EXACT, CONTAINS, PREFIX) for flexible item ID matching

Technical Details

  • ObjectiveType.TURN_IN added — not event-driven, so skipped in QuestConfig.buildIndex() inverted index
  • QuestDefinition: new fields npcViewId (String), npcDisplayName (String), npcRequiredToAccept (boolean), incompleteMarkdown/activeMarkdown/completeMarkdown (String). All nullable/optional for backward compatibility
  • QuestObjective: new detailMarkdown field with 9-param constructor overload; existing constructors chain with null
  • QuestConfig: parses new fields, getQuestsByNpcId(String) returns sorted enabled quests filtered by NPC ID
  • QuestDefaults.questToJson(): serializes new NPC, markdown, and detailMarkdown fields (only when non-null/non-default)
  • QuestService.attemptTurnIn(): binary search on canRemoveItemStacks() to find max removable amount for partial turn-in, then removeItemStack() to consume items. Returns amount turned in (0 = insufficient)
  • QuestService.isQuestVisible(): NPC-gated quests (non-null npcViewId) hidden from QuestPage when NOT_STARTED
  • QuestService.isQuestVisibleForNpc(): variant without NPC-gate filter for NpcQuestPage, still respects hidden/prereq/level checks
  • QuestService.canAcceptQuest(): new 5-param overload with boolean fromNpc; blocks non-NPC acceptance when npcRequiredToAccept is true
  • NpcQuestPage extends InteractiveCustomUIPage — two-panel layout, handles selectQuest/accept/turnIn/claim/abandon/close actions, resolves markdown content by quest state
  • NpcQuestUICommand extends AbstractAsyncCommand — admin/console command, uses PlayerLookupUtil.findByUsername() + world.execute() to open NpcQuestPage
  • QuestMarkdownRenderer.render(): splits markdown by double-newline into blocks, appends Label elements via appendInline() with appropriate font sizes and styles for headers, subheaders, and body text. Strips bold and *italic* markers (Hytale labels don't support mixed inline styles)
  • QuestPage: Turn In button in populateQuestRow() ACTIVE case, findActiveTurnInObjective() helper, turnIn action handler with notification messages, objectiveId field added to QuestEventData codec
  • New NpcQuestPage.ui and NpcQuestRow.ui UI templates; QuestRow.ui updated with #TurnInBtn and #TurnInSpacer
  • 6 new localization keys added to all 9 language files: ui.npc_quest.title, ui.npc_quest.no_quests, ui.npc_quest.quest_count, ui.quests.btn_turn_in, ui.npc_quest.turn_in_fail, notify.quest_turn_in

v0.13.2 - February 2026

SimpleEnchantments Integration

  • Enchanting XP from enchanting items - When SimpleEnchantments is installed, enchanting an item awards ENCHANTING skill XP automatically. XP scales with enchantment level — higher-level enchantments give more XP
  • Configurable XP values - Enchanting XP values are looked up from the ENCHANTING xp-map config, matching against enchantment names. Falls back to 20 XP per enchantment level if no config match
  • Enchanting skill tree - ENCHANTING now has a full 10-tier skill tree with mana-focused rewards. Enchanters unlock large mana pools alongside health, defense, and health regen bonuses
  • Auto-detected integration - SimpleEnchantments is detected and connected at startup with no configuration needed. Uses the same adapter framework as HyFishing, HyCitizens, and Perfect Parries

Direct Page Commands

  • /mmoquests command - Opens the Quest page directly. Aliases: /mmoquestlog, /mmoql. No more navigating through multiple UI pages to check your quests
  • /mmoleaderboard command - Opens the Leaderboard page directly. Aliases: /mmolb, /mmorankings. Quick access to skill rankings without going through the skill overview
  • /mmoboosts command - Opens the Boosts page directly. Alias: /mmoxpboosts. Check active boosts and activate tokens without navigating through the skill overview

Permission Prerequisites

  • Permissions in prerequisite groups - Quest prerequisites can now include permission checks alongside quest completions in AND/OR logic. Add a "permissions" array to any prerequisite group to require players to hold specific permissions before accepting the quest. Works at any nesting level, so you can combine quest completions and permissions freely (e.g., complete quest_a AND have rank.vip, or complete quest_b OR have server.bypass)
  • Quest builder updated - The docs site quest builder now supports adding permissions to prerequisite groups with a dedicated input field and removable chips

Performance

  • Fixed TPS drop with many active XP boosts - The XP multiplier calculation (called on every XP award) no longer deserializes personal boosts twice or runs global boost cleanup on every call. Servers with many active boosts should see noticeably smoother tick rates

Localization

  • Skill tree reward cards fully localized - Reward display names and descriptions on skill tree cards now use the player's language instead of always showing English. Both the title (e.g., "+3% Mining XP") and the description (e.g., "Gain 3% more XP from Mining") are generated from localization templates. Custom display names and descriptions set by server admins in overrides are preserved as-is
  • Item reward descriptions localizable - Command reward entries now support a descriptionKey field for localized descriptions. The item rewards page resolves descriptionKey via the player's language first, falling back to the literal description field
  • Empty skill tree tiers hidden - Skill tree tiers with no reward choices are now skipped in the UI instead of showing empty rows. The item rewards page already handled this

Bug Fixes

  • Fixed fish awarding combat and kill XP when HyFishing is installed - Fish entities with model IDs not in the hardcoded fish pattern list (e.g., custom fish from HyFishing) were treated as normal combat mobs, awarding weapon XP on hit and kill XP on death. Fish detection now also checks FISHING xp-map config keys, so any fish type configured for fishing XP is correctly recognized and blocked from combat XP
  • Fixed elite mob nameplates and components persisting after death - Elite mob nameplate labels, elite data, and the spawn-processed marker are now removed when the mob dies, preventing floating nameplates on corpses
  • Added target dummy compatibility - Training dummies from mods like Better Training Dummy no longer award combat XP. The Mannequin entity is now in the default mob kill XP list with 0 XP, so players can practice combat without inflating their skill levels. More broadly, any mob configured with 0 XP in mob-kill-xp.json now blocks both per-hit and kill XP
  • Fixed Settings page conflict with other mods - Renamed the Settings page UI file to avoid conflicts with other mods that also register a SettingsPage.ui

Technical Details

  • MobKillXpDefaults: Added Mannequin → 0 XP. Mannequin is the modelAssetId used by Hytale's NPC mannequin entity, which training dummy mods (e.g., Better Training Dummy) use as their base entity
  • CombatXpEventSystem: Mobs with explicit 0 XP in mob-kill-xp config now skip all per-hit combat XP (damage-based, per-hit, and quest DEAL_DAMAGE tracking). Previously only kill XP was blocked by handleKillXp()'s mobBaseXp <= 0 check
  • FishMobPatterns.isFishMob() now checks FISHING xp-map config keys (bidirectional substring match) as a fallback after the hardcoded pattern set. Both CombatXpEventSystem and MobKillEventSystem call this method, so the fix propagates to both combat hit XP and kill XP blocking
  • XpBoostService.getTotalMultiplierByName() optimized: early exit when no boosts active, throttled global cleanup (every 5s instead of every call), single-pass personal boost processing (deserialize once → remove expired → accumulate multiplier) instead of separate cleanupExpiredPersonalBoosts() + getPersonalBoosts() calls
  • getActiveBoostsForPlayer() and getActivePersonalBoostCount() also converted to single-pass patterns to avoid double deserialization
  • MobKillEventSystem.cleanupEliteComponents() calls store.removeComponent() for EliteMobComponent, EliteSpawnProcessedComponent, and Nameplate on non-player death. Runs before XP/quest processing so the elite qualifier is still read first
  • SkillTreeDefaults.getDefaults() returns empty list for ENCHANTING when IntegrationDomain.ENCHANTING has no active adapter (same gate pattern as HyCitizens quest defaults)
  • New QuestPageCommand (/mmoquests, aliases mmoquestlog, mmoql), LeaderboardCommand (/mmoleaderboard, aliases mmolb, mmorankings), and BoostPageCommand (/mmoboosts, alias mmoxpboosts) — simple AbstractPlayerCommand implementations that open their respective pages
  • New ENCHANTING integration domain with EnchantingIntegration interface, NoOpEnchantingIntegration fallback, and SimpleEnchantmentsAdapter reflection-based adapter
  • SimpleEnchantmentsAdapter registers event bus handlers for ItemEnchantedEvent and EnchantmentActivatedEvent, awards ENCHANTING XP on the world thread
  • XP formula: lookupEnchantingXp(enchantmentName) * enchantmentLevel, with 20 XP default per level
  • Extracted shared reflection helpers into ReflectionUtilresolvePluginInstance, tryInvoke, tryLoadFirst, findSingleParamMethod, findFirstMethod, invoke, createEventProxy now shared across all adapters
  • HyFishingAdapter and HyCitizensAdapter updated to use ReflectionUtil static imports (removed ~270 lines of duplicated reflection code)
  • Added getEnchantingDefaults() to SkillTreeDefaults — 10 tiers with mana-focused rewards (up to +30 mana at tier 9), XP bonuses, health, defense, and health regen
  • PrerequisiteGroup — new permissions field (4-arg constructor, backward-compatible 3-arg delegates with empty list). Added getPermissions(), getAllPermissions() recursive flattener, updated isEmpty() to check permissions
  • QuestService.evaluatePrerequisites() — new overload accepting @Nullable Player. AND mode requires all permissions held, OR mode requires at least one. canAcceptQuest() and isQuestVisible() now pass Player for permission evaluation. Permission-only prereq groups work even without a QuestComponent
  • QuestConfig.parsePrerequisiteGroup() reads "permissions" JSON array; QuestDefaults.prerequisiteGroupToJson() serializes it (omitted when empty)
  • QuestPage.buildPrereqGroupText() renders permission entries using ui.quests.req_permission localization key (added to all 9 languages)
  • Docs site quest builder: PrereqGroup type includes permissions: string[], PrereqGroupEditor has permission input + removable chips, buildPrereqGroupJson/parsePrereqGroup handle the field
  • Added reward.desc.* localization keys (25 per language) to all 9 language files for reward description templates. Templates use {0} for the formatted value and {1} for skill name (BONUS_XP, LUCK). Combat-targeted rewards append reward.desc.with_target suffix
  • Messages.getRewardDisplayTextByName() refactored: compares reward.getDisplayName() against English-generated text (case-insensitive) instead of only the enum default. SkillTreeDefaults display names like "+3 Max Health" now correctly match and get localized instead of being treated as custom overrides
  • New Messages.getRewardDescription(skills, reward, skillId) generates localized descriptions from reward.desc.* templates. Falls back to raw description if it doesn't match the English template or enum default (preserving custom admin overrides)
  • New private helpers: generateRewardDisplayText(langCode, reward, skillId), generateRewardDesc(langCode, reward, skillId), getRewardFormattedValueForLang(langCode, reward), getDescriptionValue(reward) (absolute value, no prefix/suffix)
  • SkillTreePage.build() now calls Messages.getRewardDescription() instead of reward.getDescription() for card rendering
  • SkillTreePage.build() skips tiers with empty choices list, tracking displayedTierIndex separately from data tier index. Event data includes both Tier (data) and Row (display) for correct claim handler UI selector mapping
  • CommandRewardEntry: new descriptionKey field with getDescriptionKey(), hasDescriptionKey() accessors and Builder support. 8-arg constructor chain preserved for backward compatibility
  • CommandRewardsConfig.RewardData: serializes/deserializes descriptionKey alongside existing fields
  • ItemRewardsPage: new resolveDescription() method (mirrors resolveDisplayName()) — tries descriptionKey via Messages.get() first, falls back to literal description. All three parse methods updated
  • ItemRewardsPage.resolveDisplayName() and resolveDescription() now compare the literal value against the English resolution of the localization key (case-insensitive). If they match, the player's localized version is used instead of the English literal. Custom overrides that differ from English are preserved as-is
  • Renamed SettingsPage.uiMMOSkillTreeSettingsPage.ui to avoid UI file name conflicts with other mods. Java reference in SettingsPage.java updated

v0.13.0 - Elite Hostile Mobs

Elite Hostile Mobs

  • Tiered elite mob system - Hostile mobs can now randomly become elite when they spawn into the world. Elite mobs have boosted max health, deal more damage, and take less damage. Three default tiers ship out of the box: Rare (70% chance, 1.5x HP/damage), Elite (25% chance, 2.5x HP, 2x damage), and Legendary (5% chance, 5x HP, 3x damage)
  • On-spawn elite rolling - Elite status is determined the moment a mob first appears, not during combat. This prevents archetype changes mid-fight that could cause AI or damage bugs
  • Data-driven tier config - All elite tier definitions, roll chance, and behavior are configured in mods/mmoskilltree/elite-mobs.json. Add custom tiers, adjust multipliers, or change the 10% base roll chance without touching code
  • Override-based config - Elite mobs config now uses the same override pattern as other configs. Only your customizations are saved; defaults auto-update with mod versions. A reference file at mods/mmoskilltree/_reference/defaults-elite-mobs.json shows all default values
  • Mob ID filtering - Control which mobs can become elite using allow/deny lists with substring pattern matching. Deny specific mobs (e.g., training dummies), restrict elites to certain mob types, or assign specific tiers to individual mobs via tier overrides
  • Projectile and prop safety - Non-living entities (projectiles, props, triggers) can never become elite, preventing accidental stat scaling on arrows and environmental objects
  • Admin toggle - Enable or disable the entire elite mob system from the Admin Dashboard under General Settings. Disabled by default — flip it on when you're ready

Technical Details

  • New EliteMobComponent ECS component (mmoskilltree:elitemob) with BuilderCodec persistence for tierName, incomingDamageMult, outgoingDamageMult, maxHealthMult
  • New EliteSpawnProcessedComponent marker component (mmoskilltree:elite_spawn_processed) — ensures each entity is only processed once for elite rolling
  • EliteSpawnRollTickingSystem extends EntityTickingSystem — runs every tick, skips players (PlayerRef), skips non-living entities (EntityStatMap null check), marks entities with EliteSpawnProcessedComponent, then calls tryMakeElite() once
  • EliteMobDamageSystem now handles damage scaling only — elite rolling removed from damage pipeline to avoid archetype changes inside FilterDamageGroup
  • EliteMobsService: idempotent tryMakeElite() with weighted tier selection, StaticModifier(MULTIPLICATIVE) HP modifier keyed "mmoskilltree_elite_hp", optional heal-to-full via maximizeStatValue(). Early EntityStatMap null guard prevents non-living entities from being rolled elite
  • EliteMobsConfig converted from standalone GSON config to AbstractOverrideConfig with nullable OverrideData fields, EliteMobsDefaults static defaults class, and auto-generated reference file
  • Mob ID resolution reuses existing EntityIdentifierUtil.getMobId() (ModelComponent modelAssetId extraction)
  • Admin toggle added to AdminConfigPage UI and Java (General Settings section)

v0.12.3 - February 2026

Elite Hostile Mobs

  • Tiered elite mob system - Hostile mobs can now randomly become elite when a player engages them in combat. Elite mobs have boosted max health, deal more damage, and take less damage. Three default tiers ship out of the box: Elite (70% chance, 1.5x HP/damage), Rare (25% chance, 2.5x HP, 2x damage), and Legendary (5% chance, 5x HP, 3x damage)
  • Data-driven tier config - All elite tier definitions, roll chance, and behavior are configured in mods/mmoskilltree/elite-mobs.json. Add custom tiers, adjust multipliers, or change the 10% base roll chance without touching code
  • Mob ID filtering - Control which mobs can become elite using allow/deny lists with substring pattern matching. Deny specific mobs (e.g., training dummies), restrict elites to certain mob types, or assign specific tiers to individual mobs via tier overrides
  • Admin toggle - Enable or disable the entire elite mob system from the Admin Dashboard under General Settings. Disabled by default — flip it on when you're ready

Technical Details

  • New EliteMobComponent ECS component (mmoskilltree:elitemob) with BuilderCodec persistence for tierName, incomingDamageMult, outgoingDamageMult, maxHealthMult
  • EliteMobDamageSystem extends DamageEventSystem on FilterDamageGroup — rolls elite on first player combat encounter, then scales damage both directions
  • EliteMobsService: idempotent tryMakeElite() with weighted tier selection, StaticModifier(MULTIPLICATIVE) HP modifier keyed "mmoskilltree_elite_hp", optional heal-to-full via maximizeStatValue()
  • EliteMobsConfig: standalone GSON config at mods/mmoskilltree/elite-mobs.json with setEnabled() auto-save
  • Mob ID resolution reuses existing EntityIdentifierUtil.getMobId() (ModelComponent modelAssetId extraction)
  • Player detection via Player.getComponentType() — only rolls elites when a player is involved in combat
  • Admin toggle added to AdminConfigPage UI and Java (General Settings section)

v0.12.2 - February 2026

Custom Skill Wizard

  • Command Rewards step replaces Item Requirements in the wizard - Step 3 of the Custom Skill Wizard now lets you configure command rewards (commands that run when the skill levels up) instead of item level requirements. Add rewards with a level trigger, command, and optional display name. Commands support {player}, {level}, {skill}, and {totalLevel} placeholders

Quest Expansion

  • Branching prerequisites (AND/OR logic) - Quest prerequisites can now use AND/OR grouping for branching storylines. Instead of requiring all prerequisite quests to be completed, you can create branching paths where players only need to complete one of several quests to proceed. Supports nested groups for complex quest chains like "Complete the intro quest AND (choose the warrior path OR the mage path)"
  • Mixed sequential and parallel objectives - Quests can now have objectives that must be completed in stages while allowing some objectives within the same stage to be done in any order. Assign an order value to each objective — objectives with the same order value can be completed in parallel, while higher-order objectives unlock only after all lower-order ones are done. Backward compatible: quests using "sequential": true continue to work as before
  • Structured reward types - Quest rewards now support XP grants, boost tokens, and currency alongside commands. XP rewards grant skill XP directly, boost token rewards activate timed XP multipliers, and currency rewards grant gold. Each reward type has a display name shown in the quest UI. Existing command-based rewards continue to work unchanged
  • Quest tags and variables - Quests can now include tags for filtering and metadata variables for custom key-value data. Tags are displayed as chips in the quest UI and can be used for organization. Variables store arbitrary data for future conditional logic and docs site filtering

Localization

  • Skill card buff labels and stats bar fully localized - The short buff labels on skill cards (HP, DMG, CRIT, etc.), the stats bar in the side panel (XP, DMG, BLK, CRIT, STEAL, FALL), and the tier progress text are now localized in all 9 languages instead of being hardcoded in English

Config Presets (Premium)

  • Quests now included in preset export/import - The config preset system now supports exporting and importing quest files alongside other configs. When exporting, all user quest files from mods/mmoskilltree/quests/ are merged into the preset. When importing, quest data is written to a preset-quests.json file in the quests directory. Existing user quest files are backed up before import

Technical Details

  • New PrerequisiteGroup data class with recursive AND/OR evaluation (mode, quests, groups fields)
  • QuestConfig: prerequisite field now accepts both string array (legacy AND-all) and object format with nested groups
  • QuestService.canAcceptQuest() / isQuestVisible(): recursive evaluatePrerequisites() replaces flat list check
  • Per-objective order field parsed in QuestConfig; QuestService.isObjectiveActive() checks order groups instead of strict sequential index
  • Backward compat: "sequential": true auto-assigns incrementing order values to objectives
  • New QuestRewardType enum: COMMAND, XP, BOOST_TOKEN, CURRENCY
  • QuestConfig parses reward type field with per-type validation; rewards without type default to COMMAND
  • QuestService reward executor dispatches by type: XP uses SkillService.addXp(), BOOST_TOKEN uses XpBoostService, CURRENCY uses command fallback
  • QuestPage: displays OR prerequisites, order-grouped objectives, structured reward icons, and tag chips
  • QuestDefaults: added example branching quest chain, mixed-order quest, and structured reward quest
  • Quest tags/variables parsed and stored in quest definition; tags displayed in QuestPage UI
  • New localization keys for OR prerequisites, order labels, reward types, and tags across all 9 language files
  • Added QUESTS to ExportableConfig enum with file stem "quests"
  • ConfigPresetService: new methods exportQuestsAsJson(), collectQuestFiles(), writeQuestPresetFile(), backupQuestFiles(), backupQuestFilesRecursive() for directory-based quest export/import
  • Quest export merges all user .json files from quests/ (skipping _defaults/ and backups/) into a single JSON object
  • Quest import writes to quests/preset-quests.json; existing user quest files are backed up to timestamped backup directory
  • reloadAffectedConfigs() now calls QuestConfig.getInstance().load() when the "quests" config key is present
  • ViewXpPage.formatRewardShort(): now takes SkillComponent and uses Messages.get() with reward.short.* keys instead of hardcoded String.format() (15 reward types)
  • ViewXpPage.populateBuffsBar(): 6 stat bar labels use ui.viewxp.buff_*_format localization keys
  • Tier progress text ("Tier X/Y") uses ui.viewxp.tier_progress localization key
  • 22 new localization keys (reward.short.*, ui.viewxp.buff_*_format, ui.viewxp.tier_progress) across all 9 language files

Docs Site: Gallery Improvements

  • Public/Private visibility toggle - Configs, presets, and quests uploaded to the community galleries now default to Private. Users can toggle visibility with a single click on the Eye/EyeOff badge in the top-right corner of each card on the "My" tab. Only items that are both admin-approved and set to Public appear in community listings
  • Extracting quests from presets - Extracting a preset that contains quests now saves them to the Quest Gallery instead of the Config Gallery

Technical Details: Docs Site

  • Added visibility field ("public" | "private", default "private") to ConfigPreset, ConfigTemplate, and QuestTemplate Prisma models
  • Community GET routes filter on { active: true, visibility: "public" }
  • Mine GET routes include visibility in select; new PATCH handlers toggle visibility with ownership validation
  • POST routes accept optional visibility field, defaulting to "private"
  • UI cards show 3-state badge: Pending (Clock, yellow), Public (Eye, green, clickable), Private (EyeOff, gray, clickable)

v0.12.1 - February 2026

Boost Activation Command

  • Run a custom command when any XP boost is activated - Server owners can now configure a command that runs automatically whenever a boost is activated (permission boosts, admin boosts, and token activations). Use this to send Discord webhooks, log to a database, or broadcast custom messages
  • Configurable scope filter - Choose whether the command fires for all boosts, global boosts only, or personal boosts only via a dropdown on the Admin Config page
  • Placeholders - The command template supports {player}, {multiplier}, {target}, {duration}, and {scope} placeholders that are replaced with the actual boost values at activation time
  • Admin UI controls - Toggle, command template field, scope dropdown, and placeholder reference all available on the Admin Configuration page under General Settings

Admin UI

  • Admin Config page reorganized into sections - The admin configuration page is now split into 4 clearly labeled section cards: General Settings (toggles, dropdowns, formula), Command Integration (milestone announcements and boost activation command), Leveling Parameters, and Boost Stacking Limits. Previously all settings were in a single long section
  • Admin Config page no longer scrolls to top on changes - Toggling settings, changing dropdowns, and saving values on the admin config page now update in-place without resetting your scroll position

Bug Fixes

  • Fixed hatchets (axes) not working with woodcutting level requirements and tool requirements - Hatchet item IDs were configured as Tool_Axe_ instead of the correct Tool_Hatchet_, causing woodcutting level requirements and log tool type requirements to have no effect. Also removed Steel, Silver, and Gold hatchet entries that don't exist in Hytale

Technical Details

  • Fixed ItemRequirementsDefaults: renamed Tool_Axe_*Tool_Hatchet_* for woodcutting requirements, removed non-existent Steel/Silver/Gold variants
  • Fixed ActionRequirementsDefaults: renamed Tool_Axe_Tool_Hatchet_ for all log tool requirements (15 entries + fallback)
  • Updated hint text in ActionRequirementsPage.java and ActionRequirementsPage.ui
  • SkillConfig: 3 new fields (enableBoostActivationCommand, boostActivationCommand, boostActivationCommandScope)
  • New CommandExecutor.processBoostPlaceholders() method for boost-specific placeholder substitution
  • XpBoostService.executeBoostActivationCommand() helper called in all 3 activation paths (activateBoost, activateAdminBoost, activateToken)
  • AdminConfigPage.ui: split single #SettingsSection into 4 section cards (#SettingsSection, #CommandSection, #LevelingSection, #BoostLimitsSection)
  • AdminConfigPage.java: converted 18 toggle/dropdown/apply actions from refreshPage()sendUpdate() to preserve scroll position; only reload/reset actions still use full page rebuild
  • 1 new localization key (ui.admin.command_integration) across all 9 language files
  • 7 new localization keys for boost activation command across all 9 language files

v0.12.0 - February 2026

Skill Icons

  • Skill icons on XP page - Each skill card on the Skill Overview page now displays an item icon beside the skill name representing the skill (pickaxe for Mining, longsword for Swords, bow for Archery, etc.)
  • Skill icons in notifications - XP gain and level-up notifications now include a skill icon next to the message text
  • Custom skill icon field - Custom skills can now specify an icon path in the admin Custom Skills page and the creation wizard. Icons are used for notifications; skill card icons require a matching entry in SkillCard.ui

Admin UI

  • Dropdowns replace button selectors across admin pages - Combat XP Mode, Default Language, Leveling Formula, and Boost Stack Mode on the admin dashboard now use native Hytale dropdowns instead of rows of toggle buttons. The reward type field in the Custom Skill Wizard and Skill Tree Overrides page, and the category selector in the wizard, also use dropdowns. All selections are a single click from a clean list instead of scanning multiple buttons
  • Custom Skills page: Edit button opens wizard - The separate "Edit" and "Wiz" buttons on each custom skill row have been merged into a single "Edit" button that opens the full wizard in edit mode
  • Unimplemented reward types hidden from dropdowns - Reward types without a working effect (Speed, Knockback Resistance, Haste, Fortune, Vein Miner, Effect Duration, Cooldown Reduction) are no longer shown in the reward type dropdown on admin pages

Quests

  • Multi-objective quests now show completion order hint - Quests with two or more objectives now display a label above the objective list indicating whether they must be completed in order ("Complete in order:") or can be done in any sequence ("Complete in any order:"). Localized in all 9 languages

Custom Skills Parity

  • Custom skills fully integrated in navigation - Custom skills now appear in the prev/next arrows on Skill Tree and Item Rewards pages, letting players browse through all their skills seamlessly instead of being excluded from navigation
  • Total Rewards tab available for custom skills - The "Total" tab on the Item Rewards page now works for custom skills, showing aggregated rewards across all skills
  • Custom skills treated identically everywhere - Custom skills now participate in all the same systems as built-in skills with no behavioral differences: combat stat breakdowns, boost multipliers, quest objectives, leaderboards, and admin overrides all use the same unified code path

Bug Fixes

  • Fixed players gaining XP in disabled skills - Disabling a skill via the Custom Skills admin page would hide it from the UI and leaderboards, but players could still gain XP from gameplay actions (mining, combat, kills, etc.). Disabled skills now correctly block all XP gains from triggers, combat damage, mob kills, and fishing
  • Fixed skill and tier buttons not wrapping in admin pages - When a category had many skills or a skill had many tiers, buttons would overflow horizontally instead of wrapping to the next row in the Skill Tree Overrides and Command Rewards pages. Buttons now auto-wrap to fit the available space
  • Fixed players farming fishing XP by catching and releasing the same fish - When using HyFishing, players could repeatedly catch and release the same fish to farm unlimited Fishing XP. The same fish now only awards XP once
  • Fixed fish mob kills awarding combat XP when a fishing mod is installed - When HyFishing was active, killing fish mobs would award combat weapon XP (e.g., Swords XP) instead of being blocked entirely. Fish mob kills now award no XP when a fishing integration is active, since fishing XP comes exclusively from rod catches
  • Fixed skill permissions not enforced for combat kills, damage-based XP, and fishing - When enableSkillPermissions was on, players without a skill permission (e.g., mmoskilltree.skill.swords) could still gain XP through damage-based combat, mob kills, fish kills, and the HyFishing integration. All XP award paths now respect skill permissions
  • Fixed /quest command missing permission check - The /quest command (accept, claim, abandon, status) did not check mmoskilltree.command.quest when command permissions were enabled, allowing all players to use it regardless of permissions

Technical: Unified Skill ID System (API Migration)

The SkillType enum is now deprecated. All internal code uses string-based skill IDs ("MINING", "SWORDS", "MAGIC", etc.) instead of enum values. Custom skills are now first-class citizens everywhere — no more dual-map patterns or branching on built-in vs custom.

For plugin developers using MMOSkillTreeAPI:

All existing SkillType-based methods still work but are deprecated. Migrate to the string-based equivalents:

| Deprecated Method | Replacement |

|---|---|

| getXp(store, ref, SkillType.MINING) | getXp(store, ref, "MINING") |

| getLevel(store, ref, SkillType.MINING) | getLevel(store, ref, "MINING") |

| addXp(store, ref, SkillType.MINING, amount) | addXp(store, ref, "MINING", amount) |

| removeXp(store, ref, SkillType.MINING, amount) | removeXp(store, ref, "MINING", amount) |

| setXp(store, ref, SkillType.MINING, amount) | setXp(store, ref, "MINING", amount) |

| getLevelProgress(store, ref, SkillType.MINING) | getLevelProgress(store, ref, "MINING") |

| getAllXp(store, ref)Map | getAllXpByName(store, ref)Map |

| getAllLevels(store, ref)Map | getAllLevelsByName(store, ref)Map |

The string-based methods work for both built-in skills and custom skills. getAllXpByName and getAllLevelsByName include custom skills; the deprecated SkillType versions only return built-in skills.

No player data migration required — the on-disk codec format is unchanged.

Technical Details

  • Skill system unified to string IDs — Replaced all EnumMap + Map dual-map patterns with single Map maps across SkillComponent (4 map pairs unified), XpMapsConfig, SkillTreeConfig, CommandRewardsConfig, and all service/page classes. SkillType enum is deprecated; only MMOSkillTreeAPI.java imports it. 53 files migrated from SkillType to string-based skill IDs
  • Extracted standalone TriggerType and SkillCategory enums from SkillType inner classes. Added SkillRegistry.BUILTIN_SKILL_NAMES (23-entry List), BUILTIN_SKILL_DATA (Map record with displayName/category/description/iconPath/triggerTypes), isBuiltinSkill(String), isKnownSkill(String), and category/trigger/display lookups
  • Converted SkillDefaults, SkillTreeDefaults, CommandRewardsDefaults from switch(SkillType) dispatch to Map lookups
  • Unified CombatSkillResult to single String skillId field (was SkillType builtinSkill + String customSkillId). Removed isCustom() method. Unified SkillTreeService.getCombatStatBreakdown() — no longer branches on custom vs built-in
  • Unified CombatTarget targeting: MELEE_SKILLS/RANGED_SKILLS changed from EnumSet to Set. appliesToByName(String) is now the single code path (removed deprecated appliesTo(SkillType)). Added isMeleeSkill(String)/isRangedSkill(String) static helpers
  • Removed SkillReward.appliesToCombatSkill(SkillType)appliesToCombatSkillByName(String) now delegates to CombatTarget.appliesToByName() instead of SkillType.fromString() dispatch
  • Removed RewardEffectContext.skillType field — only String skillId field remains
  • Removed deprecated SkillType-taking methods from: SkillService (merged addXp/addCustomXpaddXpByName), SkillTreeService (~13 methods), CommandRewardService, XpBoostService (getTotalMultiplier), QuestService (onLevelReached), LeaderboardDataStore, SkillDisplayUtils (8 methods), LuckUtil (3 methods), PermissionUtil, Messages (6 methods)
  • Added String iconPath field to SkillType enum (23 built-in defaults) and CustomSkill class (nullable). SkillRegistry.getIconPath(skillId) provides unified lookup. Icon paths use game content format (Icons/ItemsGenerated/...) for notifications and bundled PNGs in Pages/Icons/ for UI cards
  • Added #CardIconRow to SkillCard.ui with 25 hardcoded icon Group elements (#IconMINING, #IconSWORDS, etc.), each with Background: "Icons/filename.png" and Visible: false. Java toggles visibility by skill ID via SKILL_ICON_IDS allowlist. Card layout redesigned: icon (48x48) sits to the left of name/level/progress in a horizontal header row instead of its own centered row, reducing card height from 248 to 218
  • Updated SkillService notification calls to use 3-arg sendNotification(handler, msg, iconPath) and 4-arg sendNotification(handler, msg, subtitle, iconPath) overloads for XP gain, level-up, and first-skill notifications
  • Added iconPath field to CustomSkillsConfig.SkillData for JSON persistence, and icon path text fields to CustomSkillsPage form and CustomSkillWizardPage Step 1
  • Added #ObjectivesHint label to QuestRow.ui (hidden by default). QuestPage.populateQuestRow() shows it for quests with 2+ objectives, selecting between ui.quests.objectives_sequential and ui.quests.objectives_any_order based on quest.isSequential(). Added localization keys to all 9 language defaults
  • Added CustomSkillsConfig.isBuiltinDisabled() check to SkillService.processTrigger() (skips disabled skills during pattern matching), addXpByName(), addXp(), and addCustomXp() (early return for disabled skills). Admin commands (/setmmoxp, /bulksetxp) are unaffected since they use SkillComponent.setXpByName() directly
  • Changed #SkillSelector and #TierButtons from LayoutMode: Left to LayoutMode: LeftCenterWrap in SkillTreeOverridesPage.ui and CommandRewardsPage.ui so buttons wrap instead of overflowing. Removed separate spacer groups from tier button generation in favor of Right/Bottom anchor margins
  • Replaced text fields and toggle buttons with $C.@DropdownBox across 3 pages: reward type in CustomSkillWizardPage and SkillTreeOverridesPage, category in wizard Step 1, and Combat XP Mode / Default Language / Formula / Stack Mode in AdminConfigPage. Entries populated via DropdownEntryInfo + commandBuilder.set(".Entries", list). Events use ValueChanged bindings with @DropdownValue key prefix for UI value resolution (e.g., EventData.of("Action", "combatXpMode").append("@DropdownValue", "#CombatXpModeDropdown.Value"))
  • Removed buildLanguageButtons(), updateCombatXpModeButtons(), formula/stack-mode button styling blocks, and LANG_BUTTON_TEMPLATE from AdminConfigPage.java. Added dropdownValue field to AdminConfigEventData codec
  • Merged Edit + Wizard buttons in CustomSkillsPage: removed #WizardBtn from CustomSkillRow.ui, changed #EditBtn action to wizardEdit, removed dead editSkill handler
  • Added NoOpEffect filter to UIUtil.populateRewardTypes() — skips any RewardType whose registered handler is NoOpEffect
  • Added seenInstanceIds tracking to HyFishingAdapter — a global ConcurrentHashMap.newKeySet() storing fish instance IDs from FishCaughtEvent.getFishInstanceId(). Duplicate IDs are skipped before XP lookup. Reflection-cached with graceful fallback if the method doesn't exist on older HyFishing versions. Bounded at 10,000 entries (clear-and-re-add on overflow); not persisted since fish entities don't survive restarts
  • Added PermissionUtil.canGainSkillXpByName() checks to 4 direct SkillService.addXpByName() call sites that bypassed processTrigger(): CombatXpEventSystem.handleDamageBasedXp(), MobKillEventSystem.handleKillXp(), MobKillEventSystem.handleFishKillXp(), and HyFishingAdapter.awardFishingXp()
  • Added PermissionUtil.hasCommandPermission(player, "quest") check to PlayerQuestCommand.execute()
  • Restructured fish mob handling in MobKillEventSystem: fish mobs now return early regardless of fishing integration status — either awarding Fishing XP (no integration) or nothing (integration active). Previously the no-integration branch returned but the integration-active branch fell through to combat XP. Added isFishMobWithFishingIntegration() guard to CombatXpEventSystem to block per-hit combat XP on fish mobs when a fishing integration is active
  • Consolidated UI event binding boilerplate into UIUtil: added bindButton() (simple action) and bindButton() (action + key-value param) for Activating bindings. Migrated AwardBoostPage (28 bindings → UIUtil.bindButton()/bindTextField()) and LeaderboardPage (8 bindings → UIUtil.bindButton()), removing direct CustomUIEventBindingType/EventData imports from both

v0.11.1 - February 2026

Admin UI

  • Built-in custom skills can now be disabled - Magic and Artillery now appear in the Built-in Skills table on the Custom Skills admin page with the same enable/disable toggle as the 22 core skills. Previously they had no toggle and could not be turned off

Item Rewards

  • Default item rewards for Magic - Magic now has milestone item rewards at levels 10, 25, 50, 65, 80, and 100, progressing from wooden wands through spellbooks. Level 100 rewards include Frost, Fire, and Demon spellbooks, a mastery cape, and a global XP boost
  • Default item rewards for Artillery - Artillery now has milestone item rewards at levels 10, 25, 50, 65, 80, and 100, progressing from basic bombs through guns. Level 100 rewards include large fire bombs, frag grenades, a mastery cape, and a global XP boost

Bug Fixes

  • Fixed XP tokens not working for custom skills - Consuming a flat XP token for a custom skill (e.g., Magic or Artillery) would silently fail and not grant any XP. Custom skill tokens now correctly grant XP, trigger level-up notifications, and check for milestone rewards
  • Fixed boost tokens crashing when targeted at custom skills - Boost tokens and active boosts that targeted a custom skill by name would crash during deserialization, causing the boost to be lost. Skill-targeted boosts now work for any registered skill
  • Fixed boosts for custom skills not applying - XP boosts targeting a specific custom skill (e.g., "2x Magic XP") would never apply because the boost system only recognized built-in skill names. Boosts now correctly match against all registered skills including custom ones
  • Fixed boost target showing "Unknown" for custom skills - When a boost targeted a custom skill, the UI and notifications would display "Unknown" instead of the skill name. Boost targets now show the correct localized skill name for all skills

Technical Details

  • BoostTarget: Added skillId string field and skillByName(String) factory. fromPermissionValue() now checks SkillRegistry for custom skills. appliesByName() uses string comparison instead of enum equality, supporting category matching for custom skills via registry lookup
  • BoostToken/ActiveBoost: Serialization uses getSkillId() instead of getSkill().name(). Deserialization uses BoostTarget.skillByName() instead of SkillType.valueOf(), eliminating crashes on custom skill IDs
  • XpTokenConsumeInteraction: Switched from SkillType-based to string-based API — getSkillId(), getLevelByName(), addByName(), getXpByName(), handleLevelUpByName(), checkMilestonesByName(), getSkillNameById()
  • Messages.getBoostTargetNameForLang(): Uses getSkillNameForLangById() via target.getSkillId() instead of deprecated getSkillNameForLang(SkillType)
  • XpTokenItemGenerator: Replaced SkillType.fromString() lookups with SkillRegistry.getInstance().getDisplayName() for token display names
  • CustomSkillsPage: Built-in custom skills (MAGIC, ARTILLERY) moved from Custom Skills table to Built-in Skills table, sharing the same toggle logic. Max level filter now uses isBuiltinDisabled directly instead of only checking enum skills
  • CommandRewardsDefaults: Added getMagicDefaults() and getArtilleryDefaults() with full 6-tier milestone reward progression
  • CommandRewardsConfig.loadDefaults(): Now seeds built-in custom skill defaults via SkillDefaults.getBuiltinCustomSkillIds(). writeDefaultsReference() and removeOverrideByName() also updated to handle built-in custom skill code defaults

v0.11.0 - February 2026

New Skills: Magic & Artillery

  • Magic - A new combat skill for wands and spellbooks, split from Archery. Gain XP by dealing damage with any wand or spellbook. Includes a mana-heavy skill tree (350+ potential mana from tree rewards alone), XP tokens, boost templates, and item level requirements
  • Artillery - A new combat skill for bombs and guns, split from Archery. Gain XP by dealing damage with explosives and firearms. Includes a damage/crit/health-focused skill tree, XP tokens, boost templates, and item level requirements
  • Archery refocused - Archery now covers bows, crossbows, blowguns, and arrows/darts only. Wand, spellbook, bomb, and gun XP patterns have been moved to their respective new skills. The Archery skill tree has been updated to a stamina-focused ranged tree, replacing the old mana-heavy rewards that were designed for wand users
  • Both new skills ship as "built-in custom skills" — they appear in all UI pages, admin editors, leaderboards, and quest requirements automatically, just like any other combat skill

Ranged Weapon Level Requirements

  • Shortbow requirements added - Shortbows now have Archery level requirements matching the standard material progression (Copper: 1, Iron: 10, Cobalt: 40, Mithril: 50, Adamantite: 60, Thorium: 70, Onyxium: 80)
  • Longbow requirements added - Longbows now have Archery level requirements (Copper: 1, Iron: 10, Cobalt: 40, Adamantite: 60, Thorium: 70)
  • Per-material crossbow requirements added - Individual crossbow materials now have Archery level requirements instead of just a generic crossbow entry (Iron: 10, Cobalt: 40, Mithril: 50, Adamantite: 60, Thorium: 70, Onyxium: 80)
  • Wand requirements - Wands require Magic skill levels (Wood: 1, Tribal: 10, Root: 20, Stoneskin: 40)
  • Spellbook requirements - Spellbooks require Magic skill levels (Brown Grimoire: 15, Purple Grimoire: 25, Fire/Frost: 50, Demon: 70)
  • Bomb and gun requirements - Artillery weapons require Artillery skill levels (Bomb: 1, Gun: 20, Handgun: 30, Blunderbuss: 35, Assault Rifle: 50)
  • Item level requirements now support custom skills - Any skill (built-in or custom) can be used as a level gate for items, not just the 22 built-in skills. Existing requirements continue to work unchanged

Leaderboard

  • Custom skills now appear in leaderboard filters - When viewing a category leaderboard (e.g., Gathering), custom skills in that category now show as individual filter buttons alongside built-in skills. Players can sort and view the leaderboard by any custom skill
  • Total level no longer counts untouched skills - Skills at level 1 (no XP earned) are excluded from total and category level calculations on both the leaderboard and XP page. A player with Mining 10 and all other skills untouched now shows total level 10 instead of an inflated number

API Improvements

  • Unified skill ID API for custom skills - All public API methods now accept string-based skill IDs alongside the original enum-based parameters. This makes custom skills fully interchangeable with built-in skills when other plugins interact with MMO Skill Tree's data. The original enum-based methods continue to work but are now deprecated in favor of the string-based versions
  • For mod authors - You can discover all available skill IDs at runtime via SkillRegistry.getInstance().allSkillIds() (returns both built-in names like "MINING" and custom IDs like "HERBALISM"). Use SkillRegistry.getInstance().getDisplayName(id) for the localized display name and .getCategoryName(id) for the category ("GATHERING", "COMBAT", "CRAFTING", "MISC"). Then pass any skill ID string to MMOSkillTreeAPI.getLevel(store, ref, id), .getXp(...), .addXp(...), etc. — no need to resolve built-in vs custom yourself

Bug Fixes

  • Fixed ranged weapons not earning combat XP on hit - Wands, bows, spellbooks, bombs, guns, and other ranged weapons were not awarding per-hit or damage-based XP. The combat XP system only recognized melee damage sources, so any damage dealt via a projectile (arrows, spells, explosions) was silently ignored. Ranged weapons now correctly award XP on every hit across all combat XP modes
  • Fixed ranged weapons not applying combat bonuses - Damage bonuses, critical chance, lifesteal, and defense reduction from skill tree rewards were not being applied to ranged weapon hits. Only melee attacks received these bonuses. All combat bonuses now apply to both melee and ranged attacks
  • Fixed combat XP blocked by hostility check on ranged hits - Hitting a mob from range before it detected the player would award zero XP because the mob wasn't "hostile" yet. The hostility check has been removed from per-hit XP — the entity blacklist already prevents XP from unwanted targets, and this matches kill-based XP which never had a hostility requirement
  • Fixed Magic and Artillery not appearing in Custom Skills admin page - The built-in custom skills (Magic, Artillery) were missing from the Custom Skills section of the admin UI. They now appear with a "(built-in)" label and blue highlight, with edit/delete buttons hidden since they ship with the mod
  • Fixed combat XP not earned with wands, spellbooks, bombs, and guns - Weapons without a durability system (wands, spellbooks, bombs, guns) were incorrectly treated as "broken" by the combat XP system, silently blocking all XP gain. Damage bonuses and critical hits still applied because the damage modification system did not have this check. The broken-item guard has been removed — zero-damage hits are already filtered naturally
  • Fixed Magic and Artillery defaults not showing in XP Overrides admin page - When selecting Magic or Artillery in the admin XP Overrides editor, the default XP patterns (wands, spellbooks, bombs, guns) were not displayed. Only user overrides appeared, making it impossible to see or compare against the built-in defaults. The defaults now display correctly for all skills including built-in custom skills

Technical Details

Magic & Artillery Architecture

  • Created MagicDefaults.java and ArtilleryDefaults.java XP default files with wand/spellbook and bomb/gun patterns respectively
  • Removed wand, spellbook, bomb, and gun putAll calls from ArcheryDefaults — Archery now only loads BowDefaults plus ranged fallback patterns
  • SkillDefaults.getBuiltinCustomSkillIds() returns List.of("MAGIC", "ARTILLERY") — config loaders use this to seed defaults for built-in custom skills
  • SkillDefaults.getDefaults(String) checks built-in custom skill IDs before returning empty, enabling XpMapsConfig, SkillTreeConfig, etc. to load defaults for MAGIC/ARTILLERY
  • CombatTarget.ARTILLERY added to the combat target enum. CombatTarget.MAGIC was already present
  • SkillTreeDefaults.getMagicDefaults() — mana-heavy 10-tier tree with damage/crit rewards using CombatTarget.MAGIC
  • SkillTreeDefaults.getArtilleryDefaults() — damage/crit/health 10-tier tree using CombatTarget.ARTILLERY
  • SkillTreeDefaults.getArcheryDefaults() — reworked to stamina-focused tree, removing mana rewards
  • MAGIC and ARTILLERY registered as CustomSkill objects in SkillRegistry before config loading, so CombatWeaponUtil can resolve weapons to the correct skill
  • XpTokenDefinition: Added skillId String field and flatXpByName() factory for custom skill tokens. 26 new tokens (16 flat XP + 10 personal boost) for MAGIC/ARTILLERY (279 total, up from 253)
  • BoostTemplateDefaults: Added 4 boost template permission nodes for MAGIC/ARTILLERY (1.5x and 2.0x global boosts)
  • ItemRequirementsDefaults: Refactored ItemRequirement record from Map to Map. Updated ItemRequirementsConfig override serialization, ItemRequirementService level checks, and all default entries to use string skill IDs

SkillType Deprecation: Unified Loops

  • Replaced SkillType.values() iteration with SkillRegistry.getInstance().allSkillIds() across all services, commands, pages, and utilities
  • SkillTreeService: All 6 core loops (getActiveRewards, sumFilteredRewards, reapplyStatRewards, validateAllRewards, removeAllStatModifiers x2) now use allSkillIds() with ByName method dispatch
  • CommandRewardService: All 4 loops (checkAllRewards, extractLevelFromRemainder, extractSkillNameFromRemainder, reward validation) unified to allSkillIds()
  • SkillService: findBestXpMatch and initializeDebugXp unified to single allSkillIds() loop with XpMapsConfig.getXpMapByName()
  • SkillComponent: getTotalClaimedRewardCount and getSummedTotalLevel converted (codec loops intentionally left as enum-indexed storage)
  • CombatWeaponUtil.resolveCombatSkillFromItemId(): Replaced dual built-in/custom loop with single allSkillIds() loop
  • Commands: BulkSetXpCommand, SetXpCommand, MMOConfigCommand (list + diff), SkillTreeCommand all converted
  • Pages: AwardBoostPage, BoostPage, QuestPage, LeaderboardPage, SkillTreePage, ItemRewardsPage all converted. Navigation (prev/next skill) now includes custom skills
  • i18n: Messages.extractSkillIdFromRewardId() returns String. All 3 language defaults files generate keys for custom skills via allSkillIds()
  • RewardEffectContext: Added skillId String field and string-based constructor. SkillType constructor deprecated
  • SkillRegistry: Added hasTrigger(String skillId, TriggerType trigger) for unified trigger checking
  • MMOSkillTreePlugin, MMOSkillTreeAPI.getAllLevelsByName, LeaderboardDataStore.updatePlayer all converted to allSkillIds()

Ranged Combat & Projectile Source Fix

  • CombatXpEventSystem: Replaced source instanceof Damage.EntitySource with resolveAttackerRef(source) helper that handles both Damage.EntitySource (melee) and Damage.ProjectileSource (ranged). Previously only melee hits triggered the attacker-is-player XP logic; projectile hits fell through to the environment/fall-damage else branch
  • CombatDamageEventSystem: Same resolveAttackerRef refactor — damage bonuses, crit, lifesteal, weapon requirement checks, and defense reduction now apply to projectile damage. Defense reduction guard changed from source instanceof Damage.EntitySource to attackerRef != null
  • MobKillEventSystem already handled both source types via its own resolveAttackerRef, which is why kill-based XP worked for ranged weapons while per-hit XP did not
  • Removed HostilityUtil.isHostileTowards() guard from CombatXpEventSystem — ranged weapons hit mobs before they aggro, causing the check to fail on first contact. Entity blacklist provides sufficient filtering
  • CustomSkillsPage.buildCustomSkillsTable(): Now combines built-in custom skills from SkillRegistry with user-defined skills from CustomSkillsConfig. Built-in skills shown with "(built-in)" label, edit/delete buttons hidden
  • CombatXpEventSystem: Removed getDurability() <= 0 broken-item check from handleDamageBasedXp and handlePlayerDealsDamage. Items without a durability system (wands, spellbooks, bombs, guns) return 0 from getDurability(), causing the check to block all XP for these weapons. CombatDamageEventSystem never had this check, which is why combat bonuses (crit, damage, lifesteal) applied correctly while XP did not. The damageAmount <= 0 guard at the top of handle() already prevents XP from zero-damage hits
  • XpOverridesPage.buildPatternList(): Replaced SkillType.fromString() + ternary with direct SkillDefaults.getDefaults(currentSkillId) call. The SkillType-based overload returned null for non-enum skills (MAGIC, ARTILLERY), causing the ternary to fall through to Map.of() and display no defaults

Previous ByName API Additions (carried from 0.10.10 development)

  • SkillComponent.getSummedTotalLevel(), ViewXpPage.getSkillsTotalLevel(), and LeaderboardPage.getCategoryLevelFromEntry() now skip skills at level 1
  • Deprecated all SkillType-param methods across the public API, services, configs, and utilities (~100+ methods)
  • MMOSkillTreeAPI: Added getXp(store, ref, String), getLevel(store, ref, String), addXp(store, ref, String, long), removeXp(store, ref, String, long), setXp(store, ref, String, long), getLevelProgress(store, ref, String), getAllXpByName, getAllLevelsByName
  • SkillComponent: Added 7 missing ByName overloads. Deprecated 23 SkillType-param methods
  • SkillService: Added addXpByName, handleLevelUpByName, notifyLevelUpByName
  • SkillTreeService: Deprecated 21 methods, added 9 ByName overloads
  • CommandRewardService: Added checkAndExecuteRewardsByName, validateCommandRewardsByName
  • Config layer: Deprecated SkillType-param methods across SkillTreeConfig (16), XpMapsConfig (7), CommandRewardsConfig (7), ItemRequirementsConfig (1), SkillDefaults (1), SkillTreeDefaults (1), CommandRewardsDefaults (1)
  • Utils: Deprecated SkillType-param methods in SkillDisplayUtils (10), PermissionUtil (1), LuckUtil (3), Messages (6), RewardEffectContext (1)
  • LeaderboardPage: Changed skillFilter from SkillType to String. PlayerLeaderboardEntry uses Map instead of separate built-in/custom maps
  • ViewXpPage: Replaced 4 separate total methods with 2 unified getSkillsTotalLevel/getSkillsTotalXp taking List

v0.13.1

Elite Mobs Admin Section

  • Dedicated Elite Mobs section in Admin Dashboard - Elite mob settings now have their own section in the Admin Dashboard instead of being grouped under General Settings. The section includes the existing enable/disable toggle plus a new nameplate toggle
  • Elite Mob Nameplates toggle - New admin toggle to show or hide nameplate labels (e.g. "Rare Trork", "Legendary Bear") above elite mobs. Disabled by default — enable it when you want players to see elite rarity at a glance

HyCitizens NPC Integration

  • TALK_TO_NPC quest objectives now work automatically - When HyCitizens is installed, interacting with an NPC triggers TALK_TO_NPC quest objectives. Quest authors can use "type": "TALK_TO_NPC" with the citizen ID as the target and it will progress automatically
  • Auto-detected integration - HyCitizens is detected and connected at startup with no configuration needed. Uses the same adapter framework as HyFishing and Perfect Parries
  • Default "Daily Rounds" quest - A new daily quest auto-generates when HyCitizens is detected: talk to any NPC for a Defense XP Shard. Uses empty target to match any NPC, with a 24-hour cooldown

Technical Details

  • Added showNameplates field to EliteMobsConfig / EliteMobsDefaults (override-based, default: false)
  • EliteMobsService.tryMakeElite() now gates applyEliteNameplate() behind config.isShowNameplates()
  • New #EliteMobsSection in AdminConfigPage.ui with #ToggleEliteMobs and #ToggleEliteNameplates toggle rows
  • Added localization keys ui.admin.elite_mobs, ui.admin.elite_mobs_label, ui.admin.elite_nameplates_label to all 9 languages
  • New CITIZENS integration domain with CitizensIntegration interface, NoOpCitizensIntegration fallback, and HyCitizensAdapter reflection-based adapter
  • HyCitizensAdapter subscribes to CitizenInteractEvent via dynamic proxy on CitizensManager.addCitizenInteractListener(), dispatches to QuestService.onTalkToNpc() on the world thread
  • Added QuestService.onTalkToNpc() — routes NPC interactions to TALK_TO_NPC objectives (null CommandBuffer, same pattern as onCatchFish)
  • Removed TALK_TO_NPC "no event system" warning from QuestConfig objective parser
  • QuestDefaults.getDefaults() conditionally adds daily_npc_greeting quest when IntegrationDomain.CITIZENS is active; MMOSkillTreePlugin regenerates quest defaults after integration resolution to pick it up

v0.11.2

Skill Capes

  • Magic mastery cape - Reaching level 100 in Magic now awards a mastery cape with +30 Mana, +8% Signature damage, and light armor stats — matching the Staves cape's spellcaster focus
  • Artillery mastery cape - Reaching level 100 in Artillery now awards a mastery cape with +10% Charged damage, +14% Projectile resistance, and +16 Health — matching the Archery cape's ranged combat focus
  • Fishing mastery cape - Reaching level 100 in Fishing now awards a mastery cape with gathering-focused stats (+14% Physical resistance, +10% Projectile resistance, +22 Health)

Item Rewards

  • Per-reward auto-claim - Each item reward can now be individually marked as "auto-claim" in the Edit Rewards admin page. Rewards with auto-claim enabled are automatically given to players on level-up without requiring manual claiming from the Item Rewards UI. Custom skills now also receive item reward notifications on level-up

Admin UI

  • Skill Tree Overrides page redesigned with native Hytale styling - The skill tree overrides editor now uses the standard Hytale container layout with themed panels, scrollbars, separators, and button sounds, matching the rest of the admin dashboard
  • All skills can now add extra tiers - The "+" button to add new tiers beyond the defaults is no longer restricted to custom skills. Built-in skills (Mining, Swords, etc.) can now have additional tiers added through the Skill Tree Overrides page. Resetting an added tier removes it entirely instead of reverting to a nonexistent default
  • Custom Skill Creation Wizard - A new 4-step guided wizard accessible from the Custom Skills admin page combines skill definition, XP map configuration, item requirements, and skill tree rewards into a single flow. Step 1 defines the skill (ID, display name, category, triggers), Step 2 configures XP patterns, Step 3 sets item level requirements, and Step 4 builds skill tree tiers with milestone rewards. Steps 3 and 4 can be skipped. The wizard also supports editing existing custom skills with all fields pre-populated. All changes are saved atomically on finish — cancelling at any point discards everything

Technical Details

  • Rewrote SkillTreeOverridesPage.ui from raw Group root to $C.@Container { @CloseButton = true; } pattern with $C.@PageOverlay, $C.@ContentSeparator, ContainerPanelPatch.png panel backgrounds, $C.@DefaultScrollbarStyle, and $C.@ButtonSounds on all button styles
  • Added addBuiltinSkillTier() to SkillTreeConfig for expanding built-in skill trees beyond their default tier range
  • Fixed setTierOverride(), applyOverrides(), setSkillMilestones(), and removeSkillMilestones() to expand the effective node list when overrides exist beyond the default tier count (was silently dropping them with if (tier < effective.size()))
  • Fixed removeTierOverride() to shrink the effective list and re-index higher overrides when removing a tier beyond the default range (mirrors custom skill removal logic)
  • Created Cape_Skill_Magic.json and Cape_Skill_Artillery.json item definitions with stats matching their respective skill archetypes (Staves-like for Magic, Archery-like for Artillery)
  • Fixed Cape_Skill_Fishing.json Icon and Texture paths from Cape_Skill_Blunt references to Cape_Skill_Fishing
  • Added reward.cape.magic and reward.cape.artillery localization keys to all 9 language defaults
  • Added cape item translations (name/description) to all 9 items.lang files for Fishing, Magic, and Artillery
  • Created CustomSkillWizardPage.java (extends InteractiveCustomUIPage) with WizardEventData codec (15 fields) and TierData inner class for step 4 state
  • Created CustomSkillWizardPage.ui with step indicator bar, 4 content panels, and navigation bar. Row templates: WizardXpRow.ui, WizardReqRow.ui, WizardTreeRewardRow.ui
  • Added "Wiz" button to CustomSkillRow.ui and "Wizard" button to CustomSkillsPage.ui form header, with openWizard/wizardEdit event handlers in CustomSkillsPage.java

v0.10.9

Mod Compatibility

  • Modded weapons now earn XP automatically - Added generic weapon-type fallback patterns to all combat skill XP maps so that weapons from other mods earn XP even without explicit configuration. For example, any item with "Longsword" in its name earns Swords XP, any item with "Bow" earns Archery XP, and so on. Specific vanilla weapon entries always take priority over these generic patterns
  • New fallback patterns by skill:
  •     Swords - "Longsword" (8 XP), "Greatsword" (8 XP)
  •     Axes - "Battleaxe" (8 XP), "Hatchet" (4 XP)
  •     Archery - "Bow" (5 XP), "Shortbow" (6 XP), "Longbow" (6 XP), "Crossbow" (7 XP), "Arrow" (3 XP), "Blowgun" (4 XP)
  •     Magic - "Wand" (6 XP), "Spellbook" (8 XP)
  •     Artillery - "Bomb" (10 XP), "Gun" (25 XP), "Grenade" (30 XP)
  •     Daggers - "Knife" (4 XP), "Claw" (4 XP)
  •     Polearms - "Halberd" (6 XP), "Trident" (6 XP), "Glaive" (6 XP), "Lance" (6 XP), "Pike" (5 XP), "Javelin" (5 XP)
  •     Blunt - "Hammer" (6 XP), "Flail" (6 XP), "Maul" (8 XP)
  •     Staves - "Staff" (6 XP)

Bug Fixes

  • Fixed Defense XP maps not using mob IDs - Defense XP map entries were matching against the damage cause type (e.g., "Physical", "Sword_") instead of the attacking mob's ID. This meant server owners couldn't customize Defense XP per mob through the XP maps config or admin UI. Defense XP maps now use mob IDs, matching how all other combat XP maps work. For non-mob damage (environmental, PvP), falls back to the damage cause ID (e.g., "Physical", "Fire"). Defaults rebalanced by mob tier: critters (2-3 XP), standard enemies (5-6 XP), tough enemies (8 XP), elites (12 XP), bosses (25-30 XP)
  • /mmoboost no longer requires the --world= flag - The boost command now resolves the world automatically from the player or server context, removing the need to specify a world argument
  • Fixed combat reward targeting for custom skills - Skill tree combat rewards (Damage, Critical Chance, Lifesteal, Parry stats) can now target custom skill IDs in addition to built-in weapon types. Previously, entering a custom skill ID (e.g., "KATANA") as the target in the Skill Tree Overrides editor would silently default to "All Combat". Custom targets now work correctly — rewards targeting a custom skill only apply when using a weapon mapped to that skill
  • Custom skill tree rewards now included in combat stat calculations - Rewards claimed from custom skill trees (e.g., a custom Katana skill tree) now contribute to combat stats like damage, crit chance, and lifesteal. Previously only rewards from built-in skill trees were counted

Technical Details

  • CombatXpEventSystem.handlePlayerTakesDamage now resolves the attacker's mob ID via EntityIdentifierUtil.getMobId() and uses it as the XP map identifier instead of the damage cause ID. Falls back to damage cause for environmental/PvP damage (no mob source)
  • DefenseDefaults rewritten with mob-ID-based patterns (e.g., Trork, Skeleton, Dragon) matching the same identifiers used by MobKillXpDefaults, replacing the old damage-cause patterns (Physical, Sword_, Fire)
  • Generic fallback patterns added to aggregator defaults files (SwordsDefaults, AxesDefaults, ArcheryDefaults, DaggersDefaults, PolearmsDefaults, BluntDefaults) and StaffDefaults
  • Substring matching with longest-pattern-wins ensures specific entries (e.g., Weapon_Longsword_Iron at 24 chars) always beat generic fallbacks (e.g., Longsword at 9 chars)
  • BoostCommand migrated from AbstractAsyncWorldCommand to AbstractAsyncCommand. World is now resolved from the player sender via player.getWorld(), or from Universe.get() for console senders
  • SkillReward extended with customCombatTargetId field for targeting custom skill IDs. New appliesToCombatSkill(CombatSkillResult) and appliesToCombatSkill(SkillType) methods route targeting logic: custom targets match by string ID, built-in targets use CombatTarget enum
  • SkillTreeConfig.RewardData serialization updated: fromReward() uses getTargetString(), toReward() tries CombatTarget.fromString() first then falls back to treating unrecognized strings as custom skill IDs
  • SkillTreeService.sumFilteredRewards() and getActiveRewards() now iterate both SkillType.values() and SkillRegistry.getCustomSkillIds() to include rewards from custom skill trees
  • SkillTreeService filter predicates changed from r.getCombatTarget().appliesTo() to r.appliesToCombatSkill() for proper custom target routing
  • SkillTreeOverridesPage addReward handler treats unrecognized target input as custom skill ID instead of silently defaulting to ALL

v0.10.8

Per-Skill Parry Stats

  • Parry bonuses are now weapon-aware - The 4 parry stats (Counterattack, Reflect, Stamina Drain, Stun Damage) now respect combat targeting, matching how Damage, Critical Chance, and Lifesteal already work. A parry reward set to "Swords" only applies when holding a sword, while "All Combat" rewards apply with any weapon
  • Parry rewards now show weapon targeting in tooltips - Parry rewards with a specific combat target (e.g., Swords, Melee, Ranged) now display the target in parentheses on the skill tree and buff displays, matching the format used by Damage and Critical Chance rewards

Parry Reward Rebalance

  • Parry reward values rebalanced - All parry reward values updated based on feedback from the Perfect Parries mod developer. Values are now much more impactful and meaningful, scaling from 20% at level 40 up to 45% at level 100
  • Defense skill provides global parry rewards - Defense parry rewards apply with any weapon: Reflection Damage (9% at Lv40, 15% at Lv100), Counterattack Damage (20% at Lv50, 40% at Lv100), Stamina Loss reduction (15% at Lv65, 30% at Lv100), and Stun Damage (25% at Lv80, 40% at Lv100)
  • Weapon skills provide weapon-specific parry rewards - Swords, Daggers, Polearms, Staves, Axes, Blunt, and Unarmed now offer Counterattack Damage and Stun Damage rewards that only apply when holding that weapon type. Values scale from 20-30% at level 40 up to 30-45% at level 100
  • Fishing parry rewards updated - Fishing retains global Reflection Damage and Stamina Loss reduction at roughly half the Defense values, fitting its role as a gathering skill with light combat utility

Technical Details

  • SkillReward.isCombatReward() expanded to include PARRY_COUNTERATTACK, PARRY_REFLECT, PARRY_STAMINA_DRAIN, PARRY_STUN_DAMAGE — enables getFormattedValue() and Messages.getRewardDisplayText() to append combat target suffix
  • SkillTreeService: added 8 per-skill parry methods (getParryCounterattackForSkill, getParryReflectForSkill, getParryStaminaDrainForSkill, getParryStunDamageForSkill) with both SkillType and CombatSkillResult overloads, reusing getFilteredCombatReward()
  • MMOSkillTreeAPI: existing getParryCounterattackBonus, getParryReflectBonus, getParryStaminaDrainBonus, getParryStunDamageBonus now resolve held weapon via CombatWeaponUtil.resolveCombatSkill(store, ref) and delegate to per-skill methods instead of global totals. External callers (e.g., Perfect Parries mod) get weapon-aware filtering without API changes
  • SkillTreeDefaults: Defense parry rewards updated to new values (Reflect 9→15%, Counter 20→40%, StamDrain -15→-30%, Stun 25→40%) with CombatTarget.ALL. Weapon skills (Swords, Daggers, Polearms, Staves, Axes, Blunt, Unarmed) standardized to Counterattack + Stun Damage pair with per-skill CombatTarget (20-30% at T5-T7, 40-45% at T8-T9). Fishing scaled to ~half Defense values for Reflect + StamDrain (global)

v0.10.7

Bug Fixes

  • Fixed custom skills not showing skill tree or item reward buttons on the XP overview page - Custom skills were missing the "View Tree", "View Items" / "Claim Items" buttons on their skill cards, even when skill tree nodes or item rewards were configured for them. All buttons now appear and link to the correct pages, matching built-in skill behavior
  • Fixed skill tree page not working for custom skills - Opening the skill tree from a custom skill card now correctly shows tree tiers, allows claiming rewards, and supports resetting. Previously, clicking the tree button had no effect since the page only supported built-in skills
  • Fixed item rewards page not working for custom skills - Opening the item rewards page from a custom skill card now correctly shows and allows claiming per-skill and global skill rewards

UI Improvements

  • Skill tree reward choices redesigned as cards - Reward options on the skill tree page are now displayed as styled cards with the reward name, optional description, color-coded status (Available/Claimed/Locked), and an explicit Claim button. Replaces the flat text buttons used previously
  • Skill tree choices auto-wrap to multiple rows - Tiers with many reward options now automatically wrap cards to the next line instead of being limited to a fixed row of 6
  • Item reward cards redesigned with panel styling - Reward cards on the item rewards page now use the standard Hytale inset panel background with improved spacing, a thin separator between the name and description, and consistent visual hierarchy matching the skill tree cards
  • Card text now wraps for long names and descriptions - Reward text and descriptions on both skill tree and item reward cards now auto-wrap instead of being truncated by fixed-height labels. Cards auto-size vertically to fit their content
  • Fixed tab buttons on item rewards page - The Skill Rewards / Total Level tab buttons now correctly update their hover and pressed states when switching tabs, instead of only changing the default appearance
  • Fall damage reduction now shown in the buff bar - The top buff bar on the skill overview page now displays your total fall damage reduction percentage alongside existing combat stats
  • Claiming rewards no longer scrolls page back to the top - Claiming skill tree or item rewards now updates the card in-place without re-rendering the entire page, preserving your scroll position
  • Accepting, abandoning, and claiming quests no longer scrolls page back to the top - Quest actions now update the quest row in-place, preserving your scroll position in the quest list
  • Quest page now shows active quest count vs maximum - The active quest counter in the header now displays as "X/Y" (e.g., "3/10") so you can see how many quest slots remain

Technical Details

  • Added tree button, reward button, and tier metadata logic to buildCustomSkillCard() in ViewXpPage
  • Added hasUnclaimedItemsByName() and getRewardStatusByName() helpers in ViewXpPage for string-based skill ID lookups
  • Fixed NPE in populateSkillCard() tree/reward button event bindings — uses card.customSkillId when card.skill is null
  • ViewXpPage tree and skillRewards event handlers now fall through to custom skill constructors when SkillType.fromString() returns null
  • Added SkillTreePage(PlayerRef, String) constructor for custom skill trees with @Nullable selectedSkill / customSkillId fields
  • SkillTreePage uses getSkillKey() with byName methods (getSkillTreeByName, getNodeByName, getClaimedCountByName, etc.) for all tree operations
  • Added ItemRewardsPage(PlayerRef, String) constructor for custom skill rewards
  • ItemRewardsPage uses getSkillKey() / getLevelRewardsByName() for reward lookup and claim tracking
  • Custom skills on both pages hide prev/next navigation arrows (no cycling through built-in skills)
  • Added byName methods to SkillComponent: getClaimedRewardIdsByName, getClaimedCountByName, hasClaimedSpecificRewardByName, claimRewardByName, getPendingRewardsByName, getClaimedRewardCountByName, getClaimedTierCountByName, resetRewardsByName
  • Added SkillTreeService.claimRewardByName() and resetRewardsByName() — delegate to built-in methods for SkillType matches, use customClaimedRewards/customPendingRewards maps for custom skills
  • Replaced ChoiceButton.ui text button template with SkillTreeChoiceCard.ui — 180x130px card with ContainerPanelPatch.png background, #RewardText, #RewardDesc, #RewardStatus, and #ClaimBtn widgets
  • TierRow.ui changed from fixed Height: 90 with LayoutMode: Left choices container to auto-height with LayoutMode: LeftCenterWrap for automatic card wrapping
  • Deleted TierRowExpanded.ui, ChoiceButton.ui, ChoiceButtonSpacer.ui — wrapping is now handled by LeftCenterWrap in the single TierRow.ui
  • SkillTreePage.java uses single CHOICE_CARD_TEMPLATE constant, populates #RewardText, #RewardDesc, #RewardStatus, and #ClaimBtn per card state; removed CHOICES_PER_ROW/MAX_CHOICES limits and second-row/spacer logic
  • RewardCard.ui redesigned from flat 186x95px card to 186x140px with outer padding wrapper and inner ContainerPanelPatch.png background
  • Replaced RewardCardRow.ui (fixed LayoutMode: Left rows) with RewardCardGrid.ui (LayoutMode: LeftCenterWrap grid)
  • ItemRewardsPage.java uses CARD_GRID_TEMPLATE — one grid per level tier, cards flat-appended. Removed CARDS_PER_ROW constant and row-chunking logic. Background color targeting changed from .Background to #CardInner.Background.Color
  • Added ui.skilltree.not_selected localization key to all 9 language files
  • SkillTreeChoiceCard.ui and RewardCard.ui labels use Wrap: true in styles and Anchor: (Bottom: X) instead of fixed Height — enables text wrapping and auto-height cards following the PrefabEditorSettings/SubcommandCard pattern
  • Fixed ItemRewardsPage.java tab styling to set all 3 button states (Default/Hovered/Pressed) when switching to Total Level tab
  • Added #BuffFall label (80px, #87ceeb) to ViewXpPage.ui buffs bar; populateBuffsBar() reads FALL_DAMAGE_REDUCTION from active rewards
  • SkillTreePage.handleChoiceSelection now uses sendUpdate() instead of refreshPage() — updates claimed card appearance, tier status, unclaimed card dimming, selection info, and header counts via partial UI update. Reset action still uses full page refresh
  • ItemRewardsPage claim handlers (handleClaimSkillReward, handleClaimTotalReward, handleClaimGlobalSkillReward) now use sendUpdate() via sendCardClaimedUpdate() helper — updates card status/background/button visibility and recalculates header claimed count in-place
  • Added Selector field to ItemRewardsEventData codec — card UI selector passed from populateCard through claim button event data for targeted partial updates
  • Removed refreshSkillRewardsPage() from ItemRewardsPage — replaced by sendCardClaimedUpdate(), getSkillRewardsClaimedText(), and getTotalRewardsClaimedText() helpers
  • QuestPage accept/claim/abandon handlers now use sendUpdate() via sendQuestAcceptedUpdate(), sendQuestClaimedUpdate(), and sendQuestAbandonedUpdate() helpers — updates quest row status, hides action button, and refreshes header counts without full page refresh
  • Added Selector field to QuestEventData codec — quest row selector passed from populateQuestRow through action button event data
  • QuestPage active count display changed from plain count to "X/Y" format showing active quests vs SkillConfig.getMaxActiveQuests()

v0.10.6

Bug Fixes

  • Fixed daily harvest quest not tracking crop pickups - The "Harvest Season" daily quest was using a block-break objective type instead of item pickup, so harvesting crops via interactive pickup wasn't counting toward quest progress
  • Fixed HyFishing mod not being detected - The optional dependency declaration for HyFishing used an incorrect group ID, preventing the mod from being recognized when installed. Additionally, optional mods that load after MMO Skill Tree are now detected via a deferred re-check on first player connect
  • Fish kills no longer award Fishing XP when a fishing mod is installed - When a fishing integration like HyFishing is active, killing fish mobs awards weapon combat XP instead of Fishing XP. Fishing XP only comes from rod catches via the fishing mod. Without a fishing mod, fish kills always award Fishing XP regardless of combat mode
  • Combat XP now requires hostile targets in all modes except Kill-Only - Damage-Based and Damage+Kill modes previously awarded XP for hitting any mob (including passive/friendly). Now all modes except Kill-Only check if the target is hostile before awarding XP, consistent with Per-Hit mode

Admin

  • Max Active Quests setting added to admin config page - Server admins can now change the maximum number of simultaneous active quests per player directly from the admin dashboard instead of editing config files

UI Improvements

  • Boost Templates admin page redesigned with native Hytale styling - The boost templates editor now uses the standard Hytale container layout with themed panels, scrollbars, separators, and button sounds, matching the XP Overrides page
  • XP Token Overrides admin page redesigned with native Hytale styling - The XP token/boost token editor now uses the standard Hytale container layout with themed panels, scrollbars, separators, and button sounds
  • Fixed crash when viewing Combat skills on Boost Templates page - Selecting the Combat category (which has 11 skills) caused a disconnect because the skill selector buttons overflowed their container. Skill buttons now wrap to multiple rows
  • Fixed crash when viewing Combat skills on XP Token Overrides page - Same overflow fix applied to the XP token editor's skill selector
  • Wider level headers on item rewards page - The "Level X" section headers on the item rewards claim page are now wider to avoid text truncation
  • Wider status labels on skill tree page - The AVAILABLE, LOCKED, and COMPLETE status labels on skill tree reward tiers are now wider to prevent text from being cut off
  • Fixed arrow navigation buttons overlapping outside skill tree page - The prev/next skill arrows in the header were placed inside the container's title bar, causing them to extend beyond the page border. Navigation controls are now properly inside the content area
  • Fixed arrow navigation buttons overlapping outside item rewards page - Same fix applied to the item rewards claim page where prev/next skill arrows extended beyond the container border

Technical Details

  • Added #MaxActiveQuestsRow to AdminConfigPage.ui with text field and save button, wired in AdminConfigPage.java via maxActiveQuestsInput field, @MaxActiveQuestsInput codec key, and applyMaxQuests action handler
  • Added ui.admin.max_active_quests_label localization key to all 9 language files
  • Changed QuestDefaults daily_harvest objective from BREAK_BLOCK to PICKUP_ITEM
  • Fixed manifest.json optional dependency group ID from HyFishing:HyFishing to TheRedLotus:HyFishing
  • Added IntegrationRegistry.resolveDeferredIfNeeded(): re-resolves inactive integration domains once on first PlayerConnectEvent, fixing race where optional mods (e.g., HyFishing) load after MMO Skill Tree's setup()
  • Widened RewardTierSeparator.ui #TierLabel from 100px to 140px
  • Widened TierRow.ui and TierRowExpanded.ui #TierStatus from 100px to 140px
  • MobKillEventSystem: fish mob kills always award Fishing XP when no fishing integration is active (regardless of combat mode). When a fishing integration is active, fish kills fall through to normal weapon kill XP. Kill XP in DAMAGE_PLUS_KILL mode requires HostilityUtil.isHostileTowards() check; KILL_ONLY does not
  • CombatXpEventSystem: removed DAMAGE_BASED/DAMAGE_PLUS_KILL bypass from canAwardXp check — all modes now require hostility via HostilityUtil.isHostileTowards() (KILL_ONLY is unaffected as its switch case is empty)
  • Rewrote BoostTemplateOverridesPage.ui and XpTokenOverridesPage.ui to use $C.@Container, $C.@PageOverlay, $C.@ContentSeparator, $C.@PanelSeparatorFancy, ContainerPanelPatch.png section backgrounds, $C.@DefaultScrollbarStyle, and $C.@SmallSecondaryTextButton
  • Changed #TargetSelector from LayoutMode: Left (height 28) to LayoutMode: LeftCenterWrap (height 64) on both pages to support wrapping when categories exceed container width
  • Fixed XpTokenOverridesPage.buildTargetSelector() index mismatch: used separate btnIndex counter so skipped skills (via isImplementedSkill check) don't produce out-of-bounds #TargetSelector[i] selectors
  • Removed manual #CloseButton event bindings — Container's built-in @CloseButton = true handles page dismissal
  • Moved SkillTreePage.ui and ItemRewardsPage.ui nav buttons (#PrevSkill, #NextSkill) and skill info row from #Title into #Content; simplified #Title to centered skill name only

v0.10.5

Health & Mana Regeneration

  • Health Regen - New skill tree reward type that passively regenerates a percentage of your max health per second. Available in Defense (Lv65: +0.5%/s, Lv100: +1%/s) and gathering skill trees — Mining, Woodcutting, Excavation, Fishing, and Building (Lv80: +0.2%/s, Lv100: +0.3%/s)
  • Mana Regen - New skill tree reward type that passively regenerates a percentage of your max mana per second. Available in Staves (Lv65: +0.5%/s, Lv100: +1%/s) and Harvesting and Crafting (Lv80: +0.2%/s, Lv100: +0.3%/s)
  • Combat trees offer stronger regen rates as primary sources, while gathering trees provide lower supplementary regen for dedicated gatherers

Bug Fixes

  • Harvest quest progress now works from interactive crop pickups - Harvesting crops via interactive pickup was awarding XP correctly but not advancing quest objectives. The pickup event now fires both PICKUP_ITEM and BREAK_BLOCK quest events, so harvest quests (which use BREAK_BLOCK objectives) track progress as expected
  • Fixed quest page failing to open - A layout error in the quest page UI file prevented it from rendering

Technical Details

  • Added HEALTH_REGEN and MANA_REGEN to RewardType enum, registered as passive effects in RewardEffectRegistry
  • Added SkillTreeService.getTotalHealthRegen() / getTotalManaRegen() for querying accumulated regen bonuses
  • Added StatModifierUtil.restoreMana() for mana restoration (mirrors healEntity())
  • New RegenTickingSystem (EntityTickingSystem) — ticks each frame, applies percentage-based health/mana regen scaled by dt to all players with regen rewards. Skips dead entities and entities at full health/mana
  • Updated exhaustive switch statements in SkillReward.getFormattedValue() and Messages.getRewardFormattedValue() to include the new percentage-based types
  • Added reward.health_regen and reward.mana_regen localization keys to all 9 language files
  • Added regen rewards to SkillTreeDefaults: Defense tiers 7/9 get HEALTH_REGEN, Staves tiers 7/9 get MANA_REGEN
  • Added gathering regen rewards to SkillTreeDefaults: Mining/Woodcutting/Excavation/Fishing/Building tiers 8/9 get HEALTH_REGEN (0.002/0.003), Harvesting/Crafting tiers 8/9 get MANA_REGEN (0.002/0.003)

v0.10.4

UI Improvements

  • Buff bar shows equipped weapon stats - The combat stats in the buff bar (DMG, CRIT, STEAL) now reflect your currently equipped weapon instead of showing the highest value across all combat skills. The title shows which weapon skill is active (e.g., "BUFFS (Swords)"). Works with both built-in and custom combat skills
  • Total Level card shows global combat buffs - The Total Level summary card now shows "All Combat" reward bonuses (Damage, Critical Chance, Lifesteal) alongside other global buffs. Per-skill and per-category combat rewards are excluded since they only apply with specific weapons. Luck and Parry bonuses remain excluded as they are always skill-specific
  • Cleaner card metadata - Skill card metadata lines now show only tier progress (e.g., "Tier 3/10") instead of repeating XP and Luck stats that are already visible as buff labels
  • Skill cards redesigned with native Hytale styling - Skill cards on the overview page now use the Hytale panel background with proper separators, a real progress bar widget with percentage label, and auto-wrapping buff labels. Cards are laid out in an auto-wrapping grid instead of fixed 3-per-row, adapting to the available space
  • XP Overrides page redesigned with native Hytale styling - The XP Maps admin editor now uses the standard Hytale container layout with themed panels, scrollbars, separators, and button sounds, matching the rest of the admin dashboard
  • Skill buttons now wrap in the XP editor - Categories with many skills (e.g., Combat with 10+ weapon types) now wrap to multiple rows instead of overflowing off-screen
  • Wider buttons on Skill Tree and Item Rewards pages - Back and Reset buttons on the skill tree reward claim and item reward claim pages are now wider for easier clicking
  • Close X icon on XP, Quests, and Boosts pages - The "CLOSE" text button at the bottom of the Skill Overview, Quests, and Boosts pages has been replaced with a native close X icon in the header, matching the standard Hytale UI pattern

Bug Fixes

  • Custom combat skills now gain XP in damage-based and kill-based modes - Custom skills with melee combat triggers (e.g., a custom Katana skill) were always awarding Unarmed XP instead of the custom skill when using Damage, Damage+Kill, or Kill-Only combat XP modes. The weapon-to-skill lookup now checks both built-in and custom skill XP maps, using longest pattern match. Per-hit mode was already working correctly
  • Custom skills now included in total level and leaderboard - Custom skills with XP maps configured were not counted in the Total Level card on the skill overview page, leaderboard totals, category totals, or the leaderboard skill breakdown text. All totals and rankings now correctly include custom skills alongside built-in skills
  • Custom combat skills now receive correct combat rewards - Holding a weapon mapped to a custom combat skill (e.g., a custom Katana skill) was incorrectly applying Unarmed-specific damage bonuses, crit chance, and lifesteal instead of the global "All Combat" rewards. Custom combat skills now correctly receive only All Combat rewards from the skill tree, and the buff bar displays the custom skill name next to weapon stats

Technical Details

  • ViewXpPage.populateBuffsBar(): now accepts Store and Ref parameters. Resolves equipped weapon via CombatWeaponUtil.resolveCombatSkillFromItemId() (supports custom skills) and uses SkillTreeService.getDamageBonusForSkill()/getCriticalChanceForSkill()/getLifestealForSkill() for the equipped skill. Weapon skill name shown in #BuffsTitle (e.g., "BUFFS (Swords)") instead of per-stat suffix
  • ViewXpPage.buildTotalLevelCard(): buff entry loop skips per-skill reward types (LUCK), then queries getCombatRewardByExactTarget(skills, type, CombatTarget.ALL) for combat-targeted types (STAT_DAMAGE, CRITICAL_CHANCE, LIFESTEAL, PARRY_*) to show only the ALL-target portion. Metadata line simplified to just tier progress
  • ViewXpPage.buildSkillCard(): metadata line simplified to just tier progress (removed XP bonus and luck stats that were redundant with buff labels)
  • ViewXpPage.ui: #BuffsTitle widened (50→150) to accommodate weapon skill name; #BuffDamage/#BuffCrit/#BuffLifesteal shrunk (85→75/90/95) since weapon name moved to title
  • Messages.getSkillNameById(): added !localized.isEmpty() guard — LocalizationConfig.get() returns "" for missing keys, which was bypassing the SkillRegistry display name fallback for custom skills
  • Added ui.viewxp.buffs_title i18n key to English, Italian, and Russian language files
  • SkillCard.ui: rewritten — outer wrapper 286x248 with 5px padding, inner #CardInner uses ContainerPanelPatch.png background. Category strip 2px→3px. Replaced flex-weight progress bar hack with ProgressBar #CardProgress widget + #CardPercent label. #CardBuffsRow changed from LayoutMode: Left to LeftCenterWrap. Three #1e2a36 separators between sections. Buttons widened (Tree 90px, Items 82px, Boost 60px)
  • ViewXpPage.ui: added #SkillGrid group with LayoutMode: LeftCenterWrap inside #SkillsList for auto-wrapping card grid
  • CardBuffLabel.ui: added Right: 6 to anchor for horizontal spacing when wrapped
  • Deleted SkillCardRow.ui — replaced by LeftCenterWrap auto-wrapping
  • ViewXpPage.java: removed SKILL_CARD_ROW_TEMPLATE / CARDS_PER_ROW constants. Card loop now flat-appends to #SkillGrid. Added progressPercent field to SkillCardData. populateSkillCard() uses ProgressBar.Value instead of flex-weight hack, sets #CardPercent text, targets #CardInner.Background for total card override. Added getNextRewardPreview() method using Messages.getRewardDisplayText()
  • CombatWeaponUtil: added CombatSkillResult class (wraps either SkillType or custom skill ID) and new resolveCombatSkillFromItemId()/resolveCombatSkill() methods that check both built-in and custom skill XP maps. Added getWeaponXpValue(CombatSkillResult, itemId) for weapon XP scaling lookup. Removed deprecated getCombatSkillFromItemId()/getCombatSkill() — all callers migrated to resolve* variants
  • CombatTarget.appliesTo(CombatSkillResult): new overload — custom combat skills match only ALL target (no melee/ranged classification). Built-in skills delegate to existing appliesTo(SkillType)
  • SkillTreeService: added CombatSkillResult overloads for getDamageBonusForSkill(), getCriticalChanceForSkill(), getLifestealForSkill(), getCombatStatBreakdown(). Built-in skills delegate to SkillType overloads; custom skills use new getFilteredCombatReward(SkillComponent, RewardType, CombatSkillResult) that filters via CombatTarget.appliesTo(CombatSkillResult)
  • CombatDamageEventSystem: switched from getCombatSkill() to CombatWeaponUtil.resolveCombatSkill() returning CombatSkillResult. applyAttackerEffects() and applyLifesteal() parameter changed from SkillType to CombatSkillResult — method bodies unchanged, SkillTreeService overloads resolve correctly
  • ViewXpPage.populateBuffsBar(): isCombat check now includes custom results (always combat since they matched DEAL_DAMAGE_PHYSICAL). Uses Messages.getSkillNameById() for weapon label supporting both built-in and custom skill names
  • CombatXpEventSystem.handleDamageBasedXp(): switched from getCombatSkillFromItemId() to resolveCombatSkillFromItemId(), dispatches to addCustomXp() or addXp() based on result
  • MobKillEventSystem.handleKillXp(): same pattern — uses resolveCombatSkillFromItemId() and dispatches XP to the correct skill. Removed private getWeaponXpValue() (now in CombatWeaponUtil)
  • XpOverridesPage.ui: rewritten to use $C.@Container with @CloseButton, $C.@PageOverlay, @SectionCard, $C.@PanelSeparatorFancy, $C.@ContentSeparator, $C.@DefaultScrollbarStyle, $C.@SmallSecondaryTextButton, and $C.@ButtonSounds on all button styles
  • SkillButton.ui: added Right: 4, Bottom: 4 margin and Padding: (Horizontal: 8) for proper wrap spacing in LeftCenterWrap layout
  • XpPatternRow.ui: aligned PatternName width (360→300) with table header column
  • XpOverridesPage.java: renamed #SkillTabs#CategoryTabs, removed manual #CloseButton event binding (now handled by Container's built-in close), removed "close" action handler
  • ViewXpPage.ui, QuestPage.ui, BoostPage.ui: wrapped two-panel layout in $C.@Container { @CloseButton = true; } with hidden #Title (Height: 0) and #Content set to LayoutMode: Left with no padding. Removed ContainerPatch.png background from #RightPanel (Container provides it). Removed bottom $C.@SmallSecondaryTextButton #CloseButton. Removed .Text setter from corresponding Java pages
  • ViewXpPage.buildTotalLevelCard(): card level and XP now add custom skill totals via existing getCustomSkillsTotalLevel()/getCustomSkillsTotalXp() helpers (previously only the summary stats at the top included them)
  • LeaderboardPage.PlayerLeaderboardEntry: added customSkillLevels/customSkillXp maps (keyed by custom skill ID) alongside the existing SkillType-keyed maps
  • LeaderboardPage.createEntryFromCache() and createEntry(): now populate custom skill data from cache/SkillComponent respectively
  • LeaderboardPage.getCategoryLevelFromEntry()/getCategoryXpFromEntry(): now include custom skills matching each category via new getEnabledCustomSkillsForCategory() helper
  • LeaderboardPage.buildLevelBreakdown(): refactored to use BreakdownEntry record and collectBreakdownEntries() helper, including both built-in and custom skills in the top-3 breakdown text
  • Removed duplicate getImplementedCategoryLevel()/getImplementedCategoryXp() methods — createEntry() now reuses getCategoryLevelFromEntry()/getCategoryXpFromEntry() since entry maps are already populated at that point

v0.10.3

Leaderboard Improvements

  • Misc category now appears in leaderboard filters - The Misc category (Building and custom misc skills) was missing from the leaderboard category filters. A new "Misc" filter button lets you view and drill into Misc skill rankings
  • All combat skills now visible as leaderboard skill filters - Combat has 11 skills but the filter bar only supported 10 buttons, cutting off Acrobatics. Increased to 12 filter slots

Quests

  • Catch Fish quest objective - New CATCH_FISH objective type for quests. Tracks fish caught via fishing integrations (HyFishing) and fish mob kills when no integration is active. Use it in quest JSON to create fishing-themed quests
  • Built-in fishing quests - Four new default quests for the Fishing skill: a repeatable "Daily Catch" (catch 10 fish), and a three-part quest chain — "First Catch" (catch 5 fish), "Salmon Run" (catch 10 Salmon + reach Fishing 10), and "Deep Water Haul" (catch 5 Lobsters + 5 Crabs + reach Fishing 20)

Bug Fixes

  • Fixed leaderboard page crashing when skills are disabled or permission-gated - Opening the leaderboard would fail if any skill in a category was disabled or the player lacked permission for it
  • Total level and XP now exclude disabled skills - The "Total" level and XP shown on the leaderboard and XP page previously counted all skills, including ones that were disabled or had no XP data configured. Totals now only include enabled skills with active XP maps

Technical Details

  • LeaderboardPage.getSkillsForCategory(): fixed Arrays.asList() returning unmodifiable list causing UnsupportedOperationException on removeIf() in getSkillsForCategoryFiltered() — changed to mutable ArrayList; added MISC case mapping to SkillCategory.MISC
  • LeaderboardPage: added MISC to CategoryFilter enum with miscLevel/miscXp fields in PlayerLeaderboardEntry, #FilterMisc UI button + event binding, MISC cases in sorting/display/styles. Increased skill filter buttons from 10 to 12 (#SkillFilter10, #SkillFilter11). Added ui.leaderboard.filter_misc to all 9 language files
  • SkillComponent.getSummedTotalLevel() and getTotalXp(): now check CustomSkillsConfig.isBuiltinDisabled() in addition to XP map emptiness. getTotalXp() changed from summing all xpMap values to iterating entries and filtering by XP map + disabled status
  • LeaderboardPage: added getEnabledSkillsForCategory() method that filters by XP map + disabled status (no player permissions). Updated getImplementedCategoryLevel/Xp(), getCategoryLevel/XpFromEntry(), and buildLevelBreakdown() to use it. Added disabled check to getSkillsForCategoryFiltered()
  • ViewXpPage.getFilteredSkills(): added CustomSkillsConfig.isBuiltinDisabled() check
  • Added CATCH_FISH to ObjectiveType enum. QuestService.onCatchFish() uses nullable CommandBuffer pattern (no buffer available from fishing adapters). QuestService.processEvent() refactored to accept @Nullable CommandBuffer. HyFishingAdapter.awardFishingXp() now passes fishItemId and calls QuestService.onCatchFish(). MobKillEventSystem also fires CATCH_FISH for fish mob kills (fallback path)

v0.10.2

Custom Skills

  • Define your own skills - Server owners can now create fully custom skills beyond the 22 built-in ones. Custom skills have a unique ID, display name, description, category (Gathering, Combat, Crafting, or Misc), and trigger types — all configured through the admin UI or mods/mmoskilltree/custom-skills.json
  • Custom Skills admin page - A new page accessible from the admin dashboard (/mmoadmin > Custom Skills) lets you create, edit, and delete custom skills. Set the skill's display name, pick a category, toggle XP trigger types, and manage everything visually
  • Disable built-in skills - Any of the 22 built-in skills can be disabled from the Custom Skills page. Disabled skills are hidden from all UI pages, stop awarding XP, and are excluded from admin editors
  • Full admin editor support - Custom skills appear alongside built-in skills in the Skill Tree Overrides, Command Rewards, and XP Maps admin editors. Add reward tiers, configure milestone commands, and set XP values for custom skills the same way you would for any built-in skill
  • Skill tree tiers for custom skills - Add reward tiers to custom skills through the Skill Tree Overrides page. A "+" button lets you add new tiers, and tiers can be removed since custom skills have no built-in defaults
  • Persisted in config - Custom skill definitions are saved to mods/mmoskilltree/custom-skills.json. Skill tree tiers, command rewards, and XP maps for custom skills are saved to their respective override config files

Per-Skill Max Level Setting

  • Configurable max levels - Server admins can now set a default max level for all skills and override it for individual skills. The default max level is 200. Set any skill higher or lower to fit your server's progression design
  • XP stops at max level - Once a skill reaches its max level, no further XP is awarded. This applies to all XP sources: passive gains, XP tokens, boost multipliers, and the API
  • Admin UI - A new "Max Levels" section on the Custom Skills page (/mmoadmin > Custom Skills) lets you set the default max level and per-skill overrides visually. Skills with overrides are highlighted orange. Use "Apply All" to save changes or "Reset All" to clear overrides
  • Admin commands clamped - The /setxp command automatically clamps values to each skill's max level XP threshold
  • Persisted in config - Max level settings are saved to mods/mmoskilltree/custom-skills.json and survive server restarts

Fishing Skill Tree

  • Parry rewards for Fishing - When Perfect Parries is installed, Fishing now includes Parry Reflect and Parry Stamina Drain rewards in its skill tree at levels 40-100, matching the same progression as combat skills

Multi-Skill Quest Requirements

  • Multiple skill requirements per quest - Quests can now require players to meet multiple skill levels before accepting. For example, a quest can require both Mining 30 AND Crafting 20. All skill requirements must be met for the quest to unlock
  • Backward compatible - Existing quests with a single skill requirement continue to work unchanged. The new array format is optional and only needed when gating on more than one skill
  • UI shows all requirements - The quest log displays every skill requirement in the "Requires:" line, so players can see exactly what they need

Dynamic Quest Category Filters

  • Quest filter buttons are now dynamic - The quest page category filter tabs (Main, Daily, Misc) are no longer hardcoded. Filter buttons are generated automatically based on which categories have enabled quests. Custom categories defined in quest JSON files (e.g., "seasonal", "events", "pvp") now get their own filter button
  • Custom category colors - Custom categories get a unique accent color from a rotating palette, used on the category strip of each quest row

Bug Fixes

  • Harvest quests now track interactive crop pickups - Previously, harvesting quests only counted crops broken directly. Crops picked up interactively (the primary harvest method) now also count toward quest progress
  • Fishing now appears in leaderboard filters - Fishing was missing from the Gathering category on the leaderboard page

Technical Details

  • QuestDefinition: replaced minSkill/minSkillLevel fields with List where SkillRequirement is a new inner record (String skill, int level). Builder retains minSkill()/minSkillLevel() convenience methods via pending fields that merge into the list in build(). New skillRequirements(List) builder method for the array path
  • QuestConfig.parseQuest(): requirements field now accepts both a single JSON object (backward compatible) and a JSON array of objects. Array format takes max minLevel across entries and collects all minSkill/minSkillLevel pairs into the skill requirements list
  • QuestDefaults.questToJson(): serializes 0-1 skill requirements as a single object (backward compatible), 2+ as a JSON array with separate entries
  • QuestService: canAcceptQuest() (both null-quests and main branches) and isQuestVisible() now loop over getSkillRequirements() instead of checking a single skill
  • QuestPage.buildRequirementsText(): loops over all skill requirements, displaying each one in the "Requires:" line
  • CustomSkillsConfig: added defaultMaxLevel (int, default 200) and maxLevels (Map, keyed by uppercase skill name) fields. New methods: getMaxLevel(skillName) returns override or default, setDefaultMaxLevel(), setMaxLevel(), removeMaxLevel(), setMaxLevels() (batch), clearAllMaxLevels(), hasMaxLevelOverride(), getMaxLevelOverrides(). ConfigData inner class extended with nullable Integer defaultMaxLevel and Map maxLevels. No schema version bump (GSON handles missing fields as null)
  • SkillService.addXp(): after computing adjustedAmount, checks currentXp >= maxXp (skip XP, zero fractional remainder) or clamps adjustedAmount so total doesn't exceed getXpForLevel(maxLevel). Same pattern in addCustomXp() for custom skills
  • XpTokenConsumeInteraction.processFlatXpToken(): checks max level before consuming — returns false (don't consume token) if already at max, or clamps xpAmount to not exceed cap
  • SetXpCommand: both "all" and specific-skill paths clamp value to getXpForLevel(maxLevel) per skill before calling setXp()
  • MMOSkillTreeAPI.addXp(): enforces max level cap — returns false if already at max, clamps amount otherwise
  • CustomSkillsPage: new buildMaxLevelsSection() method dynamically populates a 4-per-row skill grid from SkillRegistry.allSkillIds() using MaxLevelSkillRow.ui template, skipping disabled built-in skills. Labels colored orange for overridden skills. New event handlers: applyDefaultMaxLevel, applyAllMaxLevels (batch save, removes overrides matching default), resetAllMaxLevels, maxLevelInput (stores field value without refresh). CustomSkillsEventData extended with defaultMaxLevelInput and maxLevelValue fields
  • CustomSkillsPage.ui: new "Max Levels" @SectionCard section at top of #ContentArea with default max level row (label + field + button + hint), #MaxLevelGrid container, and action row (Apply All + Reset All + status label)
  • PickupItemEventSystem: now fires both QuestService.onPickupItem() and QuestService.onBlockBreak() so BREAK_BLOCK quest objectives also trigger on interactive item pickups
  • SkillTreeDefaults.getFishingDefaults(): tiers 5-9 converted from List.of() to mutable ArrayList with conditional isParryModActive() parry reward additions (PARRY_REFLECT + PARRY_STAMINA_DRAIN, +3%/+5%/+7% progression)
  • LeaderboardPage.getSkillsForCategory(): added SkillType.FISHING to GATHERING category
  • QuestConfig.getActiveCategories(): new method returns all categories with enabled quests, sorted with built-in categories (main, daily, misc) first, then custom categories alphabetically
  • QuestPage.java: replaced hardcoded #FilterMain/#FilterDaily/#FilterMisc setup with dynamic loop over getActiveCategories(). Removed updateCategoryFilterStyles(), added applyCategoryTabStyle() and getCategoryDisplayName() helpers. getCategoryColor() extended with hash-based rotating palette for custom categories
  • QuestPage.ui: replaced hardcoded category buttons with #DynamicCategories group populated at runtime
  • QuestCategoryTab.ui: new template for a single dynamic category filter button

Technical: Custom Skills System

  • CustomSkill: new data class with id, displayName, categoryName, description, and Set. ID auto-uppercased, category defaults to MISC
  • SkillRegistry: singleton registry holding all custom skills alongside built-in SkillType values. init(List) called at startup before config loading. Key methods: isKnownSkill(), isCustomSkill(), getDisplayName(), getCategoryName(), allSkillIdsByCategory() (returns built-in + custom IDs matching a category)
  • CustomSkillsConfig: loads/saves custom skill definitions and disabled built-in list from custom-skills.json. Supports add/update/remove/reorder operations. SkillRegistry.reloadFromConfig() called after mutations
  • CustomSkillsPage: admin UI page for CRUD operations on custom skills — form with ID, display name, description, category dropdown, and trigger type toggles. Built-in skills section with disable/enable toggle per skill
  • SkillTreeConfig: 9 new *ByName(String skillId, ...) methods that accept string skill IDs and delegate to built-in (skillTrees/skillTreeOverrides) or custom (customSkillTrees/customSkillTreeOverrides) maps. Query: getNodeByName, getMaxTierByName, isDefaultByName (always false for custom), isTierDisabledByName, getDefaultNodeByName (null for custom). Mutation: setTierOverrideByName, disableTierByName, removeTierOverrideByName (removes tier + re-indexes for custom), addCustomSkillTier
  • CommandRewardsConfig: 4 new *ByName methods — isDefaultByName, setRewardsByName, disableLevelByName, removeOverrideByName. Delegate to built-in or custom skill reward maps
  • XpMapsConfig: 4 new *ByName methods — getOverridesByName, getDefaultValueByName (null for custom), setXpByName, removeXpByName. Delegate to built-in or custom XP maps
  • SkillTreeOverridesPage, CommandRewardsPage, XpOverridesPage: refactored from SkillType enum to String-based skill IDs. Removed static SkillType[] category arrays, getSkillsForCategory() now returns List via SkillRegistry.allSkillIdsByCategory(). buildSkillSelector() uses SkillRegistry.getDisplayName(). All config calls use *ByName methods. SkillTreeOverridesPage adds "+" button for custom skill tier creation
  • UI templates: BuiltinSkillRow.ui, CustomSkillRow.ui, CustomSkillsPage.ui, MaxLevelSkillRow.ui — new templates for the custom skills admin page

v0.10.1 - Parry Rewards & Scroll Recovery Quest

Parry Skill Tree Rewards

  • Parry rewards in combat skill trees - When the Perfect Parries mod is installed, 7 melee combat skills (Swords, Daggers, Polearms, Staves, Axes, Blunt, Unarmed) and Defense now include parry-themed rewards in their skill trees at levels 40-100
  • 4 new reward types - Parry Counterattack (chance to counterattack after parrying), Parry Reflect (reflect a percentage of blocked damage), Parry Stamina Drain (drain attacker's stamina on parry), and Parry Stun Damage (bonus damage to stunned enemies)
  • Reward flavor per weapon - Each weapon skill offers a unique pair of parry rewards that fit its playstyle: Swords get Counterattack + Reflect, Daggers get Counterattack + Stamina Drain, Polearms get Reflect + Stun Damage, Staves get Reflect + Stamina Drain, Axes get Counterattack + Stun Damage, Blunt gets Stun Damage + Reflect, Unarmed gets Counterattack + Stamina Drain, and Defense gets all four types
  • Scaling with tiers - Parry bonuses start at 3% at level 40 and scale up to 7-8% at level 100, with two parry options available at the final tier
  • No mod, no clutter - Parry rewards only appear in skill trees when Perfect Parries is detected at startup. Servers without the mod see the same reward trees as before
  • API access - Other mods can query a player's total parry bonuses via MMOSkillTreeAPI.getParryCounterattackBonus(), getParryReflectBonus(), getParryStaminaDrainBonus(), and getParryStunDamageBonus()

Leaderboard

  • Redesigned leaderboard page - The leaderboard now uses the standard Hytale container layout with a proper header bar, themed scrollbar, decorative separator above your rank section, and native close button styling. Filter buttons are wider for better readability across all languages

UI Improvements

  • Native Hytale styling across all pages - All UI pages now use the standard Hytale Common.ui layout system with themed containers, scrollbars, separators, and button sounds for a consistent look and feel
  • Admin dashboard sticky footer - The admin config page's action buttons and back button now stay fixed at the bottom of the screen instead of scrolling with the content
  • Boost multiplier precision - XP boost multipliers now display up to 3 decimal places (trimming trailing zeros) instead of being locked to 1 decimal place. A 1.125x boost now shows as "1.125x" instead of "1.1x"

Bug Fixes

  • Skill tree tiers with more than 6 reward options now wrap to a second row - Previously, only the first 6 options were shown. Tiers with 7+ choices (e.g., from mod integrations adding extra rewards) now display all options across two rows

Quests

  • "Forgot Something?" recovery quest - A new repeatable quest that gives players a replacement Skills Menu Scroll. Hidden until the Getting Started quest is completed, available once per hour, and must be manually accepted from the quest log

Technical Details

  • New IntegrationDomain.PARRIES enum value and ParriesIntegration / NoOpParriesIntegration interfaces in the API subproject
  • PerfectParriesAdapter: detection-only adapter using PluginManager.hasPlugin() — simpler than HyFishingAdapter since the parry mod queries MMOSkillTree's API rather than needing event subscriptions
  • IntegrationRegistry: registers PerfectParriesAdapter, adds getParriesIntegration() accessor and hasActiveIntegration() check
  • RewardType: 4 new enum values — PARRY_COUNTERATTACK, PARRY_REFLECT, PARRY_STAMINA_DRAIN, PARRY_STUN_DAMAGE
  • RewardEffectRegistry: parry reward types registered as passive effects (values queried by the parry mod via API, not applied as entity stat modifiers)
  • SkillReward and Messages: parry types added to percentage-format switch cases
  • SkillTreeDefaults: combat skill tree methods (Swords, Daggers, Polearms, Staves, Axes, Blunt, Unarmed, Defense) use isParryModActive() to conditionally add parry rewards to tiers 5-9. Lists converted from List.of() to mutable ArrayList for conditional appending
  • SkillTreeService: 4 new static methods — getTotalParryCounterattack(), getTotalParryReflect(), getTotalParryStaminaDrain(), getTotalParryStunDamage()
  • MMOSkillTreeAPI: 4 new public methods exposing parry bonuses for external mods
  • MMOSkillTreePlugin: reloads SkillTreeConfig after integration resolution when Parries integration is active, so parry rewards are included in the defaults
  • Parry reward localization keys added to all 9 language files (English, French, Spanish, German, Russian, Italian, Portuguese, Turkish, Hungarian)
  • QuestDefaults: added "forgot_something" quest — hidden, requires "getting_started" prerequisite, repeatable with 3600s cooldown, not auto-accepted
  • LeaderboardPage.ui: migrated from raw Group layout to Common.ui @Container pattern — uses $C.@PageOverlay, $C.@Container with @CloseButton = true, $C.@DefaultScrollbarStyle, $C.@PanelSeparatorFancy, $C.@SmallSecondaryTextButton, $C.@TitleStyle, $C.@SubtitleStyle, $C.@DefaultLabelStyle, $C.@ButtonSounds, and ContainerPanelPatch.png backgrounds for table header and rank section
  • LeaderboardPage.java: removed #CloseButton.Text set since Container's built-in close button is an icon-only Button, not a TextButton
  • UI migration to Common.ui patterns for 7 additional pages: ViewXpPage.ui, BoostPage.ui, QuestPage.ui, SkillTreePage.ui, ItemRewardsPage.ui, CommandRewardsPage.ui, AdminConfigPage.ui — each now uses $C.@PageOverlay, $C.@DefaultScrollbarStyle, $C.@ContentSeparator, $C.@ButtonSounds, label spread syntax (...$C.@DefaultLabelStyle), and where applicable $C.@Container with @CloseButton = true, $C.@PanelSeparatorFancy, $C.@SmallSecondaryTextButton, and ContainerPanelPatch.png backgrounds
  • SkillTreePage.java, ItemRewardsPage.java: removed #CloseButton.Text sets since Container's built-in close button is icon-only
  • AdminConfigPage.ui: restructured #Content from single scrollable area to LayoutMode: Top with a FlexWeight: 1 scrollable #ContentArea child, keeping action buttons and back button as non-scrolling siblings
  • SkillDisplayUtils.formatMultiplier(): new utility method formatting doubles with up to 3 decimal places, trimming trailing zeros
  • 14 occurrences of String.format("%.1f", ...) replaced with SkillDisplayUtils.formatMultiplier() across BoostPage.java, ViewXpPage.java, AdminConfigPage.java, BoostCommand.java, XpBoostService.java, ActiveBoost.java, BoostToken.java, BoostPermission.java
  • TierRow.ui: added hidden #ChoicesContainer2 second row (Height: 0 by default). SkillTreePage.java: MAX_CHOICES increased from 6 to 12, new CHOICES_PER_ROW = 6 constant — choices beyond 6 are appended to the second container, tier row height dynamically expanded from 90 to 146

v0.10.0 - Fishing Skill, Mod Integrations & White Label Fixes

Fishing Skill

  • Fishing is now active - By default, killing fish and aquatic mobs awards Fishing XP. Minnows, Salmon, Crabs, Sharks, and more all grant Fishing XP based on their difficulty — no additional mods required
  • Automatic fish detection - The plugin recognizes 18+ aquatic mob types automatically. When a fish mob is killed, it awards Fishing XP instead of combat weapon XP
  • Fishing Mastery Cape - Reach Fishing level 100 to earn the Fishing Mastery Cape with item rewards at every milestone (levels 10, 25, 50, 65, 80, 100)
  • Fishing XP Tokens - 8 flat XP token tiers and 5 personal boost token tiers for Fishing, matching all other skills
  • HyFishing mod support - When HyFishing is installed, fish catches from HyFishing's fishing rod system award Fishing XP instead of mob kills. Fish mob kills fall back to combat weapon XP so there's no double-dipping
  • Default fish XP values - Fish XP scales with difficulty: tiny fish (10 XP), small fish (20-24 XP), standard fish (30-35 XP), medium catches (40-50 XP), large catches (60-75 XP), hostile aquatic (90-100 XP), dangerous aquatic (150-200 XP)
  • Configurable - Override fish XP values via /mmoconfig fishing or mods/mmoskilltree/xp-maps.json
  • Fishing skill tree - 10-tier reward tree with health, stamina, block, mana, and XP bonuses. High-tier rewards include all-combat lifesteal at levels 80 and 100, plus fall damage reduction at level 100

Admin UI

  • Fishing in admin pages - Fishing now appears under the Gathering category in all admin pages: Command Rewards, Skill Tree Overrides, Boost Templates, and XP Tokens. Edit fishing rewards, milestones, and configurations from the dashboard

Optional Mod Integrations

  • Automatic mod detection - MMO Skill Tree can now detect and integrate with other installed mods automatically. No server configuration required — adapters are discovered at startup
  • Fishing integration support - Other mods can provide fishing adapters that hook into MMO Skill Tree's XP system. When a compatible fishing mod is installed, fish catches automatically award Fishing XP
  • Multi-mod fallback - If multiple mods provide adapters for the same feature, the highest-priority adapter that initializes successfully is used. If none succeed, the system falls back to a safe default
  • Server owner overrides - Optionally disable or re-prioritize mod adapters via mods/mmoskilltree/integrations.json. The file is generated automatically on first startup — no editing needed unless you want to customize
  • Startup logging - The full integration resolution process is logged at startup with [Integrations] prefix, showing which adapters were discovered, which were activated, and why others were skipped
  • Developer API - Third-party mod authors can build integration adapters by depending on the lightweight mmoskilltree-api jar (compile-only). See the Integration API docs for details

Combat XP

  • Damage-based modes skip hostility check - In Damage and Damage+Kill combat XP modes, all mobs now award XP when hit, regardless of hostility. Previously, only hostile mobs awarded offensive XP. Per-Hit and Kill-Only modes still require the target to be hostile

Bug Fixes

  • Fixed a disconnect when opening UI pages with White Label branding enabled
  • Server name and description now display correctly for both left and right branding positions. Left position shows the logo, server name, and description in the side panel without overriding page titles. Right position shows the logo in the title area with your server name as the heading and description below it

Technical Details

  • CombatXpEventSystem: DAMAGE_BASED and DAMAGE_PLUS_KILL modes now bypass HostilityUtil.isHostileTowards() check, awarding offensive XP for any mob hit. PER_HIT and KILL_ONLY modes retain the hostility gate
  • CommandRewardsDefaults: added getFishingDefaults() with milestone rewards at levels 10/25/50/65/80/100 including Fishing Mastery Cape at 100
  • Added mmoskilltree.skill.fishing permission node to manifest.json
  • Added reward.cape.fishing localization key to all 9 language files
  • Added 13 Fishing XP token item definitions (8 flat + 5 personal boost) and Cape_Skill_Fishing.json to Server resources
  • SkillType.FISHING now has KILL_ENTITY trigger in addition to CATCH_FISH
  • FishMobPatterns: static set of 18 aquatic mob identifier substrings with isFishMob(String) substring matching
  • FishingDefaults: default XP values for fish/aquatic mobs, tiered by difficulty (10-200 XP)
  • SkillTreeDefaults: added getFishingDefaults() with 10 tiers — health/stamina/defense/mana/XP bonuses, CombatTarget.ALL lifesteal at tiers 8-9, fall damage reduction at tier 9
  • HyFishingAdapter: fixed XP lookup to use XpMapsConfig.getXpMap(FISHING) with longest-substring match instead of MobKillXpConfig
  • Added SkillType.FISHING to GATHERING array in CommandRewardsPage, SkillTreeOverridesPage, BoostTemplateOverridesPage, and XpTokenOverridesPage
  • MobKillEventSystem: fish mob kills check FishMobPatterns.isFishMob() before combat XP. When no fishing integration is active, awards FISHING XP (raw mob XP, no weapon scaling) and returns early. When integration is active, falls through to combat weapon XP
  • HyFishingAdapter: reflection-based adapter for HyFishing mod. Uses Proxy.newProxyInstance() to subscribe to HyFishing's event API without compile-time dependencies. Resolves players by UUID via Universe.get().getPlayers(), dispatches XP awards to the world thread
  • New api subproject producing mmoskilltree-api-1.0.0.jar — contains only integration interfaces, annotations, and data classes with zero plugin dependencies (only JSR-305)
  • API classes in com.ziggfreed.mmoskilltree.integration: OptionalIntegration (base interface), FishingIntegration (domain interface), IntegrationDomain (enum), IntegrationMeta (annotation with domain and priority fields), NoOpFishingIntegration
  • IntegrationRegistry: singleton using explicit adapter registration (no ServiceLoader — Hytale mod classloaders break META-INF/services discovery). Reads @IntegrationMeta annotation for domain/priority/plugin identity. Groups by domain, sorts by effective priority, tries each initialize() until one succeeds. Skips PluginDetector gate — adapters self-detect via Class.forName() in initialize()
  • PluginDetector: reflection-safe plugin detection via PluginManager.get().hasPlugin(new PluginIdentifier(group, name)). Caches reflection handles, returns false if API unavailable. Currently bypassed in favor of adapter self-detection
  • IntegrationsConfig: override-only config at mods/mmoskilltree/integrations.json. Keys are fully qualified adapter class names. Supports enabled (boolean) and priority (integer) overrides
  • Integration lifecycle hooks added to MMOSkillTreePlugin: resolve() during startup, shutdownAll() during shutdown
  • Removed references to non-existent #ServerName UI element from BoostPage, QuestPage, ViewXpPage, LeaderboardPage, and SettingsPage
  • Added #BrandingServerName and #BrandingDescription labels to left-panel .ui files (ViewXpPage, BoostPage, QuestPage) for left branding mode — server name and description display in the side panel without overriding page titles
  • Added #BrandingDescriptionRight label to all 5 page .ui files for right-mode description display below the header
  • Wrapped #PanelTitle in #TitleContainer group on QuestPage.ui and BoostPage.ui so the branding template can be appended in right mode
  • Right mode: serverName replaces #PanelTitle/#SettingsTitle text; serverDescription shown via #BrandingDescriptionRight
  • Left mode: page titles unchanged; branding (logo, name, description) contained entirely within the left panel
  • BoostPage and QuestPage now support both left and right branding positions (previously only left)

v0.9.0 - Combat XP Modes & Item Requirements

New Combat XP Modes

  • Damage-based combat XP - New combat XP mode where offensive weapon skills gain XP equal to damage dealt (1:1 ratio). A hit dealing 15 damage awards 15 XP. Simple and intuitive progression that scales naturally with weapon power
  • Damage+Kill hybrid mode (new default) - Combines damage-based XP with mob kill bonuses. You earn 1 XP per damage dealt during combat, plus bonus XP when you land the killing blow (from mob-kill-xp.json with optional weapon scaling). This is now the default mode for new servers, providing more satisfying combat progression
  • Four combat XP modes available - Choose from Damage (1:1), Damage+Kill (hybrid), Per-Hit (traditional weapon XP maps), or Kill-Only (XP only on kills) in the admin dashboard under General Settings

Item Level Requirements

  • Skill-gated items - Weapons, tools, ores, and gems can now require specific skill levels to use. Players who don't meet the requirements see a red notification and cannot mine restricted blocks or deal damage with restricted weapons
  • Tool requirements enforced when mining - When breaking a block, both the block AND the held tool are checked for level requirements. Using a high-tier pickaxe you don't meet the requirements for will prevent mining
  • Multiple skill requirements - Items can require multiple skills with different levels (e.g., Mining 50 AND Smithing 30) plus an optional total level requirement
  • Pattern-based matching - Requirements use the same longest-substring pattern matching as XP maps, so patterns like Ore_Mithril match all mithril ore variants
  • Default ore progression - Ores are gated by Mining level out of the box: Copper (1), Iron (10), Silver (20), Gold (30), Cobalt (40), Mithril (50), Adamantite (60), Thorium (70), Onyxium (80). Gems require Mining 50-90
  • Default tool/weapon progression - Tools and weapons are gated by their respective skills (10 tiers from level 1 to 80)
  • Admin UI page - Configure item requirements from the admin dashboard via the new "Item Requirements" button. Browse by category (Tools, Weapons, Armor), view defaults vs overrides, add/edit/disable patterns
  • Enable/disable toggle - The entire system is disabled by default. Enable it from the admin UI or config when ready
  • Override-based config - Only your customizations are saved to mods/mmoskilltree/item-requirements.json. Defaults are preserved across updates

Tool & Weapon Requirements

  • Tool-gated blocks - Blocks can now require specific tool types to break. Players who try to mine ores without a pickaxe, or chop logs without an axe, see a red notification and the action is cancelled
  • Weapon-gated mobs - Mobs can require specific weapon types to damage. Server owners can configure special enemies that require specific weapons (e.g., ethereal enemies needing magic staves, armored enemies needing blunt weapons)
  • Default block tool requirements - Out of the box, ores and rocks require pickaxes, logs require axes, and soil/dirt/sand blocks require shovels
  • Pattern-based matching - Tool requirements use the same longest-substring pattern matching as other systems. Tool_Pickaxe_ matches all pickaxe variants regardless of material tier
  • Separate from level requirements - Tool requirements check what TYPE of tool you need (pickaxe vs axe), while item level requirements check your SKILL LEVEL to use that tool. Both systems work together
  • Admin UI page - Configure tool and weapon requirements from the admin dashboard via the new "Tool Requirements" button. Browse block tool requirements and mob weapon requirements in separate tabs, add/edit patterns with comma-separated tool lists
  • Enable/disable toggle - The entire system is disabled by default. Enable it from the admin UI or config when ready
  • Override-based config - Only your customizations are saved to mods/mmoskilltree/action-requirements.json. Defaults are preserved across updates

Admin UI

  • Boost template batch controls - New "Disable All" and "Enable All" buttons on the Boost Templates page let you quickly disable or enable all templates for the selected target/category

Technical Details

  • New CombatXpMode enum values: DAMAGE_BASED (1:1 damage XP) and DAMAGE_PLUS_KILL (damage + mob kill XP). Default mode changed to DAMAGE_PLUS_KILL
  • CombatXpEventSystem: handleDamageBasedXp() method awards XP equal to damage dealt for both DAMAGE_BASED and DAMAGE_PLUS_KILL modes. Uses CombatWeaponUtil.getCombatSkillFromItemId() to determine skill
  • MobKillEventSystem: awards kill XP for DAMAGE_PLUS_KILL mode (in addition to existing KILL_ONLY support)
  • New ItemRequirementsConfig: override-based singleton config at mods/mmoskilltree/item-requirements.json. Schema version 1. enabled field defaults to false. Pattern lookup via getRequirement(itemId) uses longest substring match
  • New ItemRequirementsDefaults: default requirements for tools (Mining/Woodcutting/Excavation/Harvesting), weapons (combat skills), ores (Mining 1-80), and gems (Mining 50-90)
  • New ItemRequirement record: Map skillRequirements + int totalLevelRequired with factory methods skill(), skills(), totalLevel(), skillsAndTotal()
  • New ItemRequirementService: static checkRequirement(store, ref, itemId) returns CheckResult with meetsRequirement(), getFailureMessage(), getMissingSkillLevels(), getMissingTotalLevels()
  • BreakBlockEventSystem: checks requirements for both target block AND held tool via getHeldItemId(). Calls event.setCancelled(true) when requirements not met
  • CombatDamageEventSystem: checks weapon requirements via getHeldWeaponId() before applying damage effects. Sets damage to 0 and notifies player when requirements not met
  • New ItemRequirementsPage: admin UI page with category tabs, pattern list, requirements input field (format: SKILL:LEVEL,SKILL:LEVEL,TOTAL:LEVEL), enable/disable toggle
  • Added notify.item_requirement_not_met localization key to all 9 language files
  • BoostTemplateOverridesPage: added #DisableAllBtn and #EnableAllBtn with handlers that iterate getTemplateIds() and call config.setDisabled() for each
  • New ActionRequirementsConfig: override-based singleton config at mods/mmoskilltree/action-requirements.json. Schema version 1. enabled field defaults to false. Separate maps for block tool requirements and mob weapon requirements
  • New ActionRequirementsDefaults: default tool requirements for blocks (ores/rocks → pickaxes, logs → axes, soil/sand/gravel → shovels). Mob weapon requirements empty by default (all weapons work on all mobs)
  • New ActionRequirement record: List allowedPatterns with matches(heldItemId) method using substring matching. Factory methods none(), requiring(String...)
  • New ActionRequirementService: static checkBlockToolRequirement(blockId, heldItemId) and checkMobWeaponRequirement(mobId, heldWeaponId) returning CheckResult with meetsRequirement(), getFailureMessage(), getRequiredToolsSummary()
  • New ActionRequirementsPage: admin UI page with "Block Tools" and "Mob Weapons" tabs, pattern list, target/tools input fields, enable/disable toggle. Accessible via "Tool Requirements" button in admin dashboard
  • BreakBlockEventSystem: checks tool type requirements via ActionRequirementService.checkBlockToolRequirement() before checking item level requirements. Calls event.setCancelled(true) when tool type not met
  • CombatDamageEventSystem: checks weapon type requirements via ActionRequirementService.checkMobWeaponRequirement() using EntityIdentifierUtil.getMobId() before checking weapon level requirements. Sets damage to 0 when weapon type not met
  • Added notify.tool_requirement_not_met and notify.weapon_requirement_not_met localization keys to all 9 language files

v0.8.15

Admin UI

  • White-label configuration page - Server owners with the WHITE_LABEL entitlement can now edit branding settings directly from the admin UI. Access via the "White Label" button in the Actions section. Configure server name, description, branding position, accent color, and credit visibility without editing JSON files
  • Entitlement-gated admin sections - Boost configuration sections (limits, stack mode, templates, award boost) are now hidden in the admin UI unless the server has the PERMISSION_BOOSTS entitlement active

Improvements

  • Branding extended to all pages - Custom server branding now appears on the Quests, Boosts, Leaderboard, and Settings pages when branding position is set to "left"
  • Consistent left panel styling - Settings button in Quest and Boost pages now matches the tab styling used in the main Skills Overview page

v0.8.14

New Quests

  • Getting Started - New introductory quest that rewards a Skills Menu Scroll for breaking your first block. Auto-accepts and auto-claims to welcome new players. The crafting recipe for the Skills Menu Scroll has been removed in favor of this quest

Configuration

  • Max active quests setting - New maxActiveQuests config option limits how many quests a player can have active at once (default: 10)

Improvements

  • Quest rewards now check inventory space - Autoclaim quests will wait in "ready to claim" status if your inventory is full, and manual claims will be blocked until you free up space
  • Settings moved to sidebar - The Settings button is now in the left panel navigation list instead of at the bottom
  • Better reusable boosts messaging - When reusable boosts are not enabled on the server, shows a clear message instead of "No reusable boosts available"

v0.8.13

Custom Server Branding

  • White-label branding support - Server owners with the WHITE_LABEL entitlement can now customize the skill menu with their own branding. Configure your server name in mods/mmoskilltree/white-label.json and add your logo as an asset to replace the default header

New Quests

  • Bear Bounty - New daily quest to slay 20 bears, rewarding 2 Building XP Shards. Resets every 24 hours

Bug Fixes

  • Fixed Block XP from fire damage - Players no longer gain Block XP from taking fire damage

v0.8.12

New Items

  • Skill Tree Reset Scroll - A consumable item that resets all skill tree reward selections. Hold for 3 seconds to reset your reward choices across all skills, allowing you to pick new rewards. XP and levels are preserved — only your reward selections are cleared. The scroll is consumed on use

Bug Fixes

  • Fixed non-fatal errors on startup for invalid item qualities - Removed invalid quality JSON files that were causing errors during server startup
  • Fixed combat XP exploit with invulnerable entities - Players can no longer farm combat XP by attacking invulnerable entities

Technical Details

  • New MmoSkillAllResetInteraction interaction handler registered as mmo_skill_all_reset — extends SimpleInstantInteraction, calls SkillTreeService.removeAllStatModifiers() then skills.resetAllRewards() to clear all reward selections
  • Added Mmo_Reset_Skills_Scroll.name and Mmo_Reset_Skills_Scroll.description translations to all 9 language item files
  • Added notify.skill_tree_reset localization key to all 9 language defaults files

v0.8.11

New Items

  • Skills Menu Scroll - A new consumable item that opens the Skills menu when used. Left-click to instantly open your skills page without typing commands. The scroll is reusable and not consumed on use

Technical Details

  • New MmoMenuOpenInteraction interaction handler registered as mmo_menu_open — extends SimpleInstantInteraction, opens ViewXpPage for the player on Primary interaction
  • Added Mmo_Menu_Scroll.name and Mmo_Menu_Scroll.description translations to all 9 language files

v0.8.10

Boost Permissions Overhaul

  • Boost permissions now use native permission checks - The custom permissions.json file-based boost permission system has been replaced with native player.hasPermission() checks, ensuring compatibility with any permission mod like LuckPerms or HyperPerms
  • 84+ built-in boost permission nodes - A comprehensive set of boost templates is now available out of the box, covering all-skills, category, per-skill, and global boosts at various multipliers and durations. Server owners grant the corresponding permission nodes through their preferred permission mod
  • Removed /mmoconfig reloadperms command - No longer needed since permissions are checked live through the native permission system
  • Boost page refresh no longer requires file reload - The Refresh button on the Boosts page now re-renders instantly, checking permissions in real time

Configurable Boost Templates

  • Boost templates are now a proper override-based config - All boost permission templates are stored in mods/mmoskilltree/boost-templates.json using the same override pattern as XP tokens. Only customizations are persisted; defaults are always built in
  • Custom boost nodes require registration - The mod only recognizes boost nodes that are registered as templates. To create a custom boost with values not in the 124 defaults, use the "Add" button in the admin UI — simply granting an unregistered node via a permission mod will not work
  • New admin UI page for boost templates - Browse, add, disable, and delete boost templates from the admin config page. Accessible via the "Boost Templates" button in the Actions section. Includes a copyable permission string field for pasting directly into permission mod commands
  • Add custom templates - Create new boost templates with any multiplier, duration, and cooldown combination. Select a target and scope, enter the values, and click Add — the new permission node is generated automatically and appears in the list
  • Delete custom templates - Remove custom templates you no longer need. Default templates cannot be deleted (use Disable instead)
  • Disable specific boost templates - Disable any boost template to prevent players from using it, even if they have the permission node
  • Trim redundant overrides - Clean up overrides that match their default values with a single button click

Offline Player Support for Bulk XP

  • /bulksetxp now queues changes for offline players - When targeting * (all players) or naming specific players who aren't online, XP changes are saved and applied automatically when they next join the server
  • Queued resets apply on login - Pending XP changes are applied in order, rewards are validated (revoked if levels dropped), and the leaderboard is updated — all before stat effects are reapplied
  • Persistent queue - Pending XP changes survive server restarts, stored in mods/mmoskilltree/pending-xp-resets.json

Bug Fixes

  • Fixed skill tree rewards showing duplicated values - Rewards on the skill tree page were displaying doubled text like "+10% +10% Mining XP" instead of "+10% Mining XP". The display text for custom reward names is now shown as-is without prepending the formatted value again

Technical Details

  • XpBoostService: removed buildDefaultTemplates() method (hardcoded permission strings). initTemplates() now reads from BoostTemplateConfig.getInstance().getAllDefinitions(), converts non-disabled definitions to BoostPermission via toBoostPermission(), and populates availableTemplates
  • New BoostTemplateConfig: singleton override-based config at mods/mmoskilltree/boost-templates.json. Schema version 1. Same load/save pattern as XpTokenConfig — merges BoostTemplateDefaults with user overrides
  • New BoostTemplateDefaults: static getAllDefaults() produces LinkedHashMap from permission node strings (previously in XpBoostService.buildDefaultTemplates())
  • New BoostTemplateDefinition: immutable value object wrapping BoostPermission with withOverrides() and toBoostPermission() methods for config round-tripping
  • New BoostTemplateOverridesPage: admin UI page with Personal/Global scope tabs, category/skill target selectors, and 7-column table (DEF MULT/DUR/CD, CUR MULT/DUR/CD, STATUS). Edit section supports multiplier, duration, and cooldown fields. All changes trigger XpBoostService.initTemplates() to reload templates
  • MMOSkillTreePlugin.setup(): added BoostTemplateConfig initialization before XpBoostService.initTemplates()
  • AdminConfigPage: added #EditBoostTemplatesBtn button and editBoostTemplates action handler
  • Added ui.admin.edit_boost_templates and ui.boosttemplates.title localization keys to all 9 language files
  • New PendingXpResetStore: file-based singleton store at mods/mmoskilltree/pending-xp-resets.json, keyed by lowercase username. Each entry is a list of PendingXpReset objects (skill + value). Same load/save/drain pattern as PendingQuestRewardStore
  • BulkSetXpCommand: after applying to online targets, iterates LeaderboardDataStore.getAllPlayers() (for *) or the named target list to queue resets for any player not found online. Reports queued count separately in yellow
  • MMOSkillTreePlugin: PendingXpResetStore initialized and loaded in setup(), saved in shutdown hook. PlayerReadyEvent handler drains pending resets before reapplyStatRewards() — sets XP per operation, then runs SkillTreeService.validateAllRewards() and CommandRewardService.validateAllCommandRewards() to revoke rewards where levels dropped

v0.8.8

Quest Visibility System

  • Hidden quests - Quests can now be configured to stay invisible in the UI until specific conditions are met. Hidden quests automatically appear once the player starts them (via auto-accept or admin commands)
  • Permission-gated visibility - Hide quests until the player has a specific permission, useful for secret quest chains or premium content
  • Prerequisite-gated visibility - Hide quests until prerequisite quests are completed, preventing players from seeing spoilers for quest chains they haven't reached yet
  • Level-gated visibility - Hide quests until the player meets the level requirements, keeping the quest list focused on achievable content

Quest Page Filters

  • Quest search - Filter quests by typing part of a quest name or description. Click Search to apply or the X button to clear. Case-insensitive matching against both display name and description text
  • Status filter buttons - Quickly filter quests by status: All, Active, Available, or Completed. Filter state is preserved when accepting, claiming, or abandoning quests

New Default Quests

  • 8 new default quests covering excavation, combat, crafting, woodcutting, harvesting, and mining progression:
  •     Dirt Digger (main) - Dig 200 soil blocks for Excavation XP
  •     First Blood (main) - Slay 5 Trorks for Defense XP
  •     Apprentice Crafter (main) - Craft 20 items and reach Crafting level 5
  •     Lumberjack's Path (main) - Chop Oak and Birch wood, reach Woodcutting level 10
  •     Big Game Hunter (main) - Slay Bears and Outlanders. Requires First Blood and total level 25
  •     Harvest Season (daily) - Harvest 40 crops on a 24-hour cooldown
  •     Undead Cleanup (daily) - Slay 15 Zombies on a 6-hour cooldown
  •     Gem Collector (misc) - Mine 10 each of Diamonds, Emeralds, and Rubies. Requires Mining level 30

Bug Fixes

  • Fixed custom reward names not displaying on the skill tree page - When a skill tree reward's display name was overridden in config, the skill tree page still showed the default auto-generated name instead of the custom one

Technical Details

  • New QuestVisibility data class with hidden, permission, requirePrerequisites, and requireLevel fields. Parsed from "visibility" JSON object in quest definitions
  • QuestDefinition now uses a Builder pattern instead of a 17-parameter constructor. Old constructor removed; all creation goes through QuestDefinition.builder(id, objectives).displayName(...).build()
  • QuestService.isQuestVisible(): new method evaluating visibility conditions. Already-started quests (ACTIVE, COMPLETED_UNCLAIMED, COMPLETED, ON_COOLDOWN) always visible. When hidden=true with no specific conditions, falls back to canAcceptQuest() for backward compatibility
  • QuestPage: added filterStatus and searchText fields to constructor. getFilteredQuests() now applies visibility, text search, and status filter stages. QuestEventData codec extended with Status and SearchInput keyed fields. All page reopens (accept/claim/abandon/filter) preserve filter state
  • QuestConfig.parseQuest(): parses "visibility" JSON object into QuestVisibility. New mining_expert default quest demonstrates hidden+prerequisite visibility
  • Extracted QuestDefaults class from QuestConfig — default quest definitions use the QuestDefinition.builder() API instead of positional parameters. questToJson() serializes any QuestDefinition to JSON for file output
  • QuestCommand.handleList(): appends [HIDDEN] tag for quests with visibility.hidden=true
  • Messages.getRewardDisplayText(): added early return when reward.getDisplayName() differs from reward.getType().getDisplayName(), using the overridden name with formatted value instead of auto-generating from reward type and skill ID
  • Added 6 localization keys (ui.quests.search_placeholder, ui.quests.search_btn, ui.quests.filter_status_all/active/available/completed) to all 9 language files

v0.8.7

New Weapon Defaults

  • Added Kurabiye's Longbows mod support - All 10 weapons from the Kurabiye's Longbows mod now have default Archery XP values: 9 longbow variants matching their material tier (Adamantite, Cobalt, Thorium, Iron, Copper, Crude, Flame, Frost, Poison) and the Kurabiye bow

Bug Fixes

  • Fixed skill tree rewards showing 0% instead of fractional percentages - Rewards with decimal percentage values (e.g., 0.5% Critical Chance) were displayed as 0% on the skill tree page. Fractional percentages now display correctly with one decimal place (e.g., "+0.5%"), while whole-number percentages remain unchanged (e.g., "+5%")
  • Broken weapons no longer award combat XP - Attacking with a broken weapon (0 durability) was still granting XP for the weapon's skill. Broken weapons now award no combat XP
  • Quest level objectives now refresh when opening the Quests page - Quests with "reach level" objectives could appear incomplete even after the player exceeded the required level, if the XP was gained through admin commands, tokens, or other means that didn't trigger a quest event. Opening the Quests page now re-evaluates all level-based objectives against current skill levels

Technical Details

  • Messages.getRewardFormattedValue() and SkillReward.getFormattedValue(): replaced (int)(value * 100) cast with conditional formatting — whole-number percentages use String.valueOf((int) percent), fractional percentages use String.format("%.1f", percent). Prevents truncation of values like 0.5% to 0%
  • CombatXpEventSystem.handlePlayerDealsDamage(): added early return when isBroken && identifier != null, preventing XP awards for broken (0 durability) weapons
  • BowDefaults: added 9 Weapon_Longbow_* entries (Adamantite, Cobalt, Thorium, Iron, Copper, Crude, Flame, Frost, Poison) and Weapon_Kurabiye (60L)
  • QuestService.refreshReachLevelObjectives(): new method that iterates all REACH_LEVEL objective refs, checks each active quest's objectives against the player's current skill levels, and completes any that are now satisfied. Triggers checkQuestCompletion() for newly completed objectives
  • QuestPage.build(): calls refreshReachLevelObjectives() after fetching QuestComponent, before display logic

v0.8.6

Kill-Only Combat XP Mode

  • New Kill-Only Combat XP toggle - When enabled, offensive weapon skills (Swords, Axes, Polearms, Staves, Blunt, Daggers, Archery, Unarmed) only gain XP when you kill a mob, not on every hit. Defense and Acrobatics continue gaining XP per-hit regardless of this setting. Toggle it from the admin dashboard under General Settings
  • Kill XP scales with weapon and mob - Your weapon's per-hit XP value acts as a percentage bonus on the mob's base XP. A legendary sword with 70 per-hit XP grants a 70% bonus, so killing a Trork (65 base) yields 110 XP. Weapon quality matters equally whether fighting critters or bosses
  • Weapon XP Scaling toggle - Control whether weapon quality applies as a percentage bonus on mob base XP. Toggle from the Mob Kill XP admin page. When disabled, kill XP equals only the mob's base value
  • Comprehensive mob XP defaults - Over 100 mob types have XP values sourced from the Hytale mob database, organized into clear tiers: critters/passive (5-22 XP), minor hostile (35-45), standard enemies (65), tough enemies (85), elite (130), endgame (250), and bosses (up to 500). All values are overridable
  • New Mob Kill XP config - Customize how much base XP each mob type awards on kill via the new mods/mmoskilltree/mob-kill-xp.json config file. Uses the same override-based system as other configs — only your customizations are saved, defaults auto-update with mod versions
  • New Mob Kill XP admin page - Browse and edit mob kill XP values from a visual editor accessible via the "Mob Kill XP" button on the admin dashboard. Add custom patterns, disable entries, reset to defaults, trim redundant overrides, and toggle weapon XP scaling
  • Pattern-based mob matching - Mob kill XP uses longest substring matching against mob type names (e.g., "Skeleton" matches "Skeleton_Fighter", "Skeleton_Archer", etc.)

Bug Fixes

  • Fixed /mmoboost not working when executed by other plugins - Third-party plugins like Tip4Serv that execute commands through the server's command system were rejected with "Only players or console can use this command". The boost command now correctly accepts commands from any non-player sender, such as plugin-dispatched command sources
  • Fixed crafting XP not awarded when using workstations - Crafting at workstations (anvils, furnaces, etc.) was not awarding XP or tracking quest progress because the craft event was emitted by the workstation entity, not the player. Crafting XP and quest objectives now work correctly regardless of whether you craft from your inventory or a workstation
  • Fixed quests with level requirements not completing retroactively - Quests with "reach level" objectives would stay incomplete if the player already exceeded the required level when the quest was accepted. Level-based objectives are now checked and completed immediately at accept time, so players no longer need to gain another level to unlock quest rewards
  • Milestone announcement commands now use server language - The {skill} placeholder in milestone announcement commands now resolves to the server's default language instead of the triggering player's language, so broadcast messages are consistent for all players

Technical Details

  • BoostCommand: changed sender validation from requiring exactly Player or ConsoleSender to treating any non-Player sender as console-like. This allows third-party plugins (Tip4Serv, etc.) that dispatch commands via CommandManager to use /mmoboost without being rejected
  • CraftRecipeEventSystem: changed ECS query from PlayerRef.getComponentType() to Archetype.empty() so workstation-emitted craft events are captured. Added tryGetPlayerRefFromEvent() (reflection-based) to resolve the crafting player from the event object, with fallback to the subject entity's PlayerRef component for inventory crafting. Uses playerRef.getReference() to get the player entity ref for XP awards when the subject is a workstation
  • QuestService.acceptQuest(): new overload accepting @Nullable SkillComponent. After initializing objective progress, iterates all REACH_LEVEL objectives and immediately completes any where the player's current skill level (or total level) already meets the requirement. All callers (processEvent, QuestPage, PlayerQuestCommand, QuestCommand) updated to pass SkillComponent
  • CommandExecutor.processPlaceholders(): new overload accepting @Nullable SkillComponent and boolean useServerLocale. When useServerLocale is true, {skill} resolves via Messages.getSkillNameForLang() using the server's default language. When false with a non-null SkillComponent, resolves using the player's language. Falls back to hardcoded English display name otherwise. SkillService.checkMilestoneAnnouncement() updated to use server locale
  • Added combatXpOnKillOnly boolean to SkillConfig (default false), with getter/setter, save/load, and reloadDefaults() reset
  • CombatXpEventSystem: guards handlePlayerDealsDamage() behind !SkillConfig.getInstance().isCombatXpOnKillOnly() check. Defense (TAKE_DAMAGE) and Acrobatics (FALL_DAMAGE) paths unchanged
  • MobKillEventSystem: added handleKillXp() — determines weapon skill via CombatWeaponUtil, looks up mob base XP from MobKillXpConfig, applies weapon XP as a percentage bonus via mobBaseXp * (100 + weaponXp) / 100 (when weaponXpScaling enabled), calls SkillService.addXp()
  • Extracted CombatWeaponUtil — shared weapon→skill detection utility used by both CombatDamageEventSystem and MobKillEventSystem. Uses longest substring match against DEAL_DAMAGE_PHYSICAL skill XP maps
  • New MobKillXpConfig — override-based config (schema v1) with longest substring matching for mob types, weaponXpScaling boolean (default true). Stores overrides in mods/mmoskilltree/mob-kill-xp.json, generates defaults reference file
  • New MobKillXpDefaults — 100+ default XP entries sourced from hytaleitemids.com API (295 mobs). XP tiers: critters 5, small passive 8, livestock 10-22, minor hostile 35, small hostile 45, medium hostile 55, standard enemies 65, tough 85, elite 130, high elite 175, endgame 250, bosses 400-500, fallback 25. Substring collision handling (e.g., explicit "Pigeon" to avoid "Pig" match). Undead livestock variants get hostile-appropriate XP
  • New MobKillXpPage — admin page modeled after XpOverridesPage with single scrollable pattern list, add/update/disable/reset/trim functionality
  • AdminConfigPage: added #ToggleCombatXpKillOnly toggle, #EditMobKillXpBtn navigation button, and corresponding event handlers
  • MobKillXpPage: added #ToggleWeaponScaling toggle with weaponXpScaling action handler, dynamic mode note text
  • Added 4 localization keys (ui.admin.combat_xp_kill_only_label, ui.admin.edit_mob_kill_xp, ui.mobkillxp.title, ui.mobkillxp.weapon_scaling_label) to all 9 language files

v0.8.5

Bug Fixes

  • Fixed Acrobatics skill not gaining XP - The Acrobatics skill was not awarding any XP because its default XP values were never loaded. Acrobatics now correctly awards XP when players survive fall damage
  • Improved fall damage detection - Fall damage is now detected more reliably for both Acrobatics XP awards and fall damage reduction rewards, using the damage cause ID as the primary check with a fallback to the environment source type
  • Improved mob identification in combat - Combat events now extract mob identities from their model asset ID (e.g., "Skeleton_Fighter", "Bear_Grizzly"), providing more reliable entity detection for combat XP, quest objectives, and entity blacklisting

Quests

  • New default quest: Forest Patrol - A repeatable kill quest to slay 10 Skeletons (pattern-matched, so Skeleton_Fighter, Skeleton_Archer, etc. all count). Rewards 2 Woodcutting XP Tokens. 90-minute cooldown

Technical Details

  • Uncommented case ACROBATICS in SkillDefaults switch, enabling AcrobaticsDefaults.getDefaults() to be registered
  • AcrobaticsDefaults: changed XP key from "Fall" to "" (empty catch-all pattern matching all acrobatics triggers)
  • Extracted DamageUtil.isCause(damage, id) — checks damage.getCause().getId() first, falls back to EnvironmentSource.getType(). Used by both CombatXpEventSystem (acrobatics XP) and CombatDamageEventSystem (fall damage reduction)
  • Extracted EntityIdentifierUtil — consolidates mob identification from CombatDamageEventSystem, CombatXpEventSystem, and MobKillEventSystem into a single utility. Combines ModelComponent.getModel().modelAssetId deep extraction (with regex toString() fallback), direct ModelComponent method probing, and DisplayNameComponent fallback. Also provides describeEntity() for debug logging
  • Replaced wildcard import in SkillDefaults with explicit imports

v0.8.4

Quest Config

  • Default quests are now auto-generated on every server start - Default quests are written to quests/_defaults/default-quests.json each time the server loads. This file is always overwritten, so default quests stay up-to-date with new mod versions without any manual steps
  • User quest files fully replace defaults - Create your own .json files anywhere in the quests/ folder (or subdirectories). If any user files exist, the defaults in _defaults/ are ignored entirely. The generated default file remains as a reference you can copy and customize
  • Legacy example-quests.json automatically migrated - Servers upgrading from older versions will have their example-quests.json moved to quests/backups/ automatically. No quest data is lost
  • 5 new default quests - Combat Initiate (kill Trorks), Builder Basics (place planks), Crafter Apprentice (craft planks), Daily Hunt (repeatable mob bounty), and Boss Bounty (defeat Trork Chieftain with manual reward claim). Total default quests increased from 3 to 8

Balance

  • Reduced Max Health rewards across all skill trees by 50% - Health bonuses from skill tree rewards were too powerful, allowing high-level players to accumulate excessive health pools. All Max Health reward values have been halved across every skill (Gathering, Combat, Crafting, and Misc). Defense remains the highest health-granting skill (up to +28 at level 100, down from +55). Server owners with custom skill tree overrides are not affected

Quest UI

  • Quest requirements now shown in the UI - Locked quests now display a "Requires:" line below the description showing what's needed to unlock them: prerequisite quest names, total level gates, and skill level gates. The requirements text appears in green when all requirements are met, or gray when any are unmet. Fully localized in all 9 languages

Bug Fixes

  • Quest completion notification no longer references /quest command - The "quest ready to claim" notification now directs players to the Quests page instead of the removed /quest claim command

Technical Details

  • QuestConfig restructured: load() now runs a 4-step pipeline — migrateLegacyExampleQuests(), writeDefaultQuestsFile(), load quests (user xor defaults), buildIndex()
  • New hasUserQuestFiles() method recursively checks for .json files outside _defaults/ and backups/. If any exist, loadDefaultQuests() is skipped entirely and only loadUserQuests() runs (all-or-nothing)
  • Removed isDirectoryEmpty(), writeDefaults(), loadDirectory(). Replaced by writeDefaultQuestsFile() (always regenerates), loadDefaultQuests() (non-recursive _defaults/ scan), loadUserQuests() (recursive, skips _defaults/ and backups/). loadFile() simplified back to no parameters (no per-ID override logic needed)
  • _defaults/default-quests.json includes _notice field warning users the file is auto-generated
  • All 89 STAT_HEALTH reward values in SkillTreeDefaults reduced by ~50% across 12 skills: Mining, Woodcutting, Excavation, Harvesting, Swords, Polearms, Staves, Axes, Blunt, Defense, Crafting, and Building
  • notify.quest_ready_to_claim localization key updated in all 9 languages to reference Quests UI page; removed unused {1} (questId) parameter from Messages.get() call in QuestService
  • QuestRow.ui: added #RequirementsLabel element (hidden by default) between #QuestDescription and #ObjectivesContainer
  • QuestPage.populateQuestRow(): for NOT_STARTED quests, calls new buildRequirementsText() method. Reads prerequisite IDs, minLevel, minSkill/minSkillLevel directly from QuestDefinition and builds a comma-separated display string via localization keys
  • Added 4 localization keys (ui.quests.requires_label, ui.quests.req_complete_quest, ui.quests.req_total_level, ui.quests.req_skill_level) to all 9 language files

v0.8.3

Improvements

  • Underscore delimiter for boost commands - /mmoboost give and /mmoboost global now accept underscores as an alternative to pipes for separating arguments (e.g., /mmoboost give Ziggfreed_mining_2_30 instead of /mmoboost give Ziggfreed|mining|2|30). Pipes still work as before

v0.8.2

Admin Dashboard

  • Leaderboard toggle - Server owners can now show or hide the Leaderboard tab from the admin dashboard. When disabled, the Leaderboard tab is hidden from all sidebar pages (Skill Overview, Boosts, Quests) and players cannot navigate to it. Found in General Settings alongside the existing Quests toggle
  • Reset to Defaults warning - The admin dashboard now shows a prominent warning before resetting to defaults, reminding server owners to back up their overrides first

Bug Fixes

  • Fixed orphaned stat bonuses persisting after skill tree changes - When an admin removed or changed a skill tree reward while players were offline, those players could retain phantom health, stamina, or mana bonuses on their next login. Offline players now receive full reward validation on reconnect regardless of whether the change was to XP config or the skill tree
  • Fixed stat bonuses not updating during live skill tree edits - Editing skill tree rewards via the admin dashboard or /mmoconfig reload now fully refreshes stat bonuses for all online players. Previously, changing a reward's type or removing a reward could leave stale health, stamina, or mana bonuses applied
  • Stat bonuses are now cleared on disconnect - Health, stamina, and mana bonuses from skill tree rewards are stripped when a player disconnects. On reconnect, only rewards that still exist in the current config are reapplied. This prevents stale stat modifiers from carrying over if the skill tree is edited between sessions

Technical Details

  • Added enableLeaderboard boolean to SkillConfig (default true), with getter/setter, save/load, and reloadDefaults() reset
  • AdminConfigPage: new #LeaderboardRow toggle in UI, #ToggleLeaderboard event binding, and toggleLeaderboard action handler
  • ViewXpPage, BoostPage, QuestPage: #TabLeaderboard set to Visible: false in UI files; Java conditionally shows tab and guards action handler with isLeaderboardEnabled() check
  • Added ui.admin.leaderboard_label localization key to all 9 language files
  • AdminConfigPage.ui: added #WarningLabel with red warning text above the Reset to Defaults button
  • SkillService.validateOnReady() and processTrigger(): expanded if (configChanged) guard to if (configChanged || skillTreeConfigChanged) so SkillTreeService.validateAllRewards()cleanupOrphanedRewards() runs for offline players after skill tree config changes (previously only ran for XP config changes)
  • SkillTreeService.removeAllStatModifiers(): new method (two overloads — Store/Ref and Holder) that iterates all claimed rewards and removes health, stamina, and mana modifiers from EntityStatMap
  • MMOSkillTreePlugin: registered PlayerDisconnectEvent handler that calls removeAllStatModifiers() on disconnect. Tries Ref/Store path first (playerRef.getReference()), falls back to Holder path (playerRef.getHolder()) when the player is between worlds
  • SkillTreeOverridesPage.revalidateOnlinePlayers() and MMOConfigCommand.validateAllOnlinePlayers(): added removeAllStatModifiers() call before validateAllRewards() + reapplyStatRewards() for a clean-slate approach — strips all stat modifiers first, then only reapplies valid ones

v0.8.1

Bug Fixes

  • Fixed crash when opening Quest page in a new world - Opening the Quest page before performing any quest-tracked action caused a server error. Players with no quest data (new world or first join) can now browse and accept quests immediately
  • Fixed quest JSON files using unicode escapes - Generated quest files wrote \u003d instead of = and \u0027 instead of ' in reward commands, making manual editing confusing. Quest files are now written with clean, human-readable characters
  • Fixed boost quantity flag only awarding one token - Using /mmoboost give with --quantity= greater than 1 could silently discard all but one token. Multiple tokens created in rapid succession shared the same internal ID, causing each to overwrite the previous one. Token IDs are now unique regardless of creation speed
  • Renamed admin quest command from /quests to /mmoquestadmin - Follows the existing mmo prefix naming convention (/mmoconfig, /mmoboost, /mmoadmin). Avoids conflicts with other mods that may register /quests
  • Default XP notification threshold raised to 3 - New players no longer see notifications for every 1 XP gain. The default minimum XP threshold for notifications is now 3, reducing chat spam from low-XP actions. Existing players keep their current setting

Technical Details

  • QuestService.canAcceptQuest(): changed quests parameter from @Nonnull to @Nullable. When null (no QuestComponent on entity), prerequisites block acceptance, level/permission gates still apply, quest state checks are skipped (player has no quest history)
  • QuestConfig and PendingQuestRewardStore: added disableHtmlEscaping() to GsonBuilder to prevent Gson's default HTML-safe encoding of =, <, >, &, '
  • Fixed mining_beginner default quest displayText mismatch: amount: 50 but text said "Mine 150 Stone blocks" — corrected to "Mine 50 Stone blocks"
  • BoostToken.create(): token ID generation changed from System.currentTimeMillis() suffix to UUID.randomUUID() (first 8 chars). The millisecond-resolution timestamp caused ID collisions when awardToken() was called in a tight loop, and since tokens are stored in a Map keyed by ID, duplicates silently overwrote each other
  • QuestCommand: constructor changed from super("quests", ...) to super("mmoquestadmin", ...). Updated Javadoc and all 9 language files (usage strings)
  • MMOSkillTreePlugin: added holder.ensureAndGetComponent(QuestComponent.TYPE) in PlayerConnectEvent handler, matching the existing SkillComponent pattern. Guarantees QuestComponent exists on every player from connect, eliminating null checks in QuestPage and QuestService for new players

v0.8.0

Data-Driven Quest System

  • Fully configurable quests via JSON files - Define quests in mods/mmoskilltree/quests/ using JSON. Each quest has objectives, rewards, categories, prerequisites, and level requirements. An example file with 3 starter quests is generated on first run
  • 8 objective types - BREAK_BLOCK, PLACE_BLOCK, CRAFT_ITEM, KILL_ENTITY, DEAL_DAMAGE, PICKUP_ITEM, REACH_LEVEL, and TALK_TO_NPC. Each objective supports exact, contains, or prefix matching against target identifiers
  • Quest categories - Organize quests into Main, Daily, Misc, or custom categories. Players can filter by category in the UI
  • Repeatable quests with cooldowns - Mark any quest as repeatable with a configurable cooldown period. After completing and claiming, the quest resets once the cooldown expires
  • Sequential objectives - Optionally require objectives to be completed in order. Only the next incomplete objective tracks progress when sequential mode is enabled
  • Auto-accept and auto-claim - Quests can be set to automatically start tracking when a matching event occurs, or to auto-deliver rewards on completion without manual claiming
  • Prerequisites and level gates - Quests can require other quests to be completed first, a minimum total level, or a minimum level in a specific skill before they become available
  • Command-based rewards - Rewards execute server commands with {player} and {quest} placeholders. Supports running as console or player, delayed execution, and offline queueing so players receive rewards on next login
  • Progress notifications - Players see a notification on every objective increment showing current progress (e.g., "Mine Stone Blocks (3/10)")
  • Enable/disable toggle - Turn the entire quest system on or off from skill-config.json. When disabled, the Quests tab is hidden from all UI pages

Quest UI Page

  • New Quests page accessible from the sidebar - Browse all available quests from a dedicated page with the same sidebar navigation as the Skill Overview. The Quests tab appears in the left panel when quests are enabled
  • Category filter tabs - Filter the quest list by All, Main, Daily, or Misc categories
  • State-based display - Each quest shows its current state with color-coded status labels: Available (orange), In Progress (blue), Ready to Claim (gold), Completed (green), or Cooldown with time remaining (gray)
  • Objective progress tracking - Active quests display each objective with current/required progress counts. Completed objectives are highlighted in green
  • Accept, Claim, and Abandon actions - State-appropriate action buttons on each quest row. Accept available quests, claim completed rewards, or abandon quests in progress
  • Reward preview - Quest rows show a comma-separated list of reward display names so players know what they'll earn
  • Smart sorting - Quests are sorted by relevance: active quests first, then ready-to-claim, then available, then on cooldown, then completed

Quest Commands

  • Player command /quest - Players can accept, claim, abandon, and check status of quests via commands. /quest claim all claims all pending rewards at once
  • Admin command /quests - Server admins can reload quest config, list quests by category, give quests to players, reset quest progress, complete quests instantly, and view detailed player quest status. Supports targeting all players with *
  • Offline reward delivery - Quest rewards for offline players are queued to a persistent file and automatically delivered when the player next logs in

Boost Command

  • Give multiple boost tokens at once - The /mmoboost give command now accepts an optional --quantity= argument to award multiple tokens in a single command. Defaults to 1 if omitted

XP Token Overrides Editor

  • New admin page for editing XP tokens and boost tokens - Browse and modify all 240 token definitions at runtime through a visual editor. Accessible from the admin dashboard via the new "Edit Tokens" button
  • Three browsing modes - Switch between XP Tokens, Personal Boosts, and Global Boosts tabs. Each mode adapts the navigation and table columns to the token type
  • Skill and category navigation - Browse tokens by skill (Mining, Swords, etc.) or by boost target category (All Skills, Gathering, Combat, Crafting). Personal boosts support both skill-specific and category-level targets
  • Override any token value - Change XP amounts for flat tokens, or multiplier and duration for boost tokens. Only your customizations are saved; defaults auto-update with mod versions
  • Disable individual tokens - Prevent specific tokens from being consumed without removing them entirely
  • Reset to defaults - Restore any overridden token back to its default values with one click
  • Trim redundant overrides - Remove overrides that match default values to keep your config clean
  • Enable/disable toggle - Turn the entire token system on or off from the editor page

UI Improvements

  • Boost page now has sidebar navigation - The Boosts page has been redesigned with the same left panel sidebar as the Skill Overview and Quest pages. Navigate between all sections without going back to the overview first
  • Quests tab in sidebar - All pages with the sidebar (Skill Overview, Boosts, Quests) now include a Quests tab that links to the Quest page when quests are enabled

Skill Tree

  • Admins can always reset skill trees - OPs and players with the mmoskilltree.admin permission can still reset skill tree rewards even when the "Allow Skill Tree Reset" toggle is disabled. This lets server owners lock respeccing for regular players while retaining the ability to fix issues

Bug Fixes

  • Fixed leaderboard not updating after admin XP commands - Using /setmmoxp or /bulksetxp now immediately reflects changes on the leaderboard. Previously, XP changes from these commands were invisible on the leaderboard until the player next gained XP organically
  • Fixed ghost stat bonuses after reward revocation - Revoking skill tree rewards that had been renamed or removed from config could leave orphaned health, stamina, or mana bonuses on the player. These phantom modifiers are now properly cleaned up during revocation
  • Fixed XP tokens not triggering full level-up effects - Consuming a flat XP token that caused a level-up now correctly fires milestone announcements, item reward notifications, and quest objective tracking. Previously only the level-up notification was shown
  • Fixed combat damage tracking not identifying mob types - Damage dealt to regular mobs was not being identified by entity type, preventing DEAL_DAMAGE quest objectives and per-mob-type blacklisting from working. Mob types are now correctly resolved for all entities
  • Entity blacklist now supports specific mob types - The combat XP entity blacklist (/mmoconfig blacklist) can now block individual mob types (e.g., "hytale:trork") in addition to the existing Projectile and Deployable categories. Previously, only those two special categories could be blacklisted
  • Fixed player data not initializing until first action - Skill data is now created when a player connects to the server, not on their first block break or combat event. All UI pages, settings, and leaderboard entries work immediately on join. New players also receive the server's default language automatically
  • Fixed Settings page becoming unresponsive - Clicking toggles or language buttons on the Settings page before skill data was initialized could leave the page stuck in a loading state. The page now refreshes correctly regardless of data availability

Technical Details

  • New quest/ package: QuestComponent (ECS component, registered as mmoskilltree:quests), QuestDefinition, QuestObjective, QuestObjectiveProgress, QuestReward, QuestState enum (NOT_STARTED, ACTIVE, COMPLETED, COMPLETED_UNCLAIMED, ON_COOLDOWN), ObjectiveType enum (8 types), PlayerQuestData (base64-encoded progress serialization)
  • New QuestConfig singleton: loads quests recursively from mods/mmoskilltree/quests/*.json, builds inverted index (ObjectiveType → quest/objective refs), generates example-quests.json on first run, supports hot-reload
  • New QuestService: static event hooks (onBlockBreak, onBlockPlace, onCraftItem, onEntityKill, onPickupItem, onDealDamage, onLevelReached) called from all event systems. processEvent() routes events to active objectives with MatchMode (EXACT/CONTAINS/PREFIX) pattern matching, handles auto-accept, sequential objectives, and progress notifications
  • New QuestRewardExecutor: executes command-based rewards with {player}/{quest} placeholder substitution, RunAs (CONSOLE/PLAYER), delayTicks support (1 tick = 50ms via daemon thread), offline queueing via PendingQuestRewardStore
  • New PendingQuestRewardStore: file-based JSON queue at mods/mmoskilltree/pending-quest-rewards.json. Queues commands by UUID/username, drained on PlayerReadyEvent
  • New MobKillEventSystem: detects player→mob kills via DeathComponent/Damage.Source, resolves mob identifier via ModelComponent (reflection-based, cached) with DisplayNameComponent fallback, routes to QuestService.onEntityKill()
  • New QuestPage extends InteractiveCustomUIPage: sidebar layout matching ViewXpPage, category filter tabs (All/Main/Daily/Misc), state-based quest row display, accept/claim/abandon event handlers
  • New QuestPage.ui, QuestRow.ui, QuestObjectiveRow.ui templates
  • New QuestCommand (admin, /quests): subcommands reload, list, give, reset, complete, status
  • New PlayerQuestCommand (player, /quest): subcommands accept, claim (supports all), abandon, status
  • BoostPage restructured: standalone 860×990 layout replaced with 1100×1000 sidebar layout. Added updateTabStyles(), bindTabEvents(), hasPermissionForCategory(), isCategoryTab(), getCategoryFromTabId(). BoostEventData extended with category codec field. Back button removed, replaced by sidebar tab navigation
  • SkillConfig: added enableQuests boolean (default true), isQuestsEnabled(), setQuestsEnabled(). Config version unchanged (backward compatible)
  • ViewXpPage: added #TabQuests visibility/binding when quests enabled
  • Quest hooks added to all event systems: BreakBlockEventSystem, PlaceBlockEventSystem, CraftRecipeEventSystem, PickupItemEventSystem, CombatXpEventSystem each call corresponding QuestService.on*() method
  • MMOSkillTreePlugin: registers QuestComponent ECS type, loads QuestConfig and PendingQuestRewardStore, delivers pending rewards on PlayerReadyEvent, saves PendingQuestRewardStore on shutdown
  • Added LeaderboardDataStore.getInstance().updatePlayer() calls to SetXpCommand (both "all skills" and "single skill" paths) and BulkSetXpCommand (per-target loop) after MMOSkillTreeAPI.setXp()
  • SkillTreeService.validateAndRevokeRewards(): when getChoiceById() returns null during tier revocation, removeOrphanedStatModifiers() is now called to clean up stat modifiers before clearClaimedRewardsForTier() wipes the component data. revokedCount now increments regardless of whether the reward still exists in config
  • Extracted SkillService.handleLevelUp() consolidating notifyLevelUp(), checkMilestoneAnnouncement(), checkAndNotifyItemRewards(), and QuestService.onLevelReached(). addXp() refactored to call it. XpTokenConsumeInteraction.processFlatXpToken() now calls handleLevelUp() instead of only notifyLevelUp()
  • SkillTreePage.build(): reset button visibility check now includes PermissionUtil.hasAdminPermission(player) bypass when isAllowSkillReset() is false
  • BoostCommand: added OptionalArg quantityArg via withOptionalArg("quantity", ...). Parsed in executeAsync() with validation (>= 1, defaults to 1). handleGive() and giveTokenToAllPlayers() loop awardToken() quantity times per target
  • New XpTokenOverridesPage extends InteractiveCustomUIPage with TokenTypeTab enum (XP_TOKENS, PERSONAL_BOOSTS, GLOBAL_BOOSTS) for three-mode navigation
  • New XpTokenOverridesPage.ui (950×700) and XpTokenRow.ui row template matching existing admin page styling
  • XpTokenConfig: added setOverride(), setDisabled(), removeOverride(), hasOverride(), getDefaultDefinition(), setEnabled(), trimRedundantOverrides() public methods
  • AdminConfigPage.java + .ui: added #EditTokensBtn with "editTokens" action binding
  • Added ~20 ui.quests.* localization keys, 1 notify.quest_objective_progress key, ui.admin.edit_tokens, and ui.xptokenoverrides.title to all 9 language files
  • CombatXpEventSystem.getEntityType(): added readMobIdentifier() fallback after Projectile/Deployable archetype checks. Uses ModelComponent (reflection-based method resolution) with DisplayNameComponent fallback — same pattern as MobKillEventSystem. Returns actual entity identifier (e.g., "hytale:trork") instead of null for regular mobs, enabling both DEAL_DAMAGE quest tracking and per-mob-type blacklisting
  • MMOSkillTreePlugin PlayerConnectEvent handler: Holder.ensureAndGetComponent(SkillComponent.TYPE) now creates the SkillComponent on connect instead of deferring to SkillService.processTrigger() on first action. Also sets default language via SkillConfig.getDefaultLanguage() for new players with no language preference
  • SettingsPage.handleDataEvent(): null SkillComponent fallback now refreshes the page via openCustomPage() instead of silently returning, preventing the UI from getting stuck in a loading state

v0.7.9

UI Improvements

  • Item Rewards page now shows 5 cards per row - Reward cards are more compact, fitting an extra column so you can see more rewards at a glance without scrolling

Bug Fixes

  • Fixed empty display names on reward cards - Rewards with localization keys that resolved to empty strings now properly fall back to the configured display name, then to auto-derived names from the item ID. Fixes blank titles on XP token and boost token reward cards
  • Fixed command reward display name resolution - The fallback logic now correctly handles empty localization results instead of treating them as valid resolved names

Localization

  • Boost target names are now fully localized - Boost descriptions, activation notifications, and the XP page boost summary now show skill/category names in the player's language instead of hardcoded English
  • Global boost broadcast uses server default language - The server-wide boost activation announcement now uses the configured default language instead of always English

Item Reward Balance

  • Added Void Leather to level 65 milestone rewards - Void Leather is now included at level 65 for Harvesting (x3), Daggers (x2), Archery (x2), Unarmed (x2), Block (x3), Acrobatics (x2), and Crafting (x2)

Technical Details

  • Added resolveDisplayName() helper to ItemRewardsPage — tries localization key first, falls back to displayName, handles empty strings from LocalizationConfig.get()
  • Fixed Messages.getCommandRewardDisplayName(): added !resolved.isEmpty() check alongside !resolved.equals(key), since LocalizationConfig.get() returns "" for missing keys (not the key itself)
  • Added XpToken_ and BoostToken_ prefix parsing to formatItemName() in both ItemRewardsPage and CommandRewardsDefaults
  • Added Ingredient_Leather_Void → "Void Leather" mapping to formatItemName() and getItemColor() (COLOR_VOID) in CommandRewardsDefaults
  • Added Messages.getBoostTargetName() / getBoostTargetNameForLang() — resolves BoostTarget to localized name using boost.target.all_skills, skill.*, or ui.viewxp.category.* keys
  • Added ui.boosts.desc_active, ui.boosts.desc_duration, ui.boosts.scope_global localization keys to all 9 language files
  • BoostPage.java: replaced getDisplayDescription() / getDisplayName() calls with localized equivalents using Messages
  • ViewXpPage.java: active boost target names now use Messages.getBoostTargetName() instead of BoostTarget.getDisplayName()
  • XpTokenConsumeInteraction.java: boost token consumption notification now uses localized target name
  • XpBoostService.broadcastGlobalBoostActivated(): now uses SkillConfig.getDefaultLanguage() instead of hardcoded "en"
  • RewardCard.ui: card dimensions reduced from 228×110 to 186×95, padding 10→8, claim button 80×24→68×22, qty label width 40→32
  • RewardCardRow.ui: row height reduced from 118 to 103
  • ItemRewardsPage.java: CARDS_PER_ROW changed from 4 to 5

v0.7.8

Bug Fixes

  • Fixed combat XP exploit from non-hostile mobs - Players could farm combat XP by attacking passive creatures. Combat offense XP (Swords, Axes, Archery, etc.) is now only awarded when dealing damage to hostile entities
  • Fixed XP token milestone rewards not giving items - XP tokens awarded at skill levels 25, 50, 65, 80, and 100 were using an incorrect item ID (e.g., XpToken_mining_Fragment instead of XpToken_Mining_Fragment), causing the /give command to fail silently. Tokens now resolve correctly for all 16 skills
  • Fixed reward card display for XP tokens - XP token rewards on the Item Rewards page now show their proper display name (e.g., "Fragment XP Token") instead of raw placeholder text
  • Fixed milestone announcement skill names - The {skill} placeholder in announcement commands now uses the proper display name (e.g., "Mining") instead of the lowercased enum name ("mining")
  • Removed Woodcutting Fragment craft recipe - The craftable Woodcutting XP Fragment has been removed

New XP Defaults

  • Added Emerald, Sapphire, Cinder, and Ruby weapon XP values - All gem-tier weapon variants now have default XP values for Swords, Daggers, Battleaxes, Axes, Maces, and Bows. Regular gem weapons match Adamantite-tier XP, Uncommon gem weapons match Iron-tier XP. Adds support for the Emerald Armor, Sapphire Armor & Weapons, and Ruby Armor & Weapons mods

Technical Details

  • Added HostilityUtil in util/ — checks NPC hostility via the Blackboard AttitudeView. Uses reflection to resolve getAttitude* method at runtime (cached after first call) for forward-compatibility with server builds. Returns false (non-hostile) if the entity has no blackboard or attitude data
  • CombatXpEventSystem.handle(): deal-damage XP path now calls HostilityUtil.isHostileTowards(store, targetRef, attackerRef) after the blacklist check. PvP (target is a player) bypasses the hostility gate
  • CommandExecutor.processPlaceholders(): {skill} now resolves via SkillType.fromString(skill).getDisplayName() instead of skill.toLowerCase(). Falls back to lowercase for unrecognized strings
  • ItemRewardsPage.parseGiveCommand(): checks reward.hasDisplayInfo() before falling back to parsing the item ID from the raw command string. Accepts SkillComponent parameter for localized display names

v0.7.7

Consumable XP Token Items

  • New item-based XP tokens - Physical items that players consume (hold-to-use) to instantly gain XP in a specific skill. Available in 8 tiers from Fragment (100 XP) to Artifact (500,000 XP), each for all 16 skills — 128 token variants total
  • Boost token items - Consumable items that activate timed XP multipliers. 100 personal variants (5 tiers across 20 targets) and 12 global variants (3 tiers across 4 categories) — 112 boost token variants total
  • 235 new items - Every skill × tier combination has its own item with unique name, description, and quality tier (Common, Uncommon, Rare, Epic)
  • Hold-to-consume mechanic - Tokens use a charging interaction: hold right-click to consume, interrupted if you take damage
  • Flat XP bypasses boosts - XP token grants are raw XP that won't be multiplied by active boosts, preserving their intended value
  • Boost tokens use existing boost system - Boost items activate through the same pipeline as /mmoboost, respecting stacking limits and expiration
  • XP tokens as milestone rewards - Skills now award XP tokens at levels 25, 50, 65, 80, and 100 as part of item rewards
  • Craftable Woodcutting Fragment - The Woodcutting XP Fragment can be crafted at a Workbench using 200 wood, 2 light leather, and 2 iron bars
  • Server-configurable - Override XP amounts, multipliers, durations, or disable specific tokens via mods/mmoskilltree/xp-tokens.json. Only customizations are stored (override-based config)

Technical Details

  • New data types: XpTokenType (FLAT_XP, TIMED_BOOST), XpTokenTier (8 flat tiers), BoostTokenTier (5 boost tiers), XpTokenDefinition (immutable item-to-behavior mapping with withOverrides() copy method)
  • XpTokenDefaults generates all 235 definitions via loops over IMPLEMENTED_SKILLS (16 skills) × tiers, plus category/skill boost targets × scopes. Count is 235 not 240 because "Crafting" overlaps between categories and individual skills
  • XpTokenConfig singleton: override-based config at mods/mmoskilltree/xp-tokens.json (schema v1). Stores enabled, chargeDurationTicks, per-token OverrideEntry (nullable xpAmount, multiplier, durationMinutes, disabled). Merges defaults + overrides at load time
  • XpTokenConsumeInteraction extends SimpleInstantInteraction: registered as "mmo_xp_token_consume" via getCodecRegistry(Interaction.CODEC). On firstRun(): resolves held item ID → XpTokenDefinition, dispatches to processFlatXpToken() or processBoostToken(). Consumes held item via player.getInventory().getHotbar().removeItemStack(held)
  • Flat XP path: skills.add(skillType, xpAmount) directly (bypasses SkillService.addXp() multipliers), manual level-up detection via before/after comparison, calls SkillService.notifyLevelUp() and SkillTreeService.checkMilestones()
  • Boost path: creates temporary BoostToken, stores in skills.getBoostTokens(), calls XpBoostService.activateToken(), removes token on failure (limit reached)
  • 235 item JSONs in Server/Item/Items/Consumables/XpTokens/ using vanilla quality strings (Common, Uncommon, Rare, Epic) and Charging → mmo_xp_token_consume interaction chain
  • MMOSkillTreePlugin.setup(): loads XpTokenConfig after CommandRewardsConfig, registers XpTokenConsumeInteraction codec
  • CommandRewardsDefaults: added giveXpToken(tier, qty) and giveBoostToken(target, scope, tier) factory methods using {skill} placeholder; added token rewards to getGlobalSkillDefaults() at levels 25/50/65/80/100
  • Added 6 localization keys to all 9 language files: notify.xptoken.consumed_flat, notify.xptoken.consumed_boost, notify.xptoken.consumed_boost_global, notify.xptoken.disabled, notify.xptoken.boost_limit, notify.xptoken.not_recognized

v0.7.6

Skill Card Improvements

  • Color-coded buff labels - Claimed tree buffs on each skill card now display as individually colored labels instead of plain gray text. Colors match the buff type: red for damage, blue for defense, orange for crit, green for lifesteal, purple for mana, and more
  • Tier progress and metadata - Each skill card now shows a metadata line below buffs with tier progress (e.g., "Tier 4/10"), XP bonus, and luck percentage. The Total Level card aggregates tier progress across all skills
  • Fixed boost button always showing active style - Skill cards with only unused boost tokens now correctly show a muted gray "Boosts" button instead of the bright yellow active style. Active boosts still display with yellow styling and the "+X%" label

Mastery Cape Updates

  • Master Cape stat boost - The Master Cape now provides 20% physical and projectile damage resistance (up from 18%) and 5% damage enhancement across all attack types (up from 4%), solidifying its position as the best cape in the game
  • New cape models - The Master Cape, Swords Cape, and Woodcutting Cape now have unique custom models
  • Updated cosmetic slot - All mastery capes now use the "Back" cosmetic slot for proper model display

Milestone Announcement Commands

  • Configurable level milestone announcements - Server owners can now run a command when players reach configurable level milestones (default: 10, 25, 50, 75, 100). Great for broadcasting achievements to the whole server via /say or triggering custom rewards
  • Admin UI controls - New section in the admin dashboard to enable/disable announcements, set milestone levels (comma-separated), and customize the command template
  • Default command: say {player} has reached level {level} in {skill}!
  • Supports placeholders: {player}, {level}, {skill}, {total_level}

Admin Dashboard Improvements

  • Streamlined admin layout - The config overrides display section has been removed from the admin dashboard. Editor buttons (Edit XP Maps, Edit Rewards, Edit Skill Tree) are now in the Actions bar for quicker access
  • Trim Overrides moved to editor pages - The "Trim Overrides" button now lives directly on the XP Overrides and Skill Tree Overrides pages, where it's most relevant. XP trimming also covers luck overrides

Online Status Privacy

  • Players can hide their online status - New "Show Online Status" toggle in Settings lets players choose whether other players can see them as online on the leaderboard
  • Admin bypass - Server admins (OP or mmoskilltree.admin) always see true online status regardless of player privacy settings
  • Default: enabled (visible to all, same behavior as before)

Item Reward Balance

  • Reduced ore and bar quantities across all milestone rewards - All metal bar rewards (Iron, Copper, Bronze, Cobalt, Adamantite, Thorium, Mithril, Onyxium, Prisma) have been reduced by approximately 50% across all per-skill and total level milestones. Weapons, essences, gems, capes, and boost rewards are unchanged

Technical Details

  • Halved all Ingredient_Bar_* quantities in CommandRewardsDefaults.java across all 16 per-skill methods and getTotalLevelDefaults(). Non-bar rewards (essences, gems, weapons, tools, armor, capes, storm materials, voidheart, chitin, boosts) unchanged
  • Added enableMilestoneAnnouncements (boolean), milestoneAnnouncementLevels (List\), milestoneAnnouncementCommand (String) to SkillConfig with getters/setters, load/save/reloadDefaults support, and ConfigData fields (no config version bump — new fields use defaults when missing)
  • Added checkMilestoneAnnouncement() in SkillService.addXp() after level-up detection; checks enabled flag, milestone list membership, processes placeholders via CommandExecutor.processPlaceholders(), and executes via CommandExecutor.executeAsConsole()
  • Added #MilestoneAnnounceRow, #MilestoneLevelsField, #MilestoneCommandField, #ApplyMilestoneBtn, #MilestonePlaceholders to AdminConfigPage.ui; increased #SettingsSection height 372 → 580
  • Wired AdminConfigPage.java: milestoneLevelsInput/milestoneCommandInput instance vars, AdminConfigEventData codec fields, toggle/apply handlers, text field bindings, reload/reset sync
  • Added showOnlineStatus boolean to SkillComponent (field, codec, default/full constructors, clone, getter/setter)
  • Added hideOnlineStatus field to LeaderboardDataStore.PlayerLeaderboardData, set in updatePlayer() from !skills.getShowOnlineStatus()
  • LeaderboardPage.gatherPlayerData() now accepts viewerIsAdmin parameter; filters isOnline for cached players with hideOnlineStatus=true when viewer is not admin
  • Added #OnlineStatusSetting group to SettingsPage.ui after #CombatEffectsSetting; wired toggle in SettingsPage.java with "toggleOnline" handler that updates SkillComponent and leaderboard cache
  • Added 6 localization keys to all 9 language files: ui.admin.milestone_announce_label, ui.admin.milestone_levels_label, ui.admin.milestone_command_label, ui.admin.milestone_placeholders, ui.settings.online_status, ui.settings.online_status_desc
  • Removed #OverridesSection from AdminConfigPage.ui (override counts display and trim/editor buttons); moved #EditXpBtn, #EditRewardsBtn, #EditSkillTreeBtn into #ActionsSection as a second row
  • Moved trim functionality out of AdminConfigPage: XpOverridesPage trims XP + luck overrides, SkillTreeOverridesPage trims skill tree overrides; added #TrimBtn to both .ui files and event bindings/handlers in Java
  • Removed countXpOverrides() method and unused imports (XpMapsConfig, LuckConfig, SkillTreeConfig, SkillType) from AdminConfigPage.java
  • Cape_Master.json: Physical/Projectile DamageResistance 0.18 → 0.20, Light/Charged/Signature DamageClassEnhancement 0.04 → 0.05
  • All 17 cape JSON files: CosmeticsToHide changed from ["Overtop", "Cape"] to ["Back"] for new model system
  • Fixed card.hasActiveBoost = card.showBoostBtncard.hasActiveBoost = hasPersonalBoost in buildSkillCard() to match the total card's boost logic
  • Added BuffEntry inner class (text + color) and getRewardColor(RewardType) static method with per-type color mapping
  • Replaced getSkillBuffsText()getSkillBuffEntries() returning List with color data
  • Changed SkillCardData.buffsText (String) → buffEntries (List) and added metaText field
  • populateSkillCard() now dynamically appends CardBuffLabel.ui templates into #CardBuffsRow with per-label color via commandBuilder.append()
  • New CardBuffLabel.ui template: minimal colored label for dynamic buff row population
  • Updated SkillCard.ui: replaced #CardBuffsText Label with #CardBuffsRow Group (LayoutMode: Left), added #CardMetaText Label (9pt, #7a8a9e)
  • buildSkillCard() computes metadata via skills.getClaimedTierCount(), SkillTreeService.getActiveRewardsForSkill() for XP bonus and luck
  • buildTotalLevelCard() now accepts SkillTreeConfig parameter and aggregates tier counts across all filtered skills

v0.7.5

Skill Cards Grid Layout

  • Skill overview redesigned as a card grid - The skill list on the main XP page now displays as a grid of cards (3 per row) instead of flat rows. Each card shows skill name, level, progress bar, XP, claimed buffs, and next reward hint at a glance
  • Total Level card in overview - A special summary card appears first when viewing all skills, showing your combined level, total XP, and aggregate buffs across all skills. Distinguished by a blue accent background
  • Quick-action buttons on every card - Each card can show up to three buttons depending on context:
  •     Skill Tree - Opens the skill tree for that skill (shows "Reward Ready!" in orange when unclaimed rewards are available)
  •     Rewards - Opens the item rewards page filtered to that skill (visible when skill or global rewards are configured)
  •     Boost - Shows current boost percentage and opens the boost page (visible only when an active boost applies)
  • Category color coding - Each card has a colored accent strip matching its category: green for Gathering, red for Combat, orange for Crafting, purple for Misc
  • Total Level rewards button - The Total Level card includes a Rewards button that opens item rewards in total level mode when total level rewards are configured

Bug Fixes

  • Fixed Storm Hide and Storm Leather item reward IDs - Corrected the item IDs to match the game's naming convention, so Storm materials now appear correctly when claimed

Admin Config: Skill Tree Reset Toggle

  • New "Allow Skill Tree Reset" toggle in the admin dashboard - Server owners can now disable the skill tree reset button for all players. When turned off, the Reset button is completely hidden from the skill tree page, preventing players from respeccing their reward choices
  • Toggle is located in the General Settings section of /mmoadmin, between Item Rewards and Default Language
  • Default: enabled (players can reset as before)
  • Saves automatically when toggled, like other admin settings

Technical Details

  • Added allowSkillReset boolean to SkillConfig (field, ConfigData, load/save/reloadDefaults(), getter/setter), bumped CONFIG_VERSION 10 → 11
  • Added #SkillResetRow with #SkillResetLabel and #ToggleSkillReset to AdminConfigPage.ui, increased #SettingsSection height 336 → 372
  • Wired toggle in AdminConfigPage.java: label from ui.admin.skill_reset_label, event binding for "toggleSkillReset" action, handler flips config value
  • SkillTreePage.java enforces config: when isAllowSkillReset() is false, sets #ResetButton.Visible = false before the existing claimed-count check
  • Added ui.admin.skill_reset_label localization key to all 9 language files
  • Fixed Storm material item IDs in CommandRewardsDefaults: Ingredient_Storm_HideIngredient_Hide_Storm, Ingredient_Storm_LeatherIngredient_Leather_Storm; updated formatItemName() and getItemColor() matchers
  • Replaced SKILL_ROW_TEMPLATE (SkillRow.ui) with SKILL_CARD_ROW_TEMPLATE (SkillCardRow.ui) and SKILL_CARD_TEMPLATE (SkillCard.ui), CARDS_PER_ROW = 3
  • New SkillCardRow.ui template: horizontal row container (LayoutMode: Left, Height: 210)
  • New SkillCard.ui template: 270x200px card with #CategoryStrip, #CardSkillName, #CardLevel, #CardProgress (ProgressBar), #CardXpText, #CardBuffsText, #CardHintText, and #ButtonsRow containing #TreeBtn, #RewardsBtn, #BoostBtn with spacer groups
  • New SkillCardData inner class in ViewXpPage holds all display state for one card (name, level, progress, xp, buffs, hint, categoryColor, backgroundColor, button visibility flags, skill reference, isTotalCard flag)
  • New methods: getCategoryColor(SkillCategory), buildTotalLevelCard(), buildSkillCard(), populateSkillCard() replace the inline skill row loop
  • Build method now constructs a List, chunks into rows of 3, appends row and card templates, then populates each card
  • New "skillRewards" action handler in handleDataEvent(): routes "TOTAL" to ItemRewardsPage(playerRef, MINING, true), otherwise opens ItemRewardsPage(playerRef, skill) for the specific skill
  • Existing ViewXpEventData codec reused unchanged (action/skill/category fields)

v0.7.4

Skill Tree Admin Editor

  • New in-game Skill Tree editor - Server owners can now customize skill tree reward tiers directly from the admin panel. Open /mmoadmin and click "Edit Skill Tree" to browse skills by category, view all tiers, and modify individual rewards
  • Override-based customization - Changes are saved as overrides on top of defaults. Only your modifications are stored, so new default rewards from updates propagate automatically without losing your customizations
  • Per-tier editing - Select any tier to view its rewards, edit values, add new rewards, remove rewards, or adjust level requirements and choice counts
  • Disable or reset tiers - Disable entire reward tiers or reset them to defaults with one click
  • Status indicators - Color-coded tier buttons (green = default, orange = overridden, red = disabled) and per-reward status labels (Default, Modified, Added) show exactly what's been customized
  • Trim redundant overrides - The existing "Trim Redundant" button on the admin page now also cleans up skill tree overrides that match defaults
  • Automatic migration - Existing skill-tree.json files from previous versions are automatically detected and migrated, with a backup saved

Technical Details

  • Converted SkillTreeConfig from traditional full-regeneration to override-based storage pattern (dual skillTrees + skillTreeOverrides maps)
  • New config format uses schemaVersion: 1 with per-skill per-tier override granularity; legacy V4 format auto-detected via configVersion field and migrated with diff comparison against code defaults
  • Added override query API: isDefault(), isTierDisabled(), getOverrides(), getDefaultNode(), countTotalOverrides()
  • Added override mutation API: setTierOverride(), disableTier(), removeTierOverride(), trimRedundantOverrides()
  • Reference file written to _reference/defaults-skill-tree.json showing all code defaults
  • New SkillTreeOverridesPage.java with SkillTreeOverridesEventData codec (13 fields), category/skill/tier navigation, reward list with template rows, and add/edit/remove/disable/reset actions
  • New UI templates: SkillTreeOverridesPage.ui (1100x700), SkillTreeRewardRow.ui (row template)
  • Updated AdminConfigPage with #EditSkillTreeBtn button, skill tree override count display, and trim integration
  • Localization keys added to all 9 language files: ui.admin.edit_skill_tree, ui.skilltreeoverrides.title
  • Internal NodeData and RewardData classes changed from private to package-private for rewardsMatch() access

v0.7.3

Level 100 Mastery Boost Upgrade

  • Mastering a skill now rewards a global XP boost token - Reaching level 100 in any skill now awards a 1.25x All-Skill Global XP Boost (30 min) token instead of the previous personal skill-specific boost. When activated, this boost benefits every player on the server across all skills
  • Celebrating mastery is now a server-wide event - your achievement helps everyone!

Item Rewards Page Overhaul

  • Fixed reward claiming not targeting the correct reward - Claim buttons now reliably trigger the correct reward. The rewards page has been rebuilt with a card grid layout where each reward has its own dedicated claim button
  • Rewards are now grouped by level - Clear level separators (e.g., "Level 10", "Level 25") divide rewards into tiers, making it easier to find what you've unlocked
  • Card-based layout - Rewards display as compact cards in rows of up to 4, showing item name, quantity, status, and claim button at a glance
  • Reward descriptions - Server owners can now add optional description text to item rewards in mods/mmoskilltree/command-rewards.json using the "description" field. Descriptions appear below the item name on reward cards
  • Existing configs without descriptions continue to work unchanged

Storm Leather & Storm Hide Rewards

  • New rare crafting materials added to item rewards - Storm Leather and Storm Hide now appear in milestone rewards for skills where leather and hide materials are thematically relevant
  • Available at levels 80 and 100 for Defense, Daggers, Archery, Acrobatics, Unarmed, Harvesting, and Crafting
  • Total Level milestones include Storm materials at levels 350, 500, 750, and 1000
  • Defense gets the highest quantities as the armor skill

Item Reward Balance Adjustments

  • Reduced Total Level 350 rewards - Material quantities cut roughly in half (Mithril/Onyxium 75 to 35, essences 50 to 25, etc.) and boost reduced from 2x 45m to 1.5x 30m to better match the progression stage
  • Master Cape moved to Total Level 1500 - The Master Cape is now awarded at Total Level 1500 instead of 1000, making it a true long-term goal

Rewards Editor Improvements

  • Click any reward to edit it - Clicking a reward entry in the admin Rewards Editor copies its command, display name, localization key, and color into the input fields for editing
  • Saving updates existing rewards - If you edit a reward and save, it replaces the original entry (matched by command string) instead of adding a duplicate

Technical Details

  • Replaced boostSkill_2x_30m() (personal, single-skill, 2x, 30min) with boostGlobal_1_25x_30m() (global, all-skill, 1.25x, 30min) in all 16 skill level 100 reward defaults in CommandRewardsDefaults.java
  • Added boostGlobal_1_25x_30m() factory method
  • Existing Global Skill milestone reward (1.25x Global 15min at level 100 in getGlobalSkillDefaults()) remains unchanged and stacks with the per-skill reward
  • Added COLOR_STORM (#5599dd) constant, formatItemName() and getItemColor() handlers for Ingredient_Storm_Leather and Ingredient_Storm_Hide
  • Total Level 350: reduced material quantities ~50%, removed Iron Head armor, removed Storm Hide, downgraded boost from boostAll_2x_45m() to boostAll_1_5x_30m()
  • Moved Cape_Master from level 1000 tier to new level 1500 tier in getTotalLevelDefaults()

Technical: Item Rewards Grid Layout

  • Replaced RewardTierRow.ui / RewardItem.ui templates with RewardCard.ui, RewardCardRow.ui, and RewardTierSeparator.ui
  • ItemRewardsPage.java: rewrote populateSkillRewards() and populateTotalRewards() to group rewards by level tier, chunk into rows of 4 cards, and populate each card individually via populateCard()
  • Added CardData record to bundle display info with claim state for grid population
  • Removed populateSingleRewardItem() and getRewardDisplayName() methods (replaced by card-based approach)
  • CommandRewardEntry.java: added String description field with 6-arg constructor, getDescription(), hasDescription(), builder .description() method; 5-arg constructor delegates with null description
  • CommandRewardsConfig.RewardData: added description field to serialization; Gson ignores missing fields on load for backwards compatibility
  • ItemDisplay record: added description as 4th field, propagated through parseGiveCommand(), parseBoostCommand(), and parseRewardForDisplay()

Technical: Rewards Editor Click-to-Edit

  • CommandRewardsPage.buildRewardList(): added Activating event bindings on #CommandText and #DisplayName labels per row, firing selectReward with the row index
  • New selectReward handler in handleDataEvent() reads the reward at the given index and populates commandInput, displayInput, locKeyInput, colorInput
  • addReward handler now searches currentRewards for a matching command string; replaces in-place via List.set() if found, otherwise appends
  • Extracted getCurrentRewards(config) helper to reduce duplication across selectReward, addReward, disableReward, removeReward

v0.7.2

Bug Fixes

  • Fixed skill tree stat modifiers not updating after config changes - Players who were offline during a skill tree config edit (or when the server restarted after edits) would keep outdated health, stamina, and mana bonuses from old reward values. Stat modifiers are now reconciled with current config on each player's first action after any server restart
  • Fixed orphaned rewards from removed config entries - If a server owner removes or renames skill tree rewards, players who previously claimed those rewards now have the stale stat modifiers properly cleaned up on their next session
  • Fixed item reward commands briefly blocking the server - Claiming item rewards with multiple items (e.g., level 100 rewards) would momentarily stall the server while commands were spaced out. Reward commands are now dispatched without blocking

Technical: Stat Modifier Reconciliation on Startup

  • MMOSkillTreePlugin.setup() now sets both configChanged and skillTreeConfigChanged flags to true at the end of initialization
  • On first skill action per player per restart, SkillTreeService.reapplyStatRewards() removes and reapplies all STAT_HEALTH, STAT_STAMINA, STAT_MANA EntityStatMap modifiers with current config values
  • SkillTreeService.validateAllRewards() also runs, cleaning up orphaned rewards via cleanupOrphanedRewards() (removes modifiers for reward IDs no longer present in config)
  • Per-player cost is one-time iteration of claimed rewards on first action after restart; no ongoing overhead

Technical: Command Reward Execution No Longer Blocks World Thread

  • CommandRewardService.executeRewards() previously used Thread.sleep(25) inside world.execute(), blocking the world thread between reward commands
  • Now uses CompletableFuture.delayedExecutor() to schedule staggered commands from a background thread, dispatching each to the world thread via world.execute() without blocking it

v0.7.1

Mastery Cape Armor Attributes

  • Skill capes now provide armor stats - Each mastery cape has unique attributes themed to its skill, making them functional gear on top of prestige cosmetics
  • Damage resistance - All capes provide physical and projectile damage resistance (8-18%), surpassing Epic-tier chest armor
  • Health bonuses - All capes grant bonus health (+14 to +28), scaling with the cape's combat relevance
  • Combat capes include damage enhancements matching their weapon type:
  •     Swords: +8% Light damage | Daggers: +10% Light damage | Polearms: +10% Charged damage
  •     Staves: +8% Signature damage, +30 Mana | Axes: +5% Light & Charged damage
  •     Blunt: +8% Charged damage | Archery: +10% Charged damage | Unarmed: +10% Light damage
  • Defensive capes have the highest resistance among skill capes:
  •     Defense: 16% Physical/Projectile, +26 Health (best pure tank)
  •     Acrobatics: 12% Physical, 16% Projectile, +18 Health (best projectile dodge)
  • Gathering and production capes provide balanced survivability stats
  •     Harvesting cape also grants +30 bonus Mana
  • Master Cape is the best cape - 18% Physical/Projectile resistance, +28 Health, +30 Mana, and +4% to all damage types (Light, Charged, Signature)

Technical: Cape Attribute Implementation

  • Added DamageResistance, StatModifiers, and DamageClassEnhancement blocks to all 17 cape item JSONs in Server/Item/Items/Armor/Capes/
  • Stat ranges across all capes: Physical 8-18%, Projectile 8-18%, Health +14 to +28, Mana 0-30
  • DamageClassEnhancement used on combat capes: Light, Charged, and/or Signature at 4-10% Multiplicative
  • Balance reference: base game Epic chest armor gives +24 HP / 14% resist (tank) or +17 HP / +16 Mana / 9% resist (mage); Legendary skill capes exceed both

Technical: Combat System Separation

  • Split CombatDamageEventSystem into two separate systems for cleaner separation of concerns:
  •     CombatDamageEventSystem - Damage modification only (damage bonuses, defense reduction, crits, lifesteal, fall damage reduction)
  •     CombatXpEventSystem - XP awards only (combat XP, defense XP, acrobatics XP, entity blacklist checks)
  • XP system is read-only and never modifies damage amounts
  • Entity blacklist logic (getEntityType, isEntityBlacklistedForCombatXp) moved to the XP system
  • Both systems registered in MMOSkillTreePlugin setup
  • No functional changes to combat behavior or XP awards

v0.7.0

Russian Language Support

  • New language: Russian - Full Russian translation added as the 9th built-in language
  • All UI text, notifications, commands, and skill tree content translated
  • Select Russian in Settings like any other language
  • 9 built-in languages - English, Spanish, French, Portuguese, Hungarian, Turkish, German, Italian, Russian
  • Internal language system improved to make adding new languages easier in the future
  • No changes to existing translations

Skill Mastery Capes

  • 17 mastery capes - Cosmetic cape armor awarded for reaching level 100 in any skill (or total level 1000)
  •     16 per-skill capes: Mining, Woodcutting, Excavation, Harvesting, Swords, Daggers, Polearms, Staves, Axes, Blunt, Archery, Unarmed, Block, Acrobatics, Crafting, Building
  •     1 Master Cape for total level 1000
  • Legendary armor stats - Each cape provides damage resistance, bonus health, and skill-themed bonuses (see v0.7.1). Equips in Chest slot, hides default cape cosmetic
  • Reward-only - Not craftable; awarded automatically at level 100 through the existing command rewards system
  • Custom item quality - "Mastery Cape" quality (gold text, legendary drop particles)
  • Effectively indestructible - 9999 durability
  • Fully localized - Cape names and descriptions in Hytale's items.lang, reward display names in all 9 language files

Technical: Skill Capes Resource Structure

  • 17 item JSON definitions in Server/Item/Items/Armor/Capes/ (shared model, per-cape textures)
  •     Skill capes: ItemLevel: 100, Master: ItemLevel: 200
  •     All use Parent: Armor_Cloth_Wool_Head, Quality: MMO_Skill_Cape
  • Quality definition at Server/Item/Qualities/MMO_Skill_Cape.json (QualityValue: 6, gold #ffaa00 text)
  • Language files at Server/Languages/en-US/items.lang and general.lang
  • CommandRewardsDefaults.java - Added give() call at level 100 for each skill and total level 1000
  •     New COLOR_CAPE constant, formatItemName() and getItemColor() handle cape IDs
  • 17 localization keys (reward.cape.*) added to all 9 language default files
  • Art assets (model, textures, icons, quality UI) to be created separately

Technical: Localization Architecture Overhaul

  • Extracted per-language defaults - Each language's default messages moved from LocalizationConfig.java into dedicated classes in i18n/lang/:
  •     LanguageDefaults interface (langCode(), displayName(), getDefaults())
  •     EnglishDefaults, SpanishDefaults, FrenchDefaults, PortugueseDefaults, HungarianDefaults, TurkishDefaults, GermanDefaults, ItalianDefaults, RussianDefaults
  • Registry-based language loading - LocalizationConfig now uses a BUILTIN_LANGUAGES registry (LinkedHashMap) instead of hardcoded per-language methods
  • Reduced LocalizationConfig.java from ~3,272 lines to ~377 lines
  • No functional changes to existing translations or file-based overrides

Default Server Language

  • Server-wide default language - Server operators can now set a default language for new players
  •     New players inherit the server's chosen language instead of always defaulting to English
  •     Existing players keep their previously chosen language (only affects new players)
  • CLI command - /mmoconfig language --args= to set the default language
  •     Validates against available (installed) languages
  •     Shows available language codes if invalid input is provided
  • Admin UI - Language selector added to /mmoadmin General Settings section
  •     Dynamic buttons for each available language
  •     Current selection highlighted in green
  • Config persistence - Saved in skill-config.json as defaultLanguage field
  •     Config version bumped to 10
  •     Resets to English on reloaddefaults
  • Localized label - "Default Server Language" translated in all 9 language files

XP Reset Now Revokes Item Rewards

  • Setting XP lower now properly resets item rewards - When using /setmmoxp or /bulksetxp to reduce a player's XP, any claimed command rewards (items, boosts) for levels the player no longer qualifies for are now revoked
  •     Per-skill rewards, total level rewards, and global skill rewards are all checked
  •     The revoked count is included in the existing "rewards revoked" message
  •     Previously only skill tree rewards were revoked; item milestone rewards were kept even when undeserved

Admin Rewards Editor: Custom Level Input

  • Type any level number in the Command Rewards editor - A text input field with "Go" button now appears next to the level preset buttons
  •     Enter any level number to configure rewards at levels not in the defaults
  •     Selecting a preset button clears the text field for clean interaction

Bug Fixes

  • Fixed Master Cape icon not loading - The Master Cape inventory icon and texture were named incorrectly (Cape_Grandmaster) and didn't match the item definition (Cape_Master), causing missing icons in-game

Technical: Config Package Reorganization

  • Moved per-skill XP defaults to config/xpdefaults/ subpackage (29 files)
  • SkillDefaults.java updated with wildcard import for the new subpackage
  • Config managers, utilities, and top-level dispatchers remain in config/

Technical: Command Reward Validation

  • Added validateCommandRewards() and validateAllCommandRewards() to CommandRewardService - parses claimed reward IDs, checks player level against each, and removes invalid claims
  • Added removeCommandReward(String) to SkillComponent for individual reward ID removal
  • Both SetXpCommand and BulkSetXpCommand now call validation after XP changes

v0.6.4

Leaderboard UX

  • Hide XP column in category/total views - The "Total XP" column is now hidden when viewing Total Level, Gathering, Combat, or Production leaderboard views. XP is only shown when drilling down to a specific skill (e.g., Mining). Reduces visual clutter and keeps the focus on level rankings.
  • Level breakdown in category/total views - When viewing category or total leaderboard views, each row shows a skill-level breakdown in place of the XP column (e.g., "15 Mining + 6 Woodcutting + 3 Excavation + 1 more"). Shows the top 3 skills by level with a count of remaining active skills. Applies to both leaderboard rows and the "Your Rank" section.

v0.6.3

Bug Fixes

  • Fixed item reward commands not executing - Reward commands (like /give) could fail silently when triggered from the Rewards UI. Commands now execute reliably with proper timing between multiple items.
  • Fixed inventory space check - Corrected the inventory storage check when claiming rewards.

Technical Details

  • Reward commands dispatched via world.execute() instead of running synchronously inside Store/UI event handlers, avoiding reentrancy issues
  • Multiple commands within a level staggered with 25ms delays for inventory state updates
  • Fixed getCombinedArmorHotbarStorage()getStorage() for proper inventory space validation

v0.6.2

Inventory Space Check for Item Rewards

  • Inventory space validation - Item rewards now check that you have enough inventory space before claiming
  • Full inventory protection - If your inventory is full, rewards stay unclaimed so you can pick them up later
  • Red notification tells you when your inventory is too full to claim
  • Auto-claim respects space - Level-up auto-claim also checks inventory space, so no items are lost
  • Non-item rewards unaffected - Boosts and other non-item rewards are not blocked by inventory checks
  • Localized notification in all 8 languages (EN, ES, FR, PT, HU, TR, DE, IT)

Technical Details

  • UI claim gating returns "inventory full" notification preserving unclaimed state
  • Auto-claim paths (checkAndExecuteRewards, checkGlobalSkillRewards, checkTotalLevelRewards, checkAllRewards) skip rewards when inventory full
  • Only /give commands are gated; /mmoboost give and similar pass through

v0.6.1

Boost Stacking Limits

  • Hard cap on total XP multiplier - New maxBoostMultiplier setting (default: 5.0x) prevents runaway XP gains regardless of how many boosts are active
  • Active boost count limits - Configurable caps on simultaneous personal boosts (maxPersonalBoosts, default: 3) and global boosts (maxGlobalBoosts, default: 3)
  • Stack mode selection - Choose between "additive" (all boosts sum together) or "highest" (best global + best personal only) via boostStackMode setting
  • Token preservation on limit - When a player tries to activate a boost token at the limit, the token is NOT consumed and an error message is shown
  • Admin boost gating - /mmoboost global returns an error when the global limit is reached instead of silently creating a boost
  • Boost Page UI awareness:
  •     Limits info line shows current state: "Limits: 5.0x cap | additive | Personal 2/3 | Global 1/3"
  •     Token and permission activate buttons are disabled (red "LIMIT REACHED") when the relevant limit is hit
  • Admin UI section - New "Boost Stacking Limits" section in /mmoadmin with:
  •     Text fields for max multiplier, max personal, and max global boosts
  •     Toggle buttons for additive/highest stack mode
  •     Save button applies all changes at once
  • Localized in all 8 languages (EN, ES, FR, PT, HU, TR, DE, IT)

Admin Button on XP Page

  • Quick admin access - Admins now see an "Admin" tab in the /xp sidebar, just above Settings
  • Opens the /mmoadmin configuration page directly from the skill overview
  • Only visible to players with mmoskilltree.admin permission (or OP when permissions disabled)
  • Server-side permission check on click prevents unauthorized access

Bug Fixes

  • Fixed global boost permissions not working - Global boosts created through the Admin Award Boost UI were silently broken and never appeared. Global boost permissions now work correctly.
  •     Action required: Any previously awarded global boost permissions need to be re-issued. Re-award them through the Admin UI, or manually replace .global. with .all. in existing permission entries.

Technical Details

  • AwardBoostPage generated "global" as the scope value in permission strings, but BoostScope only recognizes "all" for global boosts
  • Permissions like mmoskilltree.xpboosts.all.global.2.30.60 silently failed to parse
  • Now correctly generates "all" scope (e.g., mmoskilltree.xpboosts.all.all.2.30.60)
  • skill-config.json version 8 → 9 (auto-regenerated on first load)

v0.6.0

Command Rewards v2.0 - Schema Redesign

Major overhaul of the command rewards system with a unified schema and override-based config.

New Schema

  • Unified rewards list - All rewards are now commands (items given via /give commands)
  • Override-based config - Config stores only customizations, defaults preserved across updates
  • Per-command customization:
  •     localizationKey - Preferred way to set display name (supports all languages)
  •     displayName - Fallback literal display name
  •     color - Hex color for UI display (e.g., #ffaa00)
  •     runAs - CONSOLE (default) or PLAYER
  • Display logic - Rewards without display info are executed but hidden from UI

New Config Format

{
  "schemaVersion": 1,
  "enabled": true,
  "overrides": {
    "MINING": {
      "10": [
        {
          "command": "/give {player} Ingredient_Bar_Iron --quantity=25",
          "localizationKey": "reward.item.iron_bars",
          "color": "#ffaa00"
        }
      ]
    }
  }
}

Override Semantics

  • Missing levels use defaults automatically
  • Set level to empty array [] to disable rewards at that level
  • New defaults propagate without losing your customizations
  • Schema version only changes for structural changes

Migration

  • Automatic V4 migration - Existing configs are converted and backed up
  • Claimed rewards preserved - Level-based tracking maintained across upgrade

Global Skill Rewards

  • New reward category: GLOBAL_SKILL - Bonus rewards given to ALL skills at milestone levels
  •     Tracked per-skill: Mining 50 and Swords 50 each independently trigger level 50 global rewards
  •     Configurable via GLOBAL_SKILL key in command-rewards.json overrides
  •     Visible in the player Rewards UI as "Lv X Bonus" rows alongside per-skill rewards
  •     Default: 1.25x Global XP Boost (15 min) at Level 100 for every skill mastered

XP Boost Token Rewards

  • Boost tokens as milestone rewards - Level-up milestones now include XP boost tokens
  •     Personal skill boosts at Lv 50 (1.5x 15min) and Lv 100 (2x 30min) for each skill
  •     Personal all-skills boosts at Total Lv 100 (1.5x 30min) and Total Lv 350 (2x 45min)
  •     Global server boosts at Total Lv 500 (1.5x 30min), 750 (2x 30min), 1000 (2x 60min)
  •     Global boosts broadcast to all online players via /say when activated

Admin Command Rewards UI

  • New CommandRewardsPage - Visual editor for command rewards, accessible from /mmoadmin
  •     Category tabs: Gathering, Combat, Crafting, Misc, Total Level, All Skills (Global)
  •     Skill selector for per-skill categories, hidden for Total Level and All Skills
  •     Level selector showing all configured milestone levels
  •     Reward table with columns: Command, Display Name, Loc Key, Color, Status
  •     Add/Edit section with fields for Command, Display Name, Loc Key, Color
  •     Inline Disable button on each default reward to create override without that reward
  •     Remove button on overridden rewards to delete individual entries
  •     Disable Level to set empty override (no rewards at that level)
  •     Reset to Default to remove override and restore defaults

Auto-Derived Display Info

  • Item rewards auto-derive display names from item IDs (e.g., Ingredient_Bar_Iron → "Iron Bars")
  • Item rewards auto-derive tier colors from item ID patterns:
  •     Common materials (Iron, Copper): #c8c8c8
  •     Uncommon (Cobalt, Thorium): #4aff7f
  •     Rare (Adamantite, Mithril): #4a9eff
  •     Epic (Onyxium): #c84aff
  •     Legendary (Prisma, Voidstone): #ffaa4a
  •     Gems, essences, and tools each have appropriate colors
  • Rewards without explicit display info now visible in admin UI with auto-derived names

Bug Fixes

  • Fixed incorrect ore XP rates - Removed explicit low-XP entries for special terrain ores (Ore_Cobalt_Slate, Ore_Adamantite_Magma, Ore_Iron_Basalt) that overrode wildcard patterns with incorrect values (4-6 XP instead of proper ore rates)
  • Fixed tier label missing in player Rewards UI - localization fallback returned empty string for undefined keys, causing blank tier headers
  • Fixed Visible boolean type error - Hytale CustomUI requires boolean values for Visible property, not string representations
  • Fixed display name override in tier headers - Auto-derived item names no longer replace "Level X" tier labels

Technical Changes

  • CommandRewardEntry.java - New unified format with command, localizationKey, displayName, color, runAs
  • LevelRewards.java - New container for level -> List mapping
  • CommandRewardsConfig.java - Converted to override-based pattern (like XpMapsConfig)
  •     Stores userOverrides separately from effective config
  •     Generates _reference/defaults-command-rewards.json for server owner transparency
  •     Changed configVersion to schemaVersion
  •     Added globalSkillRewards / globalSkillOverrides with full getter/mutator/save/load support
  • CommandRewardsDefaults.java - All ItemReward definitions converted to /give commands
  •     Added formatItemName() and getItemColor() for auto-derivation
  •     Added getGlobalSkillDefaults() with milestone rewards for all skills
  •     Added boost token reward factories: boostSkill(), boostAll(), boostGlobal()
  • CommandRewardService.java - Simplified to only execute commands
  •     Added checkGlobalSkillRewards() for per-skill global reward tracking
  •     Updated checkAllRewards() to process global skill rewards
  •     Updated claimLevelRewards() to handle GLOBAL_SKILL_* keys
  • CommandRewardsPage.java (NEW) - Admin UI page for viewing/editing command rewards
  • CommandRewardsPage.ui (NEW) - Admin UI template with category tabs, skill/level selectors, reward table
  • CommandRewardRow.ui (NEW) - Row template with Disable/Remove inline buttons
  • ItemRewardsPage.java - Shows global skill bonus rewards alongside per-skill rewards
  •     Added claimGlobal action handler for claiming global skill rewards
  •     Fixed getDisplayName() to not use auto-derived displayName for tier headers
  • Messages.java - Updated getCommandRewardDisplayName() to take level parameter
  • AdminConfigPage.java - Added "Edit Rewards" button navigating to CommandRewardsPage

v0.5.11

Admin Item Rewards Toggle

  • New toggle in /mmoadmin - Enable/disable item rewards from command-rewards.json
  •     Toggle found in the "General Settings" section
  •     When disabled, the Rewards tab is hidden from ViewXpPage
  •     Config saved immediately on toggle

UI Improvements

  • Active Boosts Display - ViewXpPage now shows active XP boosts above the buffs bar
  •     Displays up to 3 active boosts with time remaining
  •     Shows scope indicator: [G] for global boosts, [P] for personal
  •     Global boosts shown in blue, personal in green
  •     Format: [G] 2.0x Mining - 14:32 (scope, multiplier, target, time)
  • Skill Tree Buff Indicator - Skill tree buffs now prefixed with [Tree]
  •     Example: [Tree] +15 HP +5% DMG
  •     Helps distinguish skill tree rewards from other sources
  • Improved Time Display - Boost durations and cooldowns now show human-readable formats
  •     Under 1 hour: mm:ss (e.g., "45:30")
  •     1-24 hours: Xh Ym (e.g., "2h 30m")
  •     Over 24 hours: Xd Yh (e.g., "3d 5h")
  •     Fully localized time units for all 8 languages

Leaderboard Fix

  • Total Level Calculation - Fixed leaderboard to use summed skill levels
  •     Previously used formula-based level from total XP (confusing)
  •     Now sums individual skill levels for intuitive display
  •     Example: Mining 50 + Woodcutting 50 = Total Level 100 (not formula result)

Technical

  • AdminConfigPage.java
  •     Added #ToggleItemRewards button with event binding
  •     Calls CommandRewardsConfig.getInstance().setEnabled() on toggle
  • ViewXpPage.java
  •     Added populateActiveBoosts() method
  •     Modified getSkillBuffsText() to add [Tree] prefix
  •     Imports: ActiveBoost, BoostScope
  • ViewXpPage.ui
  •     Added #ActiveBoostsBar section with 3 boost labels
  •     Added #ActiveBoostsSpacer for spacing control
  • LeaderboardDataStore.java
  •     Changed data.totalLevel = skills.getTotalLevel() to data.totalLevel = skills.getSummedTotalLevel()
  • LocalizationConfig.java
  •     Added ui.admin.item_rewards_label in all 8 languages
  •     Added time.days_short, time.hours_short, time.minutes_short for localized time units
  •     Added notify.command_reward_received in all 8 languages
  • XpBoostService.java
  •     Updated formatTimeRemaining() to show Xh Ym for hours, Xd Yh for days
  •     Added localized overload formatTimeRemaining(ms, skills) using language-specific time units
  • CommandExecutor.java
  •     Added overload executeAsConsole(command, username) for better logging context
  • CommandRewardService.java
  •     Now passes username to console command execution for logging

v0.5.10

New Command: `/bulksetxp`

  • Bulk XP command - Set XP for one, many, or all players at once
  •     Usage: /bulksetxp [targets]
  •     Targets: Single player name, comma-separated names, or * for all online players
  •     Examples:
  •         /bulksetxp mining 1000 - Set all players to 1000 mining XP
  •         /bulksetxp all 0 Player1 - Reset all skills to 0 for Player1
  •         /bulksetxp swords 5000 A,B,C - Set swords to 5000 for specific players
  •     Aliases: setxpall, masssetxp
  •     Admin permission required

Command Architecture Update

  • Async command pattern - /mmoboost and /bulksetxp now use AbstractAsyncWorldCommand
  •     Better console support - commands work reliably from console
  •     Uses withDefaultArg for optional parameters (cleaner syntax)
  •     Store operations properly dispatched to world thread via world.execute()
  • Simplified /mmoboost syntax - Removed --args= prefix
  •     Old: /mmoboost give --args=Steve|mining|2|30
  •     New: /mmoboost give Steve|mining|2|30
  •     Still uses | (pipe) to separate values

Technical

  • BulkSetXpCommand.java - New command extending AbstractAsyncWorldCommand
  •     Uses withDefaultArg("targets", ...) with default * for all players
  •     Store access via entityRef.getStore() inside world.execute() callback
  •     Validates rewards after XP change (revokes if level dropped)
  •     Reports success count and any failures
  • BoostCommand.java - Refactored to AbstractAsyncWorldCommand
  •     Changed from AbstractPlayerCommand to AbstractAsyncWorldCommand
  •     Uses withDefaultArg instead of withOptionalArg
  •     Subcommands needing Store access (give, clear, status) wrapped in world.execute()
  •     Subcommands without Store access (global, list) run directly
  •     Simplified message handling (direct strings instead of localization for admin commands)
  • CLAUDE.md - Documented async command pattern
  •     Added "Command Patterns" section explaining AbstractAsyncWorldCommand vs AbstractPlayerCommand
  •     Critical pattern: world.execute() required for Store operations in async context
  •     Store thread affinity causes IllegalStateException if accessed from wrong thread
  • hytale-api.md - Added "Async Commands & World Thread" section
  •     Documents the thread safety requirement for Store access
  •     Code examples showing wrong vs right approach
  •     world.execute(Runnable) dispatches work to world thread

v0.5.9

Skill Permission Filtering

  • Permission-based skill visibility - When skill permissions are enabled, skills are now filtered throughout the UI
  •     Skills without permission are hidden from the XP overview page
  •     Skills without permission are excluded from total/category level calculations
  •     Navigation in Skill Tree and Item Rewards pages skips unpermitted skills
  •     Leaderboard skill filters only show skills the viewer has access to
  •     Category tabs hidden - Category tabs (Gathering, Combat, etc.) are hidden if player has no permission for any skill in that category
  •     Leaderboard category filters also hidden when no skills available in that category
  • Admin permission help text - /mmoadmin now shows permission nodes when permissions are enabled
  •     Explains mmoskilltree.skill.* for all skills
  •     Explains mmoskilltree.skill.mining, .swords, etc. for individual skills
  •     Explains mmoskilltree.command.xp for the /mmoxp command

Award Boost Admin Page

  • Award Boost Page - New admin UI for granting boost permissions and tokens to players
  •     Access via "Award Boost" button in /mmoadmin
  •     Dual input fields:
  •         UUID field for recurring permissions (uses /perm user add)
  •         Username field for one-time tokens (uses XpBoostService directly)
  •     Dynamic skill list - Shows all skills with XP values configured
  •         Skill buttons wrap after 8 per row using SkillButtonRow.ui template
  •     Target categories: All, Gathering, Combat, Production
  •     Target individual skills: Mining, Woodcutting, Swords, etc. (all configured skills)
  •     Preset + Custom inputs - Use quick buttons or type custom values
  •         Multiplier: 1.5x, 2x, 3x, 5x, 10x presets + custom field
  •         Duration: 15m, 30m, 1h, 2h, 24h presets + custom field (minutes)
  •         Cooldown: None, 1h, 24h, 7d presets + custom field (minutes)
  •     Choose scope (Personal or Global)
  •     Live permission preview updates as you configure
  •     Rate limiting - 500ms cooldown prevents button spam
  •     Improved readability with lighter text colors
  •     Full localization in all 8 languages

Technical

  • ViewXpPage.java - getFilteredSkills() now filters by PermissionUtil.canGainSkillXp()
  •     Total/category calculations use filtered skill list
  •     hasUnclaimedItemRewards() filters by permission
  •     Added hasPermissionForCategory() to check if player has any permitted skills in a category
  •     Category tabs hidden when hasPermissionForCategory() returns false
  • SkillTreePage.java - Permission check before displaying skill tree
  •     Shows "No permission" message if player lacks skill permission
  •     Navigation methods filter by permission
  • LeaderboardPage.java - getSkillsForCategoryFiltered() filters skill buttons by permission
  •     Category filter buttons hidden when no skills available in that category
  • ItemRewardsPage.java - Permission check for skill rewards (total rewards always available)
  •     Navigation methods filter by permission
  • AdminConfigPage.ui - Added #PermissionsHelp label and #AwardBoostBtn button
  • AwardBoostPage.java - New admin page for awarding boost permissions and tokens
  •     Dual input: UUID for recurring permissions, Username for one-time tokens
  •     Dynamic skill buttons from getConfiguredSkills() using SkillButton.ui + SkillButtonRow.ui templates
  •     Skill buttons wrap after 8 per row (SKILLS_PER_ROW constant)
  •     Custom text fields for multiplier, duration, cooldown (override presets)
  •     Recurring permissions use CommandExecutor.executeAsConsole() with /perm user add UUID permission
  •     Token awards call XpBoostService.awardToken() directly (finds online player for immediate delivery)
  •     Rate limiting with ACTION_COOLDOWN_MS (500ms) prevents spam
  •     Permission string format: mmoskilltree.xpboosts.....
  • AwardBoostPage.ui - Improved layout and readability
  •     Scrollable content area (TopScrolling)
  •     Dynamic skill buttons container with SkillButtonRow.ui template
  •     Dual text fields for UUID and Username inputs
  •     Custom text fields for multiplier, duration, cooldown
  •     Lighter text colors for better readability (#b0c4d8, #8090a0)
  • SkillButtonRow.ui - New template for skill button rows (LayoutMode: Left, Height: 28)
  • BoostCommand.java - Added console execution support
  •     Checks for ConsoleSender instance to allow /mmoboost from console
  •     Console commands bypass admin permission check
  •     All subcommands (give, global, list, clear, status) support console execution
  • LocalizationConfig.java - Added keys for all 8 languages:
  •     ui.skilltree.no_permission - "No permission"
  •     ui.admin.permissions_help - Permission explanation text
  •     ui.admin.award_boost - "Award Boost" button label

Bug Fixes

  • Luck loot empty values - Empty string values in luck loot tables now correctly give no bonus item
  •     Allows disabling luck drops for specific blocks by setting empty value
  •     Disabled cracked ore variants in special terrain to prevent exploits:
  •         Ore_Thorium_Mud_Cracked, Ore_Cobalt_Slate_Cracked, Ore_Adamantite_Magma_Cracked, Ore_Iron_Basalt_Cracked
  •     XP maps also updated to disable XP for these cracked ore variants
  • Disabled XP patterns - Patterns with 0 or negative XP (-1) are now properly skipped for combat skill detection
  •     Disabled weapons don't receive skill bonuses (damage, crit, lifesteal)
  •     Consistent behavior: disabled pattern = no XP and no combat bonuses
  •     Added comment clarifying bestXp > 0 check semantics

Config Compatibility

  • No config version bump required - uses existing skillPermissionsEnabled setting
  • Existing permission nodes unchanged (mmoskilltree.skill.*, mmoskilltree.command.*)

v0.5.8

Skill Overview UX Redesign

  • Compact buff bar - All active buffs shown in a single horizontal bar at the top
  •     Shows: XP Boost, Damage, Block, Crit, Lifesteal
  •     Bright color-coded values for easy reading
  •     Displays best values across all combat skills
  • Inline skill buffs - Each skill row now shows claimed rewards inline
  •     Buffs appear below the progress bar: +15 HP +5% DMG +3% LUCK
  •     Values of same type are combined (e.g., +5 HP + +10 HP = +15 HP)
  •     See exactly what bonuses each skill provides at a glance
  • Improved readability - Better contrast and sizing
  •     Lighter text colors for better visibility on dark background
  •     Larger font sizes for buff displays
  •     XP values show whole numbers for millions (1M instead of 1.2M)
  •     Wider skill name and XP columns
  • Streamlined header - Stats moved to right side of title
  •     Total Level, Total XP, Skills count in compact layout
  •     More space for skill progress list

Permissions System

  • Reload permissions command - /mmoconfig reloadperms
  •     Reloads boost permissions from permissions.json without restart
  •     Shows count of loaded boost permission templates
  • Dynamic boost permissions - XpBoostService reads from permissions.json
  •     Boost permissions extracted from users and groups sections
  •     Refresh button in Boost page reloads permissions in real-time
  •     Supports wildcard permissions (mmoskilltree.xpboosts.*)

Bug Fixes

  • Total level calculation - Now excludes skills without XP map data
  •     Skills not configured in xp-maps.json don't count toward total
  •     More accurate representation of actual progress
  • Block label - Defense renamed to "Block" in all 8 languages
  •     More consistent with in-game terminology

Combat Reward Targeting

  • Targeted combat rewards - Combat bonuses can now apply to specific weapons/skills
  •     New CombatTarget enum: ALL, MELEE, RANGED, MAGIC, and individual skills
  •     MELEE includes: Swords, Daggers, Polearms, Staves, Axes, Blunt, Unarmed
  •     RANGED includes: Archery
  •     Rewards default to ALL (backward compatible with existing configs)
  • "All Combat" rewards at high tiers - New reward options for combat skills
  •     Tier 8: +3% Damage (All), +3% Critical (All)
  •     Tier 9: +4% Damage (All), +4% Critical (All), +2% Lifesteal (All)
  •     Global bonuses as alternative to skill-specific bonuses

Technical

  • ViewXpPage.ui - Redesigned layout with horizontal buffs bar
  • SkillRow.ui - Added inline buffs display section
  • ViewXpPage.java - New populateBuffsBar() and getSkillBuffsText() methods
  • XpBoostService - File-based permissions loading with reload support
  • MMOConfigCommand - Added reloadperms subcommand
  • SkillComponent - getSummedTotalLevel() now filters by XP map data

Config Compatibility

  • No config version bump required - combatTarget is optional with ALL default
  • Existing skill-tree.json files work unchanged
  • Defense bonus remains global (applies when taking damage, not dealing)

v0.5.7

XP Boost System

  • Global and personal XP boosts - Players can activate boosts that multiply XP gains
  •     Global boosts affect all players on the server
  •     Personal boosts affect only the activator
  •     Boosts can target all skills, specific skills, or skill categories
  •     Multiple boosts stack additively (1.5x + 2x = 2.5x total)
  • Boost Tokens - One-time boost awards via commands
  •     Admins award tokens with /mmoboost give --args=||||[scope]
  •     Tokens can be personal (default) or global scope - global tokens activate server-wide when used!
  •     Tokens are stored until the player chooses to activate them
  •     Works for offline players - tokens delivered on next login
  •     No permission required to activate awarded tokens
  •     Players activate tokens from the Boost UI when ready
  • Permission-based boost activation - Server owners control who can activate repeatable boosts
  •     Permission format: mmoskilltree.xpboosts.....
  •     Target: all, skill name, or category name
  •     Scope: self (personal) or all (global)
  •     Multiplier uses underscore for decimal: 1_5 = 1.5x
  •     Duration and cooldown in minutes
  •     Example: mmoskilltree.xpboosts.mining.self.2_0.30.60 - 2x Mining, self, 30min, 1hr cooldown
  • Boost UI Page - New "Boosts" tab in ViewXpPage with three sections:
  •     Active Boosts - Currently running boosts (global + personal) with time remaining
  •     Boost Tokens - One-time tokens awarded via commands, ready to activate
  •     Reusable Boosts - Repeatable boosts available from permissions (with cooldowns)
  •     Refresh button to update display
  • Admin /mmoboost command - OPs can manage boosts directly (uses --args= with | separator)
  •     /mmoboost give --args=||||[scope] - Award boost token
  •     /mmoboost global --args=|| - Activate global boost immediately
  •     /mmoboost list - Show active global boosts
  •     /mmoboost clear --args= - Clear boosts and tokens
  •     /mmoboost status --args= - Show boost status including tokens
  • Persistence - Boosts and tokens persist across server restarts
  •     Global boosts saved to mods/mmoskilltree/active-boosts.json
  •     Pending tokens for offline players saved in the same file
  •     Personal boosts and tokens stored in player's SkillComponent
  •     Cooldowns track last activation timestamp

Other Changes

  • Defense skill renamed to Block - Display name updated to "Block" across all code and UI
  •     Updated in all 8 languages (EN: Block, ES: Bloqueo, FR: Blocage, PT: Bloqueio, HU: Blokkolás, TR: Blok, DE: Blocken, IT: Blocco)
  • Command UX improvement - /mmoboost uses --args= with pipe-separated values
  •     Example: /mmoboost give --args=Steve|mining|2|30

Technical

  • New data classes: BoostScope, BoostTarget, ActiveBoost, BoostPermission, BoostToken
  • XpBoostService - Singleton service for boost management with token support
  • SkillComponent additions: personalBoosts, boostCooldowns, boostTokens maps with codec serialization
  • SkillService integration - Boost multiplier applied additively with reward bonuses, token delivery on first action
  • BoostPage.java - Interactive UI page for boost management (3 sections)
  • BoostCommand.java - Admin command with pipe-separated args and token scope support
  • Localization - Full boost UI translations for all 8 languages (EN, ES, FR, PT, HU, TR, DE, IT)

v0.5.6

Override-Based Config System (XpMapsConfig & LuckConfig)

  • Configs now store ONLY overrides - Customizations are preserved across mod updates
  •     File stores only values that differ from defaults
  •     Missing keys automatically use built-in defaults
  •     New defaults propagate without losing your changes
  •     XpMapsConfig: Use value -1 to explicitly disable a default XP pattern
  •     LuckConfig: Use value "__DISABLED__" to explicitly disable a default luck pattern
  • Reference file generation - mods/mmoskilltree/_reference/
  •     defaults-xp-maps.json - All default XP values per skill
  •     defaults-luck-loot.json - All default luck loot table entries
  •     Auto-generated read-only files for server owner reference
  •     Updated on every config load
  • New commands:
  •     /mmoconfig diff --args= - Show only overrides (XP and luck)
  •     /mmoconfig trim - Remove redundant overrides from both XP and luck configs
  •     /mmoconfig disable --args= - Explicitly disable a default pattern
  • Improved /mmoconfig list - Now shows override status for each pattern
  •     Cyan text indicates overridden values (shows default in brackets)
  •     Green text indicates default values
  •     Header shows override count per skill
  • Schema versioning - schemaVersion replaces configVersion for override-based configs
  •     Schema version only bumps for structural changes (new fields)
  •     Default value changes no longer require version bump
  •     Old configs with configVersion are read for backwards compatibility

Admin UI

  • New /mmoadmin command - Opens interactive admin configuration UI
  •     TextField inputs for direct value entry (base XP, scale multiplier)
  •     Toggle settings: creative mode XP, skill permissions, leveling formula
  •     Real-time milestone preview updates as you type
  •     Formula description shows actual calculation being used
  •     Config override statistics and trim functionality
  •     Quick actions: reload configs, reload language, trim overrides, reset to defaults
  •     Aliases: /mmoconfig-ui, /mmosettings
  • XP Overrides Page - Visual editor for skill XP values
  •     Access via "Edit XP Values" button in Admin Config page
  •     Category tabs: Gathering, Combat, Crafting, Misc
  •     Dynamic skill selector buttons per category
  •     Pattern list showing: name, default value, current value, status
  •     Status indicators: Default (green), Override (orange), Disabled (red)
  •     Add/Update patterns with text fields for pattern name and XP value
  •     Disable button to set patterns to -1 (disabled)
  •     Reset button per row to revert overrides to defaults
  •     Click pattern name to copy to text field for easy editing
  • Permissions toggle - Single toggle controls both skill and command permissions
  •     UI label simplified to "Enable Permissions"
  •     When enabled: skill XP gains and commands require permissions
  •     When disabled: all players can gain XP and use commands
  • Admin permission paradigm - Flexible OP/permission-based access
  •     OP players always have admin access regardless of permission settings
  •     When permissions enabled: non-OP players can get admin via mmoskilltree.admin
  •     Applied to both /mmoconfig command and /mmoadmin UI

Localization

  • Config command messages fully localized - All 8 supported languages updated
  •     Override system messages (diff, trim, disable, reset, reload)
  •     Admin page labels and buttons (20 keys per language)
  •     Support for: English, Spanish, French, Portuguese, Hungarian, Turkish, German, Italian
  •     Falls back to English for players without skill data

Technical

  • XpMapsConfig.java - Complete rewrite for override-based system
  •     userOverrides map tracks only customizations
  •     load() now merges file overrides on top of defaults
  •     save() writes only overrides (smaller config files)
  •     DISABLED_VALUE = -1 sentinel for explicit pattern removal
  •     New methods: getOverrides(), getAllOverrides(), isDefault(), getDefaultValue(), resetToDefault(), disablePattern(), trimRedundantOverrides()
  • LuckConfig.java - Complete rewrite for override-based system
  •     Same override-based architecture as XpMapsConfig
  •     DISABLED_VALUE = "__DISABLED__" sentinel for explicit luck pattern removal
  •     Reference file: _reference/defaults-luck-loot.json
  • ConfigVersionUtil.java - Added extractSchemaVersion() method
  •     Checks for schemaVersion first, falls back to configVersion
  • MMOConfigCommand.java - Added handlers for diff, trim, disable commands
  •     Updated list command to show override indicators
  •     Updated remove command to show revert-to-default behavior
  •     All config messages now use localization via Messages helper
  •     Added getPlayerSkills() and msg() helper methods
  • LocalizationConfig.java - Added 40+ new localization keys per language
  •     cmd.config.* keys for all override system messages
  •     ui.admin.* keys for admin config page (20 keys per language)
  • New files:
  •     CommandExecutor.java - Shared utility for executing commands as console or player
  •     AdminConfigPage.java - Interactive admin configuration UI page
  •     AdminConfigUICommand.java - Command to open the admin UI
  •     AdminConfigPage.ui - UI layout for admin configuration page
  •     XpOverridesPage.java - XP pattern editor page with category/skill navigation
  •     XpOverridesPage.ui - UI layout for XP overrides page
  •     XpPatternRow.ui - Template for pattern list rows
  •     SkillButton.ui - Template for dynamic skill selector buttons
  • PermissionUtil.java - Added admin permission support
  •     ADMIN_PERMISSION = "mmoskilltree.admin" constant
  •     hasAdminPermission(Player) - OP always has access; checks permission when enabled
  •     isOp(Player) - Helper for OP status checks
  • CommandRewardService.java - Refactored to use CommandExecutor utility
  •     Removed duplicate command execution methods (DRY)
  •     Uses shared CommandExecutor.executeAsConsole() and CommandExecutor.executeAsPlayer()
  • MMOConfigCommand.java - Updated permission paradigm
  •     Removed hardcoded setPermissionGroups("OP")
  •     Uses PermissionUtil.hasAdminPermission() for flexible access control

Bug Fixes

  • Fixed combat XP exploit - Players can no longer gain XP from hitting projectiles (arrows, etc.)
  •     CombatDamageEventSystem now detects Projectile component in entity archetype
  •     Projectiles are always blocked from giving XP (hardcoded, not config-dependent)

v0.5.5

UI Improvements

  • Item Rewards Fully Disabled When Config Disabled - Complete protection when item rewards are turned off
  •     Rewards tab hidden in ViewXpPage
  •     ItemRewardsPage shows "Item Rewards Disabled" if somehow accessed directly
  •     All navigation (tabs, arrows) hidden on disabled page
  •     Event handlers check isEnabled() before opening rewards page

Upgrade Safety

  • Stat Rewards Reapplied on Config Update - Existing players get correct stat values after mod updates
  •     When SkillTreeConfig version changes, all stat modifiers (Health, Stamina, Mana) are reapplied
  •     Online players: stats recalculated immediately on reload/reloaddefaults
  •     Offline players: stats recalculated on first XP action after login
  •     Prevents stale stat values from old config versions
  • Orphaned Reward Cleanup - Removes stat modifiers from rewards that no longer exist
  •     When skill tree config changes remove tiers or reward IDs, old stat modifiers are cleaned up
  •     cleanupOrphanedRewards() now calls removeOrphanedStatModifiers() for each removed reward
  •     Tries removing all stat types (Health, Stamina, Mana) since original reward type is unknown

Balance Changes

  • Block (STAT_DEFENSE) Nerfed by 50% - Block reduction values were too powerful
  •     All STAT_DEFENSE skill tree rewards reduced by half
  •     Before: 4% → 20% range across tiers
  •     After: 2% → 10% range across tiers
  •     Maximum stacked Block from all skills now ~45% (was ~90%)
  • Item Reward Tiers Expanded - Added intermediate tiers for smoother progression with hybrid OSRS formula
  •     New Level 65 tier for all 16 skills (Expert-level materials)
  •     New Level 80 tier for all 16 skills (Advanced materials)
  •     Rewards include mid-to-late game materials like Mithril, Adamantite, Onyxium
  •     Legendary materials (Prisma, Voidheart, Elemental Essences) at Level 80+
  •     Total: 6 milestone tiers per skill (10, 25, 50, 65, 80, 100)

Technical

  • SkillTreeService.java - Added reapplyStatRewards() and removeOrphanedStatModifiers() methods
  • SkillService.java - Added setSkillTreeConfigChanged() flag for tracking skill tree config updates
  • SkillTreeConfig.java - Triggers stat reapplication when config version changes on load
  • MMOConfigCommand.java - validateAllOnlinePlayers() now also reapplies stat rewards
  • ViewXpPage.java - Added CommandRewardsConfig.isEnabled() checks for tab visibility and event handling
  • ItemRewardsPage.java - Added disabled state check in build() method
  • CommandRewardsDefaults.java - Added Level 65 and 80 reward entries for all skills
  • SkillTreeDefaults.java - Reduced all STAT_DEFENSE values by 50%
  • SkillTreeConfig.java - CONFIG_VERSION bumped to 3
  • CommandRewardsConfig.java - CONFIG_VERSION bumped to 4

v0.5.4

Improvements

  • Milestone XP display - When changing formula, basexp, or scale, shows XP required for milestone levels
  •     Format: XP required: Lv10=14.3k | Lv25=95.6k | Lv50=446.2k | Lv80=2.3M | Lv100=10.0M
  •     Helps server owners understand impact of config changes instantly

Technical

  • MMOConfigCommand.java - Added showMilestoneXp() helper method with formatted output

v0.5.3

⚠️ BREAKING CHANGE: Leveling formula completely changed. XP requirements are higher at all levels, so existing players will have lower levels with the same XP. Use /mmoconfig formula --args=legacy to restore the old formula.

Hybrid OSRS Leveling Formula

  • Complete formula overhaul - Combines OSRS exponential curve with quadratic floor
  •     Early/mid levels ~10% harder than previous quadratic formula
  •     Endgame scales exponentially (OSRS-style)
  •     Level 100 = ~10M XP (true endgame grind)
  • New XP requirements (with default settings):
  •     Level 10: ~14,270 XP
  •     Level 25: ~95,573 XP
  •     Level 50: ~446,222 XP
  •     Level 80: ~2,274,045 XP
  •     Level 100: ~10,000,000 XP
  • Config parameters:
  •     baseXpPerLevel (default: 300) - Quadratic floor component
  •     levelScaleMultiplier (default: 200) - OSRS exponential factor
  •     OSRS component uses 2^(level/7) - each level requires ~10% more XP than the last
  •     Quadratic floor ensures early/mid levels aren't too easy

Improvements

  • /mmoconfig formula - Switch between hybrid (OSRS) and legacy (quadratic) leveling formulas
  •     hybrid: OSRS exponential + quadratic floor (default, ~10M XP at level 100)
  •     legacy: Simple quadratic (~1.5M XP at level 100 with base=150, scale=2)
  • /mmoconfig reload - Now reloads ALL config files (was missing XpMaps, Luck, CommandRewards)
  • /mmoconfig reloaddefaults - Now resets ALL configs to built-in defaults

Bug Fixes

  • Item Rewards UI - Fixed crash when viewing Total Level rewards with more than 15 items per tier
  •     Now supports up to 30 items per tier (6 rows)

Technical

  • SkillConfig.java - CONFIG_VERSION bumped to 8, defaults changed to hybrid values (base=300, scale=200)
  • SkillComponent.java - getXpForLevel() and calculateLevel() use hybrid OSRS formula
  • ItemRewardsPage.java - Supports up to 30 items per tier (was 15)
  • RewardTierRow.ui - Added ItemsRow4-6 for larger reward tiers
  • CommandRewardsConfig.java - Added reloadDefaults() method
  • MMOConfigCommand.java - Updated reload handlers to include all configs

v0.5.2

Bug Fixes

  • Harvest Exploit Prevention - Players can no longer gain XP by placing and harvesting items
  •     Previously, placing a fruit/crop and then harvesting it would award XP via PickupItemEvent
  •     Now tracks placed item IDs in addition to block positions
  •     When you pick up an item you recently placed, XP is skipped
  •     Separate expiration timer for items: placedItemExpireMinutes (default: 5 minutes)
  •     Other players CAN still get XP from items you placed (fair multiplayer)
  • Life Essence Crafting Exploit Prevention - Currency conversion recipes no longer give Crafting XP
  •     Ingredient_Life_Essence_100_Recipe_ → 0 XP
  •     Ingredient_Life_Essence_Concentrated_Recipe_ → 0 XP
  • No +0 XP Notifications - Actions that give 0 XP no longer show notifications

Configuration

  • placedItemExpireMinutes - New config option in skill-config.json
  •     Minutes until placed items expire for harvest tracking (default: 5)
  •     Shorter than block expiration since items are typically harvested quickly
  •     Separate from placedBlockExpireMinutes (which defaults to 0/never for blocks)

Technical

  • SkillConfig.java - CONFIG_VERSION bumped to 5
  •     Added placedItemExpireMinutes field (default: 5)
  •     Added getter/setter methods
  • PlacedBlockTracker.java - Extended to track placed item IDs
  •     New TrackedItem class with atomic count and timestamp
  •     trackPlacedItem(playerHash, itemId) - Track when player places an item
  •     consumePlacedItem(playerHash, itemId) - Check and decrement on pickup (returns true if should skip XP)
  •     Cleanup in cleanupExpired() now uses separate expiration for blocks and items
  •     getTotalTrackedItems() - Stats method for debugging
  • PlaceBlockEventSystem.java - Now calls trackPlacedItem() in addition to position tracking
  • PickupItemEventSystem.java - Checks consumePlacedItem() before awarding XP
  • CraftingDefaults.java - Added 0 XP entries for Life Essence conversion recipes
  • CraftRecipeEventSystem.java - Removed debug log statement
  • SkillService.java - Skip XP processing when bestXp <= 0 (no notifications for 0 XP)
  • MMOConfigCommand.java - /mmoconfig reload now reloads ALL configs (was missing XpMaps, Luck, CommandRewards)
  • MMOConfigCommand.java - /mmoconfig reloaddefaults now resets ALL configs to defaults
  • CommandRewardsConfig.java - Added reloadDefaults() method

v0.5.1

New Features

  • Target Player XP Command - OPs can now set other players' XP directly
  •     /setmmoxp --target= - Set XP for another player
  •     Requires mmoskilltree.command.setxp.others permission (default: OP)
  •     Target must be online and in the same world
  •     Works with all skills or "all" for bulk changes
  •     Example: /setmmoxp mining 1000 --target=PlayerName
  • Custom Reward Display Names - Configure display names for item rewards
  •     Add displayName field to reward entries in command-rewards.json
  •     Value must be a localization key (e.g., "reward.mining.starter_kit")
  •     Add the key to your localization/messages-*.json files
  •     Falls back to "Level X" if key not found or not configured
  • Localized SetXP Command Messages - All command messages now support localization
  •     Error messages (permission denied, player not found, invalid XP, unknown skill)
  •     Success messages (skill set, all skills set, with/without target)
  •     Localized in all 8 supported languages

Technical

  • SetXpCommand.java - Added --target optional argument for targeting other players
  •     Uses world.getPlayers() to iterate players in the same world (deprecated but functional)
  •     findPlayerRefByUsername() method finds target player's entity ref
  •     Uses PermissionUtil.hasCommandPermission(player, "setxp.others") for permission check
  •     Works with existing MMOSkillTreeAPI.setXp() for data modification
  •     All messages now localized via Messages.get() helper
  • manifest.json - Added new permission node
  •     mmoskilltree.command.setxp.others - Set other players' XP with --target (default: OP)
  • LocalizationConfig.java - Added setxp command message keys for all 8 languages
  •     cmd.setxp.no_permission_others, cmd.setxp.player_not_found, cmd.setxp.no_skill_data
  •     cmd.setxp.xp_negative, cmd.setxp.unknown_skill, cmd.setxp.available_skills
  •     cmd.setxp.success_all, cmd.setxp.success_all_target, cmd.setxp.success_skill, cmd.setxp.success_skill_target
  •     cmd.setxp.rewards_revoked - Notification when rewards are revoked
  • SkillTreeService.java - Added reward validation methods
  •     validateAndRevokeRewards() - Revokes rewards for a skill when level drops below tier requirements
  •     validateAllRewards() - Validates all skills (used when setting all XP)
  •     Uses RewardEffectRegistry to properly remove stat modifiers
  • SkillComponent.java - Added helper methods for reward management
  •     clearClaimedRewardsForTier() - Clears all rewards for a specific tier
  •     getAllClaimedRewards() - Returns map of tier -> list of reward IDs for a skill
  • SetXpCommand.java - Integrated reward validation after XP changes
  •     Calls SkillTreeService.validateAndRevokeRewards() for single skill changes
  •     Calls SkillTreeService.validateAllRewards() for "all" skill changes
  •     Shows revoked count to admin if any rewards were revoked
  • CommandRewardEntry.java - Added displayName field for custom reward names
  • CommandRewardsConfig.java - Parses and saves displayName from JSON config
  • Messages.java - Added getCommandRewardDisplayName() helper for resolving display names
  • ItemRewardsPage.java - Uses custom displayName if configured
  • CommandRewardService.java - Fixed ConsoleSender integration for console commands

Admin Commands

  • Reset Item Rewards Command - /mmoconfig resetrewards --args=
  •     Resets all claimed item rewards for an online player
  •     Player can re-claim rewards at their current level milestones
  •     Example: /mmoconfig resetrewards --args=PlayerName

Balance Changes

  • Leveling Curve Rebalanced - Adjusted default leveling parameters for longer progression
  •     baseXpPerLevel changed from 100 to 150
  •     levelScaleMultiplier changed from 1.1 to 1.35
  •     Creates a steeper curve where high levels require significantly more XP
  •     Level 50 now requires ~127k XP (was ~69k), Level 100 requires ~1M XP (was ~297k)
  •     Existing servers can adjust via /mmoconfig basexp and /mmoconfig scale
  • Item Rewards Scaling Improved - Default item rewards scaled up to match harder leveling curve
  •     All skill rewards increased 3-5x at higher tiers
  •     Life Essence added to all skills at every tier
  •     Level 100 rewards now include ~100 Life Essence, ~80 rare bars, ~10-15 Voidheart
  •     Total Level rewards scaled up significantly (e.g., Total 1000 now gives 1000 Life Essence)
  •     Elemental essences (Fire, Ice, Lightning) quantities increased proportionally

Reward Validation

  • Automatic Reward Revocation - Rewards are validated and revoked when player no longer qualifies
  •     Triggers when XP is reduced via /setmmoxp
  •     Triggers when leveling config changes: /mmoconfig basexp, /mmoconfig scale, /mmoconfig reload, /mmoconfig reloaddefaults
  •     Offline players are validated on their first skill action after logging in
  •     Revokes all rewards from tiers where player level is now below requirement
  •     Removes stat modifiers (health, stamina, mana) via RewardEffectRegistry
  •     Displays count of revoked rewards to admin

Config Refactoring

  • Config File Split - Separated large SkillConfig into focused configs for easier management
  •     skill-config.json - General settings only (leveling formula, permissions, placed blocks, entity blacklist)
  •     xp-maps.json (NEW) - XP values per skill (block patterns, weapon tiers, etc.)
  •     luck-loot.json (NEW) - Luck bonus loot tables (block pattern -> item ID)
  •     Each config has its own version number for independent updates
  • XpMapsConfig.java - New singleton config for XP values per skill
  • LuckConfig.java - New singleton config for luck loot tables
  • SkillConfig.java - Slimmed down, delegates XP/luck methods to new configs (marked deprecated)
  • MMOSkillTreePlugin.java - Initializes all new configs on startup

v0.5.0

New Features

  • Item Rewards at Skill Milestones - Receive item rewards when reaching specific skill levels
  •     New config file: mods/mmoskilltree/command-rewards.json
  •     Supports individual skill milestones (e.g., Mining level 10, 25, 50, 100)
  •     Supports total level milestones (50, 100, 200, 350, 500, 750, 1000)
  •     Comprehensive defaults included - All 16 skills have pre-configured item rewards
  •     Items scale with progression:
  •         Level 10 (Starter): 2-3 basic items (copper tools, common ingredients)
  •         Level 25 (Progressing): 3-4 items (iron/cobalt gear, uncommon materials)
  •         Level 50 (Skilled): 5-6 items (rare materials, gems, better gear)
  •         Level 100 (Master): 6-10 items including legendary weapons (Flame, Void, Spectral)
  •     Example: Mining 100 rewards include Flame Pickaxe, Mithril bars, Voidstones
  •     Manual claiming required - Rewards are NOT auto-claimed; use the Rewards UI
  •     Commands can also be executed (see Command Rewards below)
  • Item Rewards UI Page - New page to view and claim your item rewards
  •     Access via "Rewards" tab on the Skill Overview page
  •     Shows all tiers with status: LOCKED (gray), READY (orange), CLAIMED (green)
  •     View reward contents before unlocking
  •     CLAIM button to receive items into your inventory
  •     Items wrap to new lines after 5 per row (supports up to 15 items per tier)
  •     Toggle between Skill Rewards and Total Level Rewards tabs
  •     Navigate between skills with arrow buttons
  •     Rewards tab highlights with orange color and * when unclaimed rewards available
  • Item Reward Notifications - Get notified when new rewards become available
  •     On level-up: "Mining Lv.10 item rewards available! Open /xp > Rewards"
  •     On total level milestone: "Total Lv.100 item rewards available! Open /xp > Rewards"
  •     Localized in all 8 supported languages
  • Command Rewards - Server owners can also configure commands to run at milestones
  •     Placeholders: {player}, {level}, {skill}, {total_level}
  •     RunAs modes: CONSOLE (full permissions) or PLAYER (player's permissions)
  •     One-time rewards - commands only execute once per milestone
  •     Example: Broadcast message at total level 100

Configuration

  • command-rewards.json - New config file for item and command rewards

`json

{

"configVersion": 3,

"enabled": true,

"rewards": {

"MINING": [

{"level": 10, "displayName": "reward.mining.starter", "giveItems": ["Item_Pickaxe_Copper:1", "Ingredient_Bar_Copper:5"], "runAs": "CONSOLE"},

{"level": 100, "displayName": "reward.mining.master", "giveItems": ["Weapon_Pickaxe_Flame:1", "Rock_Gem_Voidstone:3"], "runAs": "CONSOLE"}

],

"TOTAL": [

{"level": 100, "giveItems": ["Ingredient_Bar_Mithril:10", "Rock_Gem_Diamond:5"], "commands": ["say {player} reached total level 100!"], "runAs": "CONSOLE"}

]

}

}

`

  • Per-Config Versioning - Each config file now has its own version number
  •     Previously: All configs shared a single CURRENT_CONFIG_VERSION = 10
  •     Now: SkillConfig.CONFIG_VERSION = 3, SkillTreeConfig.CONFIG_VERSION = 2, CommandRewardsConfig.CONFIG_VERSION = 3
  •     Only the changed config gets backed up when updating, not all configs

Technical

  • CommandRewardEntry.java - Data class for level/commands/giveItems/runAs
  •     ItemReward inner class for item ID and quantity
  •     ItemReward.parse() static method to parse "ItemId:Quantity" format
  • CommandRewardsConfig.java - Singleton config loader following SkillConfig pattern
  • CommandRewardsDefaults.java - Comprehensive item reward defaults for all 16 skills
  •     All item IDs verified against Hytale's item database
  •     Rewards use actual game items (weapons, tools, ingredients, gems)
  • CommandRewardService.java - Checks milestones, gives items, executes commands
  •     checkAndExecuteRewards(playerRef, skills, skillType, newLevel) - For skill level-ups
  •     checkTotalLevelRewards(playerRef, skills) - For total level milestones
  •     giveItemToPlayer() - Adds items directly to player inventory
  •     Placeholder processing: {player}, {level}, {skill}, {total_level}
  • ItemRewardsPage.java - New UI page for viewing and claiming item rewards
  •     populateSkillRewards() / populateTotalRewards() - Render reward tiers
  •     populateItems() - Distribute items across rows (5 per row, up to 3 rows)
  •     handleClaimSkillReward() / handleClaimTotalReward() - Manual claim handlers
  • ItemRewardsPage.ui - UI layout with skill/total tabs, tier list, navigation
  • RewardTierRow.ui - Template for tier rows with claim button and 3 item rows
  • RewardItem.ui - Template for individual item display (icon + quantity)
  • SkillComponent.java - Added claimedCommandRewards field for tracking claimed rewards
  •     New codec entry for persistence
  •     New methods: hasClaimedCommandReward(), claimCommandReward(), getClaimedCommandRewardIds(), resetCommandRewards()
  •     New method: getSummedTotalLevel() - Sum of individual skill levels (more intuitive than formula-based total)
  • ViewXpPage.java - Added "Rewards" tab navigation to ItemRewardsPage
  • ViewXpPage.ui - Added #TabRewards button
  • ConfigVersionUtil.java - Removed shared CURRENT_CONFIG_VERSION constant
  •     Updated checkAndBackupIfNeeded(configPath, storedVersion, expectedVersion) to take expected version parameter
  • SkillService.java - Integrated CommandRewardService after level-up detection
  • MMOSkillTreePlugin.java - Loads CommandRewardsConfig in setup()
  • LocalizationConfig.java - Added ItemRewardsPage localization keys in all 8 languages
  •     ui.rewards.title, ui.rewards.total_title, ui.rewards.level, ui.rewards.claimed, ui.rewards.available, ui.rewards.locked, ui.rewards.claim, etc.

v0.4.9

New Features

  • Deployable XP Exploit Fix - Totems and turrets no longer give combat XP
  •     Prevents XP farming by repeatedly hitting player-deployed entities
  •     Lays groundwork for future entity blacklist customization
  •     New command: /mmoconfig blacklist - view current blacklist (expansion planned)
  •     Config: combatXpEntityBlacklist array in skill-config.json

UX Improvements

  • Fractional XP Accumulator - Small XP bonuses (like +5%) now properly accumulate instead of being lost to rounding
  •     Previously: 10 XP with 5% bonus → 10 * 1.05 = 10.5 → truncated to 10 (bonus lost!)
  •     Now: Fractional remainder (0.5) is saved and added to next XP gain
  •     Example: Gain 1: 10.5 → award 10, store 0.5 | Gain 2: 10.5 + 0.5 = 11.0 → award 11
  •     Players see the bonus manifest every few actions (deterministic, no RNG)
  •     Mathematically accurate over time - no XP is ever lost

Technical

  • SkillComponent.java - Added fractionalXpMap (ConcurrentHashMap)
  •     New codec entry {SKILL}FracXp for persistence
  •     New methods: getFractionalXp(skill), setFractionalXp(skill, amount)
  • SkillService.java - Updated addXp() to use fractional accumulator pattern
  •     Calculates exact XP with bonus multiplier
  •     Adds accumulated fractional remainder from previous gains
  •     Awards integer portion, saves decimal remainder for next time
  • SkillConfig.java - Added combatXpEntityBlacklist field with pattern matching
  •     isEntityBlacklisted(entityType) - check if entity matches any blacklist pattern
  •     addEntityBlacklist(pattern) / removeEntityBlacklist(pattern) - manage entries
  • CombatDamageEventSystem.java - Added entity blacklist check before awarding combat XP
  •     getEntityType() - extracts entity archetype name from chunk
  •     Blacklisted entities still take damage/trigger effects, just no XP awarded
  • MMOConfigCommand.java - Added /mmoconfig blacklist subcommand
  • ConfigVersionUtil.java - Bumped to version 10

v0.4.8

New Features

  • Placed Block XP Exploit Prevention - Players can no longer gain XP by breaking blocks they placed
  •     Tracks block positions when players place blocks
  •     Breaking a self-placed block awards no XP and removes tracking
  •     Other players CAN still get XP from blocks you placed (fair multiplayer)
  •     Applies to Mining, Woodcutting, Excavation block breaks (not item pickups)
  •     Configurable via trackPlacedBlocks (default: true)
  •     Optional expiration via placedBlockExpireMinutes (0 = never expire)

Balance Changes

  • Building XP Simplified - Changed from dynamic 50% of gathering values to flat 4 XP per block
  •     All placed blocks now give 4 XP regardless of block type
  •     Previously calculated as 50% of the mining/woodcutting/excavation value
  •     Simpler and more predictable progression
  • Mushroom XP Reduced - Reduced mushroom harvesting XP from higher values to 1 XP
  •     Plant_Crop_Mushroom_ and Plant_Mushroom_ now give 1 XP
  •     Prevents easy XP farming from abundant mushrooms

Localization

  • Italian Language Support - Full translation of all UI, notifications, skills, and rewards
  •     New language file: messages-it.json
  •     8 languages now supported: English, Spanish, French, Portuguese, Hungarian, Turkish, German, Italian

Configuration

  • trackPlacedBlocks - Enable/disable placed block tracking (default: true)
  •     When enabled, players cannot gain XP from breaking blocks they placed
  •     Set to false to allow the old behavior
  • placedBlockExpireMinutes - Minutes until placed blocks "become natural" (default: 0)
  •     Set to 0 for blocks to never expire (only removed when broken)
  •     Set to a positive value (e.g., 30) to allow XP after that many minutes

Technical

  • PlacedBlockTracker.java - New singleton service for tracking player-placed blocks
  •     Uses ConcurrentHashMap with player hash -> Set of tracked blocks
  •     BlockPosition record stores x, y, z coordinates
  •     TrackedBlock record stores position and timestamp for expiration
  •     Methods: trackPlacement(), wasPlacedByPlayer(), removeTracking(), cleanupExpired()
  • PlaceBlockEventSystem.java - Now calls PlacedBlockTracker.trackPlacement() on block place
  • BreakBlockEventSystem.java - Checks PlacedBlockTracker.wasPlacedByPlayer() before awarding XP
  • PickupItemEventSystem.java - Same check for interactive pickups (harvesting)
  • SkillConfig.java - Added trackPlacedBlocks and placedBlockExpireMinutes fields

v0.4.7

Bug Fixes

  • Fixed leaderboard crash on multi-world servers - Leaderboard no longer crashes when players are in different world instances (e.g., dungeons, temples)
  •     Error was: IllegalStateException: Assert not in thread! when accessing player data across world threads
  •     Now uses cached leaderboard data instead of accessing live data from other world threads
  •     Online/offline indicator still works correctly (based on player connection status)
  •     Viewer's own data is still live (same thread)

Technical

  • LeaderboardPage.java - Refactored gatherPlayerData() to avoid cross-thread Store access
  •     Only calls playerRef.getUsername() on other players (thread-safe)
  •     Uses LeaderboardDataStore cache for all player XP/level data
  •     Gets live data only for the viewer (who is on the current thread)

v0.4.6

Bug Fixes

  • Fixed command permissions blocking all players - Commands like /mmoxp, /skilltree, and /xpdisplay were requiring permission nodes even when permission system was disabled
  •     Added enableCommandPermissions config option (default: false)
  •     When disabled, all players can use all commands without needing mmoskilltree.command.* permissions
  •     Enable via config file or future admin command to require permission nodes for commands
  •     Matches the existing behavior of skill permissions (enableSkillPermissions)

Technical

  • SkillConfig.java - Added enableCommandPermissions field with getter/setter and JSON serialization
  • PermissionUtil.java - Updated hasCommandPermission() to check isCommandPermissionsEnabled() before requiring permissions
  •     Added wildcard permission support (mmoskilltree.command.*) for command permissions

v0.4.5

New Features

  • Keb's Katanas Mod Support - Added default XP values for katanas from the Keb's Katanas mod
  •     Legendary: Keb_Katana_Prisma (55 XP)
  •     Epic: Keb_Katana_Mithril (48 XP), Keb_Katana_Onyxium (52 XP)
  •     Rare: Keb_Katana_Cobalt (23 XP), Keb_Katana_Adamantite (28 XP), Keb_Katana_Thorium (28 XP)
  •     Uncommon: Keb_Katana_Copper (8 XP), Keb_Katana_Steel (8 XP)
  • Configurable Permission System - Opt-in permission nodes for skill XP gain and commands
  •     Permission checking is disabled by default for backwards compatibility
  •     Enable via /mmoconfig permissions --args=true
  •     When enabled, players need mmoskilltree.skill. or mmoskilltree.skill.* to gain XP
  • Skill Permissions - Control which players can gain XP in specific skills
  •     mmoskilltree.skill.* - Wildcard for all skills (default: granted)
  •     mmoskilltree.skill.mining - Individual skill permissions
  •     All 16 skills have their own permission node
  •     Silent deny - players without permission simply don't gain XP (no error message)
  • Command Permissions - Control access to plugin commands
  •     mmoskilltree.command.xp - /mmoxp command (default: granted)
  •     mmoskilltree.command.skilltree - /skilltree command (default: granted)
  •     mmoskilltree.command.xpdisplay - /xpdisplay command (default: granted)
  •     mmoskilltree.command.setxp - /setmmoxp command (default: op)
  •     mmoskilltree.command.config - /mmoconfig command (default: op)
  • New Admin Command - /mmoconfig permissions --args=
  •     Toggle skill permission checking on/off
  •     Displays current state and usage help

Upgrade Notes

  • Config version bumped to 8 - Existing configs will be backed up automatically
  • Your old config is saved as skill-config_backup.json in mods/mmoskilltree/
  • Restore any custom XP values from the backup file after upgrading

Technical

  • ConfigVersionUtil.java - Bumped CURRENT_CONFIG_VERSION to 8
  • LongswordDefaults.java - Added 8 Keb's Katanas with tiered XP values
  • manifest.json - Added Permissions array with all skill and command permission nodes
  • SkillConfig.java - Added enableSkillPermissions field with getter/setter
  • PermissionUtil.java - New utility class for permission checking
  •     canGainSkillXp(player, skill) - Checks skill permission (respects config toggle)
  •     hasCommandPermission(player, command) - Checks command permission
  • SkillService.java - Added permission check in processTrigger() before awarding XP
  • Command classes - All 5 commands now check mmoskilltree.command. permission
  •     GetXpCommand, SkillTreeCommand, XpDisplayCommand, SetXpCommand, MMOConfigCommand
  • LocalizationConfig.java - Added cmd.no_permission key to all 7 languages

v0.4.4

New Features

  • Offline Player Leaderboard - Leaderboard now displays both online and offline players
  •     Player data is cached when XP is gained and on server shutdown
  •     Online players show live data; offline players show cached data
  •     Visual indicator distinguishes online (green dot) from offline (gray dot) players
  •     Player count now shows "X online / Y total" format

Technical

  • LeaderboardDataStore.java - New singleton service for persistent leaderboard caching
  •     Stores player skill data in mods/mmoskilltree/leaderboard-cache.json
  •     Thread-safe with ConcurrentHashMap
  •     Automatically saves on JVM shutdown via shutdown hook
  • SkillService.java - Added leaderboard cache update on XP gain
  • LeaderboardPage.java - Merged online player data with cached offline data
  •     Added isOnline field to PlayerLeaderboardEntry
  •     Added createEntryFromCache() method for offline player entries
  •     Online/offline visual styling in leaderboard rows
  • LeaderboardRow.ui - Added #OnlineStatus indicator element
  • MMOSkillTreePlugin.java - Added leaderboard cache initialization and shutdown hook
  • LocalizationConfig.java - Added ui.leaderboard.player_count_online key for all 7 languages

v0.4.3

New Features

  • Turkish Localization - Added Turkish (Türkçe) as a supported language
  •     Full translation of all UI text, notifications, skill names, and reward types
  • German Localization - Added German (Deutsch) as a supported language
  •     Full translation of all UI text, notifications, skill names, and reward types
  • Expanded Language Selector - Settings page now supports up to 15 languages
  •     Increased from 2 rows of 3 buttons to 3 rows of 5 buttons
  •     Ready for community-contributed translations

Bug Fixes

  • Fixed Wan's Wonder Weapons Battleaxe - Corrected typo in item ID
  •     WanMine_God_Slayer_BatlleaxeWanMine_God_Slayer_Battleaxe

Technical

  • LocalizationConfig.java - Added getTurkishDefaults() and getGermanDefaults() with full translations
  • LocalizationConfig.java - Added ensureTurkishDefaults() and ensureGermanDefaults() methods
  • SettingsPage.java - Increased maxLangButtons from 6 to 15
  • SettingsPage.ui - Added #LangBtn3 through #LangBtn14 buttons in 3 rows of 5
  • SettingsPage.ui - Increased #LanguageSetting height from 160 to 204
  • SettingsPage.ui - Increased #LanguageButtons height from 80 to 124

v0.4.2

Bug Fixes

  • Disabled Luck for Crafting - Removed luck bonus from crafting due to critical item duplication bug
  •     Luck bonuses were incorrectly duplicating crafted items
  •     Will be re-enabled in a future update once the underlying issue is resolved

Technical

  • CraftRecipeEventSystem.java - Removed LuckUtil.tryLuckBonus() call

v0.4.1

New Features

  • Mighty Staffs Mod Support - Added default XP values for staffs from the Mighty Staffs mod
  •     Weapon_Staff_Bo_Wood (6 XP)
  • Wan's Wonder Weapons Mod Support - Added default XP values for weapons from the Wan's Wonder Weapons mod
  •     Swords: WanMine_Lethal_Leftovers_Sword, WanMine_Gaia's_Wrath_Sword, WanMine_Quasar_Cosmic_Sword (65-70 XP)
  •     Longswords: WanMine_Soulblight_Longsword, WanMine_Soulblight_Longsword_v1 (65 XP)
  •     Daggers: WanMine_Chromatic_Cleaver_Dagger, WanMine_Daybreak_Dagger, WanMine_Heartroot_Dagger, WanMine_Ashthorn_Dagger, WanMine_Nightshade_Dagger, WanMine_Frostburn_Dagger, WanMine_Nightfall_Dagger (58-68 XP)
  •     Battleaxe: WanMine_God_Slayer_Battleaxe (70 XP)
  •     Maces: WanMine_Helioram_Mace, WanMine_Maelstrom_Mace, WanMine_Mjollnir_Mace (65-70 XP)

Technical

  • StaffDefaults.java - Added Weapon_Staff_Bo_Wood
  • SwordDefaults.java - Added Legendary tier with 3 WanMine swords
  • LongswordDefaults.java - Added 2 WanMine longswords to Legendary tier
  • DaggerDefaults.java - Added Legendary tier with 7 WanMine daggers
  • BattleaxeDefaults.java - Added Legendary tier with WanMine_God_Slayer_Battleaxe
  • MaceDefaults.java - Added Legendary tier with 3 WanMine maces

v0.4.0

New Features

  • Skill Tree Scrolling - Skill tree UI now scrolls when there are many tiers
  • XP Progress Bar - Skill tree header shows progress bar to next level with "current / needed" XP format
  • STAT_MANA Reward - New reward type for increasing maximum mana
  • Complete 10-Tier Reward Redesign - All 16 skills now have 10 tiers of rewards (Lv 5, 10, 15, 20, 30, 40, 50, 65, 75, 100)

Mana System Design

Players start with 0 mana - it must be earned through skill tree choices. Each skill class has a distinct mana identity:

  • Staves - THE mana class (360+ max mana potential). Bo staff masters channel mystical energy
  • Archery - High mana (290+) for wand and spellbook users
  • Crafting - Moderate mana (305) for enchanting and magical item creation
  • Block/Harvesting - Low mana (65-180) for survivability or nature attunement
  • Physical Combat (Swords, Daggers, Axes, Blunt, Polearms) - Minimal mana (15-38). Warriors rely on steel, not sorcery
  • Gathering (Mining, Woodcutting, Excavation) - Minimal mana (23-38). Laborers focus on physical prowess
  • Acrobatics/Building - Zero mana. Pure physical disciplines focused on stamina

Tier Structure

| Tier | Level | Choices | Required |

| ---- | ----- | ------- | -------- |

| 0-1 | 5-10 | 2 | 1 |

| 2-3 | 15-20 | 3 | 1 |

| 4-5 | 30-40 | 4 | 2 |

| 6-7 | 50-65 | 5 | 2 |

| 8 | 80 | 5 | 3 |

| 9 | 100 | 6 | 3 |

Technical

  • SkillTreePage.ui: #TiersList LayoutMode changed to TopScrolling
  • SkillTreePage.ui: Added #SkillProgress ProgressBar in header
  • SkillTreePage.java: Added progress calculation using getLevelProgress()
  • SkillTreePage.java: Fixed ProgressBar.Value to use double instead of String
  • SkillTreePage.java: MAX_CHOICES increased from 5 to 6 for tier 9
  • RewardType.java: Added STAT_MANA enum
  • StatModifierUtil.java: Added applyManaBonus/removeManaBonus methods
  • StatManaEffect.java: New effect handler
  • RewardEffectRegistry.java: Registered StatManaEffect
  • LocalizationConfig.java: Added reward.stat_mana to all 5 languages
  • SkillTreeDefaults.java: Complete rewrite with 10 tiers for all 16 skills

v0.3.5

Bug Fixes

  • Leaderboard Combat Skill Filters - Fixed combat category only showing 6 of 10 skills in the filter bar
  •     Added SkillFilter6-9 buttons to support all combat skills
  •     All 10 combat skills now visible: Swords, Daggers, Polearms, Staves, Axes, Blunt, Archery, Unarmed, Block, Acrobatics
  • Archery XP Defaults - Removed magic staves from Archery skill
  •     Staves (magic/ranged) were incorrectly included in Archery defaults
  •     Staves now correctly belong only to the STAVES skill (melee staves)

Technical

  • LeaderboardPage.ui - Added #SkillFilter6 through #SkillFilter9 buttons
  • LeaderboardPage.java:217 - Changed skill filter loop from i < 6 to i < 10
  • ArcheryDefaults.java - Removed StaffDefaults.getDefaults() call

v0.3.4

New Features

  • Leaderboard Page - View player rankings accessible from ViewXpPage
  •     Shows all players ranked by total level and XP
  •     Sort by category: Total Level, Gathering, Combat, Production
  •     Drill down by individual skill within each category
  •     Your rank displayed at the bottom with highlighting
  •     Scrollable player list for large servers

Balance Changes

  • Leveling Curve Rebalanced - Adjusted baseXpPerLevel and levelScaleMultiplier defaults
  •     Level 120 now requires approximately 1 million XP
  •     Default levelScaleMultiplier changed from 1.0 to 1.1
  •     Creates more meaningful late-game progression
  •     Existing servers can adjust via /mmoconfig basexp and /mmoconfig scale commands

Technical

  • Scrollable UI Pattern - LayoutMode: TopScrolling enables vertical scrolling in UI containers
  • New Files:
  •     LeaderboardPage.java - Leaderboard page logic with player data gathering and sorting
  •     LeaderboardPage.ui - Layout with scrolling player list and filter buttons
  •     LeaderboardRow.ui - Template for individual player rank rows
  • ViewXpPage.java - Added Leaderboard tab navigation
  • ViewXpPage.ui - Added Leaderboard tab button
  • LocalizationConfig.java - Added leaderboard localization keys for all 5 languages

v0.3.3

New Features

  • Hungarian Localization - Added Hungarian (Magyar) as a supported language
  •     Full translation of all UI text, notifications, skill names, and reward types
  • More Crossbow Tiers Mod Support - Added default XP values for crossbows from the More Crossbow Tiers mod

Bug Fixes

  • Config Reload Command - Fixed /mmoconfig reload overwriting user config edits
  •     Manual file edits are now preserved when using reload command
  •     Version check only runs on server startup, not manual reloads

Technical

  • Config Version System - Replaced hash-based version tracking with simple version numbers
  •     ConfigVersionUtil.CURRENT_CONFIG_VERSION - Bump this constant when defaults change
  •     Configs now store configVersion field instead of computed hash
  •     More reliable: user edits don't trigger false "defaults changed" detection
  • SkillConfig.java / SkillTreeConfig.java - load(false) skips version check for manual reloads

v0.3.2

New Features

  • Portuguese Localization - Added Portuguese (Português) as a supported language
  •     Full translation of all UI text, notifications, skill names, and reward types
  •     Select Portuguese in Settings page

Bug Fixes

  • Settings Language Selector - Fixed backup language files appearing in the language selector
  •     Files like messages-en_backup.json no longer show as selectable languages
  •     Only valid language files (messages-XX.json) are displayed

Technical

  • LocalizationConfig.java - Added filter to exclude _backup files from language scanning
  • LocalizationConfig.java - Added getPortugueseDefaults() with full Portuguese translations

v0.3.1

New Features

  • Tier 7 Rewards - Added Level 65 milestone rewards for all 16 skills
  •     Master-tier bonuses: higher luck, health, damage, defense values
  •     Each skill gets 5 new reward choices at this tier

Balance Changes

  • Stamina Rewards Rebalanced - Base stamina is 10, so rewards were scaled down
  •     Early tiers (5-15): +1 stamina
  •     Mid tiers (30-50): +2 stamina
  •     Master tier (65): +3 stamina
  •     Maximum stamina from all skill trees is now ~15-20 (was 100+)

Bug Fixes

  • Config Backup System - Fixed bug creating nested _backup_backup_backup... files
  •     Backup files are now skipped when creating backups
  •     Added automatic cleanup of malformed backup files on startup
  • Config Auto-Update - Configs now regenerate from new defaults when mod updates
  •     Old configs are backed up to *_backup.json before regeneration
  •     Ensures players get new tiers and balance changes automatically

Technical

  • ConfigVersionUtil.java - Added cleanupMalformedBackups() method
  • SkillConfig.java - Regenerates from defaults when hash changes instead of merging
  • SkillTreeConfig.java - Same auto-regeneration behavior
  • SkillTreeDefaults.java - Added tier 7 for all skills, fixed stamina values
  • MMOSkillTreePlugin.java - Calls cleanup on startup for both config dirs

v0.3.0

New Features

  • Luck System (renamed from Double Drop) - Complete overhaul of bonus drop mechanics
  •     Renamed RewardType.DOUBLE_DROPRewardType.LUCK throughout codebase
  •     New configurable luckLootTable - maps block patterns to specific item drops
  •     Block breaks (Mining, Excavation): Only gives luck bonus if loot table match exists
  •     Item pickups (Harvesting): Defaults to same item as bonus
  •     Default loot table includes all ore tiers (Copper → Onyxium) → raw ore items
  •     Default loot table includes all gems (Diamond, Emerald, Ruby, etc.) → gem items
  • Creative Mode XP Disable - XP gains are now disabled by default in creative mode
  •     New config option disableXpInCreative (default: true)
  •     Toggle via /mmoconfig creative --args=
  •     Players in creative mode will not gain XP unless this setting is changed

UI/Text Improvements

  • Luck Reward Text - Now specifies which skill the luck bonus applies to
  •     Before: "+5% Double Drop" (generic)
  •     After: "+5% Mining Luck" (skill-specific)
  •     Uses localization system with new reward.luck_suffix key
  • Defense renamed to Block - Clarifies that this stat is different from Hytale's built-in defense
  •     All reward text now shows "Block" instead of "Defense"
  •     Updated in all three languages (EN: "Block", ES: "Bloqueo", FR: "Blocage")

Breaking Changes

  • Config field doubleDropLootTable renamed to luckLootTable
  • Existing configs will need to rename this field manually

Technical

  • New Files:
  •     LuckUtil.java - Luck bonus logic (renamed from DoubleDropUtil)
  •     LuckDefaults.java - Default loot table mappings (renamed from DoubleDropDefaults)
  • Renamed Methods:
  •     SkillTreeService.getDoubleDropChance()getLuckChance()
  •     SkillConfig.getDoubleDropLootTable()getLuckLootTable()
  •     SkillConfig.getDoubleDropItem()getLuckItem()
  • SkillConfig.java - Added disableXpInCreative field, renamed loot table fields
  • SkillService.java - Added creative mode check in processTrigger()
  • MMOConfigCommand.java - Added creative subcommand for toggling the setting
  • Messages.java - Added LUCK handling in getRewardDisplayText() to include skill name
  • LocalizationConfig.java - Added reward.luck_suffix, renamed from reward.double_drop_suffix
  • SkillTreeDefaults.java - All DOUBLE_DROP rewards renamed to LUCK

v0.2.9

Bugfixes

  • Fixed critical hit notification - Now shows multiplier (x1.5) instead of total damage amount
  • Fixed lifesteal notification - Removed duplicate + sign that was appearing in heal messages

Technical

  • DoubleDropUtil.java - Extracted double drop logic to shared utility class for DRY
  • PickupItemEventSystem - Refactored to use DoubleDropUtil.tryDoubleDrop() instead of inline implementation

v0.2.8

New Features

  • 4 New Melee Combat Skills - Weapon types split from Swords/Axes for more focused progression:
  •     DAGGERS - Daggers, claws, kunai (high crit chance, fast attacks)
  •     POLEARMS - Spears, halberds (reach weapons, defensive bonuses)
  •     STAVES - Bo staffs, melee staves (disciplined, stamina-focused)
  •     BLUNT - Clubs, maces (heavy hitters, high damage and health)

Bugfixes

  • Combat Effect Notifications - Added missing notifications for critical hits, defense blocks, and fall damage reduction (previously only lifesteal had notifications)

Combat Skill Changes

  • SWORDS now covers only longswords and one-handed swords (bladed slashing weapons)
  • AXES now covers only axes and battleaxes (chopping weapons)
  • Daggers, spears, and melee staves moved to their own dedicated skills
  • Clubs and maces moved to new BLUNT skill

Skill Tree Additions

  • 4 New Skill Trees with thematic reward progressions:
  •     DAGGERS: High crit scaling (up to +15%), strong lifesteal
  •     POLEARMS: Balanced offense/defense, high defense bonuses
  •     STAVES: Stamina-focused, good defense and fall reduction
  •     BLUNT: Highest raw damage (+14%), highest health (+28%)

Technical

  • Config Version Hashing - Config files now track a defaultsHash to detect when mod defaults change
  •     Automatically creates backup (e.g., skill-config_backup.json) before loading if defaults changed
  •     Protects user customizations when updating the mod
  • New config files: DaggersDefaults.java, PolearmsDefaults.java, StavesDefaults.java, BluntDefaults.java
  • New utility: ConfigVersionUtil.java for hash generation and backup management
  • Updated SkillType.java with 4 new enum values
  • Updated SkillTreeDefaults.java with 4 new skill tree methods
  • Total implemented skills: 16 (was 12)

v0.2.7

Bugfixes

  • Fixed Archery skill not gaining XP - Archery now correctly awards XP when dealing damage with bows, crossbows, wands, and other ranged weapons
  •     Changed Archery trigger type from DEAL_DAMAGE_PROJECTILE to DEAL_DAMAGE_PHYSICAL
  •     Removed unused DEAL_DAMAGE_PROJECTILE trigger type

v0.2.6

New Features

  • /setmmoxp Command - Admin command to set player XP directly
  •     /setmmoxp - Set XP for a specific skill
  •     /setmmoxp all - Set XP for all skills at once
  •     Alias: /setxp
  •     Configurable permission via setXpRequiredGameMode in config

Bugfixes

  • Fixed torches giving Woodcutting XP - Torches no longer incorrectly award Woodcutting experience when broken

Configuration

  • setXpRequiredGameMode - New config option in config.json
  •     Controls who can use /setmmoxp command
  •     Values: "OP" (default), "Adventure", "Creative", etc.
  •     Example: Set to "Creative" to allow creative mode players to use the command

v0.2.5

Bugfixes

  • Fixed BONUS_XP reward display - Now correctly shows the target skill name
  •     Before: "+5% Bonus XP" (generic)
  •     After: "+5% Mining XP" or "+5% Excavation XP" (skill-specific)
  •     Extracts skill from reward ID (e.g., excavation_xp_1 → Excavation)
  •     Works correctly when skill trees offer XP bonuses for related skills

Technical

  • Messages.extractSkillFromRewardId() - Parses reward ID to determine target skill
  • BONUS_XP rewards now display localized skill name + "XP" suffix

v0.2.4

New Features

  • Localization System - Full i18n support for all UI text
  •     Players can select their preferred language in Settings
  •     Language preference saved per-player in SkillComponent
  •     Auto-generated English, Spanish, and French language files
  •     Users can add custom messages-{lang}.json files for additional languages
  • Language Selector - New section in Settings page
  •     6 language button slots in 2 rows of 3
  •     Currently selected language highlighted in green
  •     Disabled style for unavailable language slots
  •     Instant language switching without restart
  • Auto-Generated Language Files - Located in mods/mmoskilltree/localization/
  •     messages-en.json - English (auto-generated)
  •     messages-es.json - Spanish (auto-generated)
  •     messages-fr.json - French (auto-generated)
  •     Files auto-update with new keys on server start
  • Localized Reward Text - Skill tree rewards fully localized
  •     Reward type names (e.g., "Max Health" → "Santé Max" in French)
  •     Formatted values with localized patterns (+5%, +10, +3 blocks)
  •     Reward claimed notifications use localized text
  • Hot-Reload Support - /mmoconfig reloadlang command
  •     Reload language files without server restart
  •     New translations take effect immediately

Technical

  • New i18n/ package:
  •     LocalizationConfig.java - Singleton config manager for language files
  •         Loads/saves JSON message files using GSON
  •         Scans for available messages-*.json files
  •         Placeholder substitution with {0}, {1} format
  •         Merges file contents with defaults (preserves user customizations while adding new keys)
  •         Always falls back to English defaults (never shows raw keys)
  •     Messages.java - Static helper class for message access
  •         get(skills, key) / get(skills, key, args...) - Get translated message
  •         getSkillName(skills, skillType) - Get localized skill name
  •         getRewardTypeName(skills, rewardType) - Get localized reward type name
  •         getRewardFormattedValue(skills, reward) - Get localized formatted value
  •         getRewardDisplayText(skills, reward) - Get full localized reward text
  • SkillComponent.java - Added language field (default: "en")
  •     Codec entry for persistence
  •     getLanguage() / setLanguage() methods
  • UI Localization - All UI text now uses Messages helper
  •     ViewXpPage - Panel titles, tabs, labels, buttons
  •     SkillTreePage - Tier labels, status text, buttons
  •     SettingsPage - All setting labels and descriptions
  •     SkillRow/TierRow templates - Dynamic text via indexed selectors
  • SettingsPage.ui - Added element IDs for dynamic localization
  •     #SettingsTitle, #XpNotificationsLabel, #XpNotificationsDesc
  •     #CombatEffectsLabel, #CombatEffectsDesc
  •     #XpThresholdLabel, #XpThresholdDesc
  •     #LanguageLabel, #LanguageDesc
  •     #LangBtn0 through #LangBtn5 with @DisabledButtonStyle
  • ViewXpPage.ui - Added #SkillsHeader ID for localization
  • MMOConfigCommand.java - Added reloadlang subcommand

Message Key Structure

{
  "language.name": "English",
  "ui.button.close": "CLOSE",
  "ui.button.back": "< BACK",
  "ui.viewxp.title": "Skill Overview",
  "ui.viewxp.level_prefix": "Lv. {0}",
  "ui.settings.language": "Language",
  "notify.xp_gain": "+{0} {1} XP",
  "notify.level_up": "LEVEL UP! {0} is now Level {1}!",
  "skill.mining": "Mining",
  "reward.stat_health": "Max Health",
  "reward.bonus_xp": "Bonus XP",
  "reward.format.percent": "+{0}%",
  "reward.format.flat": "+{0}"
}

Adding Custom Languages

1. Create mods/mmoskilltree/localization/messages-{code}.json

2. Copy structure from messages-en.json

3. Translate values (keys must remain the same)

4. Run /mmoconfig reloadlang or restart server

5. Language appears in Settings selector

v0.2.3

New Features

  • Dynamic Skill Tree Choices - Configurable number of choices per tier (up to 5)
  •     New choicesRequired field on SkillTreeNode - how many rewards to pick per tier
  •     Global defaultChoicesRequired in config (default: 1)
  •     UI shows "Pick X of Y" when tier has multiple selections
  •     Tier status shows completion progress (e.g., "1/2" when partially complete)
  • Multi-Select Rewards - Players can now pick multiple rewards per tier
  •     Higher tiers offer more choices with multi-select (Pick 2 of 4)
  •     Prevents claiming same reward twice
  •     Tier only marked complete when all required choices made
  • Dynamic UI Rendering - Choice buttons rendered dynamically
  •     New ChoiceButton.ui template appended per choice
  •     Buttons use FlexWeight for automatic sizing
  •     Supports 2-5 choices per tier
  •     Removed hardcoded button limits
  • Expanded Reward Variety - Updated skill tree defaults
  •     Tiers 0-1: 2 choices, pick 1
  •     Tiers 2-3: 3 choices, pick 1
  •     Tiers 4-5: 4 choices, pick 2
  •     Tier 6 (Level 50): 5 choices, pick 2 - Elite rewards for all 12 skills
  •     More diverse reward options per tier
  • Tier 6 Elite Rewards (Level 50) - All 12 skills now have elite tier rewards
  •     Gathering: +20% XP, +15% Double Drops, +20 Health, +15 Stamina, synergy bonuses
  •     Combat: +10% Damage, +12-15% Crit, +8-10% Lifesteal, +20 Health
  •     Defense: +12% Defense, +30 Health, -40% Fall Damage, +5% Lifesteal
  •     Acrobatics: -50% Fall Damage, +20 Health, +8% Defense, +20 Stamina
  •     Crafting/Building: +20% XP, +20 Health/Stamina, synergy bonuses

Technical

  • SkillTreeNode.java - Added choicesRequired field with new constructor
  • SkillComponent.java - Multi-claim storage using comma-separated reward IDs per tier
  •     getClaimedRewardIds(skill, tier) returns list of claimed IDs
  •     getClaimedCount(skill, tier) returns count for tier
  •     hasClaimedSpecificReward(skill, tier, rewardId) checks specific claim
  • SkillTreeConfig.java - Added defaultChoicesRequired global setting and JSON codec
  • SkillTreePage.java - Dynamic button rendering with FlexWeight layout
  • SkillTreeService.java - Updated claiming logic for multi-select validation
  • TierRow.ui - Removed hardcoded buttons, added #SelectionInfo label
  • ChoiceButton.ui - New template for dynamic choice buttons

Config Schema Update

{
  "defaultChoicesRequired": 1,
  "skillTrees": {
    "MINING": [{
      "tier": 4,
      "levelRequired": 30,
      "choicesRequired": 2,
      "choices": [...]
    }]
  }
}

Upgrade Notes

Run /mmoconfig reloaddefaults to load the new multi-choice tier defaults.

v0.2.2

Bugfixes

  • Fixed double drops not working - Double drops now actually add bonus items to inventory
  •     Moved double drop logic from BreakBlockEventSystem to PickupItemEventSystem
  •     When items are picked up, checks DOUBLE_DROP reward chance for the relevant skill
  •     Bonus items are added directly to inventory (hotbar prioritized) via getCombinedHotbarFirst().addItemStack()
  •     Notification now shows item count: "Double Drop! (+3)"

v0.2.1

Bugfixes

  • Fixed duplicate component error - Resolved IllegalArgumentException: Entity contains component type crash when multiple XP events fired in the same tick (e.g., breaking multiple blocks rapidly or combat with multiple hits)
  •     Added pending component cache to prevent duplicate addComponent calls before CommandBuffer consumption

v0.2.0

New Features

  • Reward Effect Framework - Pluggable system for reward effects with handler registry
  •     RewardEffectHandler interface for all effect types
  •     RewardEffectRegistry singleton maps RewardType to handlers
  •     RewardEffectContext provides store, ref, playerRef, skills to handlers
  • 9 Implemented Reward Types - Beyond XP bonuses:
  •     STAT_HEALTH - Increases max health via Hytale's EntityStatMap
  •     STAT_STAMINA - Increases max stamina via EntityStatMap
  •     STAT_DAMAGE - Multiplies outgoing damage
  •     STAT_DEFENSE - Reduces incoming damage (capped at 90%)
  •     CRITICAL_CHANCE - Chance for 1.5x damage on attacks
  •     LIFESTEAL - Heals attacker for percentage of damage dealt
  •     DOUBLE_DROP - Chance for bonus resources when gathering
  •     FALL_DAMAGE_REDUCTION - Reduces fall damage (capped at 90%)
  •     BONUS_XP - Multiplies XP gain (existing)
  • Combat Effect Notifications - New settings toggle for lifesteal/crit feedback
  •     Toggle via Settings > Combat Effect Notifications
  •     Lifesteal shows green "+X.X" heal notification
  •     Saved per-player in SkillComponent
  • Hytale EntityStatMap Integration - Direct stat modification
  •     StatModifierUtil helper class for health/stamina modifiers
  •     Uses DefaultEntityStatTypes.getHealth() and getStamina()
  •     StaticModifier with Modifier.ModifierTarget.MAX for permanent bonuses
  •     healEntity() for lifesteal healing
  • Expanded Skill Tree Defaults - 10 tiers with varied reward types
  •     Gathering skills: BONUS_XP, DOUBLE_DROP, STAT_STAMINA, STAT_DEFENSE, STAT_HEALTH
  •     Combat skills: BONUS_XP, STAT_DAMAGE, CRITICAL_CHANCE, LIFESTEAL, STAT_HEALTH
  •     Defense: STAT_DEFENSE, STAT_HEALTH, FALL_DAMAGE_REDUCTION
  •     Acrobatics: FALL_DAMAGE_REDUCTION (10%→40%), STAT_STAMINA, STAT_DEFENSE
  • Debug Starting Level - Config option for testing high-level gameplay
  •     Set debugStartingLevel in config.json to initialize new players at that level
  •     Set to 0 (default) for production

Technical

  • New reward/ package:
  •     RewardEffectHandler.java - Interface for effect handlers
  •     RewardEffectRegistry.java - Singleton registry
  •     RewardEffectContext.java - Context holder
  •     StatModifierUtil.java - Hytale stat integration
  •     effects/PassiveEffect.java - Base for passive effects
  •     effects/StatHealthEffect.java - STAT_HEALTH handler
  •     effects/StatStaminaEffect.java - STAT_STAMINA handler
  •     effects/NoOpEffect.java - Placeholder for deferred types
  • CombatDamageEventSystem enhancements:
  •     Uses DamageModule.get().getFilterDamageGroup() to modify damage before health loss
  •     applyAttackerEffects() - Damage bonus + critical chance
  •     applyDefenseReduction() - Defense damage reduction
  •     applyFallDamageReduction() - Fall damage reduction
  •     applyLifesteal() - Heal on damage with optional notification
  • BreakBlockEventSystem enhancements:
  •     checkDoubleDrops() - Random check for double drops
  •     Notification when double drop triggers
  • SkillComponent additions:
  •     showCombatEffects boolean for combat notification preference
  •     Codec entry for persistence
  • SkillTreeService additions:
  •     getTotalLifesteal(), getTotalHealthBonus(), getTotalStaminaBonus()
  •     Effect application on claim/reset

Upgrade Notes

Run /mmoconfig reloaddefaults after upgrading to load the new 10-tier skill trees with varied reward types.

v0.1.1

New Features

  • Settings Page - New in-game settings UI accessible via the Settings tab on ViewXpPage
  •     Toggle XP notifications on/off with a single click
  •     Choose XP gain threshold from preset values (1, 5, 10, 25, 50, 100)
  •     Visual feedback for current settings state
  •     Back button to return to skill overview
  • Expanded Skill Tree Defaults - All 12 implemented skills now have 6 tiers of rewards
  •     Previously some skills had only 2-4 tiers
  •     Consistent progression across Mining, Woodcutting, Excavation, Harvesting, Swords, Axes, Archery, Unarmed, Defense, Acrobatics, Crafting, and Building
  •     Updated skill pairings for meaningful choices (Harvesting now pairs with Excavation/Crafting instead of unimplemented Cooking/Alchemy)
  • Dynamic Skill Tree UI - Skill tree page now uses template-based dynamic rendering
  •     Automatically scales to display any number of tiers
  •     Improved visual styling with larger fonts and clearer status indicators

Upgrade Notes

Important: After upgrading to v0.1.1, run /mmoconfig reloaddefaults to load the new 6-tier defaults for all skills. This will reset your skill tree configuration to the new defaults.

v0.1.0

New Features

  • Skill Tree System - Unlock XP bonus rewards at level milestones
  •     6 tiers per skill, unlocked at levels 5, 10, 15, 20, 30, 40
  •     Each tier offers a choice between XP bonus for the main skill OR a related skill
  •     Example: Mining tier lets you choose +5% Mining XP OR +5% Excavation XP
  •     Rewards persist across sessions via ECS component
  • Meaningful Reward Choices - Each skill pairs with thematically related skills:

_(TEMPORARY UNTIL MORE COMPLEX IMPLEMENTATION)_

  •     Mining ↔ Excavation, Smithing
  •     Woodcutting ↔ Building, Crafting
  •     Excavation ↔ Mining, Harvesting
  •     Harvesting ↔ Cooking, Alchemy
  •     Swords ↔ Defense, Axes
  •     Axes ↔ Swords, Woodcutting
  •     Archery ↔ Crafting, Acrobatics
  •     Unarmed ↔ Acrobatics, Defense
  •     Defense ↔ Acrobatics, Swords
  •     Acrobatics ↔ Defense, Unarmed
  •     Crafting ↔ Mining, Smithing
  •     Building ↔ Woodcutting, Crafting
  • Skill Tree UI (SkillTreePage)
  •     Navigate between skills with arrow buttons
  •     Shows tier status: LOCKED (gray), AVAILABLE (white), or CLAIMED (green)
  •     Color-coded buttons show selection state
  •     Reset button to clear all selections for current skill
  •     Opens via /skilltree command or Tree button on ViewXpPage
  • ViewXpPage Enhancement - Added "Tree" button next to each skill to jump directly to its skill tree
  • Bonus XP System - Claimed BONUS_XP rewards apply multiplicatively to XP gains
  •     Stack bonuses from multiple tiers for compounding effect
  • Milestone Notifications - Players notified when new tiers unlock
  •     Automatic detection when leveling up
  •     Prompts players to use /skilltree to claim

New Commands

  • /skilltree [skill] - Open the skill tree UI
  •     Aliases: /st, /rewards

Configuration

  • New config file: config/mmoskilltree/skilltree.json
  •     Configurable level thresholds per tier
  •     Per-skill reward choices with custom values
  •     Currently supports BONUS_XP reward type (other types defined but not yet implemented)

Technical

  • New data classes:
  •     RewardType.java - Enum of reward types (BONUS_XP implemented, others placeholder)
  •     SkillReward.java - Configurable reward with id, type, value, display name
  •     SkillTreeNode.java - Milestone with level threshold and reward choices
  • New config classes:
  •     SkillTreeConfig.java - Singleton config for skill trees with JSON persistence
  •     SkillTreeDefaults.java - Default rewards with meaningful skill pairings
  • New service:
  •     SkillTreeService.java - Milestone checking, reward claiming, XP multiplier calculations
  • New UI:
  •     SkillTreePage.java - Interactive page with claim/reset/navigate actions
  •     SkillTreePage.ui - Layout with 6 tiers, 2 choices each, navigation and reset buttons
  • Extended SkillComponent:
  •     claimedRewards - Map of skill → claimed reward IDs per tier
  •     pendingRewards - Map of skill → pending milestone notifications
  •     resetRewards(skill) / resetAllRewards() - Clear claimed rewards
  •     String serialization for codec compatibility
  • Extended MMOSkillTreeAPI:
  •     updateSkillComponent(PlayerRef, SkillComponent) - Explicit update hook
  • Modified SkillService:
  •     Integrated milestone checks on XP gain
  •     Applies bonus XP multiplier from claimed rewards

v0.0.11

New Features

  • Combat XP System - Full implementation of combat skills via CombatDamageEventSystem
  •     SWORDS - XP for melee damage with longswords, daggers, spears, claws, staves
  •     AXES - XP for melee damage with axes, clubs, maces, battleaxes
  •     ARCHERY - XP for ranged damage with bows, crossbows, wands, spellbooks, guns, bombs
  •     UNARMED - XP for dealing damage without weapons
  •     DEFENSE - XP for taking damage from any source
  •     ACROBATICS - XP for surviving fall damage
  • Material-Tiered XP Values - Rare weapons give more XP than common ones
  •     Common (Crude, Copper, Tribal): 5-6 XP
  •     Uncommon (Iron, Bronze, Scrap): 7-9 XP
  •     Rare (Cobalt, Adamantite, Thorium): 20-32 XP
  •     Epic (Mithril, Onyxium): 45-55 XP
  •     Legendary (Flame, Void, Spectral): 55-70 XP
  • New Skills (in SkillType enum, not yet implemented)
  •     FISHING, TAMING, REPAIR, ALCHEMY, ENCHANTING, COOKING, SMITHING

Technical

  • Separate defaults files for maintainability:
  •     SwordsDefaults.java - Longswords, daggers, spears, staves (melee)
  •     AxesDefaults.java - Axes, clubs, maces (blunt weapons)
  •     ArcheryDefaults.java - Bows, crossbows, wands, spellbooks, guns, bombs
  • SkillDefaults.java now delegates to per-skill defaults files
  • New TriggerTypes: DEAL_DAMAGE_PHYSICAL, DEAL_DAMAGE_PROJECTILE (replaced DEAL_DAMAGE)
  • Weapon detection uses inventory.getActiveHotbarItem().getItemId() for pattern matching
  • Patterns match actual item IDs (e.g., Weapon_Longsword_Mithril, Weapon_Axe_Iron)

v0.0.10

New Features

  • Crafting XP - New CraftRecipeEventSystem awards XP when crafting items
  • Building XP - New PlaceBlockEventSystem awards XP when placing blocks
  • CRAFTING skill - New general crafting skill type (15 XP per craft by default)
  • BUILDING skill defaults - Placing blocks now awards XP at 50% of the breaking value (e.g., placing stone = 2 XP vs mining stone = 3 XP)

Technical

  • New event/CraftRecipeEventSystem.java - Handles CraftRecipeEvent for crafting XP
  • New event/PlaceBlockEventSystem.java - Handles PlaceBlockEvent for building XP
  • SkillDefaults.getBuildingDefaults() dynamically computes values from mining/woodcutting/excavation at 50%

v0.0.9

New Features

  • Quick threshold commands - Set XP display threshold with /xpd 10, /xpd 20, /xpd 0, etc.

Bugfixes

  • Progress bar display - Fixed progress bar rendering in skill UI

v0.0.8

Bugfixes

  • /mmoconfig reloaddefaults - Now properly saves config after reloading defaults (previously only applied in-memory)

Changes

  • Enhanced notifications - XP gain and level up messages now use the notification system instead of chat messages for better visibility

v0.0.7

New Features

  • ViewXpPage - New custom UI page for viewing skill stats
  •     Shows total level, total XP, and number of active skills
  •     Displays individual skill progress for all configured skills
  •     Only shows skills with XP configuration data (unconfigured skills hidden)
  •     Open via player.getPageManager().setPage(ref, store, new ViewXpPage(playerRef))

Technical

  • New pages/ package for custom UI pages
  • UI files in src/main/resources/Common/UI/Custom/Pages/
  • Updated CLAUDE.md with UI page documentation and naming conventions

v0.0.6

New Features

  • /mmoconfig reloaddefaults - New admin command to reset all XP values to built-in defaults from SkillDefaults.java

v0.0.5

New Features

  • Comprehensive default XP values for all Hytale blocks based on official item database
  •     Mining: 10+ rock types, 9 ore tiers (Copper→Onyxium), 7 gem types
  •     Woodcutting: 30+ tree types across 5 tiers (Common→Special)
  •     Excavation: All soil types including grass, sand, mud, snow, ash
  •     Harvesting: Grass, ferns, bushes, flowers, crops, fruits, coral, vines
  • PickupItemEventSystem - Awards XP when picking up items

Changes

  • Professional package structure - Reorganized codebase:
  •     api/ - Public API for other plugins
  •     command/ - Command handlers
  •     config/ - Configuration and defaults
  •     data/ - Data models (SkillComponent, SkillType)
  •     event/ - Event system handlers
  •     service/ - Business logic
  •     util/ - Utility classes
  • SkillDefaults.java - Centralized default XP values (separated from config)
  • Updated import paths: com.ziggfreed.mmoskilltree.api.MMOSkillTreeAPI, com.ziggfreed.mmoskilltree.data.SkillType

v0.0.4

Bugfixes

  • Fixed issue with "harvesting" blocks not rewarding xp

v0.0.3

New Skills

New skills added for developers to access via API

New Features

  • Public API (MMOSkillTreeAPI) for other plugins to access player skill data
  •     getSkillComponent(PlayerRef) - Get full skill data
  •     getXp(PlayerRef, SkillType) / getLevel(PlayerRef, SkillType) - Get specific skill data
  •     getTotalXp(PlayerRef) / getTotalLevel(PlayerRef) - Get combined stats
  •     getAllXp(PlayerRef) / getAllLevels(PlayerRef) - Get all skills as maps
  •     addXp(PlayerRef, SkillType, long) - Add XP (silent)
  •     removeXp(PlayerRef, SkillType, long) / setXp(PlayerRef, SkillType, long) - Modify XP
  •     getLevelProgress(PlayerRef, SkillType) - Get progress to next level (0.0-1.0)
  •     calculateLevelFromXp(long) / getXpRequiredForLevel(int) - Utility methods
  •     hasSkillData(PlayerRef) / getSkillTypes() - Helper methods
  • New skill category: Misc (for Building and future utility skills)
  • New trigger types: PLACE_BLOCK, FALL_DAMAGE, TAME_ENTITY, CATCH_FISH

Changes

  • /mmoxp now only shows skills with XP > 0 (cleaner display)
  • Plugin instance now accessed via MMOSkillTreePlugin.getInstance() (singleton pattern)
  • Commands now extend AbstractPlayerCommand with PlayerRef parameter
  • Internal services use API for cache access (DRY principle)
  • Consistent cache keying via PlayerRef.hashCode()

v0.0.2

Changes

  • Changed default XP gain threshold for message display from 0 to 10
  • Fixed permission issue with /xpdisplay command

v0.0.1 - Initial Release

Skills

  • Mining - Gain XP from breaking stone and ores
  • Woodcutting - Gain XP from chopping trees and logs
  • Excavation - Gain XP from digging soil and dirt
  • Harvesting - Gain XP from harvesting plants and crops

Features

  • Quadratic XP scaling formula for balanced progression
  • Per-player XP display preferences (toggle on/off, set minimum threshold)
  • Persistent player data via ECS component codec
  • JSON config file with hot-reload support
  • Pattern-based block matching for flexible XP configuration

Commands

  • /mmoxp - View your skill levels and XP progress
  • /xpdisplay - Configure personal XP notification settings
  •     on / off - Toggle XP gain messages
  •     threshold --value= - Only show gains >= n XP
  •     status - View current settings
  • /mmoconfig (OP only) - Server configuration
  •     list --args= - View XP configurations
  •      --args= - Set XP for block pattern
  •     basexp --args= - Set base XP per level
  •     scale --args= - Set leveling speed
  •     remove --args= - Remove block pattern
  •     reload - Reload config from file

Technical

  • Dynamic skill system - adding new skills only requires enum entry + defaults
  • TriggerType system for future event handlers (combat, crafting, etc.)
  • Thread-safe skill caching for command access
  • Auto-generates config on first launch