feat: Service

This commit is contained in:
Keisuke Hirata 2025-12-09 07:24:00 +09:00
parent 8fcacf37ff
commit 0ed06e789f
5 changed files with 239 additions and 0 deletions

View File

@ -55,10 +55,20 @@ class App : JavaPlugin() {
} }
this.playerIdService = pIdService 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) // Run visualizer every 20 ticks (1 second)
server.scheduler.runTaskTimer(this, VisualizerTask(service), 20L, 10L) server.scheduler.runTaskTimer(this, VisualizerTask(service), 20L, 10L)
logger.info("Lands plugin initialized successfully.") logger.info("Lands plugin initialized successfully.")
logger.info("LandsAPI registered as a Bukkit service.")
} }
override fun onDisable() { override fun onDisable() {

View File

@ -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<Land>
/**
* 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<Land>
/**
* Gets all lands.
*
* @return List of all lands
*/
fun getAllLands(): List<Land>
/**
* 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
}

View File

@ -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<Land> {
return landService.getAllLands().filter { it.world == world }.toList()
}
override fun getLandsByOwner(actorId: Int): List<Land> {
return landService.getAllLands().filter { it.actorId == actorId }.toList()
}
override fun getAllLands(): List<Land> {
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)
}
}

View File

@ -83,6 +83,59 @@ sealed class Shape {
* Total height of the cylinder including center block * Total height of the cylinder including center block
*/ */
fun totalHeight(): Int = 1 + bottomHeight + topHeight 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<Triple<Int, Int, Int>> {
val outline = mutableSetOf<Triple<Int, Int, Int>>()
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
}
} }
} }

View File

@ -142,6 +142,15 @@ class VisualizerTask(private val landService: LandService) : Runnable {
drawLine(player, x, bottomY, z, x, topY, z, edgeColor, 0.5) 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))
}
} }
/** /**