feat: セクターの設置

This commit is contained in:
Keisuke Hirata 2025-12-09 18:50:24 +09:00
parent dfadce09dc
commit 6ba7d79175
18 changed files with 540 additions and 4 deletions

3
.gitmodules vendored
View File

@ -1,9 +1,12 @@
[submodule "hcu-core"] [submodule "hcu-core"]
path = hcu-core path = hcu-core
url = git@gitea.hareworks.net:hcu/hcu-core.git url = git@gitea.hareworks.net:hcu/hcu-core.git
branch = master
[submodule "Faction"] [submodule "Faction"]
path = Faction path = Faction
url = git@gitea.hareworks.net:hcu/Faction.git url = git@gitea.hareworks.net:hcu/Faction.git
branch = master
[submodule "Lands"] [submodule "Lands"]
path = Lands path = Lands
url = git@gitea.hareworks.net:hcu/Lands.git url = git@gitea.hareworks.net:hcu/Lands.git
branch = master

@ -1 +1 @@
Subproject commit ac7216788c98dcdd35392e4c9aeb8e04b0b82cb1 Subproject commit 661c5173a2099c89c40fcdc031452e73170b8402

2
Lands

@ -1 +1 @@
Subproject commit 1c809b3def8cd9acacbdd2b666f99bf89c728e64 Subproject commit b4c17de746ef73fb192299c84f9202115548d4dc

View File

@ -0,0 +1,68 @@
package net.hareworks.hcu.landsector
import net.hareworks.hcu.core.Main
import net.hareworks.hcu.core.player.PlayerIdService
import net.hareworks.hcu.landsector.command.LandSectorCommand
import net.hareworks.hcu.landsector.listener.SectorListener
import net.hareworks.hcu.landsector.service.SectorService
import org.bukkit.plugin.java.JavaPlugin
class LandSectorPlugin : JavaPlugin() {
companion object {
lateinit var instance: LandSectorPlugin
private set
}
var sectorService: SectorService? = null
override fun onEnable() {
instance = this
// Register commands
LandSectorCommand(this).register()
// Defer service init
server.scheduler.runTaskLater(this, Runnable {
initializeServices()
}, 60L)
logger.info("LandSector plugin has been enabled!")
}
private fun initializeServices() {
val db = try {
Main.instance.exposedDatabase()
} catch (e: Exception) {
null
}
if (db == null) {
logger.severe("hcu-core database not ready.")
return
}
val service = SectorService(db)
try {
service.init()
} catch (e: Exception) {
logger.severe("Failed to init DB: ${e.message}")
return
}
this.sectorService = service
val pIdService = server.servicesManager.load(PlayerIdService::class.java)
if (pIdService == null) {
logger.severe("PlayerIdService not found")
return
}
server.pluginManager.registerEvents(SectorListener(this, service, pIdService), this)
logger.info("LandSector initialized with services.")
}
override fun onDisable() {
logger.info("LandSector plugin has been disabled!")
}
}

View File

@ -0,0 +1,38 @@
package net.hareworks.hcu.landsector.command
import net.hareworks.hcu.landsector.LandSectorPlugin
import net.hareworks.kommand_lib.kommand
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
class LandSectorCommand(private val plugin: LandSectorPlugin) {
fun register() {
kommand(plugin) {
command("landsector") {
literal("give") {
executes {
val player = sender as? Player ?: return@executes
val item = ItemStack(Material.BEDROCK)
val meta = item.itemMeta
meta.displayName(Component.text("Sector Core", NamedTextColor.LIGHT_PURPLE))
meta.persistentDataContainer.set(
NamespacedKey(plugin, "component"),
PersistentDataType.STRING,
"sector_core"
)
item.itemMeta = meta
player.inventory.addItem(item)
sender.sendMessage(Component.text("Gave 1 Sector Core.", NamedTextColor.GREEN))
}
}
}
}
}
}

View File

@ -0,0 +1,14 @@
package net.hareworks.hcu.landsector.database
import org.jetbrains.exposed.v1.core.Table
object SectorsTable : Table("land_sectors") {
val id = integer("id").autoIncrement()
val ownerActorId = integer("owner_actor_id")
val world = varchar("world", 64)
val x = integer("x")
val y = integer("y")
val z = integer("z")
override val primaryKey = PrimaryKey(id)
}

