=============== LIBRARY RULES =============== From library maintainers: - This plugin uses hexagonal/clean architecture with distinct layers: domain, application, infrastructure, and interaction - Domain entities should never depend on external frameworks - they contain pure business logic - Application actions are the primary use cases - inject dependencies via constructor - All database operations go through repository interfaces defined in application layer - Use dependency injection (Koin) for wiring components - see di package - Claims are anchored by Bell blocks in the Minecraft world - All player interactions go through the interaction layer (commands, menus, listeners) - Use Result sealed classes to handle action outcomes - never throw exceptions for business logic failures - Guilds and Claims are separate concepts - Claims can be converted to guild ownership - Always check permissions before allowing claim modifications - Use Position3D for world coordinates and Position2D for area definitions ### Quick Start: Update Claim Icon Feature Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/README.md This example demonstrates how to implement a new feature by creating an action, defining its result, and setting up a command to trigger it. Ensure the ClaimRepository and necessary types are available. ```kotlin class UpdateClaimIcon(private val claimRepository: ClaimRepository) { fun execute(claimId: UUID, newIcon: String): UpdateClaimIconResult { val claim = claimRepository.getById(claimId) ?: return UpdateClaimIconResult.ClaimNotFound claimRepository.update(claim.copy(icon = newIcon)) return UpdateClaimIconResult.Success(claim) } } ``` ```kotlin sealed class UpdateClaimIconResult { data class Success(val claim: Claim) : UpdateClaimIconResult() object ClaimNotFound : UpdateClaimIconResult() } ``` ```kotlin @Subcommand("seticon") fun onSetIcon(player: Player) { when (val result = updateClaimIcon.execute(claimId, itemType)) { is UpdateClaimIconResult.Success -> player.sendMessage("§aIcon updated!") UpdateClaimIconResult.ClaimNotFound -> player.sendMessage("§cClaim not found!") } } ``` -------------------------------- ### YAML Navigation Structure for Getting Started Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Defines the navigation menu for the 'getting-started' section of the wiki. ```yaml nav: - Welcome: welcome.md - Your first 30 minutes: walkthrough.md - FAQ: faq.md ``` -------------------------------- ### Serve MkDocs Locally Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Start a local MkDocs development server to preview the generated documentation site. ```bash mkdocs serve ``` -------------------------------- ### LumaGuilds Configuration Example Source: https://github.com/badgersmc/lumaguilds/blob/main/README.md Example configuration file for LumaGuilds, showing database, claim, guild, and war settings. ```yaml database: type: sqlite # sqlite or mariadb claims: default-size: 15 # Default claim radius max-claims-per-player: 5 # Default claim limit claim-block-limit: 10000 # Default max blocks per player guilds: creation-cost: 1000 # Cost to create guild max-members: 50 # Member limit bank-interest-rate: 0.01 # Daily interest rate wars: enabled: true minimum-wager: 100 default-kill-objective: 10 ``` -------------------------------- ### LumaGuilds Help Topic Page Example Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-design.md This is an example of a topic-specific help page, such as when a player types `/g help homes`. It lists commands related to the topic and provides interactive elements. ```text ─── Help · Homes ─── Guild homes are teleport points your guild can share. Each guild can have multiple named homes. Access can be restricted per-rank. Commands: /g sethome [name] [click to prefill] [?] /g home [name] [click to prefill] [?] /g homes [click to run] /g removehome [click to prefill] [?] /g setallyhome [click to prefill] [?] Hover a [?] for details. Click a command to drop it into chat. Read more: https://badgersmc.github.io/LumaGuilds/players/homes/ Page 1/1 [Back to topics] ``` -------------------------------- ### Create and Use a Partition Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/domain.md Example of creating a partition with a specified area and checking if a player's position falls within it. Also demonstrates retrieving the chunks occupied by the partition. ```kotlin // Creating a partition for a new claim val initialArea = Area( Position2D(x - 7, z - 7), Position2D(x + 7, z + 7) // 15x15 default ) val partition = Partition(claimId, initialArea) // Checking if player is in partition if (partition.isPositionInPartition(playerPosition)) { // Player is within this partition } // Finding chunks to update val affectedChunks = partition.getChunks() // Update chunk cache for each chunk ``` -------------------------------- ### Branch Setup for Migration Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-03-10-paper-1-21-11-migration-plan.md Commands to set up a new branch for the migration process. ```bash git checkout main git pull git checkout -b feature/paper-1-21-11 ``` -------------------------------- ### Teleport Player to Claim Action Example Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/infrastructure.md Example usage of BukkitLocationAdapter within an action to teleport a player to a specific claim's location. ```kotlin // In an action or service class TeleportPlayerToClaimAction( private val locationAdapter: BukkitLocationAdapter, private val claimRepository: ClaimRepository ) { fun execute(playerId: UUID, claimId: UUID) { val claim = claimRepository.getById(claimId) ?: return val location = locationAdapter.toLocation(claim.position, claim.worldId) location?.let { val player = Bukkit.getPlayer(playerId) player?.teleport(it) } } } ``` -------------------------------- ### PartitionModificationEvent Usage Example Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/application.md Demonstrates how to create and publish a PartitionModificationEvent after a partition change. ```kotlin // After partition modification val event = PartitionModificationEvent( claimId = partition.claimId, partitionId = partition.id, modificationType = ModificationType.CREATED, affectedChunks = partition.getChunks() ) eventBus.publish(event) // Listeners can react (e.g., update chunk cache) ``` -------------------------------- ### Create Partition Action Example Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/application.md Demonstrates a complete action for creating a partition, including claim verification, overlap checks, block limit validation, and partition creation. This action is part of the application's domain logic. ```kotlin package net.lumalyte.lg.application.actions.claim.partition class CreatePartition( private val partitionRepository: PartitionRepository, private val claimRepository: ClaimRepository, private val playerMetadataService: PlayerMetadataService, private val getClaimBlockCount: GetClaimBlockCount ) { fun execute( claimId: UUID, area: Area ): CreatePartitionResult { // 1. Verify claim exists val claim = claimRepository.getById(claimId) ?: return CreatePartitionResult.ClaimNotFound // 2. Check for overlaps val existingPartitions = partitionRepository.getByClaim(claimId) if (existingPartitions.any { it.isAreaOverlap(area) }) { return CreatePartitionResult.OverlapsExistingPartition } // 3. Check block limit val currentBlocks = getClaimBlockCount.execute(claimId) val newBlocks = area.getBlockCount() val playerLimit = playerMetadataService.getPlayerClaimBlockLimit(claim.playerId) if (currentBlocks + newBlocks > playerLimit) { return CreatePartitionResult.InsufficientBlocks( required = newBlocks, available = playerLimit - currentBlocks ) } // 4. Create partition val partition = Partition(claimId, area) partitionRepository.add(partition) return CreatePartitionResult.Success(partition) } } // Result class sealed class CreatePartitionResult { data class Success(val partition: Partition) : CreatePartitionResult() object ClaimNotFound : CreatePartitionResult() object OverlapsExistingPartition : CreatePartitionResult() data class InsufficientBlocks( val required: Int, val available: Int ) : CreatePartitionResult() } ``` -------------------------------- ### MkDocs Local Build and Activation Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Commands to set up a Python virtual environment, activate it, install dependencies, and build the MkDocs site locally. ```bash python -m venv .venv . .venv/Scripts/activate # Windows pip install -r wiki/requirements.txt mkdocs build --strict ``` -------------------------------- ### Guild Banner Storage Example Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/infrastructure.md Example of using BukkitItemStackAdapter to serialize and store a player's main hand item as a guild banner, and later deserialize it. ```kotlin // Storing guild banner val bannerItemStack = player.inventory.itemInMainHand val serialized = itemStackAdapter.serialize(bannerItemStack) guildRepository.update( guild.copy(banner = serialized) ) // Retrieving guild banner val guild = guildRepository.getById(guildId) guild.banner?.let { bannerData -> val itemStack = itemStackAdapter.deserialize(bannerData) player.inventory.addItem(itemStack) } ``` -------------------------------- ### CreateClaim Usage Example Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/application.md Demonstrates how to use the 'CreateClaim' action from the interaction layer, handling different result types and providing feedback to the player. ```kotlin // In a command or listener val result = createClaim.execute( playerId = player.uniqueId, name = "MyBase", position3D = Position3D(x, y, z), worldId = world.uid ) when (result) { is CreateClaimResult.Success -> { player.sendMessage("Claim '${result.claim.name}' created!") } CreateClaimResult.LimitExceeded -> { player.sendMessage("You've reached your claim limit!") } CreateClaimResult.NameCannotBeBlank -> { player.sendMessage("Claim name cannot be blank!") } CreateClaimResult.NameAlreadyExists -> { player.sendMessage("You already have a claim with that name!") } CreateClaimResult.TooCloseToWorldBorder -> { player.sendMessage("Too close to world border!") } } ``` -------------------------------- ### Create Claim Example Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/api-reference.md Demonstrates how to create a new claim and handle the result. Ensure all necessary parameters like player ID, name, position, and world ID are provided. The result can indicate success with the claim ID or a limit being exceeded. ```kotlin val result = createClaim.execute( playerId = player.uniqueId, name = "MyBase", position3D = Position3D(x, y, z), worldId = world.uid ) when (result) { is CreateClaimResult.Success -> println("Created: ${result.claim.id}") CreateClaimResult.LimitExceeded -> println("Limit exceeded") // ... handle other cases } ``` -------------------------------- ### Clone and Build LumaGuilds Project Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/getting-started.md Clone the repository and build the project using Gradle. Ensure you have JDK 21+ and Git installed. ```bash git clone https://github.com/your-org/bell-claims.git cd bell-claims ./gradlew build ``` -------------------------------- ### Create and Validate a Claim Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/domain.md Example of instantiating a new personal claim and demonstrating validation by attempting to create a claim with an invalid name. ```kotlin // Creating a new claim val claim = Claim( worldId = world.uniqueId, playerId = player.uniqueId, teamId = null, // Personal claim position = Position3D(x, y, z), name = "My Base" ) // Validating claim properties try { val invalidClaim = Claim(/* ... */, name = "") // Throws IllegalArgumentException } catch (e: IllegalArgumentException) { // Handle validation failure } ``` -------------------------------- ### Koin Module for Application Dependencies Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/application.md Defines the dependency injection setup using Koin, including repositories, services, and actions. ```kotlin // Koin module definition val applicationModule = module { // Repositories (implemented in infrastructure) single { ExposedClaimRepository(get()) } single { ExposedPartitionRepository(get()) } // Services single { PlayerMetadataService(get()) } single { WorldManipulationService(get()) } // Actions (factory = new instance each time) factory { CreateClaim(get(), get(), get(), get(), get(), get()) } factory { GrantPlayerClaimPermission(get(), get()) } factory { IsPlayerActionAllowed(get(), get(), get(), get()) } } ``` -------------------------------- ### Create Walkthrough Page Content Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md This Markdown content defines the 'Your first 30 minutes' page, serving as a guided tour for new players. It includes front-matter and an introduction to the seven-step walkthrough. The page is designed to be scannable with short paragraphs and code blocks for commands. ```markdown --- title: Your first 30 minutes audience: player topic: walkthrough summary: A guided tour for new players — spawn to working guild in seven steps. keywords: [walkthrough, tutorial, onboarding, first time, getting started] related: [welcome, guilds, homes, chat] updated: 2026-05-13 --- # Your first 30 minutes A guided tour for new players. Follow it top-to-bottom and you'll end up in a working guild with a tag, a home, chat configured, and a clear next move. ``` -------------------------------- ### Example Usage of Area Class in Kotlin Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/domain.md Demonstrates how to instantiate an Area object and utilize its methods for checking player position, overlapping claims, and calculating claim size. ```kotlin // Define claim area val area = Area( Position2D(0, 0), Position2D(15, 15) ) // Check if player is in area if (area.isPositionInArea(playerPosition)) { // Player is in claimed area } // Check for overlapping claims val newArea = Area(Position2D(10, 10), Position2D(25, 25)) if (existingArea.isAreaOverlap(newArea)) { // Cannot create claim - overlaps existing } // Calculate claim size val blocks = area.getBlockCount() // 256 blocks ``` -------------------------------- ### LumaGuilds PlaceholderAPI Placeholders Example Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/integration.md Demonstrates how to use LumaGuilds' built-in placeholders within your scoreboard or other plugins that support PlaceholderAPI. Ensure the placeholders are correctly formatted. ```yaml lines: - "&6Claims: &e%lumaguilds_claim_count%" - "&6Guild: &e%lumaguilds_guild_name%" - "&6Level: &e%lumaguilds_guild_level%" - "&6Bank: &e%lumaguilds_guild_bank%" - "&6Members: &e%lumaguilds_guild_member_count%" - "&6In Claim: &e%lumaguilds_in_claim%" - "&6Claim Owner: &e%lumaguilds_claim_owner%" ``` -------------------------------- ### Economy Integration: Charge for Claim Creation (Kotlin) Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/integration.md Example of integrating with an economy plugin (like Vault) to charge players for creating claims. It checks for sufficient funds and withdraws the cost. ```kotlin /** * Example: Charge money for claim creation */ class EconomyIntegration( private val economy: Economy // Vault economy ) : Listener { @EventHandler fun onClaimCreated(event: ClaimCreatedEvent) { val player = event.player val claim = event.claim val blockCount = getClaimBlockCount.execute(claim.id) val cost = blockCount * 10.0 // $10 per block if (!economy.has(player, cost)) { // Refund - delete claim deleteClaim.execute(claim.id) player.sendMessage("§cInsufficient funds! Need $$cost") return } economy.withdrawPlayer(player, cost) player.sendMessage("§aPaid $$cost for claim") } } ``` -------------------------------- ### Infrastructure: Persisting Data (Good Practice) Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/infrastructure.md Example of a repository implementation that focuses solely on data persistence without including business logic. This adheres to the principle of keeping business logic out of the infrastructure layer. ```kotlin // Good: Infrastructure just persists class ClaimRepositorySQLite : ClaimRepository { override fun add(claim: Claim): Boolean { // Just save to database storage.connection.executeUpdate(/* ... */) } } ``` -------------------------------- ### Lint and Build Documentation Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Execute linting for wiki frontmatter and build the documentation strictly. This is followed by staging and committing the updated FAQ file. ```bash python tools/wiki/lint_frontmatter.py mkdocs build --strict git add wiki/docs/getting-started/faq.md git commit -m "docs(wiki): write Getting Started FAQ" ``` -------------------------------- ### Bash Command to Install PyYAML Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Install the PyYAML library, which is a dependency for the front-matter linting script. ```bash pip install pyyaml ``` -------------------------------- ### Build a Partition using Builder Pattern Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/domain.md Demonstrates creating a partition using a builder pattern, allowing for step-by-step construction of the partition's area. ```kotlin // Creating partition with builder val builder = Partition.Builder(claimId, firstCorner) builder.secondPosition2D = secondCorner val partition = builder.build() ``` -------------------------------- ### MkDocs Requirements Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md List of Python packages required for the MkDocs project. Install using `pip install -r wiki/requirements.txt`. ```text mkdocs==1.6.1 mkdocs-material==9.5.49 mkdocs-awesome-pages-plugin==2.9.3 mkdocs-redirects==1.2.2 mkdocs-llmstxt==0.2.0 ``` -------------------------------- ### Build and Lint Project Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-14-player-wiki-phase-2-admins-plan.md Run these commands to lint the project's front-matter and build the documentation site. Ensure the documentation is up-to-date and adheres to project standards before committing. ```bash python tools/wiki/lint_frontmatter.py python -m mkdocs build --strict ``` -------------------------------- ### Resize a Partition using Resizer Pattern Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/domain.md Illustrates how to resize an existing partition by defining a corner to move and a new position. It also shows how to retrieve the number of extra blocks required for the resize operation. ```kotlin // Resizing existing partition val resizer = Partition.Resizer(partition, cornerToMove) resizer.setNewCorner(newCornerPosition) val extraBlocks = resizer.getExtraBlockCount() if (playerHasEnoughBlocks(extraBlocks)) { val resizedPartition = Partition( partition.id, partition.claimId, resizer.newArea ) } ``` -------------------------------- ### Python Script for Linting Wiki Front-Matter Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md This script validates the YAML front-matter of Markdown files in the wiki documentation. It checks for required fields, valid audience values, correct topic slugs, and summary length. Ensure PyYAML is installed (`pip install pyyaml`). ```python #!/usr/bin/env python3 """Validate YAML front-matter on every wiki/docs/**/*.md page. Required fields: title, audience, topic, summary, keywords, related, updated. audience must be one of: player, admin, dev. topic must be a lowercase-hyphenated slug matching the filename stem. summary must be ≤140 chars. Exit code 1 on any failure; prints all failures before exiting. """ from __future__ import annotations import re import sys from pathlib import Path try: import yaml except ImportError: print("PyYAML is required. Install with: pip install pyyaml", file=sys.stderr) sys.exit(2) REPO_ROOT = Path(__file__).resolve().parents[2] DOCS_ROOT = REPO_ROOT / "wiki" / "docs" REQUIRED_FIELDS = {"title", "audience", "topic", "summary", "keywords", "related", "updated"} VALID_AUDIENCES = {"player", "admin", "dev"} SLUG_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$") SUMMARY_MAX = 140 ``` -------------------------------- ### Render Help Topic Page with Adventure Components Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Renders a detailed help topic page, including header, summary, commands with prefill and hover events, and a link to the wiki. Uses Adventure components for rich text formatting and interactivity. ```kotlin fun renderTopicPage(topic: HelpTopic): Component { val header = Component.text("─── Help · ${topic.displayName} ───", NamedTextColor.GOLD, TextDecoration.BOLD) val summary = Component.text(topic.summary, NamedTextColor.GRAY) val commandLines = topic.commands.map { entry -> val line = Component.text() .append(Component.text(" ", NamedTextColor.DARK_GRAY)) .append(Component.text(entry.syntax, NamedTextColor.WHITE)) if (entry.prefill.isNotEmpty()) { line.clickEvent(ClickEvent.suggestCommand(entry.prefill)) .hoverEvent( HoverEvent.showText( Component.text() .append(Component.text(entry.blurb, NamedTextColor.GOLD)) .append(Component.newline()) .append(Component.text("Click to prefill in chat", NamedTextColor.GRAY)) .build(), ), ) } else { line.hoverEvent(HoverEvent.showText(Component.text(entry.blurb, NamedTextColor.GOLD))) } line.build() } val wikiUrl = "${HelpTopics.WIKI_BASE_URL}/${topic.slug}/" val wikiLine = Component.text() .append(Component.text("Read more: ", NamedTextColor.GRAY)) .append( Component.text(wikiUrl, NamedTextColor.AQUA, TextDecoration.UNDERLINED) .clickEvent(ClickEvent.openUrl(wikiUrl)) .hoverEvent(HoverEvent.showText(Component.text("Open in browser", NamedTextColor.GOLD))) ) .build() val back = Component.text("[Back to topics]", NamedTextColor.YELLOW) .clickEvent(ClickEvent.runCommand("/g help")) .hoverEvent(HoverEvent.showText(Component.text("Open the topic menu", NamedTextColor.GOLD))) val out = Component.text() .append(header).append(Component.newline()) .append(summary).append(Component.newline()) .append(Component.text("Commands:", NamedTextColor.YELLOW)).append(Component.newline()) commandLines.forEach { out.append(it).append(Component.newline()) } out.append(Component.newline()) .append(wikiLine).append(Component.newline()) .append(back) return out.build() } } ``` -------------------------------- ### Integration Pattern: Economy Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/integration.md Example of integrating with an economy plugin to charge players for creating claims. ```APIDOC ## Integration Pattern: Economy ### Description This pattern shows how to integrate LumaGuilds with an economy plugin (like Vault) to manage costs associated with in-game actions, such as creating claims. ### Example: Charging for Claim Creation ```kotlin // In your plugin that handles claim creation events import org.bukkit.event.EventHandler import org.bukkit.event.Listener import net.lumalyte.lg.api.LumaGuildsPlugin import net.lumalyte.lg.api.event.ClaimCreatedEvent // Assume Economy and other necessary imports like deleteClaim.execute, getClaimBlockCount.execute class EconomyIntegration( private val economy: Economy // Assuming Economy is an interface from Vault or similar ) : Listener { @EventHandler fun onClaimCreated(event: ClaimCreatedEvent) { val player = event.player val claim = event.claim // Assume 'getClaimBlockCount' is an accessible service or method val blockCount = getClaimBlockCount.execute(claim.id) val cost = blockCount * 10.0 // Example cost: $10 per block if (!economy.has(player, cost)) { // Refund - delete claim // Assume 'deleteClaim' is an accessible service or method deleteClaim.execute(claim.id) player.sendMessage("§cInsufficient funds! Need $$cost") return } economy.withdrawPlayer(player, cost) player.sendMessage("§aPaid $$cost for claim") } } ``` ``` -------------------------------- ### Integration Pattern: Territory Control Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/integration.md Example of integrating with a war plugin to restrict combat in non-PvP claims. ```APIDOC ## Integration Pattern: Territory Control ### Description This pattern demonstrates how to use the LumaGuildsAPI to check claim information and enforce rules, such as disabling PvP in specific claims. ### Example: Restricting Combat in Non-PvP Claims ```kotlin // In your plugin that handles combat events import org.bukkit.entity.Player import org.bukkit.event.EventHandler import org.bukkit.event.Listener import org.bukkit.event.entity.EntityDamageByEntityEvent import net.lumalyte.lg.api.LumaGuildsPlugin import net.lumalyte.lg.api.model.Position3D // Assume other necessary imports like ClaimFlag, doesClaimHaveFlag.execute class CombatIntegration : Listener { private val api = LumaGuildsPlugin.api @EventHandler fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) { val attacker = (event.damager as? Player) ?: return val victim = (event.entity as? Player) ?: return val location = victim.location val position = Position3D(location.blockX, location.blockY, location.blockZ) // Check if the victim is in a claim val claim = api.getClaimAt(position, victim.world.uid) if (claim != null) { // Check if the claim has the PvP flag enabled // Assume 'doesClaimHaveFlag' is an accessible service or method if (!doesClaimHaveFlag.execute(claim.id, ClaimFlag.PVP)) { event.isCancelled = true attacker.sendMessage("§cPvP is disabled in this claim!") } } } } ``` ``` -------------------------------- ### Early Validation for Permissions and Preconditions Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/interaction.md Shows the recommended practice of checking permissions and obtaining necessary data like player partitions before performing potentially expensive operations or opening menus. ```kotlin // Good: Check first if (!player.hasPermission("lumaguilds.command.claim")) { player.sendMessage("No permission!") return } val partition = getPartitionAtPlayer(player) ?: return openMenu(player, partition) // Bad: Check after expensive operations val partition = getPartitionAtPlayer(player) ?: return loadAllData() if (!player.hasPermission("lumaguilds.command.claim")) return ``` -------------------------------- ### Create HelpTopic registry Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Initializes a map of all help topics for quick lookup by slug. Provides a function to retrieve a help topic by its slug, case-insensitively. ```kotlin private val bySlugMap: Map = all.associateBy { it.slug } fun bySlug(slug: String): HelpTopic? = bySlugMap[slug.lowercase()] ``` -------------------------------- ### Run Wiki Lint and Build Commands Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Execute the front-matter linting script and build the MkDocs project with strict mode enabled. Both commands are expected to pass. ```bash python tools/wiki/lint_frontmatter.py mkdocs build --strict ``` -------------------------------- ### Update Koin Registration for GuildService Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-03-11-guild-history-impl.md Adjust the Koin registration for `GuildService` to include the new `MembershipHistoryRepository` dependency, increasing the `get()` calls. ```kotlin single { GuildServiceBukkit(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } ``` -------------------------------- ### Build LumaGuilds from Source Source: https://github.com/badgersmc/lumaguilds/blob/main/README.md Instructions for cloning the LumaGuilds repository and building the shadow JAR using Gradle. The output JAR will be located in build/libs. ```bash git clone https://github.com/BadgersMC/LumaGuilds.git cd LumaGuilds ./gradlew shadowJar # Output: build/libs/LumaGuilds-0.5.0.jar ``` -------------------------------- ### Record Guild Join Event Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-03-11-guild-history-design.md Hook into the member joining process to record the start of a new guild stint in the history repository. ```kotlin historyRepository.openStint(playerId, guildId) ``` -------------------------------- ### YAML Navigation Structure for Admin Documentation Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Defines the navigation menu for the 'admins' section of the wiki, covering installation, configuration, and advanced features. ```yaml nav: - Installation: installation.md - Permission nodes: permissions.md - Override & recovery: override.md - Troubleshooting: troubleshooting.md - PlaceholderAPI: placeholderapi.md - RoseChat: rosechat.md - Geyser/Floodgate: geyser.md - Lunar Client: lunar.md - Web leaderboard API: leaderboard-api.md - Claims: claims.md ``` -------------------------------- ### Run Gradle tests for HelpTopics Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Executes Gradle tests specifically for the 'HelpTopics' class. This command is used to verify the implementation of the help topics registry. ```bash ./gradlew test --tests "net.lumalyte.lg.interaction.help.HelpTopicsTest" ``` -------------------------------- ### Add New GUI Menu Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/getting-started.md Illustrates how to create a new GUI menu using Bukkit API, including inventory creation, item setting, and click handling. ```kotlin package net.lumalyte.lg.interaction.menus.[category] class [MenuName]( private val [data]: [DataType], private val [action1]: [Action1], private val menuNavigator: MenuNavigator ) : Menu { override fun open(player: Player) { val inventory = Bukkit.createInventory( null, [size], // Must be multiple of 9 "[Title]" ) // Add items inventory.setItem([slot], ItemStack(Material.[TYPE]).apply { itemMeta = itemMeta?.apply { setDisplayName("[Display Name]") lore = listOf("[Description]") } }) player.openInventory(inventory) } override fun handleClick(player: Player, slot: Int, clickType: ClickType) { when (slot) { [slot] -> { // Handle click val result = [action].execute(/* ... */) when (result) { is [Result].Success -> { player.sendMessage("Success!") refresh(player) } // ... handle other results } } } } override fun refresh(player: Player) { open(player) } override fun close(player: Player) { player.closeInventory() } } ``` -------------------------------- ### Add UpdateClaimIcon to DI Module Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/getting-started.md Integrates the new UpdateClaimIcon action into the Koin dependency injection module. Requires existing Koin setup. ```kotlin // File: di/ApplicationModule.kt val applicationModule = module { // ... existing actions ... factory { UpdateClaimIcon(get()) } } ``` -------------------------------- ### Get LumaGuilds Plugin Instance (Kotlin) Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/integration.md Obtain the LumaGuilds plugin instance to interact with its API. Ensure LumaGuilds is present, or disable the integrating plugin. ```kotlin package com.yourplugin import net.lumalyte.lg.LumaGuildsPlugin import org.bukkit.Bukkit class YourPlugin : JavaPlugin() { private lateinit var lumaGuilds: LumaGuildsPlugin override fun onEnable() { // Get LumaGuilds instance lumaGuilds = Bukkit.getPluginManager().getPlugin("LumaGuilds") as? LumaGuildsPlugin ?: run { logger.severe("LumaGuilds not found!") Bukkit.getPluginManager().disablePlugin(this) return } logger.info("LumaGuilds integration enabled!") } } ``` -------------------------------- ### Commit Welcome Page Changes Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Stage and commit the updated 'welcome.md' file to the repository. ```bash git add wiki/docs/getting-started/welcome.md git commit -m "docs(wiki): write Getting Started welcome page" ``` -------------------------------- ### Build and Test Commands Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-03-10-paper-1-21-11-migration-plan.md Execute these commands to clean the project, build the shadow JAR, and run all tests. Ensure zero deprecation warnings and all tests pass. ```bash ./gradlew clean shadowJar ./gradlew test ``` -------------------------------- ### Define HelpCommandEntry and HelpTopic Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Defines data structures for help commands and topics, used to organize in-game help information. ```kotlin HelpCommandEntry("/g ranks", "Open the rank management menu.", "/g ranks"), HelpCommandEntry("/g menu", "Open the guild control panel.", "/g menu") ``` ```kotlin HelpTopic( slug = "chat", displayName = "Chat", summary = "Toggle guild and ally chat, and customize how your tag appears in messages.", commands = listOf( HelpCommandEntry("/g chat", "Toggle guild chat on/off.", "/g chat"), HelpCommandEntry("/g allychat", "Toggle ally chat on/off.", "/g allychat") ) ) ``` ```kotlin HelpTopic( slug = "alliances", displayName = "Alliances & Diplomacy", summary = "Form alliances, declare enemies, sign truces, and manage ally-home access.", commands = listOf( HelpCommandEntry("/g ally ", "Request or accept an alliance.", "/g ally "), HelpCommandEntry("/g enemy ", "Mark a guild as enemy.", "/g enemy "), HelpCommandEntry("/g truce ", "Sign a truce.", "/g truce "), HelpCommandEntry("/g neutral ", "Clear a relation.", "/g neutral ") ) ) ``` ```kotlin HelpTopic( slug = "war", displayName = "War", summary = "Declare and fight wars between guilds.", commands = listOf( HelpCommandEntry("/g war ", "Open the war control flow.", "/g war ") ) ) ``` ```kotlin HelpTopic( slug = "progression", displayName = "Progression & Levels", summary = "Earn guild XP from member activity, level up, and unlock perks.", commands = listOf( HelpCommandEntry("/g info", "See your guild's level and XP.", "/g info") ) ) ``` ```kotlin HelpTopic( slug = "vault", displayName = "Vault", summary = "Use the shared guild vault for storing items.", commands = listOf( HelpCommandEntry("/g vault", "Open the guild vault.", "/g vault"), HelpCommandEntry("/g getvault", "Get a vault chest item.", "/g getvault") ) ) ``` ```kotlin HelpTopic( slug = "identity", displayName = "Tags, Banners & Identity", summary = "Set your guild's tag, description, and banner.", commands = listOf( HelpCommandEntry("/g tag [text]", "Set or edit your guild tag.", "/g tag "), HelpCommandEntry("/g desc ", "Set your guild description.", "/g desc "), HelpCommandEntry("/g rename ", "Rename your guild.", "/g rename "), HelpCommandEntry("/g emoji", "Pick a guild emoji.", "/g emoji") ) ) ``` ```kotlin HelpTopic( slug = "mode", displayName = "Mode (Peaceful / Hostile)", summary = "Switch your guild between peaceful and hostile mode.", commands = listOf( HelpCommandEntry("/g mode", "Open the mode selection menu.", "/g mode") ) ) ``` ```kotlin HelpTopic( slug = "shop", displayName = "Shop integration", summary = "Link guild-owned shops.", commands = listOf( HelpCommandEntry("/g setshop", "Mark your current shop as guild-owned.", "/g setshop") ) ) ``` ```kotlin HelpTopic( slug = "lfg", displayName = "LFG & Invites", summary = "Manage invites, decline requests, and use LFG to find a guild.", commands = listOf( HelpCommandEntry("/g invite ", "Invite a player to your guild.", "/g invite "), HelpCommandEntry("/g invites", "List your pending invites.", "/g invites"), HelpCommandEntry("/g decline ", "Decline an invite.", "/g decline "), HelpCommandEntry("/g lfg", "Toggle LFG (looking-for-guild).", "/g lfg"), HelpCommandEntry("/g kick ", "Kick a member from your guild.", "/g kick ") ) ) ``` ```kotlin HelpTopic( slug = "bedrock", displayName = "Bedrock differences", summary = "Where LumaGuilds behaves differently on Bedrock/Geyser.", commands = emptyList().let { listOf( HelpCommandEntry( syntax = "(see wiki)", blurb = "Bedrock menus open as forms; clickable chat maps to taps.", prefill = "", ) ) } ) ``` -------------------------------- ### Idempotent Migration Example Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/MIGRATION_SAFETY.md This SQL snippet shows an idempotent migration command that checks for the existence of a column before attempting to add it, preventing errors if the migration is run multiple times. ```sql if (!columnExists("guilds", "is_open")) { sqlCommands.add("ALTER TABLE guilds ADD COLUMN is_open INTEGER DEFAULT 0;") } ``` -------------------------------- ### Register HomeAccessMenu in MenuFactory Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-10-rank-and-home-perms-impl.md This function in `MenuFactory.kt` is responsible for creating and returning an instance of the `HomeAccessMenu`. It takes necessary parameters like `menuNavigator`, `player`, `guild`, and `homeName` to initialize the menu. ```kotlin fun createHomeAccessMenu( menuNavigator: MenuNavigator, player: Player, guild: net.lumalyte.lg.domain.entities.Guild, homeName: String ): Menu = net.lumalyte.lg.interaction.menus.guild.HomeAccessMenu(menuNavigator, player, guild, homeName) ``` -------------------------------- ### Update Guild Homes Table Schema Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-10-rank-and-home-perms-impl.md Modify the `CREATE TABLE IF NOT EXISTS guild_homes` statement to include the `allowed_ranks` column. This is for fresh installations to ensure the schema supports rank permissions. ```sql CREATE TABLE IF NOT EXISTS guild_homes ( guild_id TEXT NOT NULL, name TEXT NOT NULL, world_id TEXT NOT NULL, x INTEGER NOT NULL, y INTEGER NOT NULL, z INTEGER NOT NULL, allowed_ranks TEXT, PRIMARY KEY (guild_id, name), FOREIGN KEY (guild_id) REFERENCES guilds(id) ON DELETE CASCADE ); ``` -------------------------------- ### LumaGuilds Help Topic Menu Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-design.md This is the initial output when a player types `/g help`. It displays a categorized menu of available help topics. ```text ─── LumaGuilds Help ─── Pick a topic: [Guilds] Create, join, leave, disband [Homes] Set and visit guild homes [Ranks] Permissions and rank management [Chat] /g chat, ally chat, tags [Alliances] Ally, truce, enemy, neutral [War] Declaring and fighting wars [Progression] XP, levels, perks [Vault] Guild storage [Identity] Tags, banners, descriptions [Mode] Peaceful / Hostile [Other] LFG, info, history, leaderboards Type /g help or click a topic above. Full wiki: https://badgersmc.github.io/LumaGuilds/ ``` -------------------------------- ### Perform Full Build Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-10-rank-and-home-perms-impl.md Builds the entire project, including compiling code and packaging artifacts, using Gradle. Verifies that a JAR file is produced. ```bash ./gradlew build ``` -------------------------------- ### Territory Control Integration: Restrict Combat (Kotlin) Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/integration.md Example of integrating with a war plugin to restrict combat in non-PvP claims. This listener checks claim flags before allowing damage events. ```kotlin /** * Example: Integrating with a war plugin * Restrict combat in non-PvP claims */ class CombatIntegration : Listener { private val api = LumaGuildsPlugin.api @EventHandler fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) { val attacker = (event.damager as? Player) ?: return val victim = (event.entity as? Player) ?: return val location = victim.location val position = Position3D(location.blockX, location.blockY, location.blockZ) // Check if in claim val claim = api.getClaimAt(position, victim.world.uid) ?: return // Check if PvP flag enabled if (!doesClaimHaveFlag.execute(claim.id, ClaimFlag.PVP)) { event.isCancelled = true attacker.sendMessage("§cPvP is disabled in this claim!") } } } ``` -------------------------------- ### Run Wiki Linting and Parity Checks Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Execute local checks for wiki frontmatter and topic parity before building the documentation. ```bash python tools/wiki/lint_frontmatter.py python tools/wiki/check_topic_parity.py mkdocs build --strict ./gradlew test ``` -------------------------------- ### Top-N Guild Leaderboard Examples Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/placeholders.md Illustrates how to use the Top-N leaderboard placeholders to retrieve specific guild information based on various ranking categories and ranks. These are cached for 30 seconds. ```text %lumaguilds_top_balance_1_name% → "DiamondDelvers" %lumaguilds_top_balance_1_value% → "152340" %lumaguilds_top_level_3_tag_plain% → "Wolves" %lumaguilds_top_activity_2_members% → "12" %lumaguilds_top_age_1_age_days% → "287" ``` -------------------------------- ### LumaGuilds Placeholder Expansion for PlaceholderAPI Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/infrastructure.md Implements PlaceholderAPI expansion to provide custom placeholders for guild information like claim count, guild name, and guild level. Ensure PlaceholderAPI is installed for this to function. ```kotlin package net.lumalyte.lg.infrastructure.placeholders class LumaGuildsPlaceholderExpansion( private val getPlayerClaims: GetPlayerClaims, private val getPlayerGuild: GetPlayerGuild ) : PlaceholderExpansion() { override fun getIdentifier() = "lumaguilds" override fun getAuthor() = "Lumalyte" override fun getVersion() = "1.0.0" override fun onPlaceholderRequest(player: Player?, params: String): String? { if (player == null) return null return when (params) { "claim_count" -> { val claims = getPlayerClaims.execute(player.uniqueId) claims.size.toString() } "guild_name" -> { val guild = getPlayerGuild.execute(player.uniqueId) guild?.name ?: "None" } "guild_level" -> { val guild = getPlayerGuild.execute(player.uniqueId) guild?.level?.toString() ?: "0" } else -> null } } } ``` ```yaml # In a chat plugin config format: "%lumaguilds_guild_name% %player_name%: %message%" # In a scoreboard plugin lines: - "Claims: %lumaguilds_claim_count%" - "Guild: %lumaguilds_guild_name%" ``` -------------------------------- ### Build Project with Strict Checks Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md Run the `mkdocs build --strict` command to build the project and ensure there are no warnings or errors. ```bash mkdocs build --strict ``` -------------------------------- ### Infrastructure: Using Adapters for External APIs (Good Practice) Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/infrastructure.md Demonstrates wrapping an external API (Bukkit API) within an adapter class. This practice isolates dependencies and makes the application layer independent of specific external implementations. ```kotlin // Good: Bukkit API wrapped in adapter class BukkitLocationAdapter { fun toLocation(position: Position3D, worldId: UUID): Location? { // Adapter code... } } ``` -------------------------------- ### Infrastructure: Business Logic in Repository (Bad Practice) Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/infrastructure.md Example of a repository implementation that incorrectly includes business logic checks. This violates the principle of separating concerns and keeping business logic out of the infrastructure layer. ```kotlin // Bad: Business logic in infrastructure class ClaimRepositorySQLite : ClaimRepository { override fun add(claim: Claim): Boolean { // Checking business rules in infrastructure! if (claim.name.length > 50) throw Exception() storage.connection.executeUpdate(/* ... */) } } ``` -------------------------------- ### Adventure Component API Usage for Help Commands Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-design.md Illustrates how Adventure's Component API is used to create interactive help commands. Commands can be pre-filled, run directly, or have hover events for more details. ```kotlin Built with Adventure `Component` API: each command line is a `Component` with `ClickEvent.suggestCommand` (prefill) or `ClickEvent.runCommand`, plus `HoverEvent.showText` for the per-command blurb. Works in Java client, Geyser/Bedrock (Geyser maps click events to taps), and console (clicks degrade to plain text — commands still readable). ``` -------------------------------- ### Create Welcome Page Content Source: https://github.com/badgersmc/lumaguilds/blob/main/docs/plans/2026-05-13-player-wiki-and-help-redesign-plan.md This Markdown content defines the 'Welcome to EnthusiaSMP' page. It includes front-matter with metadata and the main body content for new players, outlining server features and wiki navigation. Ensure the front-matter is preserved and the body content is replaced. ```markdown --- title: Welcome to EnthusiaSMP audience: player topic: welcome summary: Orientation for new players — what EnthusiaSMP is, what LumaGuilds adds, and where to go next. keywords: [welcome, new player, onboarding, enthusiasmp] related: [walkthrough, faq] updated: 2026-05-13 --- # Welcome to Enthusia SMP This page orients you to the server and the guild system in about two minutes. When you're ready for the hands-on tour, head to **[Your first 30 minutes](walkthrough.md)**. ## What is EnthusiaSMP? ## What does LumaGuilds add? LumaGuilds is the guild plugin. It lets you: - Form a guild with friends and share a tag, banner, and identity. - Set named guild homes that any member (or specific ranks) can teleport to. - Form alliances, declare wars, sign truces. - Earn guild XP together as you play, unlocking perks and bigger member caps. - Run a shared vault. ## What this wiki is for - **[Getting Started → Your first 30 minutes](walkthrough.md)** — guided tour. - **[Players](../players/how-do-i.md)** — every feature explained, organized by topic. - **[Admins](../admins/installation.md)** and **[Developers](../developers/architecture.md)** — if you run a server or work on the plugin. In-game, type `/g help` for the same content as a clickable menu. ## What this wiki is *not* - Not a list of rules. Server rules live elsewhere (ask staff). - Not a release log. See [`CHANGELOG.md`](https://github.com/BadgersMC/LumaGuilds/blob/main/CHANGELOG.md) on GitHub. Ready? **[Start the 30-minute walkthrough →](walkthrough.md)** ```