chore: 多依存環境向けにビルド設定を変更

This commit is contained in:
Keisuke Hirata 2025-12-09 10:54:33 +09:00
parent 08a2470167
commit ac7216788c
5 changed files with 181 additions and 48 deletions

View File

@ -31,7 +31,6 @@ dependencies {
compileOnly("org.postgresql:postgresql:42.7.8") compileOnly("org.postgresql:postgresql:42.7.8")
compileOnly("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1") compileOnly("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
compileOnly("org.jetbrains.exposed:exposed-core:$exposedVersion") compileOnly("org.jetbrains.exposed:exposed-core:$exposedVersion")
compileOnly("org.jetbrains.exposed:exposed-dao:$exposedVersion") compileOnly("org.jetbrains.exposed:exposed-dao:$exposedVersion")
compileOnly("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") compileOnly("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")

@ -1 +1 @@
Subproject commit 388d0df943f2b4a0232c1f4f55f1cecb2d372fca Subproject commit 520a378cd5c3da6321305d07cc0b402b73292add

View File

@ -1,6 +1,8 @@
rootProject.name = "faction" rootProject.name = "faction"
if (gradle.parent == null) {
includeBuild("hcu-core") includeBuild("hcu-core")
includeBuild("hcu-core/kommand-lib") includeBuild("hcu-core/kommand-lib")
includeBuild("hcu-core/kommand-lib/permits-lib") includeBuild("hcu-core/kommand-lib/permits-lib")
}

View File

@ -14,6 +14,7 @@ import org.bukkit.plugin.java.JavaPlugin
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor import net.kyori.adventure.text.format.NamedTextColor
import org.bukkit.entity.Player import org.bukkit.entity.Player
import net.hareworks.hcu.faction.service.FactionInfo
class FactionCommand(private val plugin: JavaPlugin, private val service: FactionService) { class FactionCommand(private val plugin: JavaPlugin, private val service: FactionService) {
@ -22,6 +23,40 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
command("faction") { command("faction") {
description = "Manage your faction" description = "Manage your faction"
executes {
val sender = sender
if (sender !is Player) return@executes
val factionId = service.getFactionOfPlayer(sender.uniqueId)
if (factionId == null) {
sender.sendMessage(Component.text("You are not in a faction.", NamedTextColor.RED))
return@executes
}
val factionName = service.getFactionName(factionId)
if (factionName != null) {
displayFactionInfo(sender, factionName, 1)
}
}
literal("info") {
string("name") {
executes {
val sender = sender
val name = argument<String>("name")
displayFactionInfo(sender, name, 1)
}
integer("page") {
executes {
val sender = sender
val name = argument<String>("name")
val page = argument<Int>("page")
displayFactionInfo(sender, name, page)
}
}
}
}
literal("create") { literal("create") {
string("name") { string("name") {
executes { executes {
@ -44,12 +79,13 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
} }
literal("invite") { literal("invite") {
player("player") { string("player") {
executes { executes {
val sender = sender val sender = sender
if (sender !is Player) return@executes if (sender !is Player) return@executes
val target = argument<Player>("player") val targetName = argument<String>("player")
val target = plugin.server.getOfflinePlayer(targetName)
val factionId = service.getFactionOfPlayer(sender.uniqueId) val factionId = service.getFactionOfPlayer(sender.uniqueId)
if (factionId == null) { if (factionId == null) {
@ -97,8 +133,6 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
} }
} }
// Remove join command
literal("accept") { literal("accept") {
executes { executes {
val sender = sender val sender = sender
@ -124,30 +158,20 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
if (sender !is Player) return@executes if (sender !is Player) return@executes
val targetName = argument<String>("target") val targetName = argument<String>("target")
// Check if target is a Faction Name (Accepting Invite)
val factionIdByName = service.getFactionIdByName(targetName) val factionIdByName = service.getFactionIdByName(targetName)
// Check if target is a Player Name (Accepting Join Request - Leader)
val factionIdOfSender = service.getFactionOfPlayer(sender.uniqueId) val factionIdOfSender = service.getFactionOfPlayer(sender.uniqueId)
var processedAsRequest = false
if (factionIdOfSender != null) { if (factionIdOfSender != null) {
val role = service.getRole(factionIdOfSender, sender.uniqueId) val role = service.getRole(factionIdOfSender, sender.uniqueId)
if (role != FactionRole.MEMBER && role != null) { if (role != FactionRole.MEMBER && role != null) {
val targetPlayer = plugin.server.getOfflinePlayer(targetName) val targetPlayer = plugin.server.getOfflinePlayer(targetName)
// Try request accept
// Use a simplified check or just try calling the service.
// Service returns error if no request found.
// But if we have ambiguity (Faction name == Player name), what to do?
// We prioritize Request if Sender is Leader? Or Invite?
// Let's try Request first if Leader.
service.acceptRequest(factionIdOfSender, targetPlayer.uniqueId) service.acceptRequest(factionIdOfSender, targetPlayer.uniqueId)
.onSuccess { .onSuccess {
sender.sendMessage(Component.text("Accepted join request from $targetName", NamedTextColor.GREEN)) sender.sendMessage(Component.text("Accepted join request from $targetName", NamedTextColor.GREEN))
return@executes return@executes
} }
// If failed (e.g. no request), proceed to check if it's a Faction Invite for ME.
} }
} }
@ -157,7 +181,6 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
sender.sendMessage(Component.text("You joined $targetName", NamedTextColor.GREEN)) sender.sendMessage(Component.text("You joined $targetName", NamedTextColor.GREEN))
} }
.onFailure { err -> .onFailure { err ->
// If we also failed request above, user might be confused.
sender.sendMessage(Component.text("Error: $err (or no request found from player)", NamedTextColor.RED)) sender.sendMessage(Component.text("Error: $err (or no request found from player)", NamedTextColor.RED))
} }
return@executes return@executes
@ -205,8 +228,9 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
} }
} }
} }
literal("promote") { literal("promote") {
player("player") { string("player") {
executes { executes {
val sender = sender val sender = sender
if (sender !is Player) return@executes if (sender !is Player) return@executes
@ -222,7 +246,8 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
return@executes return@executes
} }
val target = argument<Player>("player") val targetName = argument<String>("player")
val target = plugin.server.getOfflinePlayer(targetName)
service.promote(factionId, target.uniqueId) service.promote(factionId, target.uniqueId)
.onSuccess { sender.sendMessage(Component.text("Promoted ${target.name}", NamedTextColor.GREEN)) } .onSuccess { sender.sendMessage(Component.text("Promoted ${target.name}", NamedTextColor.GREEN)) }
@ -232,7 +257,7 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
} }
literal("demote") { literal("demote") {
player("player") { string("player") {
executes { executes {
val sender = sender val sender = sender
if (sender !is Player) return@executes if (sender !is Player) return@executes
@ -248,7 +273,8 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
return@executes return@executes
} }
val target = argument<Player>("player") val targetName = argument<String>("player")
val target = plugin.server.getOfflinePlayer(targetName)
service.demote(factionId, target.uniqueId) service.demote(factionId, target.uniqueId)
.onSuccess { sender.sendMessage(Component.text("Demoted ${target.name}", NamedTextColor.GREEN)) } .onSuccess { sender.sendMessage(Component.text("Demoted ${target.name}", NamedTextColor.GREEN)) }
@ -326,11 +352,12 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
} }
literal("kick") { literal("kick") {
player("player") { string("player") {
executes { executes {
val sender = sender val sender = sender
if (sender !is Player) return@executes if (sender !is Player) return@executes
val target = argument<Player>("player") val targetName = argument<String>("player")
val target = plugin.server.getOfflinePlayer(targetName)
service.kickPlayer(sender.uniqueId, target.uniqueId) service.kickPlayer(sender.uniqueId, target.uniqueId)
.onSuccess { sender.sendMessage(Component.text("Kicked ${target.name}", NamedTextColor.GREEN)) } .onSuccess { sender.sendMessage(Component.text("Kicked ${target.name}", NamedTextColor.GREEN)) }
@ -353,26 +380,102 @@ class FactionCommand(private val plugin: JavaPlugin, private val service: Factio
} }
} }
} }
}
}
}
literal("info") { private fun displayFactionInfo(sender: org.bukkit.command.CommandSender, factionName: String, page: Int) {
string("name") { if (page < 1) {
executes { sender.sendMessage(Component.text("Page must be 1 or higher.", NamedTextColor.RED))
val name = argument<String>("name") return
val info = service.getFactionInfo(name) }
val info = service.getFactionInfo(factionName)
if (info == null) { if (info == null) {
sender.sendMessage(Component.text("Faction not found", NamedTextColor.RED)) sender.sendMessage(Component.text("Faction not found.", NamedTextColor.RED))
} else { return
sender.sendMessage(Component.text("Faction: ${info.name}", NamedTextColor.GOLD))
sender.sendMessage(Component.text("Tag: ${info.tag ?: "None"}", NamedTextColor.WHITE))
sender.sendMessage(Component.text("Members: ${info.memberCount}", NamedTextColor.WHITE))
sender.sendMessage(Component.text("Open: ${info.open}", NamedTextColor.WHITE))
}
}
}
}
}
}
}
} }
sender.sendMessage(Component.text("----- ${info.name} -----", NamedTextColor.GOLD))
sender.sendMessage(Component.text("Tag: ", NamedTextColor.GRAY).append(Component.text(info.tag ?: "None", NamedTextColor.WHITE)))
sender.sendMessage(Component.text("Owner: ", NamedTextColor.GRAY).append(Component.text(info.ownerName ?: "Unknown", NamedTextColor.WHITE)))
sender.sendMessage(Component.text("Members (${info.memberCount}):", NamedTextColor.GRAY))
val pageSize = 10
val members = service.getFactionMembers(info.id, page, pageSize)
if (members.isEmpty() && page > 1) {
sender.sendMessage(Component.text("No members on this page.", NamedTextColor.YELLOW))
}
val viewerId = (sender as? Player)?.uniqueId
val viewerFactionId = if (viewerId != null) service.getFactionOfPlayer(viewerId) else null
val viewerRole = if (viewerId != null && viewerFactionId == info.id) service.getRole(info.id, viewerId) else null
val isSameFaction = viewerFactionId == info.id
members.forEach { (uuid, name, role) ->
val roleColor = when (role) {
FactionRole.OWNER -> NamedTextColor.RED
FactionRole.EXEC -> NamedTextColor.GOLD
FactionRole.MEMBER -> NamedTextColor.GREEN
else -> NamedTextColor.WHITE
}
var line = Component.text("- ", NamedTextColor.DARK_GRAY)
.append(Component.text("[$role] ", roleColor))
.append(Component.text(name, NamedTextColor.WHITE))
if (isSameFaction && viewerRole != null && uuid != viewerId) {
// Add management buttons based on role
if (viewerRole == FactionRole.OWNER) {
// Owner can kick anyone (except self, handled by uuid check), promote member/demote exec, transfer
val kickBtn = Component.text(" [Kick]", NamedTextColor.RED)
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand("/faction kick $name"))
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(Component.text("Kick $name")))
line = line.append(kickBtn)
if (role == FactionRole.MEMBER) {
val promoteBtn = Component.text(" [Promote]", NamedTextColor.AQUA)
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand("/faction promote $name"))
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(Component.text("Promote to Exec")))
line = line.append(promoteBtn)
}
if (role == FactionRole.EXEC) {
val demoteBtn = Component.text(" [Demote]", NamedTextColor.YELLOW)
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand("/faction demote $name"))
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(Component.text("Demote to Member")))
line = line.append(demoteBtn)
}
} else if (viewerRole == FactionRole.EXEC) {
// Exec can kick member
if (role == FactionRole.MEMBER) {
val kickBtn = Component.text(" [Kick]", NamedTextColor.RED)
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand("/faction kick $name"))
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(Component.text("Kick $name")))
line = line.append(kickBtn)
}
}
}
sender.sendMessage(line)
}
// Pagination footer
val totalPages = (info.memberCount + pageSize - 1) / pageSize
if (totalPages > 1) {
val prevPage = if (page > 1) page - 1 else 1
val nextPage = if (page < totalPages) page + 1 else totalPages
val nav = Component.text("Page $page/$totalPages ", NamedTextColor.GRAY)
if (page > 1) {
nav.append(Component.text("[<]", NamedTextColor.AQUA)
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand("/faction info ${info.name} $prevPage")))
}
nav.append(Component.text(" "))
if (page < totalPages) {
nav.append(Component.text("[>]", NamedTextColor.AQUA)
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand("/faction info ${info.name} $nextPage")))
}
sender.sendMessage(nav)
}
}
}

