From 0ed06e789f114b13ea3f2d78d7773858ede1fd80 Mon Sep 17 00:00:00 2001 From: Hare Date: Tue, 9 Dec 2025 07:24:00 +0900 Subject: [PATCH] feat: Service --- .../kotlin/net/hareworks/hcu/lands/App.kt | 10 ++ .../net/hareworks/hcu/lands/api/LandsAPI.kt | 104 ++++++++++++++++++ .../hareworks/hcu/lands/api/LandsAPIImpl.kt | 63 +++++++++++ .../net/hareworks/hcu/lands/model/Land.kt | 53 +++++++++ .../hcu/lands/task/VisualizerTask.kt | 9 ++ 5 files changed, 239 insertions(+) create mode 100644 src/main/kotlin/net/hareworks/hcu/lands/api/LandsAPI.kt create mode 100644 src/main/kotlin/net/hareworks/hcu/lands/api/LandsAPIImpl.kt diff --git a/src/main/kotlin/net/hareworks/hcu/lands/App.kt b/src/main/kotlin/net/hareworks/hcu/lands/App.kt index 3ec21fb..e5509ad 100644 --- a/src/main/kotlin/net/hareworks/hcu/lands/App.kt +++ b/src/main/kotlin/net/hareworks/hcu/lands/App.kt @@ -55,10 +55,20 @@ class App : JavaPlugin() { } this.playerIdService = pIdService + // Register public API as a Bukkit service + val landsAPI = net.hareworks.hcu.lands.api.LandsAPIImpl(service) + server.servicesManager.register( + net.hareworks.hcu.lands.api.LandsAPI::class.java, + landsAPI, + this, + org.bukkit.plugin.ServicePriority.Normal + ) + // Run visualizer every 20 ticks (1 second) server.scheduler.runTaskTimer(this, VisualizerTask(service), 20L, 10L) logger.info("Lands plugin initialized successfully.") + logger.info("LandsAPI registered as a Bukkit service.") } override fun onDisable() { diff --git a/src/main/kotlin/net/hareworks/hcu/lands/api/LandsAPI.kt b/src/main/kotlin/net/hareworks/hcu/lands/api/LandsAPI.kt new file mode 100644 index 0000000..db24354 --- /dev/null +++ b/src/main/kotlin/net/hareworks/hcu/lands/api/LandsAPI.kt @@ -0,0 +1,104 @@ +package net.hareworks.hcu.lands.api + +import net.hareworks.hcu.lands.model.Land +import net.hareworks.hcu.lands.model.LandData +import org.bukkit.Location +import org.bukkit.block.Block + +/** + * Public API for the Lands plugin. + * This service can be obtained via Bukkit's ServicesManager. + * + * Example usage: + * ```kotlin + * val landsAPI = Bukkit.getServicesManager().load(LandsAPI::class.java) + * val land = landsAPI?.getLandAt(block) + * ``` + */ +interface LandsAPI { + + /** + * Gets the land that contains the specified block. + * + * @param block The block to check + * @return The land containing the block, or null if no land contains it + */ + fun getLandAt(block: Block): Land? + + /** + * Gets the land that contains the specified location. + * + * @param location The location to check + * @return The land containing the location, or null if no land contains it + */ + fun getLandAt(location: Location): Land? + + /** + * Gets the land that contains the specified coordinates. + * + * @param world The world name + * @param x The x coordinate + * @param y The y coordinate + * @param z The z coordinate + * @return The land containing the coordinates, or null if no land contains it + */ + fun getLandAt(world: String, x: Double, y: Double, z: Double): Land? + + /** + * Gets a land by its name. + * + * @param name The name of the land + * @return The land with the specified name, or null if not found + */ + fun getLand(name: String): Land? + + /** + * Gets all lands in the specified world. + * + * @param world The world name + * @return List of all lands in the world + */ + fun getLandsInWorld(world: String): List + + /** + * Gets all lands owned by the specified actor. + * + * @param actorId The actor ID + * @return List of all lands owned by the actor + */ + fun getLandsByOwner(actorId: Int): List + + /** + * Gets all lands. + * + * @return List of all lands + */ + fun getAllLands(): List + + /** + * Creates a new land. + * + * @param name The name of the land + * @param actorId The owner's actor ID + * @param world The world name + * @return true if the land was created successfully, false if a land with that name already exists + */ + fun createLand(name: String, actorId: Int, world: String): Boolean + + /** + * Deletes a land. + * + * @param name The name of the land to delete + * @return true if the land was deleted successfully, false if the land doesn't exist + */ + fun deleteLand(name: String): Boolean + + /** + * Modifies a land's data. + * + * @param name The name of the land to modify + * @param modifier A function that modifies the land's data + * @return true if the land was modified successfully, false if the land doesn't exist + */ + fun modifyLand(name: String, modifier: (LandData) -> Unit): Boolean +} diff --git a/src/main/kotlin/net/hareworks/hcu/lands/api/LandsAPIImpl.kt b/src/main/kotlin/net/hareworks/hcu/lands/api/LandsAPIImpl.kt new file mode 100644 index 0000000..5286919 --- /dev/null +++ b/src/main/kotlin/net/hareworks/hcu/lands/api/LandsAPIImpl.kt @@ -0,0 +1,63 @@ +package net.hareworks.hcu.lands.api + +import net.hareworks.hcu.lands.model.Land +import net.hareworks.hcu.lands.model.LandData +import net.hareworks.hcu.lands.service.LandService +import org.bukkit.Location +import org.bukkit.block.Block + +/** + * Implementation of the LandsAPI interface. + * This is registered as a Bukkit service and delegates to the internal LandService. + */ +class LandsAPIImpl(private val landService: LandService) : LandsAPI { + + override fun getLandAt(block: Block): Land? { + return getLandAt(block.world.name, block.x + 0.5, block.y + 0.5, block.z + 0.5) + } + + override fun getLandAt(location: Location): Land? { + return getLandAt(location.world.name, location.x, location.y, location.z) + } + + override fun getLandAt(world: String, x: Double, y: Double, z: Double): Land? { + return landService.getAllLands() + .filter { it.world == world } + .firstOrNull { land -> + land.data.parts.any { shape -> + when (shape) { + is net.hareworks.hcu.lands.model.Shape.Cuboid -> shape.contains(x, y, z) + is net.hareworks.hcu.lands.model.Shape.Cylinder -> shape.contains(x, y, z) + } + } + } + } + + override fun getLand(name: String): Land? { + return landService.getLand(name) + } + + override fun getLandsInWorld(world: String): List { + return landService.getAllLands().filter { it.world == world }.toList() + } + + override fun getLandsByOwner(actorId: Int): List { + return landService.getAllLands().filter { it.actorId == actorId }.toList() + } + + override fun getAllLands(): List { + return landService.getAllLands().toList() + } + + override fun createLand(name: String, actorId: Int, world: String): Boolean { + return landService.createLand(name, actorId, world) + } + + override fun deleteLand(name: String): Boolean { + return landService.deleteLand(name) + } + + override fun modifyLand(name: String, modifier: (LandData) -> Unit): Boolean { + return landService.modifyLand(name, modifier) + } +} diff --git a/src/main/kotlin/net/hareworks/hcu/lands/model/Land.kt b/src/main/kotlin/net/hareworks/hcu/lands/model/Land.kt index c78ac44..155890a 100644 --- a/src/main/kotlin/net/hareworks/hcu/lands/model/Land.kt +++ b/src/main/kotlin/net/hareworks/hcu/lands/model/Land.kt @@ -83,6 +83,59 @@ sealed class Shape { * Total height of the cylinder including center block */ fun totalHeight(): Int = 1 + bottomHeight + topHeight + + /** + * Returns a set of blocks that form the outline of the blockified cylinder. + * This includes blocks at the edge of the cylinder's radius at each Y level. + */ + fun getBlockifiedOutline(): Set> { + val outline = mutableSetOf>() + val centerX = x + 0.5 + val centerZ = z + 0.5 + val radiusSquared = radius * radius + + // Calculate the bounding box for the cylinder + val minBlockX = (x - radius - 1).toInt() + val maxBlockX = (x + radius + 1).toInt() + val minBlockZ = (z - radius - 1).toInt() + val maxBlockZ = (z + radius + 1).toInt() + + // For each Y level in the cylinder + val minY = y - bottomHeight + val maxY = y + topHeight + + for (blockY in minY..maxY) { + // Check each block in the XZ plane + for (blockX in minBlockX..maxBlockX) { + for (blockZ in minBlockZ..maxBlockZ) { + // Check if this block is inside the cylinder + val dx = (blockX + 0.5) - centerX + val dz = (blockZ + 0.5) - centerZ + val isInside = (dx * dx + dz * dz) <= radiusSquared + + if (isInside) { + // Check if any adjacent block is outside (making this an edge block) + val hasOutsideNeighbor = listOf( + Pair(blockX - 1, blockZ), + Pair(blockX + 1, blockZ), + Pair(blockX, blockZ - 1), + Pair(blockX, blockZ + 1) + ).any { (nx, nz) -> + val ndx = (nx + 0.5) - centerX + val ndz = (nz + 0.5) - centerZ + (ndx * ndx + ndz * ndz) > radiusSquared + } + + if (hasOutsideNeighbor) { + outline.add(Triple(blockX, blockY, blockZ)) + } + } + } + } + } + + return outline + } } } diff --git a/src/main/kotlin/net/hareworks/hcu/lands/task/VisualizerTask.kt b/src/main/kotlin/net/hareworks/hcu/lands/task/VisualizerTask.kt index ead7ac0..2efb547 100644 --- a/src/main/kotlin/net/hareworks/hcu/lands/task/VisualizerTask.kt +++ b/src/main/kotlin/net/hareworks/hcu/lands/task/VisualizerTask.kt @@ -142,6 +142,15 @@ class VisualizerTask(private val landService: LandService) : Runnable { drawLine(player, x, bottomY, z, x, topY, z, edgeColor, 0.5) } + + // Draw blockified outline + val blockifiedOutline = cylinder.getBlockifiedOutline() + val blockColor = Particle.DustOptions(Color.fromRGB(150, 220, 255), 0.5f) + + for ((bx, by, bz) in blockifiedOutline) { + // Draw a subtle outline for each block in the blockified cylinder + drawBlockOutline(player, bx, by, bz, Color.fromRGB(150, 220, 255)) + } } /**