View File

@ -0,0 +1,80 @@
package net.hareworks.hcu.landsector.listener
import net.hareworks.hcu.landsector.LandSectorPlugin
import net.hareworks.hcu.landsector.service.SectorService
import net.hareworks.hcu.core.player.PlayerIdService
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.entity.EntityType
import org.bukkit.entity.Shulker
import org.bukkit.attribute.Attribute
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.block.BlockPlaceEvent
import org.bukkit.persistence.PersistentDataType
class SectorListener(
private val plugin: LandSectorPlugin,
private val sectorService: SectorService,
private val playerIdService: PlayerIdService
) : Listener {
@EventHandler
fun onPlace(event: BlockPlaceEvent) {
val item = event.itemInHand
val meta = item.itemMeta ?: return
val key = NamespacedKey(plugin, "component")
if (meta.persistentDataContainer.get(key, PersistentDataType.STRING) != "sector_core") {
return
}
val player = event.player
val playerEntry = playerIdService.find(player.uniqueId)
if (playerEntry == null) {
player.sendMessage(Component.text("Identity not found.", NamedTextColor.RED))
event.isCancelled = true
return
}
val block = event.blockPlaced
val loc = block.location
// Space check
val above1 = block.getRelative(0, 1, 0)
val above2 = block.getRelative(0, 2, 0)
if (!above1.type.isAir || !above2.type.isAir) {
player.sendMessage(Component.text("Not enough space for Sector Core.", NamedTextColor.RED))
event.isCancelled = true
return
}
// Spawn Shulker
val shulkerLoc = loc.clone().add(0.5, 1.0, 0.5)
val shulker = player.world.spawnEntity(shulkerLoc, EntityType.SHULKER) as Shulker
shulker.setAI(false)
shulker.isInvulnerable = true
shulker.isInvisible = true
val param = shulker.getAttribute(Attribute.MAX_HEALTH)
param?.baseValue = 1000.0
shulker.health = 1000.0
// Place top bedrock
above2.type = Material.BEDROCK
// Record to DB (using Shulker pos as center)
sectorService.createSector(
playerEntry.actorId,
player.world.name,
above1.x,
above1.y,
above1.z
)
player.sendMessage(Component.text("Sector Core placed!", NamedTextColor.GREEN))
}
}

View File

@ -0,0 +1,10 @@
package net.hareworks.hcu.landsector.model
data class Sector(
val id: Int,
val ownerActorId: Int,
val world: String,
val x: Int,
val y: Int,
val z: Int
)

View File

@ -0,0 +1,54 @@
package net.hareworks.hcu.landsector.service
import net.hareworks.hcu.landsector.database.SectorsTable
import net.hareworks.hcu.landsector.model.Sector
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.selectAll
import org.jetbrains.exposed.v1.jdbc.andWhere
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
class SectorService(private val database: Database) {
fun init() {
transaction(database) {
SchemaUtils.createMissingTablesAndColumns(SectorsTable)
}
}
fun createSector(ownerActorId: Int, world: String, x: Int, y: Int, z: Int): Sector? {
return transaction(database) {
val id = SectorsTable.insert {
it[SectorsTable.ownerActorId] = ownerActorId
it[SectorsTable.world] = world
it[SectorsTable.x] = x
it[SectorsTable.y] = y
it[SectorsTable.z] = z
}[SectorsTable.id]
Sector(id, ownerActorId, world, x, y, z)
}
}
fun getSectorAt(world: String, x: Int, y: Int, z: Int): Sector? {
return transaction(database) {
SectorsTable.selectAll()
.andWhere { SectorsTable.world eq world }
.andWhere { SectorsTable.x eq x }
.andWhere { SectorsTable.y eq y }
.andWhere { SectorsTable.z eq z }
.map {
Sector(
it[SectorsTable.id],
it[SectorsTable.ownerActorId],
it[SectorsTable.world],
it[SectorsTable.x],
it[SectorsTable.y],
it[SectorsTable.z]
)
}.singleOrNull()
}
}
}

21
bin/main/paper-plugin.yml Normal file
View File

