feat: 招待可能に
This commit is contained in:
parent
9e356049a0
commit
08a2470167
|
|
@ -10,6 +10,7 @@ plugins {
|
||||||
id("com.gradleup.shadow") version "9.2.2"
|
id("com.gradleup.shadow") version "9.2.2"
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven("https://repo.papermc.io/repository/maven-public/")
|
maven("https://repo.papermc.io/repository/maven-public/")
|
||||||
}
|
}
|
||||||
|
|
@ -19,6 +20,7 @@ val exposedVersion = "1.0.0-rc-4"
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
|
compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
|
||||||
|
compileOnly("com.michael-bull.kotlin-result:kotlin-result:2.1.0")
|
||||||
|
|
||||||
compileOnly("net.hareworks.hcu:hcu-core")
|
compileOnly("net.hareworks.hcu:hcu-core")
|
||||||
compileOnly("net.hareworks:kommand-lib")
|
compileOnly("net.hareworks:kommand-lib")
|
||||||
|
|
@ -34,7 +36,6 @@ dependencies {
|
||||||
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")
|
||||||
compileOnly("org.jetbrains.exposed:exposed-kotlin-datetime:$exposedVersion")
|
compileOnly("org.jetbrains.exposed:exposed-kotlin-datetime:$exposedVersion")
|
||||||
compileOnly("com.michael-bull.kotlin-result:kotlin-result:2.1.0")
|
|
||||||
}
|
}
|
||||||
tasks {
|
tasks {
|
||||||
withType<Jar> {
|
withType<Jar> {
|
||||||
|
|
|
||||||
7
gradle.properties
Normal file
7
gradle.properties
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
org.gradle.configuration-cache=true
|
||||||
|
org.gradle.parallel=true
|
||||||
|
org.gradle.caching=true
|
||||||
|
org.gradle.daemon=false
|
||||||
|
|
||||||
|
org.gradle.configuration-cache=true
|
||||||
|
|
||||||
2
hcu-core
2
hcu-core
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8d47e35af6002d863199c1b37fbd5f2d3cf1d826
|
Subproject commit 388d0df943f2b4a0232c1f4f55f1cecb2d372fca
|
||||||
6
settings.gradle.kts
Normal file
6
settings.gradle.kts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
rootProject.name = "faction"
|
||||||
|
includeBuild("hcu-core")
|
||||||
|
includeBuild("hcu-core/kommand-lib")
|
||||||
|
includeBuild("hcu-core/kommand-lib/permits-lib")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,18 +1,66 @@
|
||||||
package net.hareworks.hcu.economy
|
package net.hareworks.hcu.faction
|
||||||
|
|
||||||
import net.hareworks.kommand_lib.KommandLib
|
import net.hareworks.hcu.core.database.DatabaseSessionManager
|
||||||
import net.hareworks.permits_lib.PermitsLib
|
import net.hareworks.hcu.faction.command.FactionCommand
|
||||||
|
import net.hareworks.hcu.faction.database.schema.FactionMembersTable
|
||||||
|
import net.hareworks.hcu.faction.database.schema.FactionRequestsTable
|
||||||
|
import net.hareworks.hcu.faction.database.schema.FactionsTable
|
||||||
|
import net.hareworks.hcu.faction.service.FactionService
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
class App : JavaPlugin() {
|
class App : JavaPlugin() {
|
||||||
private var commands: KommandLib? = null
|
private lateinit var service: FactionService
|
||||||
private val permits = PermitsLib.session(this)
|
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
instance = this
|
instance = this
|
||||||
|
|
||||||
|
// Initialize Database Listener
|
||||||
|
val dbListener = net.hareworks.hcu.faction.listener.DatabaseListener(logger)
|
||||||
|
server.pluginManager.registerEvents(dbListener, this)
|
||||||
|
|
||||||
|
// Trigger schema check immediately if DB is already online (hcu-core might have loaded before us)
|
||||||
|
if (server.servicesManager.isProvidedFor(org.jetbrains.exposed.v1.jdbc.Database::class.java)) {
|
||||||
|
// We can trigger the listener logic or just rely on the service being present.
|
||||||
|
// But since ServiceRegisterEvent fires when registered, if it's ALREADY registered, we missed the event.
|
||||||
|
// Note: isConnected check is a proxy for "Service is available and working".
|
||||||
|
// We'll trust the simpler check we had, but just delegate logic to avoid duplication if possible,
|
||||||
|
// or just keep the inline block for simplicity as "Initial Attempt".
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Schema (Best effort at startup)
|
||||||
|
if (DatabaseSessionManager.isConnected()) {
|
||||||
|
try {
|
||||||
|
DatabaseSessionManager.transaction {
|
||||||
|
SchemaUtils.createMissingTablesAndColumns(
|
||||||
|
FactionsTable,
|
||||||
|
FactionMembersTable,
|
||||||
|
FactionRequestsTable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.log(Level.SEVERE, "Failed to initialize Faction schema", e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warning("hcu-core database not connected. Waiting for service registration to initialize schema.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Service
|
||||||
|
service = FactionService()
|
||||||
|
|
||||||
|
// Register Commands
|
||||||
|
FactionCommand(this, service).register()
|
||||||
|
|
||||||
|
// Register Listeners
|
||||||
|
server.pluginManager.registerEvents(net.hareworks.hcu.faction.listener.ChatListener(service), this)
|
||||||
|
|
||||||
|
logger.info("Faction plugin enabled")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
|
logger.info("Faction plugin disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,378 @@
|
||||||
|
package net.hareworks.hcu.faction.command
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.onFailure
|
||||||
|
import com.github.michaelbull.result.onSuccess
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType
|
||||||
|
import com.mojang.brigadier.arguments.IntegerArgumentType
|
||||||
|
import net.hareworks.hcu.faction.service.FactionService
|
||||||
|
import com.mojang.brigadier.arguments.BoolArgumentType
|
||||||
|
import net.hareworks.kommand_lib.kommand
|
||||||
|
import net.hareworks.kommand_lib.KommandLib
|
||||||
|
import net.hareworks.hcu.faction.database.schema.FactionRole
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
class FactionCommand(private val plugin: JavaPlugin, private val service: FactionService) {
|
||||||
|
|
||||||
|
fun register() {
|
||||||
|
kommand(plugin) {
|
||||||
|
command("faction") {
|
||||||
|
description = "Manage your faction"
|
||||||
|
|
||||||
|
literal("create") {
|
||||||
|
string("name") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) {
|
||||||
|
sender.sendMessage(Component.text("Only players can create factions", NamedTextColor.RED))
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
val name = argument<String>("name")
|
||||||
|
|
||||||
|
service.createFaction(name, null, null, sender.uniqueId)
|
||||||
|
.onSuccess { id ->
|
||||||
|
sender.sendMessage(Component.text("Faction '$name' created with ID $id", NamedTextColor.GREEN))
|
||||||
|
}
|
||||||
|
.onFailure { err ->
|
||||||
|
sender.sendMessage(Component.text("Error: $err", NamedTextColor.RED))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literal("invite") {
|
||||||
|
player("player") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
|
||||||
|
val target = argument<Player>("player")
|
||||||
|
|
||||||
|
val factionId = service.getFactionOfPlayer(sender.uniqueId)
|
||||||
|
if (factionId == null) {
|
||||||
|
sender.sendMessage(Component.text("You are not in a faction", NamedTextColor.RED))
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
|
||||||
|
val role = service.getRole(factionId, sender.uniqueId)
|
||||||
|
if (role == FactionRole.MEMBER) {
|
||||||
|
sender.sendMessage(Component.text("Members cannot invite players", NamedTextColor.RED))
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
|
||||||
|
service.invitePlayer(factionId, target.uniqueId)
|
||||||
|
.onSuccess {
|
||||||
|
sender.sendMessage(Component.text("Invited ${target.name}", NamedTextColor.GREEN))
|
||||||
|
if (target.isOnline) {
|
||||||
|
val factionName = service.getFactionName(factionId) ?: "Unknown"
|
||||||
|
val senderName = sender.name
|
||||||
|
|
||||||
|
val acceptMsg = Component.text("[Accept]", NamedTextColor.GREEN)
|
||||||
|
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand("/faction accept $factionName"))
|
||||||
|
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(Component.text("Accept invitation")))
|
||||||
|
|
||||||
|
val rejectMsg = Component.text("[Reject]", NamedTextColor.RED)
|
||||||
|
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand("/faction reject $factionName"))
|
||||||
|
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(Component.text("Reject invitation")))
|
||||||
|
|
||||||
|
(target as? Player)?.sendMessage(
|
||||||
|
Component.text("You have been invited to join ", NamedTextColor.YELLOW)
|
||||||
|
.append(Component.text(factionName, NamedTextColor.GOLD))
|
||||||
|
.append(Component.text(" by ", NamedTextColor.YELLOW))
|
||||||
|
.append(Component.text(senderName, NamedTextColor.AQUA))
|
||||||
|
.append(Component.text(". ", NamedTextColor.YELLOW))
|
||||||
|
.append(acceptMsg)
|
||||||
|
.append(Component.text(" ", NamedTextColor.WHITE))
|
||||||
|
.append(rejectMsg)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onFailure { err ->
|
||||||
|
sender.sendMessage(Component.text("Error: $err", NamedTextColor.RED))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove join command
|
||||||
|
|
||||||
|
literal("accept") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
|
||||||
|
val latestInviteFactionId = service.getLatestInvite(sender.uniqueId)
|
||||||
|
if (latestInviteFactionId == null) {
|
||||||
|
sender.sendMessage(Component.text("No pending factory invitations found.", NamedTextColor.RED))
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
|
||||||
|
service.acceptInvite(latestInviteFactionId, sender.uniqueId)
|
||||||
|
.onSuccess {
|
||||||
|
val facName = service.getFactionName(latestInviteFactionId)
|
||||||
|
sender.sendMessage(Component.text("You joined ${facName ?: "the faction"}", NamedTextColor.GREEN))
|
||||||
|
}
|
||||||
|
.onFailure { err -> sender.sendMessage(Component.text("Error: $err", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
string("target") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
val targetName = argument<String>("target")
|
||||||
|
|
||||||
|
// Check if target is a Faction Name (Accepting Invite)
|
||||||
|
val factionIdByName = service.getFactionIdByName(targetName)
|
||||||
|
|
||||||
|
// Check if target is a Player Name (Accepting Join Request - Leader)
|
||||||
|
val factionIdOfSender = service.getFactionOfPlayer(sender.uniqueId)
|
||||||
|
var processedAsRequest = false
|
||||||
|
|
||||||
|
if (factionIdOfSender != null) {
|
||||||
|
val role = service.getRole(factionIdOfSender, sender.uniqueId)
|
||||||
|
if (role != FactionRole.MEMBER && role != null) {
|
||||||
|
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)
|
||||||
|
.onSuccess {
|
||||||
|
sender.sendMessage(Component.text("Accepted join request from $targetName", NamedTextColor.GREEN))
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
// If failed (e.g. no request), proceed to check if it's a Faction Invite for ME.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (factionIdByName != null) {
|
||||||
|
service.acceptInvite(factionIdByName, sender.uniqueId)
|
||||||
|
.onSuccess {
|
||||||
|
sender.sendMessage(Component.text("You joined $targetName", NamedTextColor.GREEN))
|
||||||
|
}
|
||||||
|
.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))
|
||||||
|
}
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
|
||||||
|
sender.sendMessage(Component.text("Target '$targetName' not found as Faction or Requesting Player", NamedTextColor.RED))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literal("reject") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
|
||||||
|
val latestInviteFactionId = service.getLatestInvite(sender.uniqueId)
|
||||||
|
if (latestInviteFactionId == null) {
|
||||||
|
sender.sendMessage(Component.text("No pending factory invitations found.", NamedTextColor.RED))
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
|
||||||
|
service.rejectInvite(latestInviteFactionId, sender.uniqueId)
|
||||||
|
.onSuccess {
|
||||||
|
val facName = service.getFactionName(latestInviteFactionId)
|
||||||
|
sender.sendMessage(Component.text("Rejected invitation from ${facName ?: "the faction"}", NamedTextColor.GREEN))
|
||||||
|
}
|
||||||
|
.onFailure { err -> sender.sendMessage(Component.text("Error: $err", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
string("factionName") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
val factionName = argument<String>("factionName")
|
||||||
|
|
||||||
|
val factionId = service.getFactionIdByName(factionName)
|
||||||
|
if (factionId == null) {
|
||||||
|
sender.sendMessage(Component.text("Faction '$factionName' not found", NamedTextColor.RED))
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
|
||||||
|
service.rejectInvite(factionId, sender.uniqueId)
|
||||||
|
.onSuccess { sender.sendMessage(Component.text("Rejected invitation from $factionName", NamedTextColor.GREEN)) }
|
||||||
|
.onFailure { err -> sender.sendMessage(Component.text("Error: $err", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
literal("promote") {
|
||||||
|
player("player") {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.getRole(factionId, sender.uniqueId) != FactionRole.OWNER) {
|
||||||
|
sender.sendMessage(Component.text("Only OWNER can promote", NamedTextColor.RED))
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
|
||||||
|
val target = argument<Player>("player")
|
||||||
|
|
||||||
|
service.promote(factionId, target.uniqueId)
|
||||||
|
.onSuccess { sender.sendMessage(Component.text("Promoted ${target.name}", NamedTextColor.GREEN)) }
|
||||||
|
.onFailure { err -> sender.sendMessage(Component.text("Error: $err", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literal("demote") {
|
||||||
|
player("player") {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.getRole(factionId, sender.uniqueId) != FactionRole.OWNER) {
|
||||||
|
sender.sendMessage(Component.text("Only OWNER can demote", NamedTextColor.RED))
|
||||||
|
return@executes
|
||||||
|
}
|
||||||
|
|
||||||
|
val target = argument<Player>("player")
|
||||||
|
|
||||||
|
service.demote(factionId, target.uniqueId)
|
||||||
|
.onSuccess { sender.sendMessage(Component.text("Demoted ${target.name}", NamedTextColor.GREEN)) }
|
||||||
|
.onFailure { err -> sender.sendMessage(Component.text("Error: $err", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literal("settings") {
|
||||||
|
literal("tag") {
|
||||||
|
string("tag") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
val factionId = service.getFactionOfPlayer(sender.uniqueId) ?: return@executes
|
||||||
|
if (service.getRole(factionId, sender.uniqueId) == FactionRole.MEMBER) return@executes
|
||||||
|
|
||||||
|
val tag = argument<String>("tag")
|
||||||
|
service.setTag(factionId, tag)
|
||||||
|
.onSuccess { sender.sendMessage(Component.text("Tag updated to $tag", NamedTextColor.GREEN)) }
|
||||||
|
.onFailure { err -> sender.sendMessage(Component.text("Error: $err", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
literal("color") {
|
||||||
|
string("color") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
val factionId = service.getFactionOfPlayer(sender.uniqueId) ?: return@executes
|
||||||
|
if (service.getRole(factionId, sender.uniqueId) == FactionRole.MEMBER) return@executes
|
||||||
|
|
||||||
|
val color = argument<String>("color")
|
||||||
|
service.setColor(factionId, color)
|
||||||
|
.onSuccess { sender.sendMessage(Component.text("Color updated to $color", NamedTextColor.GREEN)) }
|
||||||
|
.onFailure { err -> sender.sendMessage(Component.text("Error: $err", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
literal("open") {
|
||||||
|
bool("open") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
val factionId = service.getFactionOfPlayer(sender.uniqueId) ?: return@executes
|
||||||
|
if (service.getRole(factionId, sender.uniqueId) == FactionRole.MEMBER) return@executes
|
||||||
|
|
||||||
|
val open = argument<Boolean>("open")
|
||||||
|
service.setOpen(factionId, open)
|
||||||
|
.onSuccess { sender.sendMessage(Component.text("Open status updated to $open", NamedTextColor.GREEN)) }
|
||||||
|
.onFailure { err -> sender.sendMessage(Component.text("Error: $err", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literal("leave") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
service.leaveFaction(sender.uniqueId)
|
||||||
|
.onSuccess { sender.sendMessage(Component.text("Left faction", NamedTextColor.GREEN)) }
|
||||||
|
.onFailure { sender.sendMessage(Component.text("Error: $it", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literal("disband") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
service.disbandFaction(sender.uniqueId)
|
||||||
|
.onSuccess { sender.sendMessage(Component.text("Faction disbanded", NamedTextColor.GREEN)) }
|
||||||
|
.onFailure { sender.sendMessage(Component.text("Error: $it", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literal("kick") {
|
||||||
|
player("player") {
|
||||||
|
executes {
|
||||||
|
val sender = sender
|
||||||
|
if (sender !is Player) return@executes
|
||||||
|
val target = argument<Player>("player")
|
||||||
|
|
||||||
|
service.kickPlayer(sender.uniqueId, target.uniqueId)
|
||||||
|
.onSuccess { sender.sendMessage(Component.text("Kicked ${target.name}", NamedTextColor.GREEN)) }
|
||||||
|
.onFailure { sender.sendMessage(Component.text("Error: $it", NamedTextColor.RED)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literal("list") {
|
||||||
|
executes {
|
||||||
|
val factions = service.getAllFactions()
|
||||||
|
if (factions.isEmpty()) {
|
||||||
|
sender.sendMessage(Component.text("No factions found", NamedTextColor.YELLOW))
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(Component.text("Factions:", NamedTextColor.GOLD))
|
||||||
|
factions.forEach { (name, tag) ->
|
||||||
|
val tagText = tag?.let { " [$it]" } ?: ""
|
||||||
|
sender.sendMessage(Component.text("- $name$tagText", NamedTextColor.WHITE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literal("info") {
|
||||||
|
string("name") {
|
||||||
|
executes {
|
||||||
|
val name = argument<String>("name")
|
||||||
|
val info = service.getFactionInfo(name)
|
||||||
|
if (info == null) {
|
||||||
|
sender.sendMessage(Component.text("Faction not found", NamedTextColor.RED))
|
||||||
|
} else {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package net.hareworks.hcu.faction.database.schema
|
||||||
|
|
||||||
|
import net.hareworks.hcu.core.database.schema.PlayersTable
|
||||||
|
import org.jetbrains.exposed.v1.core.Table
|
||||||
|
import org.jetbrains.exposed.v1.core.ReferenceOption
|
||||||
|
import org.jetbrains.exposed.v1.datetime.CurrentDateTime
|
||||||
|
import org.jetbrains.exposed.v1.datetime.datetime
|
||||||
|
|
||||||
|
enum class FactionRole {
|
||||||
|
OWNER,
|
||||||
|
EXEC,
|
||||||
|
MEMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
object FactionMembersTable : Table("faction_members") {
|
||||||
|
val factionId = integer("faction_id")
|
||||||
|
.references(FactionsTable.id, onDelete = ReferenceOption.CASCADE)
|
||||||
|
val playerUuid = uuid("player_uuid")
|
||||||
|
.references(PlayersTable.uuid, onDelete = ReferenceOption.CASCADE)
|
||||||
|
val role = enumerationByName("role", 16, FactionRole::class)
|
||||||
|
val joinedAt = datetime("joined_at").defaultExpression(CurrentDateTime)
|
||||||
|
|
||||||
|
override val primaryKey = PrimaryKey(factionId, playerUuid)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package net.hareworks.hcu.faction.database.schema
|
||||||
|
|
||||||
|
import net.hareworks.hcu.core.database.schema.PlayersTable
|
||||||
|
import org.jetbrains.exposed.v1.core.Table
|
||||||
|
import org.jetbrains.exposed.v1.core.ReferenceOption
|
||||||
|
import org.jetbrains.exposed.v1.datetime.CurrentDateTime
|
||||||
|
import org.jetbrains.exposed.v1.datetime.datetime
|
||||||
|
|
||||||
|
enum class FactionRequestType {
|
||||||
|
INVITE,
|
||||||
|
REQUEST
|
||||||
|
}
|
||||||
|
|
||||||
|
object FactionRequestsTable : Table("faction_requests") {
|
||||||
|
val id = long("id").autoIncrement()
|
||||||
|
val factionId = integer("faction_id")
|
||||||
|
.references(FactionsTable.id, onDelete = ReferenceOption.CASCADE)
|
||||||
|
val playerUuid = uuid("player_uuid")
|
||||||
|
.references(PlayersTable.uuid, onDelete = ReferenceOption.CASCADE)
|
||||||
|
val type = enumerationByName("type", 16, FactionRequestType::class)
|
||||||
|
val createdAt = datetime("created_at").defaultExpression(CurrentDateTime)
|
||||||
|
|
||||||
|
override val primaryKey = PrimaryKey(id)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package net.hareworks.hcu.faction.database.schema
|
||||||
|
|
||||||
|
import net.hareworks.hcu.core.database.schema.ActorsTable
|
||||||
|
import org.jetbrains.exposed.v1.core.Table
|
||||||
|
import org.jetbrains.exposed.v1.core.ReferenceOption
|
||||||
|
|
||||||
|
object FactionsTable : Table("factions") {
|
||||||
|
val id = integer("id")
|
||||||
|
.references(ActorsTable.actorId, onDelete = ReferenceOption.CASCADE)
|
||||||
|
val name = varchar("name", 64).uniqueIndex()
|
||||||
|
val tag = varchar("tag", 16).nullable()
|
||||||
|
val color = varchar("color", 16).nullable()
|
||||||
|
val description = text("description").nullable()
|
||||||
|
val open = bool("open").default(false)
|
||||||
|
|
||||||
|
override val primaryKey = PrimaryKey(id)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package net.hareworks.hcu.faction.listener
|
||||||
|
|
||||||
|
import io.papermc.paper.event.player.AsyncChatEvent
|
||||||
|
import net.hareworks.hcu.faction.service.FactionService
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
|
||||||
|
class ChatListener(private val service: FactionService) : Listener {
|
||||||
|
@EventHandler
|
||||||
|
fun onChat(event: AsyncChatEvent) {
|
||||||
|
val tag = service.getFactionTag(event.player.uniqueId) ?: return
|
||||||
|
|
||||||
|
val originalRenderer = event.renderer()
|
||||||
|
event.renderer { source, sourceDisplayName, message, viewer ->
|
||||||
|
val rendered = originalRenderer.render(source, sourceDisplayName, message, viewer)
|
||||||
|
|
||||||
|
Component.text()
|
||||||
|
.append(Component.text("[$tag] ", NamedTextColor.GOLD))
|
||||||
|
.append(rendered)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package net.hareworks.hcu.faction.listener
|
||||||
|
|
||||||
|
import net.hareworks.hcu.core.database.DatabaseSessionManager
|
||||||
|
import net.hareworks.hcu.faction.database.schema.FactionMembersTable
|
||||||
|
import net.hareworks.hcu.faction.database.schema.FactionRequestsTable
|
||||||
|
import net.hareworks.hcu.faction.database.schema.FactionsTable
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.server.ServiceRegisterEvent
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.Database
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
|
||||||
|
import java.util.logging.Level
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
class DatabaseListener(private val logger: Logger) : Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onServiceRegister(event: ServiceRegisterEvent) {
|
||||||
|
if (event.provider.service == Database::class.java) {
|
||||||
|
logger.info("Database service registered. Ensuring Faction schema...")
|
||||||
|
ensureSchema()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ensureSchema() {
|
||||||
|
if (!DatabaseSessionManager.isConnected()) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
DatabaseSessionManager.transaction {
|
||||||
|
SchemaUtils.createMissingTablesAndColumns(
|
||||||
|
FactionsTable,
|
||||||
|
FactionMembersTable,
|
||||||
|
FactionRequestsTable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
logger.info("Faction schema initialized successfully")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.log(Level.SEVERE, "Failed to initialize Faction schema", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,412 @@
|
||||||
|
package net.hareworks.hcu.faction.service
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
|
import com.github.michaelbull.result.onFailure
|
||||||
|
import com.github.michaelbull.result.onSuccess
|
||||||
|
|
||||||
|
|
||||||
|
import net.hareworks.hcu.core.database.DatabaseSessionManager
|
||||||
|
import net.hareworks.hcu.core.database.schema.ActorIdSequence
|
||||||
|
import net.hareworks.hcu.core.database.schema.ActorsTable
|
||||||
|
import net.hareworks.hcu.core.database.schema.PlayersTable
|
||||||
|
import net.hareworks.hcu.faction.database.schema.*
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import net.hareworks.hcu.faction.database.schema.*
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insert
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.update
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.deleteWhere
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.andWhere
|
||||||
|
import org.jetbrains.exposed.v1.datetime.CurrentDateTime
|
||||||
|
import org.jetbrains.exposed.v1.core.and
|
||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import kotlinx.datetime.TimeZone
|
||||||
|
import kotlinx.datetime.toLocalDateTime
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class FactionService {
|
||||||
|
|
||||||
|
fun createFaction(name: String, tag: String?, color: String?, ownerUuid: UUID): Result<Int, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
// Check uniqueness
|
||||||
|
if (FactionsTable.selectAll().andWhere { FactionsTable.name eq name }.count() > 0L) {
|
||||||
|
return@transaction Err("Faction name already taken")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Actor
|
||||||
|
val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date
|
||||||
|
val actorIdVal = ActorsTable.insert {
|
||||||
|
it[type] = "faction"
|
||||||
|
it[registeredAt] = today
|
||||||
|
it[removed] = false
|
||||||
|
} get ActorsTable.actorId
|
||||||
|
|
||||||
|
// Create Faction
|
||||||
|
FactionsTable.insert {
|
||||||
|
it[id] = actorIdVal
|
||||||
|
it[this.name] = name
|
||||||
|
it[this.tag] = tag
|
||||||
|
it[this.color] = color
|
||||||
|
it[open] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Owner
|
||||||
|
FactionMembersTable.insert {
|
||||||
|
it[factionId] = actorIdVal
|
||||||
|
it[playerUuid] = ownerUuid
|
||||||
|
it[role] = FactionRole.OWNER
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(actorIdVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFactionOfPlayer(playerUuid: UUID): Int? {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
FactionMembersTable
|
||||||
|
.selectAll()
|
||||||
|
.andWhere { FactionMembersTable.playerUuid eq playerUuid }
|
||||||
|
.singleOrNull()
|
||||||
|
?.get(FactionMembersTable.factionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRole(factionId: Int, playerUuid: UUID): FactionRole? {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
FactionMembersTable
|
||||||
|
.selectAll()
|
||||||
|
.andWhere { FactionMembersTable.factionId eq factionId }
|
||||||
|
.andWhere { FactionMembersTable.playerUuid eq playerUuid }
|
||||||
|
.singleOrNull()
|
||||||
|
?.get(FactionMembersTable.role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFactionTag(playerUuid: UUID): String? {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
(FactionMembersTable innerJoin FactionsTable)
|
||||||
|
.selectAll()
|
||||||
|
.andWhere { FactionMembersTable.playerUuid eq playerUuid }
|
||||||
|
.singleOrNull()
|
||||||
|
?.get(FactionsTable.tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFactionIdByName(name: String): Int? {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
FactionsTable
|
||||||
|
.selectAll()
|
||||||
|
.andWhere { FactionsTable.name eq name }
|
||||||
|
.singleOrNull()
|
||||||
|
?.get(FactionsTable.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFactionName(factionId: Int): String? {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
FactionsTable
|
||||||
|
.selectAll()
|
||||||
|
.andWhere { FactionsTable.id eq factionId }
|
||||||
|
.singleOrNull()
|
||||||
|
?.get(FactionsTable.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invitePlayer(factionId: Int, targetUuid: UUID): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
if (isMember(factionId, targetUuid)) {
|
||||||
|
return@transaction Err("Player is already a member")
|
||||||
|
}
|
||||||
|
if (hasPendingRequest(factionId, targetUuid, FactionRequestType.INVITE)) {
|
||||||
|
return@transaction Err("Player already invited")
|
||||||
|
}
|
||||||
|
|
||||||
|
FactionRequestsTable.insert {
|
||||||
|
it[this.factionId] = factionId
|
||||||
|
it[this.playerUuid] = targetUuid
|
||||||
|
it[type] = FactionRequestType.INVITE
|
||||||
|
}
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun acceptInvite(factionId: Int, playerUuid: UUID): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
val request = FactionRequestsTable.selectAll()
|
||||||
|
.andWhere { FactionRequestsTable.factionId eq factionId }
|
||||||
|
.andWhere { FactionRequestsTable.playerUuid eq playerUuid }
|
||||||
|
.andWhere { FactionRequestsTable.type eq FactionRequestType.INVITE }
|
||||||
|
.singleOrNull() ?: return@transaction Err("No invite found")
|
||||||
|
|
||||||
|
addMember(factionId, playerUuid, FactionRole.MEMBER)
|
||||||
|
|
||||||
|
val reqId = request[FactionRequestsTable.id]
|
||||||
|
FactionRequestsTable.deleteWhere { FactionRequestsTable.id eq reqId }
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rejectInvite(factionId: Int, playerUuid: UUID): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
val request = FactionRequestsTable.selectAll()
|
||||||
|
.andWhere { FactionRequestsTable.factionId eq factionId }
|
||||||
|
.andWhere { FactionRequestsTable.playerUuid eq playerUuid }
|
||||||
|
.andWhere { FactionRequestsTable.type eq FactionRequestType.INVITE }
|
||||||
|
.singleOrNull() ?: return@transaction Err("No invite found")
|
||||||
|
|
||||||
|
val reqId = request[FactionRequestsTable.id]
|
||||||
|
FactionRequestsTable.deleteWhere { FactionRequestsTable.id eq reqId }
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLatestInvite(playerUuid: UUID): Int? {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
FactionRequestsTable.selectAll()
|
||||||
|
.andWhere { FactionRequestsTable.playerUuid eq playerUuid }
|
||||||
|
.andWhere { FactionRequestsTable.type eq FactionRequestType.INVITE }
|
||||||
|
.sortedByDescending { it[FactionRequestsTable.createdAt] }
|
||||||
|
.firstOrNull()
|
||||||
|
?.get(FactionRequestsTable.factionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addMember(factionId: Int, playerUuid: UUID, role: FactionRole) {
|
||||||
|
FactionMembersTable.insert {
|
||||||
|
it[this.factionId] = factionId
|
||||||
|
it[this.playerUuid] = playerUuid
|
||||||
|
it[this.role] = role
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isMember(factionId: Int, playerUuid: UUID): Boolean {
|
||||||
|
return FactionMembersTable.selectAll()
|
||||||
|
.andWhere { FactionMembersTable.factionId eq factionId }
|
||||||
|
.andWhere { FactionMembersTable.playerUuid eq playerUuid }
|
||||||
|
.count() > 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestJoin(factionId: Int, playerUuid: UUID): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
if (isMember(factionId, playerUuid)) return@transaction Err("Already a member")
|
||||||
|
|
||||||
|
// If invited, join immediately
|
||||||
|
if (hasPendingRequest(factionId, playerUuid, FactionRequestType.INVITE)) {
|
||||||
|
addMember(factionId, playerUuid, FactionRole.MEMBER)
|
||||||
|
// Delete invite. Find it first to delete by ID to avoid 'and'.
|
||||||
|
val inviteId = FactionRequestsTable.selectAll()
|
||||||
|
.andWhere { FactionRequestsTable.factionId eq factionId }
|
||||||
|
.andWhere { FactionRequestsTable.playerUuid eq playerUuid }
|
||||||
|
.andWhere { FactionRequestsTable.type eq FactionRequestType.INVITE }
|
||||||
|
.singleOrNull()?.get(FactionRequestsTable.id)
|
||||||
|
|
||||||
|
if (inviteId != null) {
|
||||||
|
FactionRequestsTable.deleteWhere { FactionRequestsTable.id eq inviteId }
|
||||||
|
}
|
||||||
|
return@transaction Ok(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPendingRequest(factionId, playerUuid, FactionRequestType.REQUEST)) return@transaction Err("Request already pending")
|
||||||
|
|
||||||
|
val isOpen = FactionsTable
|
||||||
|
.selectAll()
|
||||||
|
.andWhere { FactionsTable.id eq factionId }
|
||||||
|
.singleOrNull()?.get(FactionsTable.open) ?: return@transaction Err("Faction not found")
|
||||||
|
|
||||||
|
if (isOpen) {
|
||||||
|
addMember(factionId, playerUuid, FactionRole.MEMBER)
|
||||||
|
Ok(Unit)
|
||||||
|
} else {
|
||||||
|
FactionRequestsTable.insert {
|
||||||
|
it[this.factionId] = factionId
|
||||||
|
it[this.playerUuid] = playerUuid
|
||||||
|
it[type] = FactionRequestType.REQUEST
|
||||||
|
}
|
||||||
|
Ok(Unit) // Request sent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun acceptRequest(factionId: Int, targetUuid: UUID): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
if (!hasPendingRequest(factionId, targetUuid, FactionRequestType.REQUEST)) {
|
||||||
|
return@transaction Err("No pending request from this player")
|
||||||
|
}
|
||||||
|
|
||||||
|
addMember(factionId, targetUuid, FactionRole.MEMBER)
|
||||||
|
|
||||||
|
val reqId = FactionRequestsTable.selectAll()
|
||||||
|
.andWhere { FactionRequestsTable.factionId eq factionId }
|
||||||
|
.andWhere { FactionRequestsTable.playerUuid eq targetUuid }
|
||||||
|
.andWhere { FactionRequestsTable.type eq FactionRequestType.REQUEST }
|
||||||
|
.singleOrNull()?.get(FactionRequestsTable.id)
|
||||||
|
|
||||||
|
if (reqId != null) {
|
||||||
|
FactionRequestsTable.deleteWhere { FactionRequestsTable.id eq reqId }
|
||||||
|
}
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTag(factionId: Int, tag: String): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
FactionsTable.update({ FactionsTable.id eq factionId }) {
|
||||||
|
it[this.tag] = tag
|
||||||
|
}
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setColor(factionId: Int, color: String): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
FactionsTable.update({ FactionsTable.id eq factionId }) {
|
||||||
|
it[this.color] = color
|
||||||
|
}
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOpen(factionId: Int, open: Boolean): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
FactionsTable.update({ FactionsTable.id eq factionId }) {
|
||||||
|
it[this.open] = open
|
||||||
|
}
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun promote(factionId: Int, targetUuid: UUID): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
val currentRole = getRole(factionId, targetUuid) ?: return@transaction Err("Player not in faction")
|
||||||
|
|
||||||
|
val newRole = when (currentRole) {
|
||||||
|
FactionRole.MEMBER -> FactionRole.EXEC
|
||||||
|
FactionRole.EXEC -> return@transaction Err("Cannot promote EXEC, transfer ownership instead")
|
||||||
|
FactionRole.OWNER -> return@transaction Err("Already OWNER")
|
||||||
|
}
|
||||||
|
|
||||||
|
FactionMembersTable.update({ (FactionMembersTable.factionId eq factionId) and (FactionMembersTable.playerUuid eq targetUuid) }) {
|
||||||
|
it[role] = newRole
|
||||||
|
}
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun demote(factionId: Int, targetUuid: UUID): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
val currentRole = getRole(factionId, targetUuid) ?: return@transaction Err("Player not in faction")
|
||||||
|
|
||||||
|
val newRole = when (currentRole) {
|
||||||
|
FactionRole.EXEC -> FactionRole.MEMBER
|
||||||
|
FactionRole.MEMBER -> return@transaction Err("Cannot demote MEMBER")
|
||||||
|
FactionRole.OWNER -> return@transaction Err("Cannot demote OWNER")
|
||||||
|
}
|
||||||
|
|
||||||
|
FactionMembersTable.update({ (FactionMembersTable.factionId eq factionId) and (FactionMembersTable.playerUuid eq targetUuid) }) {
|
||||||
|
it[role] = newRole
|
||||||
|
}
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasPendingRequest(factionId: Int, playerUuid: UUID, type: FactionRequestType): Boolean {
|
||||||
|
return FactionRequestsTable.selectAll()
|
||||||
|
.andWhere { FactionRequestsTable.factionId eq factionId }
|
||||||
|
.andWhere { FactionRequestsTable.playerUuid eq playerUuid }
|
||||||
|
.andWhere { FactionRequestsTable.type eq type }
|
||||||
|
.count() > 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
fun leaveFaction(playerUuid: UUID): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
val factionId = getFactionOfPlayer(playerUuid) ?: return@transaction Err("Not in a faction")
|
||||||
|
val role = getRole(factionId, playerUuid)
|
||||||
|
if (role == FactionRole.OWNER) return@transaction Err("Owner cannot leave. Disband or transfer ownership first.")
|
||||||
|
|
||||||
|
FactionMembersTable.deleteWhere {
|
||||||
|
(FactionMembersTable.factionId eq factionId) and (FactionMembersTable.playerUuid eq playerUuid)
|
||||||
|
}
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun kickPlayer(actorUuid: UUID, targetUuid: UUID): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
val factionId = getFactionOfPlayer(actorUuid) ?: return@transaction Err("You are not in a faction")
|
||||||
|
val targetFactionId = getFactionOfPlayer(targetUuid)
|
||||||
|
|
||||||
|
if (factionId != targetFactionId) return@transaction Err("Player is not in your faction")
|
||||||
|
|
||||||
|
val actorRole = getRole(factionId, actorUuid) ?: return@transaction Err("Error fetching role")
|
||||||
|
val targetRole = getRole(factionId, targetUuid) ?: return@transaction Err("Error fetching target role")
|
||||||
|
|
||||||
|
if (actorRole == FactionRole.MEMBER) return@transaction Err("Members cannot kick")
|
||||||
|
if (actorRole == FactionRole.EXEC && targetRole != FactionRole.MEMBER) return@transaction Err("Execs can only kick members")
|
||||||
|
if (targetRole == FactionRole.OWNER) return@transaction Err("Cannot kick owner")
|
||||||
|
|
||||||
|
FactionMembersTable.deleteWhere {
|
||||||
|
(FactionMembersTable.factionId eq factionId) and (FactionMembersTable.playerUuid eq targetUuid)
|
||||||
|
}
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disbandFaction(actorUuid: UUID): Result<Unit, String> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
val factionId = getFactionOfPlayer(actorUuid) ?: return@transaction Err("Not in a faction")
|
||||||
|
val role = getRole(factionId, actorUuid)
|
||||||
|
if (role != FactionRole.OWNER) return@transaction Err("Only owner can disband")
|
||||||
|
|
||||||
|
FactionMembersTable.deleteWhere { FactionMembersTable.factionId eq factionId }
|
||||||
|
FactionRequestsTable.deleteWhere { FactionRequestsTable.factionId eq factionId }
|
||||||
|
FactionsTable.deleteWhere { FactionsTable.id eq factionId }
|
||||||
|
|
||||||
|
ActorsTable.update({ ActorsTable.actorId eq factionId }) {
|
||||||
|
it[removed] = true
|
||||||
|
}
|
||||||
|
Ok(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllFactions(): List<Pair<String, String?>> {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
FactionsTable.selectAll().map {
|
||||||
|
it[FactionsTable.name] to it[FactionsTable.tag]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFactionInfo(name: String): FactionInfo? {
|
||||||
|
return DatabaseSessionManager.transaction {
|
||||||
|
val row = FactionsTable.selectAll().andWhere { FactionsTable.name eq name }.singleOrNull() ?: return@transaction null
|
||||||
|
val id = row[FactionsTable.id]
|
||||||
|
val count = FactionMembersTable.selectAll().andWhere { FactionMembersTable.factionId eq id }.count()
|
||||||
|
val ownerUuid = FactionMembersTable.selectAll()
|
||||||
|
.andWhere { FactionMembersTable.factionId eq id }
|
||||||
|
.andWhere { FactionMembersTable.role eq FactionRole.OWNER }
|
||||||
|
.singleOrNull()?.get(FactionMembersTable.playerUuid)
|
||||||
|
|
||||||
|
FactionInfo(
|
||||||
|
row[FactionsTable.name],
|
||||||
|
row[FactionsTable.tag],
|
||||||
|
row[FactionsTable.color],
|
||||||
|
row[FactionsTable.open],
|
||||||
|
count,
|
||||||
|
ownerUuid
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FactionInfo(
|
||||||
|
val name: String,
|
||||||
|
val tag: String?,
|
||||||
|
val color: String?,
|
||||||
|
val open: Boolean,
|
||||||
|
val memberCount: Long,
|
||||||
|
val owner: UUID?
|
||||||
|
)
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user