- Name: IllyriaPlus
- Type: Single-module Minecraft Paper plugin project (server-side only)
- MC Version: 1.21.11
- Language: Kotlin (JVM 25)
- Build Tool: Gradle with Kotlin DSL
| Category | Technology | Purpose |
|---|---|---|
| Core API | Paper API 1.21.11 | Minecraft server plugin API |
| Language | Kotlin 2.3.21 | JVM language |
| Build Tool | Gradle (Kotlin DSL) | Build automation |
| Gradle Plugins | Shadow 9.4.1 | Fat JAR creation |
| run-paper 3.0.2 | Local test server | |
| resource-factory 1.3.1 | paper-plugin.yml generation |
|
| foojay-resolver 1.0.0 | Auto-download JVM toolchains | |
| Text Formatting | MiniMessage | Adventure API component-based text |
| Docs | Dokka | Kotlin API documentation |
| Code Style | ktlint | Kotlin linting (IDE plugin) |
- Documentation: https://docs.papermc.io/paper/dev/
- JavaDoc: https://jd.papermc.io/paper/1.21.11/ (matches project version)
- Uses modern lifecycle/registry APIs (experimental — requires
@Suppress("UnstableApiUsage")) - Custom enchantments use
RegistryEvents.ENCHANTMENT - Item tags created via
LifecycleEvents.TAGS.postFlatten - Plugin bootstrapper pattern for early registry access
Documentation is generated with Dokka from KDoc comments in the source code.
- Run
./gradlew dokkaGenerateHtmlto generate documentation - Output goes to
docs/directory (published to GitHub Pages automatically) - Auto-deployed via GitHub Actions on pushes to main
- Key files to document: interfaces, managers, and public APIs
- Use KDoc format:
/** ... */with Markdown support
# Build the plugin
./gradlew shadowJar
# Run local test server (auto-downloads Paper 1.21.11)
./gradlew runServer
# Generate Dokka documentation
./gradlew dokkaGenerateHtml
# Run linting
./gradlew ktlintCheck
# Fix linting issues
./gradlew ktlintFormatIllyriaPlus/
├── build.gradle.kts # Build configuration
├── settings.gradle.kts # Project settings
├── src/ # Source directory
│ ├── IllyriaPlus.kt # Main plugin class
│ ├── IllyriaPlusBootstrap.kt # Bootstrap class
│ ├── mechanics/ # Feature mechanics
│ ├── enchantments/ # Enchantment implementations
│ ├── interfaces/ # ModuleInterface, EnchantmentInterface, RecipeInterface
│ ├── managers/ # XpManager, PlayerMessageManager, SpellManager
│ ├── pdcs/ # PlayerPDC, ItemPDC
│ ├── recipes/ # Recipe implementations
│ ├── data/ # Data classes
│ └── utils/ # Utility functions
└── docs/ # Generated documentation
- IllyriaPlusBootstrap (
PluginBootstrap) — Runs before plugin enable. Registers custom enchantments into Paper's registry and creates item tags. - IllyriaPlus (
JavaPlugin) — Main class. Validates server version, registers recipes, and enables all mechanics.
- All features are Kotlin
objectsingletons implementingModuleInterface(extends BukkitListener) - Mechanics self-register in
IllyriaPlus.onEnable()viaregister() - Each mechanic has a nested
object Configwith hardcoded defaults — no file-based config system - To disable a mechanic at compile time, override
enabledtofalsein the mechanic object
Custom enchantments implement EnchantmentInterface with:
- Auto-generated
TypedKey<Enchantment>from class name (e.g.,VerdanceEnchantment→vanillaplus:verdance) invoke(builder)to configure registry entry (description, cost, levels, weight, slots)get()to retrieve liveEnchantmentinstance from registry
Spell System:
- Blaze Rod spell enchantments cost XP to cast
- All spells are compatible — can combine multiple on one wand
- Left-click: Cast selected spell (consumes XP, free in Creative)
- Right-click: Cycle spells (shows in action bar)
XpManagerhandles XP cost validation and playsNO_XP_SOUNDon insufficient XP
- All internal classes use
internalvisibility - All mechanics are
objectsingletons - Use MiniMessage (
Utils.MM) for all text formatting - Add
@Suppress("UnstableApiUsage")when using Paper's experimental APIs - ktlint is enforced; suppress wildcard imports per-file with
@file:Suppress("ktlint:standard:no-wildcard-imports")if needed - Recipe
NamespacedKeynaming:{descriptive_name}_{recipe_type} - Import types instead of using fully qualified paths — e.g.,
import org.bukkit.inventory.meta.PotionMetainstead oforg.bukkit.inventory.meta.PotionMeta - Use
itfor single-parameter lambdas — e.g.,list.forEach { it.doSomething() }instead oflist.forEach { item -> item.doSomething() } - Use
ItemStack.of()instead ofItemStack()constructor — Paper's modern API for creating item stacks - Don't create intermediate
const valfor override properties — assign directly to the override, e.g.,override val key: String = "vanillaplus:mana_potion"instead of creating aconst val KEYand thenoverride val key = KEY - Don't add KDoc to implemented overrides — the base interface/class already has documentation; let it inherit naturally
- Use data class builders — e.g.,
potion(PotionData(color = X, displayName = Y))instead of lambda receivers for simpler configuration - Use explicit named factory functions — prefer
potion()andsplash()overinvoke()operator for clarity - Alphabetical order for static collections —
SPELL_MAPand similar static maps/lists should be sorted alphabetically by key
Order members from top to bottom:
const val— compile-time constantsval— read-only properties (overrides first)var— mutable properties (overrides first)fun— functions (overrides first)object Config— nested config object (at bottom for mechanics)
Within each group:
overridemembers go above regular members@EventHandlerfunctions go above regularpublicfunctions@EventHandlerfunctions should always be namedon(event: <EventType>)— Kotlin allows multiple@EventHandler fun on(...)as long as parameter types differ@EventHandlerfunctions should not have KDoc comments (the event type is self-documenting)publicmembers go aboveprivatemembers
- No automated tests in this project
- Test by running
./gradlew runServerand manually verifying in-game
- No file-based configuration — all settings are compile-time constants in mechanic
Configobjects - Enchantments must be registered in
IllyriaPlusBootstrapAND tagged as tradeable/non-treasure/enchanting-table - Spell enchantments use
XpManagerto consume XP on cast - Project uses Paper's modern lifecycle/registry APIs extensively
When creating tasks:
- Number tasks in the name (e.g., "1. Add Verdance enchantment", "2. Update mana system")
- This makes it easy to reference specific tasks in conversation
After completing each task:
- Ask the user if they want to git commit the changes or adjust before committing
When all tasks in a worktree are complete:
- Ask the user if they want to git publish (push) the changes or adjust before publishing
Always update documentation when code changes:
-
ARCHITECTURE.md — Update if you:
- Add/remove enchantments, mechanics, recipes, or managers
- Change the mechanic system or interfaces
- Modify the mana system or spell mechanics
- Change project structure or conventions
-
GUIDE.md — Update if you:
- Change build commands or installation steps
- Add/remove major features
-
KDoc comments — Add/update if you:
- Add new public APIs (interfaces, managers, utils)
- Change existing function signatures or behavior
- Add complex logic that needs explanation
- Run
./gradlew dokkaGenerateHtmlto regenerate docs after changes
Rule of thumb: If a code change would confuse someone reading the docs, update the docs.
GitHub Actions workflows in .github/workflows/:
- kotlin.yml — Builds shadow JAR on push/PR, uploads artifacts, creates nightly release
- enforce_pr_title.yml — Validates PR titles follow conventional commits
- Create new file in
src/enchantments/YournameEnchantment.kt - Implement
EnchantmentInterfaceas anobject - In
invoke(builder), configure:description(),supportedItems(),anvilCost(),maxLevel(),weight(),slotGroup() - In
IllyriaPlusBootstrap.kt:- Add
YournameEnchantmentto theENCHANTMENTSlist - Add it to the tags (tradeable, non-treasure, enchanting-table)
- Add supported items to appropriate
ItemTagif needed
- Add
- If it's a spell (Blaze Rod enchantment):
- Register in
SpellManager.SPELL_MAP(alphabetical order) - Store cost in
PlayerPDCconstants - Add to enchantment compatibility group
- Implement
@EventHandlerforPlayerInteractEventor projectile logic
- Register in
- Update
ARCHITECTURE.mdenchantment table - Add KDoc comments to explain the enchantment's behavior
- Run
./gradlew dokkaGenerateHtmlto regenerate documentation
- Create new file in
src/mechanics/YourMechanic.kt - Implement
ModuleInterfaceas anobject - Override
Configobject with settings as compile-time constants - Implement
@EventHandlermethods for events - Register commands/permissions in
register()if needed - In
IllyriaPlus.kt, addYourMechanicto the mechanic list inonEnable() - Add KDoc comments explaining the mechanic's purpose and features
- Update
ARCHITECTURE.mdmechanic count
- Create new file in
src/recipes/YourRecipe.kt - Implement
RecipeInterfaceas anobject - Define
recipeslist for crafting/smelting recipes, orpotionslist for brewing recipes - Use naming pattern
{descriptive_name}_{recipe_type}forNamespacedKey - In
IllyriaPlus.kt, addYourRecipeto the recipe list inonEnable() - Add KDoc comments describing the recipe
- For player data: edit
src/pdcs/PlayerPDC.kt - For item data: edit
src/pdcs/ItemPDC.kt - Add a new property delegate using
bywithnamespacedKey() - Use primitive types or custom serializers for complex data
- Access via
player.mana,item.customData, etc. directly in code - Document the new PDC field in
ARCHITECTURE.mdif significant
- Create new file in
src/interfaces/YourInterface.kt - Design interface with common methods for the feature category
- Keep interfaces minimal and focused on one responsibility
- Document the interface purpose in
ARCHITECTURE.md
- Create new file in
src/data/YourData.kt - Define
data classwith properties for structured data - Keep data classes immutable (
valproperties) - Add appropriate helper methods or companion object factory functions
- Document if used across multiple mechanics
- For general: edit
src/utils/Utils.ktor createsrc/utils/YourUtils.kt - Keep utility functions
internalvisibility - Prefer extension functions on existing types
- Use
Utils.MMfor MiniMessage formatting - Add Javadoc comments for complex utilities
This project uses Claude Code's persistent memory in .claude/memory/. These files persist across sessions and different PCs. Review MEMORY.md for existing context about the user and project.