@ -0,0 +1,21 @@
api-version: 1.21.10
name: landsector
version: 1.0-SNAPSHOT
main: net.hareworks.hcu.landsector.LandSectorPlugin
description: Land sector management plugin for HCU server
authors:
- Hare-K02
dependencies:
server:
faction:
load: BEFORE
required: true
join-classpath: true
hcu-core:
load: BEFORE
required: true
join-classpath: true
lands:
load: BEFORE
required: true
join-classpath: true

View File

@ -49,6 +49,12 @@ tasks {
shadowJar { shadowJar {
archiveClassifier.set("min") archiveClassifier.set("min")
minimize() minimize()
dependencies {
exclude(dependency("org.jetbrains.kotlin:kotlin-stdlib"))
exclude(dependency("org.jetbrains.kotlin:kotlin-reflect"))
exclude(dependency("org.jetbrains.kotlin:kotlin-stdlib-jdk8"))
exclude(dependency("org.jetbrains.kotlin:kotlin-stdlib-jdk7"))
}
} }
} }

@ -1 +1 @@
Subproject commit 520a378cd5c3da6321305d07cc0b402b73292add Subproject commit 6931466f979e8e48141afb4297cb27aac3e2da88

View File

@ -1,5 +1,10 @@
package net.hareworks.hcu.landsector package net.hareworks.hcu.landsector
import net.hareworks.hcu.core.Main
import net.hareworks.hcu.core.player.PlayerIdService
import net.hareworks.hcu.landsector.command.LandSectorCommand
import net.hareworks.hcu.landsector.listener.SectorListener
import net.hareworks.hcu.landsector.service.SectorService
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
class LandSectorPlugin : JavaPlugin() { class LandSectorPlugin : JavaPlugin() {
@ -9,11 +14,52 @@ class LandSectorPlugin : JavaPlugin() {
private set private set
} }
var sectorService: SectorService? = null
override fun onEnable() { override fun onEnable() {
instance = this instance = this
// Register commands
LandSectorCommand(this).register()
// Defer service init
server.scheduler.runTaskLater(this, Runnable {
initializeServices()
}, 60L)
logger.info("LandSector plugin has been enabled!") logger.info("LandSector plugin has been enabled!")
logger.info("Dependencies: Faction, Lands") }
private fun initializeServices() {
val db = try {
Main.instance.exposedDatabase()
} catch (e: Exception) {
null
}
if (db == null) {
logger.severe("hcu-core database not ready.")
return
}
val service = SectorService(db)
try {
service.init()
} catch (e: Exception) {
logger.severe("Failed to init DB: ${e.message}")
return
}
this.sectorService = service
val pIdService = server.servicesManager.load(PlayerIdService::class.java)
if (pIdService == null) {
logger.severe("PlayerIdService not found")
return
}
server.pluginManager.registerEvents(SectorListener(this, service, pIdService), this)
logger.info("LandSector initialized with services.")
} }
override fun onDisable() { override fun onDisable() {

View File

@ -0,0 +1,38 @@
package net.hareworks.hcu.landsector.command
import net.hareworks.hcu.landsector.LandSectorPlugin
import net.hareworks.kommand_lib.kommand
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
class LandSectorCommand(private val plugin: LandSectorPlugin) {
fun register() {
kommand(plugin) {
command("landsector") {
literal("give") {
executes {
val player = sender as? Player ?: return@executes
val item = ItemStack(Material.BEDROCK)
val meta = item.itemMeta
meta.displayName(Component.text("Sector Core", NamedTextColor.LIGHT_PURPLE))
meta.persistentDataContainer.set(
NamespacedKey(plugin, "component"),
PersistentDataType.STRING,
"sector_core"
)
item.itemMeta = meta
player.inventory.addItem(item)
sender.sendMessage(Component.text("Gave 1 Sector Core.", NamedTextColor.GREEN))
}
}
}
}
}
}

View File

@ -0,0 +1,14 @@
package net.hareworks.hcu.landsector.database
import org.jetbrains.exposed.v1.core.Table
object SectorsTable : Table("land_sectors") {
val id = integer("id").autoIncrement()
val ownerActorId = integer("owner_actor_id")
val world = varchar("world", 64)
val x = integer("x")
val y = integer("y")
val z = integer("z")
override val primaryKey = PrimaryKey(id)
}

View File

@ -0,0 +1,80 @@
package net.hareworks.hcu.landsector.listener
import net.hareworks.hcu.landsector.LandSectorPlugin
import net.hareworks.hcu.landsector.service.SectorService
import net.hareworks.hcu.core.player.PlayerIdService
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.entity.EntityType
import org.bukkit.entity.Shulker
import org.bukkit.attribute.Attribute
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.block.BlockPlaceEvent
import org.bukkit.persistence.PersistentDataType
class SectorListener(
private val plugin: LandSectorPlugin,
private val sectorService: SectorService,
private val playerIdService: PlayerIdService
) : Listener {
@EventHandler
fun onPlace(event: BlockPlaceEvent) {
val item = event.itemInHand
val meta = item.itemMeta ?: return
val key = NamespacedKey(plugin, "component")
if (meta.persistentDataContainer.get(key, PersistentDataType.STRING) != "sector_core") {
return
}
val player = event.player
val playerEntry = playerIdService.find(player.uniqueId)
if (playerEntry == null) {
player.sendMessage(Component.text("Identity not found.", NamedTextColor.RED))
event.isCancelled = true
return
}
val block = event.blockPlaced
val loc = block.location
// Space check
val above1 = block.getRelative(0, 1, 0)
val above2 = block.getRelative(0, 2, 0)
if (!above1.type.isAir || !above2.type.isAir) {
player.sendMessage(Component.text("Not enough space for Sector Core.", NamedTextColor.RED))
event.isCancelled = true
return
}
// Spawn Shulker
val shulkerLoc = loc.clone().add(0.5, 1.0, 0.5)
val shulker = player.world.spawnEntity(shulkerLoc, EntityType.SHULKER) as Shulker
shulker.setAI(false)
shulker.isInvulnerable = true
shulker.isInvisible = true
val param = shulker.getAttribute(Attribute.MAX_HEALTH)
param?.baseValue = 1000.0
shulker.health = 1000.0
// Place top bedrock
above2.type = Material.BEDROCK
// Record to DB (using Shulker pos as center)
sectorService.createSector(
playerEntry.actorId,
player.world.name,
above1.x,
above1.y,
above1.z
)
player.sendMessage(Component.text("Sector Core placed!", NamedTextColor.GREEN))
}
}

View File

@ -0,0 +1,10 @@
package net.hareworks.hcu.landsector.model
data class Sector(
val id: Int,
val ownerActorId: Int,
val world: String,
val x: Int,
val y: Int,
val z: Int
)

View File

@ -0,0 +1,54 @@
package net.hareworks.hcu.landsector.service
import net.hareworks.hcu.landsector.database.SectorsTable
import net.hareworks.hcu.landsector.model.Sector
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.selectAll
import org.jetbrains.exposed.v1.jdbc.andWhere
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
class SectorService(private val database: Database) {
fun init() {
transaction(database) {
SchemaUtils.createMissingTablesAndColumns(SectorsTable)
}
}
fun createSector(ownerActorId: Int, world: String, x: Int, y: Int, z: Int): Sector? {
return transaction(database) {
val id = SectorsTable.insert {
it[SectorsTable.ownerActorId] = ownerActorId
it[SectorsTable.world] = world
it[SectorsTable.x] = x
it[SectorsTable.y] = y
it[SectorsTable.z] = z
}[SectorsTable.id]
Sector(id, ownerActorId, world, x, y, z)
}
}
fun getSectorAt(world: String, x: Int, y: Int, z: Int): Sector? {
return transaction(database) {
SectorsTable.selectAll()
.andWhere { SectorsTable.world eq world }
.andWhere { SectorsTable.x eq x }
.andWhere { SectorsTable.y eq y }
.andWhere { SectorsTable.z eq z }
.map {
Sector(
it[SectorsTable.id],
it[SectorsTable.ownerActorId],
it[SectorsTable.world],
it[SectorsTable.x],
it[SectorsTable.y],
it[SectorsTable.z]
)
}.singleOrNull()
}
}
}