View File

@ -379,6 +379,24 @@ class FactionService {
} }
} }
fun getFactionMembers(factionId: Int, page: Int, pageSize: Int = 10): List<Triple<UUID, String, FactionRole>> {
return DatabaseSessionManager.transaction {
(FactionMembersTable innerJoin PlayersTable)
.selectAll()
.andWhere { FactionMembersTable.factionId eq factionId }
.sortedBy { it[FactionMembersTable.role] } // Sort in-memory to avoid import issues
.drop((page - 1) * pageSize)
.take(pageSize)
.map {
Triple(
it[PlayersTable.uuid],
it[PlayersTable.username],
it[FactionMembersTable.role]
)
}
}
}
fun getFactionInfo(name: String): FactionInfo? { fun getFactionInfo(name: String): FactionInfo? {
return DatabaseSessionManager.transaction { return DatabaseSessionManager.transaction {
val row = FactionsTable.selectAll().andWhere { FactionsTable.name eq name }.singleOrNull() ?: return@transaction null val row = FactionsTable.selectAll().andWhere { FactionsTable.name eq name }.singleOrNull() ?: return@transaction null
@ -389,24 +407,35 @@ class FactionService {
.andWhere { FactionMembersTable.role eq FactionRole.OWNER } .andWhere { FactionMembersTable.role eq FactionRole.OWNER }
.singleOrNull()?.get(FactionMembersTable.playerUuid) .singleOrNull()?.get(FactionMembersTable.playerUuid)
// Get owner name
val ownerName = ownerUuid?.let { uuid ->
PlayersTable.selectAll().andWhere { PlayersTable.uuid eq uuid }.singleOrNull()?.get(PlayersTable.username)
}
FactionInfo( FactionInfo(
id,
row[FactionsTable.name], row[FactionsTable.name],
row[FactionsTable.tag], row[FactionsTable.tag],
row[FactionsTable.color], row[FactionsTable.color],
row[FactionsTable.description],
row[FactionsTable.open], row[FactionsTable.open],
count, count,
ownerUuid ownerUuid,
ownerName
) )
} }
} }
} }
data class FactionInfo( data class FactionInfo(
val id: Int,
val name: String, val name: String,
val tag: String?, val tag: String?,
val color: String?, val color: String?,
val description: String?,
val open: Boolean, val open: Boolean,
val memberCount: Long, val memberCount: Long,
val owner: UUID? val owner: UUID?,
val ownerName: String?
) )