feat: kommand-lib対応
This commit is contained in:
parent
dd69f06346
commit
9933aa7820
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,5 +1,3 @@
|
||||||
# Ignore Gradle project-specific cache directory
|
|
||||||
.gradle
|
.gradle
|
||||||
|
bin
|
||||||
# Ignore Gradle build output directory
|
|
||||||
build
|
build
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6c62d3306e2cc0e0fefe8ec7fb9b64a47caae3cb
|
Subproject commit 3835c9b9e2d488681b39f4cfd827e3e3371a1e1b
|
||||||
|
|
@ -1,22 +1,33 @@
|
||||||
package net.hareworks.npc_mannequin
|
package net.hareworks.npc_mannequin
|
||||||
|
|
||||||
import net.hareworks.kommand_lib.KommandLib
|
import net.hareworks.kommand_lib.KommandLib
|
||||||
|
import java.util.logging.Level
|
||||||
import net.hareworks.npc_mannequin.commands.MannequinCommands
|
import net.hareworks.npc_mannequin.commands.MannequinCommands
|
||||||
import net.hareworks.npc_mannequin.service.MannequinController
|
import net.hareworks.npc_mannequin.service.MannequinController
|
||||||
|
import net.hareworks.npc_mannequin.service.MannequinListener
|
||||||
import net.hareworks.npc_mannequin.service.MannequinRegistry
|
import net.hareworks.npc_mannequin.service.MannequinRegistry
|
||||||
import net.hareworks.npc_mannequin.storage.MannequinStorage
|
import net.hareworks.npc_mannequin.storage.MannequinStorage
|
||||||
|
import net.hareworks.permits_lib.PermitsLib
|
||||||
|
import net.hareworks.permits_lib.bukkit.MutationSession
|
||||||
import org.bukkit.plugin.ServicePriority
|
import org.bukkit.plugin.ServicePriority
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
class Plugin : JavaPlugin() {
|
class Plugin : JavaPlugin() {
|
||||||
private var kommand: KommandLib? = null
|
private var kommand: KommandLib? = null
|
||||||
private lateinit var registry: MannequinRegistry
|
private lateinit var registry: MannequinRegistry
|
||||||
|
private var permissionSession: MutationSession? = null
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
val storage = MannequinStorage(this)
|
val storage = MannequinStorage(this)
|
||||||
val controller = MannequinController(this)
|
val controller = MannequinController(this)
|
||||||
registry = MannequinRegistry(this, storage, controller)
|
registry = MannequinRegistry(this, storage, controller)
|
||||||
kommand = MannequinCommands(this, registry).register()
|
permissionSession = runCatching { PermitsLib.session(this) }
|
||||||
|
.onFailure { logger.log(Level.WARNING, "Failed to acquire permits session", it) }
|
||||||
|
.getOrNull()
|
||||||
|
|
||||||
|
server.pluginManager.registerEvents(MannequinListener(this, registry), this)
|
||||||
|
|
||||||
|
kommand = MannequinCommands.register(this, registry, permissionSession)
|
||||||
server.servicesManager.register(MannequinRegistry::class.java, registry, this, ServicePriority.Normal)
|
server.servicesManager.register(MannequinRegistry::class.java, registry, this, ServicePriority.Normal)
|
||||||
logger.info("Loaded ${registry.all().size} mannequin definitions.")
|
logger.info("Loaded ${registry.all().size} mannequin definitions.")
|
||||||
}
|
}
|
||||||
|
|
@ -24,5 +35,8 @@ class Plugin : JavaPlugin() {
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
server.servicesManager.unregisterAll(this)
|
server.servicesManager.unregisterAll(this)
|
||||||
kommand?.unregister()
|
kommand?.unregister()
|
||||||
|
permissionSession?.clearAll()
|
||||||
|
permissionSession = null
|
||||||
|
kommand = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package net.hareworks.npc_mannequin.commands
|
package net.hareworks.npc_mannequin.commands
|
||||||
|
|
||||||
|
import java.util.Locale
|
||||||
import net.hareworks.kommand_lib.KommandLib
|
import net.hareworks.kommand_lib.KommandLib
|
||||||
import net.hareworks.kommand_lib.context.KommandContext
|
import net.hareworks.kommand_lib.context.KommandContext
|
||||||
import net.hareworks.kommand_lib.kommand
|
import net.hareworks.kommand_lib.kommand
|
||||||
|
|
@ -9,8 +10,10 @@ import net.hareworks.npc_mannequin.mannequin.StoredLocation
|
||||||
import net.hareworks.npc_mannequin.mannequin.StoredProfile
|
import net.hareworks.npc_mannequin.mannequin.StoredProfile
|
||||||
import net.hareworks.npc_mannequin.service.MannequinRegistry
|
import net.hareworks.npc_mannequin.service.MannequinRegistry
|
||||||
import net.hareworks.npc_mannequin.text.TextSerializers
|
import net.hareworks.npc_mannequin.text.TextSerializers
|
||||||
import net.hareworks.permits_lib.PermitsLib
|
import net.hareworks.permits_lib.bukkit.MutationSession
|
||||||
import net.kyori.adventure.text.Component
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.event.ClickEvent
|
||||||
|
import net.kyori.adventure.text.event.HoverEvent
|
||||||
import net.kyori.adventure.text.format.NamedTextColor
|
import net.kyori.adventure.text.format.NamedTextColor
|
||||||
import org.bukkit.entity.Entity
|
import org.bukkit.entity.Entity
|
||||||
import org.bukkit.entity.Mannequin
|
import org.bukkit.entity.Mannequin
|
||||||
|
|
@ -18,25 +21,25 @@ import org.bukkit.entity.Player
|
||||||
import org.bukkit.entity.Pose
|
import org.bukkit.entity.Pose
|
||||||
import org.bukkit.inventory.MainHand
|
import org.bukkit.inventory.MainHand
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class MannequinCommands(
|
object MannequinCommands {
|
||||||
private val plugin: JavaPlugin,
|
fun register(
|
||||||
private val registry: MannequinRegistry
|
plugin: JavaPlugin,
|
||||||
) {
|
registry: MannequinRegistry,
|
||||||
fun register(): KommandLib = kommand(plugin) {
|
permissionSession: MutationSession?
|
||||||
|
): KommandLib = kommand(plugin) {
|
||||||
permissions {
|
permissions {
|
||||||
namespace = "hareworks"
|
namespace = "npc-mannequin"
|
||||||
rootSegment = "command"
|
rootSegment = "command"
|
||||||
defaultDescription { ctx ->
|
defaultDescription { ctx ->
|
||||||
"Allows /${ctx.commandName} ${ctx.path.joinToString(" ")}"
|
"Allows /${ctx.commandName} ${ctx.path.joinToString(" ")}"
|
||||||
}
|
}
|
||||||
session { PermitsLib.session(it) }
|
permissionSession?.let { session(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
command("mannequin", listOf("mnpc", "mnq", "npcmannequin")) {
|
command("mannequin", listOf("mnpc", "mnq", "npcmannequin")) {
|
||||||
description = "Register and manage mannequin NPCs"
|
description = "Register and manage mannequin NPCs"
|
||||||
permission = "hareworks.command.mannequin"
|
permission = "npc-mannequin.command.mannequin"
|
||||||
|
|
||||||
executes { listMannequins(registry) }
|
executes { listMannequins(registry) }
|
||||||
|
|
||||||
|
|
@ -48,24 +51,25 @@ class MannequinCommands(
|
||||||
string("id") {
|
string("id") {
|
||||||
selector("target") {
|
selector("target") {
|
||||||
executes {
|
executes {
|
||||||
val id = argument<String>("id")
|
val id = argument<String>("id")
|
||||||
val entity = argument<List<Entity>>("target").firstOrNull { it is Mannequin } as? Mannequin
|
val entity = argument<List<Entity>>("target").firstOrNull { it is Mannequin } as? Mannequin
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
error("Selector must target at least one mannequin entity.")
|
error("Selector must target at least one mannequin entity.")
|
||||||
return@executes
|
return@executes
|
||||||
}
|
|
||||||
runCatching {
|
|
||||||
registry.register(id, entity, overwrite = false)
|
|
||||||
}.onSuccess {
|
|
||||||
success("Registered mannequin '$id' from entity ${entity.uniqueId}.")
|
|
||||||
}.onFailure {
|
|
||||||
error(it.message ?: "Failed to register mannequin.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
runCatching {
|
||||||
|
registry.register(id, entity, overwrite = false)
|
||||||
|
}.onSuccess {
|
||||||
|
success("Registered mannequin '$id' from entity ${entity.uniqueId}.")
|
||||||
|
}.onFailure {
|
||||||
|
error(it.message ?: "Failed to register mannequin.")
|
||||||
|
}
|
||||||
|
}
|
||||||
literal("--overwrite") {
|
literal("--overwrite") {
|
||||||
executes {
|
executes {
|
||||||
val id = argument<String>("id")
|
val id = argument<String>("id")
|
||||||
val entity = argument<List<Entity>>("target").firstOrNull { it is Mannequin } as? Mannequin
|
val entity =
|
||||||
|
argument<List<Entity>>("target").firstOrNull { it is Mannequin } as? Mannequin
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
error("Selector must target at least one mannequin entity.")
|
error("Selector must target at least one mannequin entity.")
|
||||||
return@executes
|
return@executes
|
||||||
|
|
@ -96,6 +100,7 @@ class MannequinCommands(
|
||||||
|
|
||||||
literal("move") {
|
literal("move") {
|
||||||
string("id") {
|
string("id") {
|
||||||
|
suggests { prefix -> registry.all().map { it.id }.filter { it.startsWith(prefix) } }
|
||||||
executes {
|
executes {
|
||||||
val player = requirePlayer() ?: return@executes
|
val player = requirePlayer() ?: return@executes
|
||||||
val id = argument<String>("id")
|
val id = argument<String>("id")
|
||||||
|
|
@ -112,6 +117,7 @@ class MannequinCommands(
|
||||||
|
|
||||||
literal("apply") {
|
literal("apply") {
|
||||||
string("id") {
|
string("id") {
|
||||||
|
suggests { prefix -> registry.all().map { it.id }.filter { it.startsWith(prefix) } }
|
||||||
executes {
|
executes {
|
||||||
val id = argument<String>("id")
|
val id = argument<String>("id")
|
||||||
runCatching {
|
runCatching {
|
||||||
|
|
@ -128,6 +134,7 @@ class MannequinCommands(
|
||||||
|
|
||||||
literal("remove") {
|
literal("remove") {
|
||||||
string("id") {
|
string("id") {
|
||||||
|
suggests { prefix -> registry.all().map { it.id }.filter { it.startsWith(prefix) } }
|
||||||
executes {
|
executes {
|
||||||
val id = argument<String>("id")
|
val id = argument<String>("id")
|
||||||
runCatching { registry.remove(id, deleteEntity = false) }
|
runCatching { registry.remove(id, deleteEntity = false) }
|
||||||
|
|
@ -147,15 +154,19 @@ class MannequinCommands(
|
||||||
|
|
||||||
literal("set") {
|
literal("set") {
|
||||||
string("id") {
|
string("id") {
|
||||||
|
suggests { prefix -> registry.all().map { it.id }.filter { it.startsWith(prefix) } }
|
||||||
literal("pose") {
|
literal("pose") {
|
||||||
string("pose") {
|
string("pose") {
|
||||||
suggests { prefix -> Pose.entries.map { it.name.lowercase() }.filter { it.startsWith(prefix.lowercase()) } }
|
suggests { prefix ->
|
||||||
|
MannequinSettings.POSES.map { it.name.lowercase() }
|
||||||
|
.filter { it.startsWith(prefix.lowercase()) }
|
||||||
|
}
|
||||||
executes {
|
executes {
|
||||||
val id = argument<String>("id")
|
val id = argument<String>("id")
|
||||||
val poseToken = argument<String>("pose")
|
val poseToken = argument<String>("pose")
|
||||||
val pose = runCatching { Pose.valueOf(poseToken.uppercase()) }.getOrNull()
|
val pose = runCatching { Pose.valueOf(poseToken.uppercase()) }.getOrNull()
|
||||||
if (pose == null) {
|
if (pose == null || pose !in MannequinSettings.POSES) {
|
||||||
error("Unknown pose '$poseToken'.")
|
error("Unknown or invalid pose '$poseToken'.")
|
||||||
return@executes
|
return@executes
|
||||||
}
|
}
|
||||||
registry.updateSettings(id) { it.copy(pose = pose) }
|
registry.updateSettings(id) { it.copy(pose = pose) }
|
||||||
|
|
@ -166,7 +177,10 @@ class MannequinCommands(
|
||||||
|
|
||||||
literal("mainhand") {
|
literal("mainhand") {
|
||||||
string("hand") {
|
string("hand") {
|
||||||
suggests { prefix -> MainHand.entries.map { it.name.lowercase() }.filter { it.startsWith(prefix.lowercase()) } }
|
suggests { prefix ->
|
||||||
|
MainHand.entries.map { it.name.lowercase() }
|
||||||
|
.filter { it.startsWith(prefix.lowercase()) }
|
||||||
|
}
|
||||||
executes {
|
executes {
|
||||||
val id = argument<String>("id")
|
val id = argument<String>("id")
|
||||||
val handToken = argument<String>("hand")
|
val handToken = argument<String>("hand")
|
||||||
|
|
@ -183,7 +197,14 @@ class MannequinCommands(
|
||||||
|
|
||||||
literal("immovable") {
|
literal("immovable") {
|
||||||
string("state") {
|
string("state") {
|
||||||
suggests { prefix -> listOf("true", "false", "on", "off").filter { it.startsWith(prefix.lowercase()) } }
|
suggests { prefix ->
|
||||||
|
listOf(
|
||||||
|
"true",
|
||||||
|
"false",
|
||||||
|
"on",
|
||||||
|
"off"
|
||||||
|
).filter { it.startsWith(prefix.lowercase()) }
|
||||||
|
}
|
||||||
executes {
|
executes {
|
||||||
val id = argument<String>("id")
|
val id = argument<String>("id")
|
||||||
val input = argument<String>("state")
|
val input = argument<String>("state")
|
||||||
|
|
@ -200,21 +221,24 @@ class MannequinCommands(
|
||||||
|
|
||||||
literal("description") {
|
literal("description") {
|
||||||
literal("text") {
|
literal("text") {
|
||||||
executes {
|
greedyString("content") {
|
||||||
val id = argument<String>("id")
|
executes {
|
||||||
val payload = remainingInput(DESCRIPTION_TEXT_OFFSET)
|
val id = argument<String>("id")
|
||||||
if (payload.isNullOrBlank()) {
|
val payload = argument<String>("content")
|
||||||
error("Provide MiniMessage text after the command, e.g. /mannequin set $id description text <text>")
|
val component = runCatching { TextSerializers.miniMessage(payload) }
|
||||||
return@executes
|
.onFailure { error("MiniMessage parse failed: ${it.message}") }
|
||||||
|
.getOrNull()
|
||||||
|
if (component == null) {
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
registry.updateSettings(id) {
|
||||||
|
it.copy(
|
||||||
|
description = component,
|
||||||
|
hideDescription = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
success("Updated description for '$id'.")
|
||||||
}
|
}
|
||||||
val component = runCatching { TextSerializers.miniMessage(payload) }
|
|
||||||
.onFailure { error("MiniMessage parse failed: ${it.message}") }
|
|
||||||
.getOrNull()
|
|
||||||
if (component == null) {
|
|
||||||
return@executes
|
|
||||||
}
|
|
||||||
registry.updateSettings(id) { it.copy(description = component, hideDescription = false) }
|
|
||||||
success("Updated description for '$id'.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
literal("clear") {
|
literal("clear") {
|
||||||
|
|
@ -226,7 +250,14 @@ class MannequinCommands(
|
||||||
}
|
}
|
||||||
literal("hide") {
|
literal("hide") {
|
||||||
string("state") {
|
string("state") {
|
||||||
suggests { prefix -> listOf("true", "false", "on", "off").filter { it.startsWith(prefix.lowercase()) } }
|
suggests { prefix ->
|
||||||
|
listOf(
|
||||||
|
"true",
|
||||||
|
"false",
|
||||||
|
"on",
|
||||||
|
"off"
|
||||||
|
).filter { it.startsWith(prefix.lowercase()) }
|
||||||
|
}
|
||||||
executes {
|
executes {
|
||||||
val id = argument<String>("id")
|
val id = argument<String>("id")
|
||||||
val input = argument<String>("state")
|
val input = argument<String>("state")
|
||||||
|
|
@ -304,6 +335,62 @@ class MannequinCommands(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
literal("respawn-delay") {
|
||||||
|
integer("seconds") {
|
||||||
|
executes {
|
||||||
|
val id = argument<String>("id")
|
||||||
|
val seconds = argument<Int>("seconds")
|
||||||
|
registry.updateSettings(id) { it.copy(respawnDelay = seconds) }
|
||||||
|
if (seconds < 0) {
|
||||||
|
success("Disabled auto-respawn for '$id'.")
|
||||||
|
} else {
|
||||||
|
success("Set auto-respawn delay for '$id' to $seconds seconds.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literal("command") {
|
||||||
|
literal("console") {
|
||||||
|
literal("set") {
|
||||||
|
greedyString("command") {
|
||||||
|
executes {
|
||||||
|
val id = argument<String>("id")
|
||||||
|
val cmd = argument<String>("command")
|
||||||
|
registry.updateSettings(id) { it.copy(serverCommand = cmd) }
|
||||||
|
success("Set console command for '$id'.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
literal("clear") {
|
||||||
|
executes {
|
||||||
|
val id = argument<String>("id")
|
||||||
|
registry.updateSettings(id) { it.copy(serverCommand = null) }
|
||||||
|
success("Cleared console command for '$id'.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
literal("player") {
|
||||||
|
literal("set") {
|
||||||
|
greedyString("command") {
|
||||||
|
executes {
|
||||||
|
val id = argument<String>("id")
|
||||||
|
val cmd = argument<String>("command")
|
||||||
|
registry.updateSettings(id) { it.copy(playerCommand = cmd) }
|
||||||
|
success("Set player command for '$id'.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
literal("clear") {
|
||||||
|
executes {
|
||||||
|
val id = argument<String>("id")
|
||||||
|
registry.updateSettings(id) { it.copy(playerCommand = null) }
|
||||||
|
success("Cleared player command for '$id'.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -311,6 +398,7 @@ class MannequinCommands(
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val DESCRIPTION_TEXT_OFFSET = 4
|
private const val DESCRIPTION_TEXT_OFFSET = 4
|
||||||
|
private const val COMMAND_TEXT_OFFSET = 5
|
||||||
|
|
||||||
private fun KommandContext.listMannequins(registry: MannequinRegistry) {
|
private fun KommandContext.listMannequins(registry: MannequinRegistry) {
|
||||||
val entries = registry.all()
|
val entries = registry.all()
|
||||||
|
|
@ -320,11 +408,28 @@ private fun KommandContext.listMannequins(registry: MannequinRegistry) {
|
||||||
}
|
}
|
||||||
sender.sendMessage(Component.text("Registered mannequins (${entries.size}):", NamedTextColor.GRAY))
|
sender.sendMessage(Component.text("Registered mannequins (${entries.size}):", NamedTextColor.GRAY))
|
||||||
entries.forEach { record ->
|
entries.forEach { record ->
|
||||||
val status = if (registry.locate(record.id) != null) "active" else "offline"
|
val isAlive = registry.locate(record.id) != null
|
||||||
|
val statusText = if (isAlive) "active" else "offline"
|
||||||
|
val statusColor = if (isAlive) NamedTextColor.GREEN else NamedTextColor.RED
|
||||||
|
|
||||||
|
val idComponent = Component.text("- ${record.id}: ", NamedTextColor.WHITE)
|
||||||
|
.clickEvent(ClickEvent.suggestCommand("/mannequin set ${record.id} "))
|
||||||
|
.hoverEvent(HoverEvent.showText(Component.text("Click to configure settings", NamedTextColor.GRAY)))
|
||||||
|
|
||||||
|
val statusBase = Component.text(statusText, statusColor)
|
||||||
|
val statusComponent = if (!isAlive) {
|
||||||
|
statusBase
|
||||||
|
.clickEvent(ClickEvent.runCommand("/mannequin apply ${record.id}"))
|
||||||
|
.hoverEvent(HoverEvent.showText(Component.text("Click to respawn", NamedTextColor.YELLOW)))
|
||||||
|
} else {
|
||||||
|
statusBase
|
||||||
|
.hoverEvent(HoverEvent.showText(Component.text("Mannequin is active", NamedTextColor.GREEN)))
|
||||||
|
}
|
||||||
|
|
||||||
val location = formatLocation(record.location)
|
val location = formatLocation(record.location)
|
||||||
sender.sendMessage(
|
sender.sendMessage(
|
||||||
Component.text("- ${record.id}: ", NamedTextColor.WHITE)
|
idComponent
|
||||||
.append(Component.text(status, if (status == "active") NamedTextColor.GREEN else NamedTextColor.DARK_GRAY))
|
.append(statusComponent)
|
||||||
.append(Component.text(" @ $location", NamedTextColor.GRAY))
|
.append(Component.text(" @ $location", NamedTextColor.GRAY))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -345,11 +450,6 @@ private fun KommandContext.error(message: String) {
|
||||||
sender.sendMessage(Component.text(message, NamedTextColor.RED))
|
sender.sendMessage(Component.text(message, NamedTextColor.RED))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KommandContext.remainingInput(offset: Int): String? {
|
|
||||||
if (args.size <= offset) return null
|
|
||||||
return args.drop(offset).joinToString(" ").trim().ifEmpty { null }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun layerSuggestions(prefix: String): List<String> =
|
private fun layerSuggestions(prefix: String): List<String> =
|
||||||
MannequinHiddenLayer.entries.map { it.key }.filter { it.startsWith(prefix.lowercase()) }
|
MannequinHiddenLayer.entries.map { it.key }.filter { it.startsWith(prefix.lowercase()) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,22 @@ data class MannequinSettings(
|
||||||
val description: Component? = null,
|
val description: Component? = null,
|
||||||
val hideDescription: Boolean = false,
|
val hideDescription: Boolean = false,
|
||||||
val hiddenLayers: Set<MannequinHiddenLayer> = emptySet(),
|
val hiddenLayers: Set<MannequinHiddenLayer> = emptySet(),
|
||||||
val profile: StoredProfile? = null
|
val profile: StoredProfile? = null,
|
||||||
|
val respawnDelay: Int = 0,
|
||||||
|
val serverCommand: String? = null,
|
||||||
|
val playerCommand: String? = null
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
val POSES = setOf(
|
||||||
|
Pose.STANDING, // Typical upright standing pose
|
||||||
|
Pose.SLEEPING, // Laying horizontal, as if in a bed
|
||||||
|
Pose.SNEAKING, // Crouched/sneaking posture
|
||||||
|
Pose.SWIMMING, // Horizontal swimming animation
|
||||||
|
Pose.SPIN_ATTACK, // Riptide trident spin animation
|
||||||
|
Pose.LONG_JUMPING, // Goat long jump animation (arms raised)
|
||||||
|
Pose.FALL_FLYING // Elytra gliding pose
|
||||||
|
)
|
||||||
|
|
||||||
fun from(entity: Mannequin): MannequinSettings {
|
fun from(entity: Mannequin): MannequinSettings {
|
||||||
val skinParts = entity.skinParts
|
val skinParts = entity.skinParts
|
||||||
return MannequinSettings(
|
return MannequinSettings(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package net.hareworks.npc_mannequin.service
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent
|
||||||
|
import org.bukkit.entity.Mannequin
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
class MannequinListener(
|
||||||
|
private val plugin: JavaPlugin,
|
||||||
|
private val registry: MannequinRegistry
|
||||||
|
) : Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onEntityRemove(event: EntityRemoveFromWorldEvent) {
|
||||||
|
val entity = event.entity
|
||||||
|
if (entity !is Mannequin) return
|
||||||
|
|
||||||
|
// If the entity is being removed because the chunk is unloading, we don't want to respawn it.
|
||||||
|
// Usually, isDead is false in that case.
|
||||||
|
// We only care if the entity is actually destroyed/killed.
|
||||||
|
if (!entity.isDead) return
|
||||||
|
|
||||||
|
val record = registry.all().firstOrNull { it.entityId == entity.uniqueId } ?: return
|
||||||
|
|
||||||
|
val delaySeconds = record.settings.respawnDelay
|
||||||
|
if (delaySeconds < 0) return
|
||||||
|
|
||||||
|
val delayTicks = (delaySeconds * 20).toLong()
|
||||||
|
|
||||||
|
plugin.server.scheduler.runTaskLater(plugin, Runnable {
|
||||||
|
// Verify record still exists (wasn't removed from registry while waiting)
|
||||||
|
if (registry.find(record.id) == null) return@Runnable
|
||||||
|
registry.apply(record.id, spawnIfMissing = true)
|
||||||
|
}, delayTicks)
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onInteract(event: org.bukkit.event.player.PlayerInteractEntityEvent) {
|
||||||
|
val entity = event.rightClicked
|
||||||
|
if (entity !is Mannequin) return
|
||||||
|
|
||||||
|
val record = registry.all().firstOrNull { it.entityId == entity.uniqueId } ?: return
|
||||||
|
val settings = record.settings
|
||||||
|
val player = event.player
|
||||||
|
|
||||||
|
settings.serverCommand?.let { cmd ->
|
||||||
|
val cleanCmd = if (cmd.startsWith("/")) cmd.substring(1) else cmd
|
||||||
|
val finalCmd = cleanCmd.replace("<player>", player.name)
|
||||||
|
plugin.server.dispatchCommand(plugin.server.consoleSender, finalCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.playerCommand?.let { cmd ->
|
||||||
|
val cleanCmd = if (cmd.startsWith("/")) cmd.substring(1) else cmd
|
||||||
|
val finalCmd = cleanCmd.replace("<player>", player.name)
|
||||||
|
player.performCommand(finalCmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -106,11 +106,11 @@ class MannequinRegistry(
|
||||||
|
|
||||||
fun remove(id: String, deleteEntity: Boolean) {
|
fun remove(id: String, deleteEntity: Boolean) {
|
||||||
val record = require(id)
|
val record = require(id)
|
||||||
|
records.remove(id)
|
||||||
|
persist()
|
||||||
if (deleteEntity) {
|
if (deleteEntity) {
|
||||||
controller.locate(record)?.remove()
|
controller.locate(record)?.remove()
|
||||||
}
|
}
|
||||||
records.remove(id)
|
|
||||||
persist()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun locate(id: String): Mannequin? = controller.locate(require(id))
|
fun locate(id: String): Mannequin? = controller.locate(require(id))
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,9 @@ class MannequinStorage(private val plugin: JavaPlugin) {
|
||||||
description = description,
|
description = description,
|
||||||
hideDescription = hideDescription,
|
hideDescription = hideDescription,
|
||||||
hiddenLayers = hiddenLayers,
|
hiddenLayers = hiddenLayers,
|
||||||
profile = profile
|
profile = profile,
|
||||||
|
serverCommand = section.getString("serverCommand"),
|
||||||
|
playerCommand = section.getString("playerCommand")
|
||||||
)
|
)
|
||||||
return MannequinRecord(id, settings, location, entityId)
|
return MannequinRecord(id, settings, location, entityId)
|
||||||
}
|
}
|
||||||
|
|
@ -96,6 +98,8 @@ class MannequinStorage(private val plugin: JavaPlugin) {
|
||||||
section.set("hideDescription", record.settings.hideDescription)
|
section.set("hideDescription", record.settings.hideDescription)
|
||||||
section.set("description", TextSerializers.miniMessage(record.settings.description))
|
section.set("description", TextSerializers.miniMessage(record.settings.description))
|
||||||
section.set("hiddenLayers", record.settings.hiddenLayers.map { it.key })
|
section.set("hiddenLayers", record.settings.hiddenLayers.map { it.key })
|
||||||
|
section.set("serverCommand", record.settings.serverCommand)
|
||||||
|
section.set("playerCommand", record.settings.playerCommand)
|
||||||
record.settings.profile?.let { profile ->
|
record.settings.profile?.let { profile ->
|
||||||
val profileSection = section.createSection("profile")
|
val profileSection = section.createSection("profile")
|
||||||
profileSection.set("name", profile.name)
|
profileSection.set("name", profile.name)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user