From 888a6db72d3c0446dc8f09fbd1ac1332e1530460 Mon Sep 17 00:00:00 2001 From: Ammo Date: Thu, 19 Feb 2026 14:11:41 +0100 Subject: [PATCH 01/17] feat: add ParentPhase class and update phase parents to use it --- .../event/oneblock/progress/PhaseConfig.kt | 73 +++++++++++-------- .../event/oneblock/progress/default-phases.kt | 42 +++++------ 2 files changed, 64 insertions(+), 51 deletions(-) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt index f7a43c6..099e4aa 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt @@ -52,13 +52,19 @@ data class PhaseConfig( } } + @ConfigSerializable + data class ParentPhase( + val id: String, + val weightOverride: Int? = null + ) + @ConfigSerializable data class Phase( val id: String, val displayName: String = id.replaceFirstChar { it.uppercaseChar() }.replace('_', ' '), val startsAt: Int, val weight: Int, - val parents: List, + val parents: List, val blocks: List, val entities: List ) { @@ -92,29 +98,32 @@ data class PhaseConfig( var remainingBudget = parentBudget var levelFactor = 1.0 - var levelFactorSum = 0.0 - val levelFactors = ArrayList(parents.size) - for (i in parents.indices) { - levelFactors += levelFactor - levelFactorSum += levelFactor + val levelFactors = parents.map { + val f = levelFactor levelFactor *= PARENT_DECAY + f } - for ((idx, parentId) in parents.withIndex()) { + val effectiveParentWeights = parents.mapIndexed { idx, parentPhase -> + val override = parentPhase.weightOverride?.toDouble() + (levelFactors[idx]) * (override ?: 1.0) + } + + val totalParentWeight = effectiveParentWeights.sum() + + for ((idx, parentPhase) in parents.withIndex()) { if (remainingBudget <= 1e-9) break - val parent = config.findById(parentId) ?: continue + val parent = config.findById(parentPhase.id) ?: continue val parentBlocks = parent.blocks if (parentBlocks.isEmpty()) continue - val parentRawTotal = parentBlocks.sumOf { it.weight.toDouble() } - if (parentRawTotal <= 0.0) continue - - val shareForThisParent = if (levelFactorSum > 0.0) - parentBudget * (levelFactors[idx] / levelFactorSum) - else 0.0 + val shareForThisParent = + if (totalParentWeight > 0.0) + parentBudget * (effectiveParentWeights[idx] / totalParentWeight) + else 0.0 val assigned = shareForThisParent.coerceAtMost(remainingBudget) - val scale = assigned / parentRawTotal + val scale = assigned / parentBlocks.sumOf { it.weight.toDouble() } choices.ensureCapacity(choices.size + parentBlocks.size) for (entry in parentBlocks) { @@ -144,34 +153,38 @@ data class PhaseConfig( val extraSteps = (this.weight - 1).coerceAtLeast(0) val parentShare = (extraSteps * PARENT_SHARE_PER_WEIGHT).coerceIn(0.0, PARENT_SHARE_MAX) - val parentBudget = (if (ownTotal > 0.0) ownTotal else 1.0) * parentShare var remainingBudget = parentBudget var levelFactor = 1.0 - var levelFactorSum = 0.0 - val levelFactors = ArrayList(parents.size) - for (i in parents.indices) { - levelFactors += levelFactor - levelFactorSum += levelFactor + val levelFactors = parents.map { + val f = levelFactor levelFactor *= PARENT_DECAY + f + } + + val effectiveParentWeights = parents.mapIndexed { idx, parentPhase -> + val override = parentPhase.weightOverride?.toDouble() + (levelFactors[idx]) * (override ?: 1.0) } - for ((idx, parentId) in parents.withIndex()) { + val totalParentWeight = effectiveParentWeights.sum() + + for ((idx, parentPhase) in parents.withIndex()) { if (remainingBudget <= 1e-9) break - val parent = config.findById(parentId) ?: continue + val parent = config.findById(parentPhase.id) ?: continue val parentEntities = parent.entities if (parentEntities.isEmpty()) continue - val parentRawTotal = parentEntities.sumOf { it.weight } - if (parentRawTotal <= 0.0) continue - - val shareForThisParent = if (levelFactorSum > 0.0) - parentBudget * (levelFactors[idx] / levelFactorSum) - else 0.0 + val shareForThisParent = + if (totalParentWeight > 0.0) + parentBudget * (effectiveParentWeights[idx] / totalParentWeight) + else 0.0 val assigned = shareForThisParent.coerceAtMost(remainingBudget) - val scale = assigned / parentRawTotal + val parentTotal = parentEntities.sumOf { it.weight } + if (parentTotal <= 0.0) continue + val scale = assigned / parentTotal for (e in parentEntities) { val w = e.weight * scale diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt index 5548667..3d68c13 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt @@ -24,7 +24,7 @@ val defaultPhases = listOf( id = "early_mining", startsAt = 150, weight = 2, - parents = listOf("start_plains"), + parents = listOf(ParentPhase("start_plains")), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 50), BlockEntry(data = "minecraft:coal_ore", weight = 20), @@ -40,7 +40,7 @@ val defaultPhases = listOf( id = "early_caves", startsAt = 350, weight = 2, - parents = listOf("early_mining"), + parents = listOf(ParentPhase("early_mining")), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 40), BlockEntry(data = "minecraft:coal_ore", weight = 25), @@ -57,7 +57,7 @@ val defaultPhases = listOf( id = "iron_age", startsAt = 700, weight = 3, - parents = listOf("early_caves"), + parents = listOf(ParentPhase("early_caves")), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 35), BlockEntry(data = "minecraft:iron_ore", weight = 30), @@ -74,7 +74,7 @@ val defaultPhases = listOf( id = "abandoned_mineshaft", startsAt = 1100, weight = 3, - parents = listOf("iron_age"), + parents = listOf(ParentPhase("iron_age")), blocks = listOf( BlockEntry(data = "minecraft:oak_planks", weight = 20), BlockEntry(data = "minecraft:oak_log", weight = 15), @@ -91,7 +91,7 @@ val defaultPhases = listOf( id = "deep_caves", startsAt = 1600, weight = 4, - parents = listOf("iron_age"), + parents = listOf(ParentPhase("iron_age")), blocks = listOf( BlockEntry(data = "minecraft:deepslate", weight = 40), BlockEntry(data = "minecraft:iron_ore", weight = 20), @@ -108,7 +108,7 @@ val defaultPhases = listOf( id = "redstone_labs", startsAt = 2200, weight = 4, - parents = listOf("deep_caves"), + parents = listOf(ParentPhase("deep_caves")), blocks = listOf( BlockEntry(data = "minecraft:redstone_ore", weight = 35), BlockEntry(data = "minecraft:deepslate", weight = 30), @@ -125,7 +125,7 @@ val defaultPhases = listOf( id = "lava_depths", startsAt = 3000, weight = 5, - parents = listOf("deep_caves"), + parents = listOf(ParentPhase("deep_caves")), blocks = listOf( BlockEntry(data = "minecraft:basalt", weight = 30), BlockEntry(data = "minecraft:magma_block", weight = 15), @@ -141,7 +141,7 @@ val defaultPhases = listOf( id = "dripstone_caves", startsAt = 3600, weight = 5, - parents = listOf("deep_caves"), + parents = listOf(ParentPhase("deep_caves")), blocks = listOf( BlockEntry(data = "minecraft:dripstone_block", weight = 40), BlockEntry(data = "minecraft:pointed_dripstone", weight = 20), @@ -157,7 +157,7 @@ val defaultPhases = listOf( id = "lush_caves", startsAt = 4200, weight = 5, - parents = listOf("deep_caves"), + parents = listOf(ParentPhase("deep_caves")), blocks = listOf( BlockEntry(data = "minecraft:moss_block", weight = 30), BlockEntry(data = "minecraft:clay", weight = 25), @@ -173,7 +173,7 @@ val defaultPhases = listOf( id = "nether_entry", startsAt = 5000, weight = 6, - parents = listOf("lava_depths"), + parents = listOf(ParentPhase("lava_depths")), blocks = listOf( BlockEntry(data = "minecraft:netherrack", weight = 60), BlockEntry(data = "minecraft:nether_quartz_ore", weight = 20), @@ -189,7 +189,7 @@ val defaultPhases = listOf( id = "nether_fortress", startsAt = 5800, weight = 7, - parents = listOf("nether_entry"), + parents = listOf(ParentPhase("nether_entry")), blocks = listOf( BlockEntry(data = "minecraft:nether_bricks", weight = 40), BlockEntry(data = "minecraft:soul_sand", weight = 20), @@ -205,7 +205,7 @@ val defaultPhases = listOf( id = "crimson_forest", startsAt = 6500, weight = 7, - parents = listOf("nether_entry"), + parents = listOf(ParentPhase("nether_entry")), blocks = listOf( BlockEntry(data = "minecraft:crimson_nylium", weight = 30), BlockEntry(data = "minecraft:crimson_stem", weight = 25), @@ -221,7 +221,7 @@ val defaultPhases = listOf( id = "warped_forest", startsAt = 7200, weight = 7, - parents = listOf("nether_entry"), + parents = listOf(ParentPhase("nether_entry")), blocks = listOf( BlockEntry(data = "minecraft:warped_nylium", weight = 30), BlockEntry(data = "minecraft:warped_stem", weight = 25), @@ -237,7 +237,7 @@ val defaultPhases = listOf( id = "basalt_deltas", startsAt = 7800, weight = 8, - parents = listOf("nether_entry"), + parents = listOf(ParentPhase("nether_entry")), blocks = listOf( BlockEntry(data = "minecraft:basalt", weight = 50), BlockEntry(data = "minecraft:blackstone", weight = 30), @@ -252,7 +252,7 @@ val defaultPhases = listOf( id = "diamond_depths", startsAt = 9000, weight = 8, - parents = listOf("deep_caves"), + parents = listOf(ParentPhase("deep_caves")), blocks = listOf( BlockEntry(data = "minecraft:deepslate", weight = 50), BlockEntry(data = "minecraft:diamond_ore", weight = 15), @@ -269,7 +269,7 @@ val defaultPhases = listOf( id = "ancient_city", startsAt = 10000, weight = 9, - parents = listOf("diamond_depths"), + parents = listOf(ParentPhase("diamond_depths")), blocks = listOf( BlockEntry(data = "minecraft:sculk", weight = 40), BlockEntry(data = "minecraft:sculk_catalyst", weight = 10), @@ -286,7 +286,7 @@ val defaultPhases = listOf( id = "stronghold", startsAt = 11000, weight = 9, - parents = listOf("diamond_depths"), + parents = listOf(ParentPhase("diamond_depths")), blocks = listOf( BlockEntry(data = "minecraft:stone_bricks", weight = 40), BlockEntry(data = "minecraft:cracked_stone_bricks", weight = 15), @@ -303,7 +303,7 @@ val defaultPhases = listOf( id = "the_end", startsAt = 13000, weight = 10, - parents = listOf("stronghold"), + parents = listOf(ParentPhase("stronghold")), blocks = listOf( BlockEntry(data = "minecraft:end_stone", weight = 70), BlockEntry(data = "minecraft:obsidian", weight = 20), @@ -318,7 +318,7 @@ val defaultPhases = listOf( id = "end_cities", startsAt = 15000, weight = 11, - parents = listOf("the_end"), + parents = listOf(ParentPhase("the_end")), blocks = listOf( BlockEntry(data = "minecraft:purpur_block", weight = 40), BlockEntry(data = "minecraft:end_stone", weight = 30), @@ -335,7 +335,7 @@ val defaultPhases = listOf( id = "overworld_chaos", startsAt = 17000, weight = 12, - parents = listOf("the_end"), + parents = listOf(ParentPhase("the_end")), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 30), BlockEntry(data = "minecraft:deepslate", weight = 30), @@ -352,7 +352,7 @@ val defaultPhases = listOf( id = "event_finale", startsAt = 20000, weight = 13, - parents = listOf("overworld_chaos"), + parents = listOf(ParentPhase("overworld_chaos")), blocks = listOf( BlockEntry(data = "minecraft:ancient_debris", weight = 10), BlockEntry(data = "minecraft:netherite_block", weight = 1), From ac5ca0a83701373e7676f0f27e92377aba846fa3 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 17:32:15 +0100 Subject: [PATCH 02/17] feat: update version to 1.21.11-1.0.5-SNAPSHOT and enhance relocation command functionality --- gradle.properties | 2 +- .../slne/surf/event/base/paper/PaperMain.kt | 3 + .../event/oneblock/command/RelocateCommand.kt | 119 ++++++++++-------- .../surf/event/oneblock/db/IslandService.kt | 4 + .../event/oneblock/session/PlayerSession.kt | 103 ++------------- 5 files changed, 83 insertions(+), 148 deletions(-) diff --git a/gradle.properties b/gradle.properties index c59f880..b9d7ca8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ kotlin.stdlib.default.dependency=false org.gradle.caching=true org.gradle.parallel=true org.gradle.jvmargs=-Xmx4G -version=1.21.11-1.0.3-SNAPSHOT \ No newline at end of file +version=1.21.11-1.0.5-SNAPSHOT \ No newline at end of file diff --git a/surf-event-base/surf-event-base-paper/src/main/kotlin/dev/slne/surf/event/base/paper/PaperMain.kt b/surf-event-base/surf-event-base-paper/src/main/kotlin/dev/slne/surf/event/base/paper/PaperMain.kt index dbd51da..bcd7735 100644 --- a/surf-event-base/surf-event-base-paper/src/main/kotlin/dev/slne/surf/event/base/paper/PaperMain.kt +++ b/surf-event-base/surf-event-base-paper/src/main/kotlin/dev/slne/surf/event/base/paper/PaperMain.kt @@ -1,6 +1,8 @@ package dev.slne.surf.event.base.paper import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin +import dev.slne.surf.event.base.api.common.state.EventServerState +import dev.slne.surf.event.base.core.access.eventServerAccess import dev.slne.surf.event.base.core.loader.redisLoader import dev.slne.surf.event.base.paper.command.eventServerCommand import dev.slne.surf.event.base.paper.config.EventServerConfigHolder @@ -31,6 +33,7 @@ class PaperMain : SuspendingJavaPlugin() { } override suspend fun onDisableAsync() { + eventServerAccess.setEventServerState(EventServerState.UNKNOWN) redisLoader.disconnect() } diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/RelocateCommand.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/RelocateCommand.kt index aa4e285..6a109b7 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/RelocateCommand.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/RelocateCommand.kt @@ -1,76 +1,91 @@ package dev.slne.surf.event.oneblock.command import com.github.shynixn.mccoroutine.folia.launch -import dev.jorel.commandapi.arguments.LocationType -import dev.jorel.commandapi.kotlindsl.* +import dev.jorel.commandapi.kotlindsl.commandTree +import dev.jorel.commandapi.kotlindsl.literalArgument +import dev.jorel.commandapi.kotlindsl.playerExecutor +import dev.slne.surf.event.oneblock.db.IslandService import dev.slne.surf.event.oneblock.permission.OneBlockPermissions import dev.slne.surf.event.oneblock.plugin import dev.slne.surf.event.oneblock.session.PlayerSession -import org.bukkit.Location -import org.bukkit.command.CommandSender -import org.bukkit.entity.Player -import java.util.function.Predicate +import dev.slne.surf.surfapi.core.api.messages.adventure.clickCallback +import dev.slne.surf.surfapi.core.api.messages.adventure.sendText +import java.util.* +import java.util.concurrent.ConcurrentHashMap +import kotlin.time.Duration.Companion.minutes -private val isRelocatingPredicate = Predicate { sender -> - if (sender is Player) { - PlayerSession[sender.uniqueId].isRelocating - } else { - false - } -} +private val lastTimeRelocated = ConcurrentHashMap() +private val relocatingMillis = 5.minutes.inWholeMilliseconds fun relocateCommand() = commandTree("relocate") { withPermission(OneBlockPermissions.RELOCATE_COMMAND) - playerExecutor { sender, args -> - startRelocate(sender) - } + literalArgument("here") { + playerExecutor { player, _ -> + val location = player.location - literalArgument("place") { - withRequirement(isRelocatingPredicate) + if (IslandService.anyNearIslands(location)) { + player.sendText { + appendErrorPrefix() + error("In der Nähe gibt es bereits einen OneBlock. Bitte wähle einen anderen Ort.") + } + return@playerExecutor + } - locationArgument("location", LocationType.BLOCK_POSITION) { - playerExecutor { sender, args -> - val location: Location by args - finishRelocate(sender, location) + if (System.currentTimeMillis() - (lastTimeRelocated[player.uniqueId] + ?: 0) < relocatingMillis + ) { + player.sendText { + appendErrorPrefix() + error("Bitte warte noch ") + variableValue( + formatRemaining( + relocatingMillis - (System.currentTimeMillis() - (lastTimeRelocated[player.uniqueId] + ?: 0)) + ) + ) + error(" bevor du deinen OneBlock erneut verschieben kannst.") + } + return@playerExecutor } - } - } - literalArgument("abort") { - withRequirement(isRelocatingPredicate) - playerExecutor { sender, args -> - abortRelocate(sender) - } - } -} + player.sendText { + appendInfoPrefix() + info("Möchtest du deinen Oneblock hierhin verschieben? ") + append { + darkSpacer("[") + success("Verschieben") + darkSpacer("]") + clickCallback { + plugin.launch { + val session = PlayerSession[player.uniqueId] + val result = session.relocate(location) -private fun startRelocate(player: Player) { - plugin.launch { - val session = PlayerSession[player.uniqueId] - val result = session.startRelocate(player) - player.sendMessage(result) + if (result.isSuccess()) { + lastTimeRelocated[player.uniqueId] = System.currentTimeMillis() + } - if (result == PlayerSession.RelocateResult.START_RELOCATING) { - player.updateCommands() + player.sendText { + append(result) + } + } + } + } + } } } } -private fun finishRelocate(player: Player, loc: Location) { - plugin.launch { - val session = PlayerSession[player.uniqueId] - val result = session.finishRelocate(player, loc) - player.sendMessage(result) - player.updateCommands() - } -} +private fun formatRemaining(remainingMillis: Long) = buildString { + val seconds = remainingMillis / 1000 + val minutes = seconds / 60 + val remainingSeconds = seconds % 60 -private fun abortRelocate(player: Player) { - plugin.launch { - val session = PlayerSession[player.uniqueId] - session.abortRelocate() - player.sendMessage(PlayerSession.RelocateResult.ABORTED) - player.updateCommands() + if (minutes > 0) { + append("$minutes Minuten") + } + if (remainingSeconds > 0) { + if (minutes > 0) append(" und ") + append("$remainingSeconds Sekunden") } } \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt index 782c78b..4023df9 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt @@ -79,6 +79,10 @@ object IslandService { } } + fun anyNearIslands(location: Location): Boolean = all().any { island -> + island.oneBlock.distanceSquared(location) < 5 + } + suspend fun flushAll() { plugin.logger.info("Flushing ${islands.asMap().size} islands to database...") IslandRepository.saveAll(islands.asMap().values.toList()) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt index 504e6e7..58e767b 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt @@ -1,10 +1,8 @@ package dev.slne.surf.event.oneblock.session -import com.github.shynixn.mccoroutine.folia.entityDispatcher import com.github.shynixn.mccoroutine.folia.launch import com.github.shynixn.mccoroutine.folia.regionDispatcher import com.github.shynixn.mccoroutine.folia.ticks -import dev.slne.surf.event.oneblock.config.config import dev.slne.surf.event.oneblock.data.PlayerStateDTO import dev.slne.surf.event.oneblock.db.IslandService import dev.slne.surf.event.oneblock.db.PlayerStateService @@ -15,7 +13,6 @@ import dev.slne.surf.event.oneblock.progress.RollEngine import dev.slne.surf.surfapi.core.api.messages.adventure.buildText import dev.slne.surf.surfapi.core.api.messages.adventure.sendText import dev.slne.surf.surfapi.core.api.messages.builder.SurfComponentBuilder -import glm_.pow import kotlinx.coroutines.delay import kotlinx.coroutines.withContext import net.kyori.adventure.text.ComponentLike @@ -58,89 +55,31 @@ class PlayerSession(val uuid: UUID, private val state: PlayerStateDTO) : Closeab ProgressService.onBlockMined(player) } - suspend fun startRelocate(player: Player): RelocateResult { - if (isRelocating) { - return RelocateResult.ALREADY_RELOCATING - } - - if (!isInRangeOfOneBlock(player)) { - return RelocateResult.NOT_IN_RANGE - } - - if (isOnRelocationCooldown()) { - return RelocateResult.COOLDOWN - } - - state.relocating = true - flushState() - - return RelocateResult.START_RELOCATING - } - - suspend fun finishRelocate(player: Player, loc: Location): RelocateResult { + suspend fun relocate(location: Location): RelocateResult { val island = IslandService.getIsland(uuid) ?: error("Island not found for player $uuid") - if (!isInRelocationRadius(player, loc)) { - return RelocateResult.TOO_FAR - } - - return withContext(plugin.regionDispatcher(loc)) { - val block = loc.block + return withContext(plugin.regionDispatcher(location)) { + val block = location.block if (!block.isEmpty) { return@withContext RelocateResult.LOCATION_OCCUPIED } block.blockData = BlockType.DIRT.createBlockData() - IslandManager.migrateOneBlock(block, uuid, island.oneBlock) - IslandService.updateOneBlockLocation(uuid, loc) - state.relocating = false - state.relocateTimestamp = System.currentTimeMillis() - flushState() + IslandManager.migrateOneBlock(block, uuid, island.oneBlock) + IslandService.updateOneBlockLocation(uuid, location) RelocateResult.RELOCATED } } - fun abortRelocate() { - if (isRelocating) { - state.relocating = false - state.relocateTimestamp = System.currentTimeMillis() - flushState() - } - } - - private fun isOnRelocationCooldown(): Boolean { - val cooldown = config.relocate.relocateCooldownSeconds * 1000L - return System.currentTimeMillis() - state.relocateTimestamp < cooldown - } - - suspend fun isInRangeOfOneBlock(player: Player): Boolean { - val island = IslandService.getIsland(uuid) ?: return false - return isInRelocationRadius(player, island.oneBlock) - } - - private suspend fun isInRelocationRadius(player: Player, location: Location): Boolean { - if (player.world != location.world) { - return false - } - - val maxDistanceSquared = config.relocate.relocateRadius pow 2 - - return withContext(plugin.entityDispatcher(player)) { - player.location.distanceSquared(location) <= maxDistanceSquared - } - } - private fun flushState() { plugin.launch { PlayerStateService.flushState(state) } } - override fun close() { - abortRelocate() - } + override fun close() {} companion object { operator fun get(uuid: UUID): PlayerSession { @@ -149,38 +88,10 @@ class PlayerSession(val uuid: UUID, private val state: PlayerStateDTO) : Closeab } enum class RelocateResult(message: SurfComponentBuilder.() -> Unit) : ComponentLike { - START_RELOCATING({ - appendSuccessPrefix() - success("Du kannst nun einen neuen Ort für deinen OneBlock auswählen.") - appendNewPrefixedLine { - info("Wähle dazu einen Block aus und benutze ") - info("/relocate place ") - } - }), RELOCATED({ appendSuccessPrefix() success("Dein OneBlock wurde erfolgreich umgezogen.") }), - ABORTED({ - appendSuccessPrefix() - success("Der Umzug wurde abgebrochen.") - }), - COOLDOWN({ - appendErrorPrefix() - error("Du musst noch warten, bevor du erneut Umziehen kannst.") - }), - NOT_IN_RANGE({ - appendErrorPrefix() - error("Du bist zu weit von deinem OneBlock entfernt.") - }), - TOO_FAR({ - appendErrorPrefix() - error("Der ausgewählte Ort ist zu weit von dir entfernt.") - }), - ALREADY_RELOCATING({ - appendErrorPrefix() - error("Du befindest dich bereits im Umzugsmodus.") - }), LOCATION_OCCUPIED({ appendErrorPrefix() error("Der Zielort ist ungültig oder bereits belegt.") @@ -188,5 +99,7 @@ class PlayerSession(val uuid: UUID, private val state: PlayerStateDTO) : Closeab val message = buildText(message) override fun asComponent() = message + + fun isSuccess() = this == RELOCATED } } \ No newline at end of file From 4771a6ad8e8ab751d7c51fa2243ac3955af7c2ad Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 17:35:43 +0100 Subject: [PATCH 03/17] feat: add relocation height validation to PlayerSession --- .../event/oneblock/session/PlayerSession.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt index 58e767b..7c461e8 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt @@ -3,6 +3,7 @@ package dev.slne.surf.event.oneblock.session import com.github.shynixn.mccoroutine.folia.launch import com.github.shynixn.mccoroutine.folia.regionDispatcher import com.github.shynixn.mccoroutine.folia.ticks +import dev.slne.surf.event.oneblock.config.config import dev.slne.surf.event.oneblock.data.PlayerStateDTO import dev.slne.surf.event.oneblock.db.IslandService import dev.slne.surf.event.oneblock.db.PlayerStateService @@ -58,6 +59,14 @@ class PlayerSession(val uuid: UUID, private val state: PlayerStateDTO) : Closeab suspend fun relocate(location: Location): RelocateResult { val island = IslandService.getIsland(uuid) ?: error("Island not found for player $uuid") + if (location.y >= config.islandPlacement.maxY) { + return RelocateResult.TO_HIGH + } + + if (location.y <= config.islandPlacement.minY) { + return RelocateResult.TO_LOW + } + return withContext(plugin.regionDispatcher(location)) { val block = location.block if (!block.isEmpty) { @@ -95,6 +104,14 @@ class PlayerSession(val uuid: UUID, private val state: PlayerStateDTO) : Closeab LOCATION_OCCUPIED({ appendErrorPrefix() error("Der Zielort ist ungültig oder bereits belegt.") + }), + TO_HIGH({ + appendErrorPrefix() + error("Der Zielort ist zu hoch. Bitte wählen einen niedrigeren Ort.") + }), + TO_LOW({ + appendErrorPrefix() + error("Der Zielort ist zu niedrig. Bitte wählen einen höheren Ort.") }); val message = buildText(message) From 9de077a98f94302ef92cf7c0882cb72ecc685924 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 17:40:51 +0100 Subject: [PATCH 04/17] fix: remove unnecessary newline and simplify player spawn condition --- .../slne/surf/event/oneblock/listener/OneBlockSaveListener.kt | 1 - .../slne/surf/event/oneblock/listener/OneBlockSpawnListener.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSaveListener.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSaveListener.kt index a2fec5c..9213f2d 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSaveListener.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSaveListener.kt @@ -21,5 +21,4 @@ object OneBlockSaveListener : Listener { IslandManager.saveIdx() } } - } \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSpawnListener.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSpawnListener.kt index 8bb1f43..5dcfb24 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSpawnListener.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSpawnListener.kt @@ -13,7 +13,7 @@ object OneBlockSpawnListener : Listener { fun onPlayerSpawnLocation(event: AsyncPlayerSpawnLocationEvent) { val player = event.connection.profile.id ?: return - if (!OneBlockConnectionListener.shouldTeleportToIsland(player) && event.isNewPlayer) { + if (!OneBlockConnectionListener.shouldTeleportToIsland(player)) { return } From 427ceb2d91bde46c40bd0e3fc52d858ad0bedd67 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 20:15:03 +0100 Subject: [PATCH 05/17] feat: add leaderboard placeholders and integrate LuckPerms for player prefixes --- .../surf-oneblock/build.gradle.kts | 1 + .../event/oneblock/command/OneBlockCommand.kt | 6 +++ .../oneblock/papi/OneBlockPapiExpansion.kt | 6 ++- .../surf/event/oneblock/papi/ValueHolder.kt | 21 ++++++++++ .../LeaderboardOwnPlacePlaceholder.kt | 39 +++++++++++++++++++ .../LeaderboardPlacePlaceholder.kt | 26 +++++++++++++ .../papi/leaderboard/leaderboard-util.kt | 27 +++++++++++++ .../permission/OneBlockPermissions.kt | 2 + 8 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/ValueHolder.kt create mode 100644 surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardOwnPlacePlaceholder.kt create mode 100644 surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardPlacePlaceholder.kt create mode 100644 surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/leaderboard-util.kt diff --git a/surf-event-events/surf-oneblock/build.gradle.kts b/surf-event-events/surf-oneblock/build.gradle.kts index 007dff7..eda1467 100644 --- a/surf-event-events/surf-oneblock/build.gradle.kts +++ b/surf-event-events/surf-oneblock/build.gradle.kts @@ -24,4 +24,5 @@ dependencies { compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit") { isTransitive = false } compileOnly("de.oliver:FancyHolograms:2.7.0") + compileOnly("net.luckperms:api:5.4") } \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/OneBlockCommand.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/OneBlockCommand.kt index 4513246..d459644 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/OneBlockCommand.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/OneBlockCommand.kt @@ -12,10 +12,16 @@ fun oneBlockCommand() = commandTree("oneblock") { withPermission(OneBlockPermissions.ONE_BLOCK_COMMAND) literalArgument("reload") { + withPermission(OneBlockPermissions.ONE_BLOCK_COMMAND_RELOAD) anyExecutor { sender, _ -> reload(sender) } } + + literalArgument("resetOneBlock") { + withPermission(OneBlockPermissions.ONE_BLOCK_COMMAND_RESET) + + } } private fun reload(sender: CommandSender) { diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/OneBlockPapiExpansion.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/OneBlockPapiExpansion.kt index a667dae..43475da 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/OneBlockPapiExpansion.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/OneBlockPapiExpansion.kt @@ -1,5 +1,7 @@ package dev.slne.surf.event.oneblock.papi +import dev.slne.surf.event.oneblock.papi.leaderboard.LeaderboardOwnPlacePlaceholder +import dev.slne.surf.event.oneblock.papi.leaderboard.LeaderboardPlacePlaceholder import dev.slne.surf.event.oneblock.plugin import dev.slne.surf.surfapi.bukkit.api.hook.papi.expansion.PapiExpansion @@ -9,7 +11,9 @@ class OneBlockPapiExpansion : PapiExpansion( LevelPlaceholder(), TotalBlocksGlobalPlaceholder(), PlayerNamePlaceholder(), - TotalBlocksPlaceholder() + TotalBlocksPlaceholder(), + LeaderboardPlacePlaceholder, + LeaderboardOwnPlacePlaceholder ), "twisti, Ammo, red", plugin.pluginMeta.version diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/ValueHolder.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/ValueHolder.kt new file mode 100644 index 0000000..8752bbc --- /dev/null +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/ValueHolder.kt @@ -0,0 +1,21 @@ +package dev.slne.surf.event.oneblock.papi + +import dev.slne.surf.event.oneblock.db.IslandService +import java.util.* + +object ValueHolder { + fun getUuid(place: Int) = IslandService.all() + .sortedByDescending { it.totalMined } + .getOrNull(place - 1) + ?.owner + + fun getPlace(uuid: UUID) = IslandService.all() + .sortedByDescending { it.totalMined } + .indexOfFirst { it.owner == uuid } + .takeIf { it != -1 } + ?.plus(1) + + fun getMined(uuid: UUID) = IslandService.all() + .firstOrNull { it.owner == uuid } + ?.totalMined +} \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardOwnPlacePlaceholder.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardOwnPlacePlaceholder.kt new file mode 100644 index 0000000..581b3f4 --- /dev/null +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardOwnPlacePlaceholder.kt @@ -0,0 +1,39 @@ +package dev.slne.surf.event.oneblock.papi.leaderboard + +import dev.slne.surf.event.oneblock.papi.ValueHolder +import dev.slne.surf.surfapi.bukkit.api.hook.papi.expansion.PapiPlaceholder +import dev.slne.surf.surfapi.core.api.messages.Colors +import dev.slne.surf.surfapi.core.api.messages.adventure.buildText +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer +import org.bukkit.OfflinePlayer + +object LeaderboardOwnPlacePlaceholder : PapiPlaceholder("ownplace") { + + override fun parse(player: OfflinePlayer, args: List): String? { + val operation = args.getOrNull(0)?.lowercase()?.trim() + val number = args.getOrNull(1)?.toIntOrNull() + + val uuid = player.uniqueId + val ownPlace = ValueHolder.getPlace(uuid) ?: return null + + val targetPlace = when { + operation == null -> ownPlace + number == null -> return null + operation == "plus" -> ownPlace + number + operation == "minus" -> ownPlace - number + else -> return null + } + + if (targetPlace <= 0) return null + + val targetUuid = ValueHolder.getUuid(targetPlace) ?: return "Du bist Letzter? haha" + val mined = ValueHolder.getMined(targetUuid)?.toString() ?: "???" + + return LegacyComponentSerializer.legacySection().serialize(buildText { + text("#$targetPlace ", Colors.GOLD) + text(prefixPlayer(targetUuid)) + spacer(": ") + variableValue(mined) + }) + } +} diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardPlacePlaceholder.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardPlacePlaceholder.kt new file mode 100644 index 0000000..771922b --- /dev/null +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardPlacePlaceholder.kt @@ -0,0 +1,26 @@ +package dev.slne.surf.event.oneblock.papi.leaderboard + +import dev.slne.surf.event.oneblock.papi.ValueHolder +import dev.slne.surf.surfapi.bukkit.api.extensions.server +import dev.slne.surf.surfapi.bukkit.api.hook.papi.expansion.PapiPlaceholder +import dev.slne.surf.surfapi.core.api.messages.adventure.buildText +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer +import org.bukkit.OfflinePlayer + +object LeaderboardPlacePlaceholder : PapiPlaceholder("place") { + override fun parse( + player: OfflinePlayer, + args: List + ): String? { + require(args.size == 1) { "Invalid number of arguments. A place must be provided." } + val place = args[0].toIntOrNull() ?: return null + val playerUniqueId = ValueHolder.getUuid(place) ?: return "$place. ???" + val player = server.getOfflinePlayer(playerUniqueId) + + return LegacyComponentSerializer.legacySection().serialize(buildText { + text(prefixPlayer(player.uniqueId)) + spacer(": ") + variableValue(ValueHolder.getMined(playerUniqueId)?.toString() ?: "???") + }) + } +} \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/leaderboard-util.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/leaderboard-util.kt new file mode 100644 index 0000000..37433cb --- /dev/null +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/leaderboard-util.kt @@ -0,0 +1,27 @@ +package dev.slne.surf.event.oneblock.papi.leaderboard + +import com.github.shynixn.mccoroutine.folia.launch +import dev.slne.surf.event.oneblock.plugin +import dev.slne.surf.surfapi.bukkit.api.extensions.server +import net.luckperms.api.LuckPermsProvider +import java.util.* + +fun prefixPlayer(uuid: UUID): String { + val luckPerms = LuckPermsProvider.get() + + val player = server.getOfflinePlayer(uuid) + val user = luckPerms.userManager.getUser(uuid) ?: run { + plugin.launch { + luckPerms.userManager.loadUser(uuid) + } + + return player.name ?: "Fehler" + } + + val primaryGroup = user.primaryGroup + + val group = luckPerms.groupManager.getGroup(primaryGroup) ?: return player.name ?: "Fehler" + val prefix = group.cachedData.metaData.prefix + + return "$prefix ${player.name}" +} \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/permission/OneBlockPermissions.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/permission/OneBlockPermissions.kt index ff10e72..86bfb0c 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/permission/OneBlockPermissions.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/permission/OneBlockPermissions.kt @@ -13,4 +13,6 @@ object OneBlockPermissions : PermissionRegistry() { val PHASE_CHEST_COMMAND = create("$COMMAND_PREFIX.phaseChest") val PHASE_COMMAND = create("$COMMAND_PREFIX.phase") val ONE_BLOCK_COMMAND = create("$COMMAND_PREFIX.oneBlock") + val ONE_BLOCK_COMMAND_RELOAD = create("$COMMAND_PREFIX.oneBlock.reload") + val ONE_BLOCK_COMMAND_RESET = create("$COMMAND_PREFIX.oneBlock.reset") } \ No newline at end of file From 917eed098e76eabb35d4a30849fc802a65159da6 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 20:41:38 +0100 Subject: [PATCH 06/17] feat: implement island reset functionality and add leaderboard placeholder --- .../surf-oneblock/build.gradle.kts | 1 + .../event/oneblock/command/OneBlockCommand.kt | 31 +++++++++++++++++- .../event/oneblock/db/IslandRepository.kt | 10 +++--- .../surf/event/oneblock/db/IslandService.kt | 32 +++++++++++++++++++ .../LeaderboardIsTopTenPlaceholder.kt | 20 ++++++++++++ 5 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt diff --git a/surf-event-events/surf-oneblock/build.gradle.kts b/surf-event-events/surf-oneblock/build.gradle.kts index eda1467..a102ef0 100644 --- a/surf-event-events/surf-oneblock/build.gradle.kts +++ b/surf-event-events/surf-oneblock/build.gradle.kts @@ -13,6 +13,7 @@ surfPaperPluginApi { serverDependencies { registerRequired("FastAsyncWorldEdit") registerRequired("FancyHolograms") + registerRequired("LuckPerms") } } diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/OneBlockCommand.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/OneBlockCommand.kt index d459644..3ed7742 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/OneBlockCommand.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/command/OneBlockCommand.kt @@ -1,10 +1,17 @@ package dev.slne.surf.event.oneblock.command +import com.github.shynixn.mccoroutine.folia.launch +import dev.jorel.commandapi.arguments.AsyncPlayerProfileArgument import dev.jorel.commandapi.kotlindsl.anyExecutor +import dev.jorel.commandapi.kotlindsl.argument import dev.jorel.commandapi.kotlindsl.commandTree import dev.jorel.commandapi.kotlindsl.literalArgument import dev.slne.surf.event.oneblock.config.OneBlockConfigHolder +import dev.slne.surf.event.oneblock.db.IslandService import dev.slne.surf.event.oneblock.permission.OneBlockPermissions +import dev.slne.surf.event.oneblock.plugin +import dev.slne.surf.surfapi.bukkit.api.command.util.awaitAsyncPlayerProfile +import dev.slne.surf.surfapi.bukkit.api.command.util.idOrThrow import dev.slne.surf.surfapi.core.api.messages.adventure.sendText import org.bukkit.command.CommandSender @@ -20,7 +27,29 @@ fun oneBlockCommand() = commandTree("oneblock") { literalArgument("resetOneBlock") { withPermission(OneBlockPermissions.ONE_BLOCK_COMMAND_RESET) - + + argument(AsyncPlayerProfileArgument("target")) { + anyExecutor { sender, args -> + plugin.launch { + val target = args.awaitAsyncPlayerProfile("target") + val success = IslandService.resetIsland(target.idOrThrow()) + + if (success) { + sender.sendText { + appendSuccessPrefix() + success("Die Oneblock Insel von ") + variableValue(target.name ?: target.idOrThrow().toString()) + success(" wurde zurückgesetzt.") + } + } else { + sender.sendText { + appendErrorPrefix() + error("Die Insel konnte nicht zurückgesetzt werden. Möglicherweise hat der Spieler keine Insel oder es ist ein Fehler aufgetreten.") + } + } + } + } + } } } diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandRepository.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandRepository.kt index 50852c7..5b53525 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandRepository.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandRepository.kt @@ -1,10 +1,11 @@ package dev.slne.surf.event.oneblock.db +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.batchUpsert import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.insert import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction -import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.upsert +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.update import dev.slne.surf.event.oneblock.data.IslandDTO import dev.slne.surf.event.oneblock.db.table.IslandTable import kotlinx.coroutines.flow.map @@ -69,20 +70,19 @@ object IslandRepository { } suspend fun updateProgress(uuid: UUID, totalMined: Long) = suspendTransaction { - IslandTable.upsert { - it[ownerUuid] = uuid + IslandTable.update({ IslandTable.ownerUuid eq uuid }) { it[this.totalMined] = totalMined } } suspend fun updatePosition(uuid: UUID, x: Double, y: Double, z: Double, worldUuid: UUID) = suspendTransaction { - IslandTable.upsert { - it[ownerUuid] = uuid + IslandTable.update({ IslandTable.ownerUuid eq uuid }) { it[oneBlockX] = x it[oneBlockY] = y it[oneBlockZ] = z it[oneBlockWorld] = worldUuid } } + } \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt index 4023df9..51b32e3 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt @@ -3,6 +3,8 @@ package dev.slne.surf.event.oneblock.db import com.github.benmanes.caffeine.cache.Caffeine import com.github.shynixn.mccoroutine.folia.launch import dev.slne.surf.event.oneblock.data.IslandDTO +import dev.slne.surf.event.oneblock.island.IslandManager +import dev.slne.surf.event.oneblock.overworld import dev.slne.surf.event.oneblock.plugin import dev.slne.surf.surfapi.core.api.util.toObjectList import org.bukkit.Location @@ -33,6 +35,36 @@ object IslandService { }?.owner } + suspend fun resetIsland(uuid: UUID): Boolean { + val dto = islands.getIfPresent(uuid) ?: return false + + val newSpot = IslandManager.nextFreeSpot(overworld) ?: return false + val oldLoc = dto.oneBlock.clone() + + dto.totalMined = 0L + dto.oneBlock = newSpot + + IslandRepository.updatePosition( + uuid, + newSpot.x, + newSpot.y, + newSpot.z, + newSpot.world.uid + ) + + IslandRepository.updateProgress(uuid, 0L) + IslandManager.generateIsland(dto) + + IslandManager.migrateOneBlock( + newSpot.block, + uuid, + oldLoc + ) + + return true + } + + suspend fun createIslandForPlayer(uuid: UUID, oneBlockLocation: Location): IslandDTO { islands.getIfPresent(uuid)?.let { return it } diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt new file mode 100644 index 0000000..0b6aee2 --- /dev/null +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt @@ -0,0 +1,20 @@ +package dev.slne.surf.event.oneblock.papi.leaderboard + +import dev.slne.surf.event.oneblock.papi.ValueHolder +import dev.slne.surf.surfapi.bukkit.api.hook.papi.expansion.PapiPlaceholder +import org.bukkit.OfflinePlayer + +object LeaderboardIsTopTenPlaceholder : PapiPlaceholder("place") { + override fun parse( + player: OfflinePlayer, + args: List + ): String { + val uuid = player.uniqueId + val ownPlace = ValueHolder.getPlace(uuid) ?: return "false" + + if (ownPlace <= 10) { + return "true" + } + return "false" + } +} \ No newline at end of file From 3b4d57d3cc024215513d446aef4bf5e018ec1d32 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 20:50:37 +0100 Subject: [PATCH 07/17] Revert "feat: add ParentPhase class and update phase parents to use it" This reverts commit 888a6db72d3c0446dc8f09fbd1ac1332e1530460. --- .../event/oneblock/progress/PhaseConfig.kt | 73 ++++++++----------- .../event/oneblock/progress/default-phases.kt | 42 +++++------ 2 files changed, 51 insertions(+), 64 deletions(-) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt index 099e4aa..f7a43c6 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt @@ -52,19 +52,13 @@ data class PhaseConfig( } } - @ConfigSerializable - data class ParentPhase( - val id: String, - val weightOverride: Int? = null - ) - @ConfigSerializable data class Phase( val id: String, val displayName: String = id.replaceFirstChar { it.uppercaseChar() }.replace('_', ' '), val startsAt: Int, val weight: Int, - val parents: List, + val parents: List, val blocks: List, val entities: List ) { @@ -98,32 +92,29 @@ data class PhaseConfig( var remainingBudget = parentBudget var levelFactor = 1.0 - val levelFactors = parents.map { - val f = levelFactor + var levelFactorSum = 0.0 + val levelFactors = ArrayList(parents.size) + for (i in parents.indices) { + levelFactors += levelFactor + levelFactorSum += levelFactor levelFactor *= PARENT_DECAY - f } - val effectiveParentWeights = parents.mapIndexed { idx, parentPhase -> - val override = parentPhase.weightOverride?.toDouble() - (levelFactors[idx]) * (override ?: 1.0) - } - - val totalParentWeight = effectiveParentWeights.sum() - - for ((idx, parentPhase) in parents.withIndex()) { + for ((idx, parentId) in parents.withIndex()) { if (remainingBudget <= 1e-9) break - val parent = config.findById(parentPhase.id) ?: continue + val parent = config.findById(parentId) ?: continue val parentBlocks = parent.blocks if (parentBlocks.isEmpty()) continue - val shareForThisParent = - if (totalParentWeight > 0.0) - parentBudget * (effectiveParentWeights[idx] / totalParentWeight) - else 0.0 + val parentRawTotal = parentBlocks.sumOf { it.weight.toDouble() } + if (parentRawTotal <= 0.0) continue + + val shareForThisParent = if (levelFactorSum > 0.0) + parentBudget * (levelFactors[idx] / levelFactorSum) + else 0.0 val assigned = shareForThisParent.coerceAtMost(remainingBudget) - val scale = assigned / parentBlocks.sumOf { it.weight.toDouble() } + val scale = assigned / parentRawTotal choices.ensureCapacity(choices.size + parentBlocks.size) for (entry in parentBlocks) { @@ -153,38 +144,34 @@ data class PhaseConfig( val extraSteps = (this.weight - 1).coerceAtLeast(0) val parentShare = (extraSteps * PARENT_SHARE_PER_WEIGHT).coerceIn(0.0, PARENT_SHARE_MAX) + val parentBudget = (if (ownTotal > 0.0) ownTotal else 1.0) * parentShare var remainingBudget = parentBudget var levelFactor = 1.0 - val levelFactors = parents.map { - val f = levelFactor + var levelFactorSum = 0.0 + val levelFactors = ArrayList(parents.size) + for (i in parents.indices) { + levelFactors += levelFactor + levelFactorSum += levelFactor levelFactor *= PARENT_DECAY - f - } - - val effectiveParentWeights = parents.mapIndexed { idx, parentPhase -> - val override = parentPhase.weightOverride?.toDouble() - (levelFactors[idx]) * (override ?: 1.0) } - val totalParentWeight = effectiveParentWeights.sum() - - for ((idx, parentPhase) in parents.withIndex()) { + for ((idx, parentId) in parents.withIndex()) { if (remainingBudget <= 1e-9) break - val parent = config.findById(parentPhase.id) ?: continue + val parent = config.findById(parentId) ?: continue val parentEntities = parent.entities if (parentEntities.isEmpty()) continue - val shareForThisParent = - if (totalParentWeight > 0.0) - parentBudget * (effectiveParentWeights[idx] / totalParentWeight) - else 0.0 + val parentRawTotal = parentEntities.sumOf { it.weight } + if (parentRawTotal <= 0.0) continue + + val shareForThisParent = if (levelFactorSum > 0.0) + parentBudget * (levelFactors[idx] / levelFactorSum) + else 0.0 val assigned = shareForThisParent.coerceAtMost(remainingBudget) - val parentTotal = parentEntities.sumOf { it.weight } - if (parentTotal <= 0.0) continue - val scale = assigned / parentTotal + val scale = assigned / parentRawTotal for (e in parentEntities) { val w = e.weight * scale diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt index 3d68c13..5548667 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt @@ -24,7 +24,7 @@ val defaultPhases = listOf( id = "early_mining", startsAt = 150, weight = 2, - parents = listOf(ParentPhase("start_plains")), + parents = listOf("start_plains"), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 50), BlockEntry(data = "minecraft:coal_ore", weight = 20), @@ -40,7 +40,7 @@ val defaultPhases = listOf( id = "early_caves", startsAt = 350, weight = 2, - parents = listOf(ParentPhase("early_mining")), + parents = listOf("early_mining"), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 40), BlockEntry(data = "minecraft:coal_ore", weight = 25), @@ -57,7 +57,7 @@ val defaultPhases = listOf( id = "iron_age", startsAt = 700, weight = 3, - parents = listOf(ParentPhase("early_caves")), + parents = listOf("early_caves"), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 35), BlockEntry(data = "minecraft:iron_ore", weight = 30), @@ -74,7 +74,7 @@ val defaultPhases = listOf( id = "abandoned_mineshaft", startsAt = 1100, weight = 3, - parents = listOf(ParentPhase("iron_age")), + parents = listOf("iron_age"), blocks = listOf( BlockEntry(data = "minecraft:oak_planks", weight = 20), BlockEntry(data = "minecraft:oak_log", weight = 15), @@ -91,7 +91,7 @@ val defaultPhases = listOf( id = "deep_caves", startsAt = 1600, weight = 4, - parents = listOf(ParentPhase("iron_age")), + parents = listOf("iron_age"), blocks = listOf( BlockEntry(data = "minecraft:deepslate", weight = 40), BlockEntry(data = "minecraft:iron_ore", weight = 20), @@ -108,7 +108,7 @@ val defaultPhases = listOf( id = "redstone_labs", startsAt = 2200, weight = 4, - parents = listOf(ParentPhase("deep_caves")), + parents = listOf("deep_caves"), blocks = listOf( BlockEntry(data = "minecraft:redstone_ore", weight = 35), BlockEntry(data = "minecraft:deepslate", weight = 30), @@ -125,7 +125,7 @@ val defaultPhases = listOf( id = "lava_depths", startsAt = 3000, weight = 5, - parents = listOf(ParentPhase("deep_caves")), + parents = listOf("deep_caves"), blocks = listOf( BlockEntry(data = "minecraft:basalt", weight = 30), BlockEntry(data = "minecraft:magma_block", weight = 15), @@ -141,7 +141,7 @@ val defaultPhases = listOf( id = "dripstone_caves", startsAt = 3600, weight = 5, - parents = listOf(ParentPhase("deep_caves")), + parents = listOf("deep_caves"), blocks = listOf( BlockEntry(data = "minecraft:dripstone_block", weight = 40), BlockEntry(data = "minecraft:pointed_dripstone", weight = 20), @@ -157,7 +157,7 @@ val defaultPhases = listOf( id = "lush_caves", startsAt = 4200, weight = 5, - parents = listOf(ParentPhase("deep_caves")), + parents = listOf("deep_caves"), blocks = listOf( BlockEntry(data = "minecraft:moss_block", weight = 30), BlockEntry(data = "minecraft:clay", weight = 25), @@ -173,7 +173,7 @@ val defaultPhases = listOf( id = "nether_entry", startsAt = 5000, weight = 6, - parents = listOf(ParentPhase("lava_depths")), + parents = listOf("lava_depths"), blocks = listOf( BlockEntry(data = "minecraft:netherrack", weight = 60), BlockEntry(data = "minecraft:nether_quartz_ore", weight = 20), @@ -189,7 +189,7 @@ val defaultPhases = listOf( id = "nether_fortress", startsAt = 5800, weight = 7, - parents = listOf(ParentPhase("nether_entry")), + parents = listOf("nether_entry"), blocks = listOf( BlockEntry(data = "minecraft:nether_bricks", weight = 40), BlockEntry(data = "minecraft:soul_sand", weight = 20), @@ -205,7 +205,7 @@ val defaultPhases = listOf( id = "crimson_forest", startsAt = 6500, weight = 7, - parents = listOf(ParentPhase("nether_entry")), + parents = listOf("nether_entry"), blocks = listOf( BlockEntry(data = "minecraft:crimson_nylium", weight = 30), BlockEntry(data = "minecraft:crimson_stem", weight = 25), @@ -221,7 +221,7 @@ val defaultPhases = listOf( id = "warped_forest", startsAt = 7200, weight = 7, - parents = listOf(ParentPhase("nether_entry")), + parents = listOf("nether_entry"), blocks = listOf( BlockEntry(data = "minecraft:warped_nylium", weight = 30), BlockEntry(data = "minecraft:warped_stem", weight = 25), @@ -237,7 +237,7 @@ val defaultPhases = listOf( id = "basalt_deltas", startsAt = 7800, weight = 8, - parents = listOf(ParentPhase("nether_entry")), + parents = listOf("nether_entry"), blocks = listOf( BlockEntry(data = "minecraft:basalt", weight = 50), BlockEntry(data = "minecraft:blackstone", weight = 30), @@ -252,7 +252,7 @@ val defaultPhases = listOf( id = "diamond_depths", startsAt = 9000, weight = 8, - parents = listOf(ParentPhase("deep_caves")), + parents = listOf("deep_caves"), blocks = listOf( BlockEntry(data = "minecraft:deepslate", weight = 50), BlockEntry(data = "minecraft:diamond_ore", weight = 15), @@ -269,7 +269,7 @@ val defaultPhases = listOf( id = "ancient_city", startsAt = 10000, weight = 9, - parents = listOf(ParentPhase("diamond_depths")), + parents = listOf("diamond_depths"), blocks = listOf( BlockEntry(data = "minecraft:sculk", weight = 40), BlockEntry(data = "minecraft:sculk_catalyst", weight = 10), @@ -286,7 +286,7 @@ val defaultPhases = listOf( id = "stronghold", startsAt = 11000, weight = 9, - parents = listOf(ParentPhase("diamond_depths")), + parents = listOf("diamond_depths"), blocks = listOf( BlockEntry(data = "minecraft:stone_bricks", weight = 40), BlockEntry(data = "minecraft:cracked_stone_bricks", weight = 15), @@ -303,7 +303,7 @@ val defaultPhases = listOf( id = "the_end", startsAt = 13000, weight = 10, - parents = listOf(ParentPhase("stronghold")), + parents = listOf("stronghold"), blocks = listOf( BlockEntry(data = "minecraft:end_stone", weight = 70), BlockEntry(data = "minecraft:obsidian", weight = 20), @@ -318,7 +318,7 @@ val defaultPhases = listOf( id = "end_cities", startsAt = 15000, weight = 11, - parents = listOf(ParentPhase("the_end")), + parents = listOf("the_end"), blocks = listOf( BlockEntry(data = "minecraft:purpur_block", weight = 40), BlockEntry(data = "minecraft:end_stone", weight = 30), @@ -335,7 +335,7 @@ val defaultPhases = listOf( id = "overworld_chaos", startsAt = 17000, weight = 12, - parents = listOf(ParentPhase("the_end")), + parents = listOf("the_end"), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 30), BlockEntry(data = "minecraft:deepslate", weight = 30), @@ -352,7 +352,7 @@ val defaultPhases = listOf( id = "event_finale", startsAt = 20000, weight = 13, - parents = listOf(ParentPhase("overworld_chaos")), + parents = listOf("overworld_chaos"), blocks = listOf( BlockEntry(data = "minecraft:ancient_debris", weight = 10), BlockEntry(data = "minecraft:netherite_block", weight = 1), From 120e8677f0239c8f6e1976a87982378181e5d266 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 20:52:00 +0100 Subject: [PATCH 08/17] fix: adjust distance threshold for nearby islands check --- .../kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt index 51b32e3..31c2415 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandService.kt @@ -60,7 +60,7 @@ object IslandService { uuid, oldLoc ) - + return true } @@ -112,7 +112,7 @@ object IslandService { } fun anyNearIslands(location: Location): Boolean = all().any { island -> - island.oneBlock.distanceSquared(location) < 5 + island.oneBlock.distanceSquared(location) < 10 } suspend fun flushAll() { From 51cd0ecc22dbf42275ba6b2dcc6102f2d36becd9 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 20:53:57 +0100 Subject: [PATCH 09/17] feat: rename placeholder for top ten leaderboard check --- .../oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt index 0b6aee2..d1198f2 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt @@ -4,7 +4,7 @@ import dev.slne.surf.event.oneblock.papi.ValueHolder import dev.slne.surf.surfapi.bukkit.api.hook.papi.expansion.PapiPlaceholder import org.bukkit.OfflinePlayer -object LeaderboardIsTopTenPlaceholder : PapiPlaceholder("place") { +object LeaderboardIsTopTenPlaceholder : PapiPlaceholder("isTopTen") { override fun parse( player: OfflinePlayer, args: List From f11ffc45917a7a2d6c74cd8ff52995edabc2140e Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 20:55:20 +0100 Subject: [PATCH 10/17] fix: correct German grammar in relocation error messages --- .../dev/slne/surf/event/oneblock/session/PlayerSession.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt index 7c461e8..927166f 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/session/PlayerSession.kt @@ -107,11 +107,11 @@ class PlayerSession(val uuid: UUID, private val state: PlayerStateDTO) : Closeab }), TO_HIGH({ appendErrorPrefix() - error("Der Zielort ist zu hoch. Bitte wählen einen niedrigeren Ort.") + error("Der Zielort ist zu hoch. Bitte wähle einen niedrigeren Ort.") }), TO_LOW({ appendErrorPrefix() - error("Der Zielort ist zu niedrig. Bitte wählen einen höheren Ort.") + error("Der Zielort ist zu niedrig. Bitte wähle einen höheren Ort.") }); val message = buildText(message) From 679d3a8e9397f4dc09853fe158e0d203b98c88e4 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 21:02:37 +0100 Subject: [PATCH 11/17] feat: update leaderboard placeholders for top ten and own place checks --- .../slne/surf/event/oneblock/papi/OneBlockPapiExpansion.kt | 4 +++- .../papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/OneBlockPapiExpansion.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/OneBlockPapiExpansion.kt index 43475da..23a0e82 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/OneBlockPapiExpansion.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/OneBlockPapiExpansion.kt @@ -1,5 +1,6 @@ package dev.slne.surf.event.oneblock.papi +import dev.slne.surf.event.oneblock.papi.leaderboard.LeaderboardIsTopTenPlaceholder import dev.slne.surf.event.oneblock.papi.leaderboard.LeaderboardOwnPlacePlaceholder import dev.slne.surf.event.oneblock.papi.leaderboard.LeaderboardPlacePlaceholder import dev.slne.surf.event.oneblock.plugin @@ -13,7 +14,8 @@ class OneBlockPapiExpansion : PapiExpansion( PlayerNamePlaceholder(), TotalBlocksPlaceholder(), LeaderboardPlacePlaceholder, - LeaderboardOwnPlacePlaceholder + LeaderboardOwnPlacePlaceholder, + LeaderboardIsTopTenPlaceholder ), "twisti, Ammo, red", plugin.pluginMeta.version diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt index d1198f2..680cc68 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/LeaderboardIsTopTenPlaceholder.kt @@ -10,11 +10,11 @@ object LeaderboardIsTopTenPlaceholder : PapiPlaceholder("isTopTen") { args: List ): String { val uuid = player.uniqueId - val ownPlace = ValueHolder.getPlace(uuid) ?: return "false" + val ownPlace = ValueHolder.getPlace(uuid) ?: return "scoreboard02" if (ownPlace <= 10) { - return "true" + return "scoreboard01" } - return "false" + return "scoreboard02" } } \ No newline at end of file From da75a833fd3f352296536833081f92c56a237e2f Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 19 Feb 2026 21:09:28 +0100 Subject: [PATCH 12/17] feat: enhance player name serialization with miniMessage support --- .../event/oneblock/papi/leaderboard/leaderboard-util.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/leaderboard-util.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/leaderboard-util.kt index 37433cb..fc64a45 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/leaderboard-util.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/leaderboard-util.kt @@ -3,6 +3,8 @@ package dev.slne.surf.event.oneblock.papi.leaderboard import com.github.shynixn.mccoroutine.folia.launch import dev.slne.surf.event.oneblock.plugin import dev.slne.surf.surfapi.bukkit.api.extensions.server +import dev.slne.surf.surfapi.core.api.minimessage.miniMessage +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer import net.luckperms.api.LuckPermsProvider import java.util.* @@ -15,7 +17,8 @@ fun prefixPlayer(uuid: UUID): String { luckPerms.userManager.loadUser(uuid) } - return player.name ?: "Fehler" + return LegacyComponentSerializer.legacySection() + .serialize(miniMessage.deserialize(player.name ?: "Fehler")) } val primaryGroup = user.primaryGroup @@ -23,5 +26,6 @@ fun prefixPlayer(uuid: UUID): String { val group = luckPerms.groupManager.getGroup(primaryGroup) ?: return player.name ?: "Fehler" val prefix = group.cachedData.metaData.prefix - return "$prefix ${player.name}" + return LegacyComponentSerializer.legacySection() + .serialize(miniMessage.deserialize("$prefix ${player.name}")) } \ No newline at end of file From a012c1e3055ec8b7821ac1f02dd9f41f62e56eb3 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Fri, 20 Feb 2026 19:52:07 +0100 Subject: [PATCH 13/17] feat: implement player stats flushing on quit and save events --- .../surf-oneblock/build.gradle.kts | 3 ++ .../surf-oneblock/libs/surf-stats-api.jar | Bin 0 -> 21129 bytes .../dev/slne/surf/event/oneblock/PaperMain.kt | 2 ++ .../listener/OneBlockConnectionListener.kt | 26 +++++++++++++++ .../oneblock/listener/OneBlockSaveListener.kt | 2 ++ .../oneblock/progress/ProgressService.kt | 31 +++++++++++++++++- 6 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 surf-event-events/surf-oneblock/libs/surf-stats-api.jar diff --git a/surf-event-events/surf-oneblock/build.gradle.kts b/surf-event-events/surf-oneblock/build.gradle.kts index a102ef0..298793a 100644 --- a/surf-event-events/surf-oneblock/build.gradle.kts +++ b/surf-event-events/surf-oneblock/build.gradle.kts @@ -14,12 +14,15 @@ surfPaperPluginApi { registerRequired("FastAsyncWorldEdit") registerRequired("FancyHolograms") registerRequired("LuckPerms") + registerRequired("surf-stats-paper") } } dependencies { implementation("dev.slne.surf:surf-database-r2dbc:1.0.0-SNAPSHOT") + compileOnly(files("libs/surf-stats-api.jar")) + implementation(platform("com.intellectualsites.bom:bom-newest:1.55")) // Ref: https://github.com/IntellectualSites/bom compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core") compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit") { isTransitive = false } diff --git a/surf-event-events/surf-oneblock/libs/surf-stats-api.jar b/surf-event-events/surf-oneblock/libs/surf-stats-api.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6a9da8f8e8b29eeec759c4093d973d756e0b83e GIT binary patch literal 21129 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 literal 0 HcmV?d00001 diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt index 24824fb..dd060f3 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt @@ -15,6 +15,7 @@ import dev.slne.surf.event.oneblock.listener.OneBlockConnectionListener import dev.slne.surf.event.oneblock.listener.OneBlockSaveListener import dev.slne.surf.event.oneblock.listener.OneBlockSpawnListener import dev.slne.surf.event.oneblock.papi.OneBlockPapiExpansion +import dev.slne.surf.event.oneblock.progress.ProgressService import dev.slne.surf.surfapi.bukkit.api.event.register import dev.slne.surf.surfapi.bukkit.api.extensions.server import dev.slne.surf.surfapi.bukkit.api.hook.papi.papiHook @@ -56,6 +57,7 @@ class PaperMain : SuspendingJavaPlugin() { } override suspend fun onDisableAsync() { + ProgressService.flushStats() IslandManager.saveIdx() IslandService.flushAll() diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockConnectionListener.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockConnectionListener.kt index d7c14ad..cb46761 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockConnectionListener.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockConnectionListener.kt @@ -1,15 +1,20 @@ package dev.slne.surf.event.oneblock.listener import com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent +import com.github.shynixn.mccoroutine.folia.launch import dev.slne.surf.event.oneblock.db.IslandService import dev.slne.surf.event.oneblock.island.IslandManager import dev.slne.surf.event.oneblock.messages.MessageManager +import dev.slne.surf.event.oneblock.plugin +import dev.slne.surf.event.oneblock.progress.phaseConfig import dev.slne.surf.event.oneblock.session.PlayerSessionManager +import dev.slne.surf.stats.api.surfStatsApi import dev.slne.surf.surfapi.core.api.util.logger import io.papermc.paper.event.connection.configuration.AsyncPlayerConnectionConfigureEvent import kotlinx.coroutines.runBlocking import org.bukkit.event.EventHandler import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerQuitEvent import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -53,4 +58,25 @@ object OneBlockConnectionListener : Listener { fun shouldTeleportToIsland(playerId: UUID): Boolean { return tpToIsland.remove(playerId) } + + @EventHandler + fun onQuit(event: PlayerQuitEvent) { + val island = IslandService.getIsland(event.player.uniqueId) ?: return + + plugin.launch { + surfStatsApi.saveCustomStat( + event.player.uniqueId, + event.player.name, + "minecraft:one_block_mined", + island.totalMined + ) + + surfStatsApi.saveCustomStat( + event.player.uniqueId, + event.player.name, + "minecraft:one_block_level", + phaseConfig.currentPhase(island.totalMined).weight.toLong() + ) + } + } } \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSaveListener.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSaveListener.kt index 9213f2d..007d2ff 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSaveListener.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockSaveListener.kt @@ -6,6 +6,7 @@ import dev.slne.surf.event.oneblock.global.GlobalGoals import dev.slne.surf.event.oneblock.island.IslandManager import dev.slne.surf.event.oneblock.overworld import dev.slne.surf.event.oneblock.plugin +import dev.slne.surf.event.oneblock.progress.ProgressService import org.bukkit.event.EventHandler import org.bukkit.event.Listener import org.bukkit.event.world.WorldSaveEvent @@ -16,6 +17,7 @@ object OneBlockSaveListener : Listener { if (event.world != overworld) return plugin.launch { + ProgressService.flushStats() IslandService.flushAll() GlobalGoals.flush() IslandManager.saveIdx() diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ProgressService.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ProgressService.kt index fda3f63..e414697 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ProgressService.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ProgressService.kt @@ -2,11 +2,15 @@ package dev.slne.surf.event.oneblock.progress import dev.slne.surf.event.oneblock.db.IslandService import dev.slne.surf.event.oneblock.global.GlobalGoals +import dev.slne.surf.event.oneblock.plugin +import dev.slne.surf.stats.api.surfStatsApi import dev.slne.surf.surfapi.core.api.messages.adventure.sendText +import io.papermc.paper.threadedregions.scheduler.ScheduledTask +import org.bukkit.Bukkit import org.bukkit.entity.Player object ProgressService { - + lateinit var statsSaveTask: ScheduledTask fun onBlockMined(player: Player) { val island = IslandService.incrementMined(player.uniqueId) ?: return val newPhase = phaseConfig.currentPhase(island.totalMined) @@ -24,4 +28,29 @@ object ProgressService { GlobalGoals.onBlockMined() } + suspend fun flushStats() { + val saved = Bukkit.getOnlinePlayers().map { + saveStats(it) + } + + plugin.logger.info("Successfully flushed stats for ${saved.size} players.") + } + + private suspend fun saveStats(player: Player) { + val island = IslandService.getIsland(player.uniqueId) ?: return + + surfStatsApi.saveCustomStat( + player.uniqueId, + player.name, + "minecraft:one_block_mined", + island.totalMined + ) + + surfStatsApi.saveCustomStat( + player.uniqueId, + player.name, + "minecraft:one_block_level", + phaseConfig.currentPhase(island.totalMined).weight.toLong() + ) + } } \ No newline at end of file From 13498cb8741edcff2e48514136d3b541aead572b Mon Sep 17 00:00:00 2001 From: twisti Date: Fri, 20 Feb 2026 21:24:10 +0100 Subject: [PATCH 14/17] feat: implement configuration migration for phase parents to enhance data structure --- .../dev/slne/surf/event/oneblock/PaperMain.kt | 5 ++ .../oneblock/progress/ConfigMigration.kt | 77 +++++++++++++++++++ .../event/oneblock/progress/PhaseConfig.kt | 40 ++++------ .../event/oneblock/progress/default-phases.kt | 42 +++++----- 4 files changed, 119 insertions(+), 45 deletions(-) create mode 100644 surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ConfigMigration.kt diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt index dd060f3..910340a 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt @@ -15,12 +15,15 @@ import dev.slne.surf.event.oneblock.listener.OneBlockConnectionListener import dev.slne.surf.event.oneblock.listener.OneBlockSaveListener import dev.slne.surf.event.oneblock.listener.OneBlockSpawnListener import dev.slne.surf.event.oneblock.papi.OneBlockPapiExpansion +import dev.slne.surf.event.oneblock.progress.ConfigMigration import dev.slne.surf.event.oneblock.progress.ProgressService import dev.slne.surf.surfapi.bukkit.api.event.register import dev.slne.surf.surfapi.bukkit.api.extensions.server import dev.slne.surf.surfapi.bukkit.api.hook.papi.papiHook import org.bukkit.World import org.bukkit.plugin.java.JavaPlugin +import org.spongepowered.configurate.transformation.ConfigurationTransformation +import kotlin.io.path.div class PaperMain : SuspendingJavaPlugin() { private lateinit var databaseApi: DatabaseApi @@ -34,6 +37,8 @@ class PaperMain : SuspendingJavaPlugin() { PlayerStateTable ) } + + ConfigMigration.upgradeFile(dataPath / "phases.yml") } override fun onEnable() { diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ConfigMigration.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ConfigMigration.kt new file mode 100644 index 0000000..e36ce6c --- /dev/null +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ConfigMigration.kt @@ -0,0 +1,77 @@ +package dev.slne.surf.event.oneblock.progress + +import dev.slne.surf.event.oneblock.plugin +import org.spongepowered.configurate.ConfigurationNode +import org.spongepowered.configurate.NodePath +import org.spongepowered.configurate.transformation.ConfigurationTransformation +import org.spongepowered.configurate.yaml.NodeStyle +import org.spongepowered.configurate.yaml.YamlConfigurationLoader +import java.nio.file.Path + +object ConfigMigration { + private const val VERSION_LATEST = 0 + + fun create(): ConfigurationTransformation.Versioned { + return ConfigurationTransformation.versionedBuilder() + .addVersion(0, initialMigrateParents()) + .build() + } + + fun initialMigrateParents(): ConfigurationTransformation { + return ConfigurationTransformation.builder() + .addAction( + NodePath.path( + "phases", + ConfigurationTransformation.WILDCARD_OBJECT, + "parents" + ) + ) { _, parentsNode -> + if (!parentsNode.isList) return@addAction null + + val children = parentsNode.childrenList() + for (i in children.indices) { + val entry = parentsNode.node(i) + + val id = entry.string + if (id != null) { + entry.set(null) + entry.node("id").set(id) + entry.node("weight").set(1.0) + } else { + if (!entry.node("weight").virtual()) continue + if (!entry.node("id").virtual() && entry.node("weight").virtual()) { + entry.node("weight").set(1.0) + } + } + } + + null + } + .build() + } + + fun updateNode(node: N): N { + if (!node.virtual()) { + val trans = create() + val start = trans.version(node) + trans.apply(node) + val end = trans.version(node) + if (start != end) { + plugin.logger.info("Updated config schema from version $start to $end") + } + } + return node + } + + fun upgradeFile(path: Path) { + val loader = YamlConfigurationLoader.builder() + .nodeStyle(NodeStyle.BLOCK) + .defaultOptions { + it.shouldCopyDefaults(true) + } + .path(path) + .build() + + loader.save(updateNode(loader.load())) + } +} \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt index f7a43c6..9050c11 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt @@ -52,13 +52,19 @@ data class PhaseConfig( } } + @ConfigSerializable + data class ParentEntry( + val id: String, + val weight: Double = 1.0 + ) + @ConfigSerializable data class Phase( val id: String, val displayName: String = id.replaceFirstChar { it.uppercaseChar() }.replace('_', ' '), val startsAt: Int, val weight: Int, - val parents: List, + val parents: List, val blocks: List, val entities: List ) { @@ -91,18 +97,11 @@ data class PhaseConfig( val parentBudget = ownTotal * parentShare var remainingBudget = parentBudget - var levelFactor = 1.0 - var levelFactorSum = 0.0 - val levelFactors = ArrayList(parents.size) - for (i in parents.indices) { - levelFactors += levelFactor - levelFactorSum += levelFactor - levelFactor *= PARENT_DECAY - } + val levelFactorSum = parents.sumOf { it.weight } - for ((idx, parentId) in parents.withIndex()) { + for (parentEntry in parents) { if (remainingBudget <= 1e-9) break - val parent = config.findById(parentId) ?: continue + val parent = config.findById(parentEntry.id) ?: continue val parentBlocks = parent.blocks if (parentBlocks.isEmpty()) continue @@ -110,7 +109,7 @@ data class PhaseConfig( if (parentRawTotal <= 0.0) continue val shareForThisParent = if (levelFactorSum > 0.0) - parentBudget * (levelFactors[idx] / levelFactorSum) + parentBudget * (parentEntry.weight / levelFactorSum) else 0.0 val assigned = shareForThisParent.coerceAtMost(remainingBudget) @@ -126,7 +125,7 @@ data class PhaseConfig( remainingBudget -= assigned } - if (choices.isEmpty()) { + if (choices.isEmpty) { choices += WeightedBlock.dirt() } @@ -148,18 +147,11 @@ data class PhaseConfig( val parentBudget = (if (ownTotal > 0.0) ownTotal else 1.0) * parentShare var remainingBudget = parentBudget - var levelFactor = 1.0 - var levelFactorSum = 0.0 - val levelFactors = ArrayList(parents.size) - for (i in parents.indices) { - levelFactors += levelFactor - levelFactorSum += levelFactor - levelFactor *= PARENT_DECAY - } + val levelFactorSum = parents.sumOf { it.weight } - for ((idx, parentId) in parents.withIndex()) { + for (parentEntry in parents) { if (remainingBudget <= 1e-9) break - val parent = config.findById(parentId) ?: continue + val parent = config.findById(parentEntry.id) ?: continue val parentEntities = parent.entities if (parentEntities.isEmpty()) continue @@ -167,7 +159,7 @@ data class PhaseConfig( if (parentRawTotal <= 0.0) continue val shareForThisParent = if (levelFactorSum > 0.0) - parentBudget * (levelFactors[idx] / levelFactorSum) + parentBudget * (parentEntry.weight / levelFactorSum) else 0.0 val assigned = shareForThisParent.coerceAtMost(remainingBudget) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt index 5548667..0f2be48 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/default-phases.kt @@ -24,7 +24,7 @@ val defaultPhases = listOf( id = "early_mining", startsAt = 150, weight = 2, - parents = listOf("start_plains"), + parents = listOf(ParentEntry("start_plains")), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 50), BlockEntry(data = "minecraft:coal_ore", weight = 20), @@ -40,7 +40,7 @@ val defaultPhases = listOf( id = "early_caves", startsAt = 350, weight = 2, - parents = listOf("early_mining"), + parents = listOf(ParentEntry("early_mining")), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 40), BlockEntry(data = "minecraft:coal_ore", weight = 25), @@ -57,7 +57,7 @@ val defaultPhases = listOf( id = "iron_age", startsAt = 700, weight = 3, - parents = listOf("early_caves"), + parents = listOf(ParentEntry("early_caves")), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 35), BlockEntry(data = "minecraft:iron_ore", weight = 30), @@ -74,7 +74,7 @@ val defaultPhases = listOf( id = "abandoned_mineshaft", startsAt = 1100, weight = 3, - parents = listOf("iron_age"), + parents = listOf(ParentEntry("iron_age")), blocks = listOf( BlockEntry(data = "minecraft:oak_planks", weight = 20), BlockEntry(data = "minecraft:oak_log", weight = 15), @@ -91,7 +91,7 @@ val defaultPhases = listOf( id = "deep_caves", startsAt = 1600, weight = 4, - parents = listOf("iron_age"), + parents = listOf(ParentEntry("iron_age")), blocks = listOf( BlockEntry(data = "minecraft:deepslate", weight = 40), BlockEntry(data = "minecraft:iron_ore", weight = 20), @@ -108,7 +108,7 @@ val defaultPhases = listOf( id = "redstone_labs", startsAt = 2200, weight = 4, - parents = listOf("deep_caves"), + parents = listOf(ParentEntry("deep_caves")), blocks = listOf( BlockEntry(data = "minecraft:redstone_ore", weight = 35), BlockEntry(data = "minecraft:deepslate", weight = 30), @@ -125,7 +125,7 @@ val defaultPhases = listOf( id = "lava_depths", startsAt = 3000, weight = 5, - parents = listOf("deep_caves"), + parents = listOf(ParentEntry("deep_caves")), blocks = listOf( BlockEntry(data = "minecraft:basalt", weight = 30), BlockEntry(data = "minecraft:magma_block", weight = 15), @@ -141,7 +141,7 @@ val defaultPhases = listOf( id = "dripstone_caves", startsAt = 3600, weight = 5, - parents = listOf("deep_caves"), + parents = listOf(ParentEntry("deep_caves")), blocks = listOf( BlockEntry(data = "minecraft:dripstone_block", weight = 40), BlockEntry(data = "minecraft:pointed_dripstone", weight = 20), @@ -157,7 +157,7 @@ val defaultPhases = listOf( id = "lush_caves", startsAt = 4200, weight = 5, - parents = listOf("deep_caves"), + parents = listOf(ParentEntry("deep_caves")), blocks = listOf( BlockEntry(data = "minecraft:moss_block", weight = 30), BlockEntry(data = "minecraft:clay", weight = 25), @@ -173,7 +173,7 @@ val defaultPhases = listOf( id = "nether_entry", startsAt = 5000, weight = 6, - parents = listOf("lava_depths"), + parents = listOf(ParentEntry("lava_depths")), blocks = listOf( BlockEntry(data = "minecraft:netherrack", weight = 60), BlockEntry(data = "minecraft:nether_quartz_ore", weight = 20), @@ -189,7 +189,7 @@ val defaultPhases = listOf( id = "nether_fortress", startsAt = 5800, weight = 7, - parents = listOf("nether_entry"), + parents = listOf(ParentEntry("nether_entry")), blocks = listOf( BlockEntry(data = "minecraft:nether_bricks", weight = 40), BlockEntry(data = "minecraft:soul_sand", weight = 20), @@ -205,7 +205,7 @@ val defaultPhases = listOf( id = "crimson_forest", startsAt = 6500, weight = 7, - parents = listOf("nether_entry"), + parents = listOf(ParentEntry("nether_entry")), blocks = listOf( BlockEntry(data = "minecraft:crimson_nylium", weight = 30), BlockEntry(data = "minecraft:crimson_stem", weight = 25), @@ -221,7 +221,7 @@ val defaultPhases = listOf( id = "warped_forest", startsAt = 7200, weight = 7, - parents = listOf("nether_entry"), + parents = listOf(ParentEntry("nether_entry")), blocks = listOf( BlockEntry(data = "minecraft:warped_nylium", weight = 30), BlockEntry(data = "minecraft:warped_stem", weight = 25), @@ -237,7 +237,7 @@ val defaultPhases = listOf( id = "basalt_deltas", startsAt = 7800, weight = 8, - parents = listOf("nether_entry"), + parents = listOf(ParentEntry("nether_entry")), blocks = listOf( BlockEntry(data = "minecraft:basalt", weight = 50), BlockEntry(data = "minecraft:blackstone", weight = 30), @@ -252,7 +252,7 @@ val defaultPhases = listOf( id = "diamond_depths", startsAt = 9000, weight = 8, - parents = listOf("deep_caves"), + parents = listOf(ParentEntry("deep_caves")), blocks = listOf( BlockEntry(data = "minecraft:deepslate", weight = 50), BlockEntry(data = "minecraft:diamond_ore", weight = 15), @@ -269,7 +269,7 @@ val defaultPhases = listOf( id = "ancient_city", startsAt = 10000, weight = 9, - parents = listOf("diamond_depths"), + parents = listOf(ParentEntry("diamond_depths")), blocks = listOf( BlockEntry(data = "minecraft:sculk", weight = 40), BlockEntry(data = "minecraft:sculk_catalyst", weight = 10), @@ -286,7 +286,7 @@ val defaultPhases = listOf( id = "stronghold", startsAt = 11000, weight = 9, - parents = listOf("diamond_depths"), + parents = listOf(ParentEntry("diamond_depths")), blocks = listOf( BlockEntry(data = "minecraft:stone_bricks", weight = 40), BlockEntry(data = "minecraft:cracked_stone_bricks", weight = 15), @@ -303,7 +303,7 @@ val defaultPhases = listOf( id = "the_end", startsAt = 13000, weight = 10, - parents = listOf("stronghold"), + parents = listOf(ParentEntry("stronghold")), blocks = listOf( BlockEntry(data = "minecraft:end_stone", weight = 70), BlockEntry(data = "minecraft:obsidian", weight = 20), @@ -318,7 +318,7 @@ val defaultPhases = listOf( id = "end_cities", startsAt = 15000, weight = 11, - parents = listOf("the_end"), + parents = listOf(ParentEntry("the_end")), blocks = listOf( BlockEntry(data = "minecraft:purpur_block", weight = 40), BlockEntry(data = "minecraft:end_stone", weight = 30), @@ -335,7 +335,7 @@ val defaultPhases = listOf( id = "overworld_chaos", startsAt = 17000, weight = 12, - parents = listOf("the_end"), + parents = listOf(ParentEntry("the_end")), blocks = listOf( BlockEntry(data = "minecraft:stone", weight = 30), BlockEntry(data = "minecraft:deepslate", weight = 30), @@ -352,7 +352,7 @@ val defaultPhases = listOf( id = "event_finale", startsAt = 20000, weight = 13, - parents = listOf("overworld_chaos"), + parents = listOf(ParentEntry("overworld_chaos")), blocks = listOf( BlockEntry(data = "minecraft:ancient_debris", weight = 10), BlockEntry(data = "minecraft:netherite_block", weight = 1), From d885e1cd6ab122a9cbc80b6e37d148751c4bda4a Mon Sep 17 00:00:00 2001 From: twisti Date: Fri, 20 Feb 2026 21:50:54 +0100 Subject: [PATCH 15/17] feat: enhance phase configuration loading with error handling and refactor data structure --- .../dev/slne/surf/event/oneblock/PaperMain.kt | 3 +- .../oneblock/progress/ConfigMigration.kt | 28 +++++++++++-------- .../event/oneblock/progress/PhaseConfig.kt | 9 ++++-- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt index 910340a..cd37b4c 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt @@ -16,13 +16,13 @@ import dev.slne.surf.event.oneblock.listener.OneBlockSaveListener import dev.slne.surf.event.oneblock.listener.OneBlockSpawnListener import dev.slne.surf.event.oneblock.papi.OneBlockPapiExpansion import dev.slne.surf.event.oneblock.progress.ConfigMigration +import dev.slne.surf.event.oneblock.progress.PhaseConfig import dev.slne.surf.event.oneblock.progress.ProgressService import dev.slne.surf.surfapi.bukkit.api.event.register import dev.slne.surf.surfapi.bukkit.api.extensions.server import dev.slne.surf.surfapi.bukkit.api.hook.papi.papiHook import org.bukkit.World import org.bukkit.plugin.java.JavaPlugin -import org.spongepowered.configurate.transformation.ConfigurationTransformation import kotlin.io.path.div class PaperMain : SuspendingJavaPlugin() { @@ -42,6 +42,7 @@ class PaperMain : SuspendingJavaPlugin() { } override fun onEnable() { + PhaseConfig OneBlockConnectionListener.register() OneBlockBlockListener.register() OneBlockSaveListener.register() diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ConfigMigration.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ConfigMigration.kt index e36ce6c..c34ce85 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ConfigMigration.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ConfigMigration.kt @@ -28,21 +28,25 @@ object ConfigMigration { ) { _, parentsNode -> if (!parentsNode.isList) return@addAction null - val children = parentsNode.childrenList() - for (i in children.indices) { - val entry = parentsNode.node(i) + val entries: List> = + parentsNode.childrenList().mapNotNull { child -> + child.string?.let { return@mapNotNull it to 1.0 } - val id = entry.string - if (id != null) { - entry.set(null) - entry.node("id").set(id) - entry.node("weight").set(1.0) - } else { - if (!entry.node("weight").virtual()) continue - if (!entry.node("id").virtual() && entry.node("weight").virtual()) { - entry.node("weight").set(1.0) + val id = child.node("id").string + if (id != null) { + val weight = child.node("weight").getDouble(1.0) + return@mapNotNull id to weight } + + null } + + parentsNode.set(null) + + for ((i, entry) in entries.withIndex()) { + val (id, weight) = entry + parentsNode.node(i).node("id").set(id) + parentsNode.node(i).node("weight").set(weight) } null diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt index 9050c11..3793a44 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/PhaseConfig.kt @@ -207,8 +207,13 @@ data class PhaseConfig( private val manager: SpongeConfigManager init { - surfConfigApi.createSpongeYmlConfig(plugin.dataPath, "phases.yml") - manager = surfConfigApi.getSpongeConfigManagerForConfig(PhaseConfig::class.java) + try { + surfConfigApi.createSpongeYmlConfig(plugin.dataPath, "phases.yml") + manager = surfConfigApi.getSpongeConfigManagerForConfig(PhaseConfig::class.java) + } catch (e: Throwable) { + plugin.componentLogger.error("Failed to load phase config!", e) + throw e + } } val config: PhaseConfig From 61d707b2f089dcb609c9ee890788493736ae07f4 Mon Sep 17 00:00:00 2001 From: twisti Date: Fri, 20 Feb 2026 22:13:10 +0100 Subject: [PATCH 16/17] feat: update island structure placement logic to use region dispatcher and set block data --- .../event/oneblock/island/IslandStructure.kt | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/island/IslandStructure.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/island/IslandStructure.kt index 19aa3bd..0773fca 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/island/IslandStructure.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/island/IslandStructure.kt @@ -1,23 +1,25 @@ package dev.slne.surf.event.oneblock.island -import com.sk89q.worldedit.WorldEdit -import com.sk89q.worldedit.bukkit.BukkitAdapter -import com.sk89q.worldedit.world.block.BlockTypes -import kotlinx.coroutines.Dispatchers +import com.github.shynixn.mccoroutine.folia.regionDispatcher +import dev.slne.surf.event.oneblock.plugin import kotlinx.coroutines.withContext import org.bukkit.Location +import org.bukkit.block.BlockType object IslandStructure { - suspend fun place(center: Location) = withContext(Dispatchers.IO) { + suspend fun place(center: Location) = withContext(plugin.regionDispatcher(center)) { val cx = center.blockX val cy = center.blockY val cz = center.blockZ + + center.block.blockData = BlockType.GRASS_BLOCK.createBlockData() + // val baseY = cy - 1 - WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(center.world)).use { session -> +// WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(center.world)).use { session -> // val islandState = BukkitAdapter.adapt(config.islandPlacement.islandBlockData) // val bedrockState = BlockTypes.BEDROCK!!.defaultState - val oneBlockState = BlockTypes.GRASS_BLOCK!!.defaultState +// val oneBlockState = BlockTypes.GRASS_BLOCK!!.defaultState // val islandRegion = CuboidRegion( // BlockVector3.at(cx - 1, baseY - 2, cz - 1), @@ -32,8 +34,8 @@ object IslandStructure { // // session.setBlocks(islandRegion, islandState) // session.setBlocks(bedrockRegion, bedrockState) - session.setBlock(cx, cy, cz, oneBlockState) +// session.setBlock(cx, cy, cz, oneBlockState) // session.setBlock(cx, baseY, cz, bedrockState) - } +// } } } \ No newline at end of file From fbe07745002f08ca22685dcf63a000deea089279 Mon Sep 17 00:00:00 2001 From: twisti Date: Sat, 21 Feb 2026 18:10:31 +0100 Subject: [PATCH 17/17] feat: update database column types to use nativeUuid for improved compatibility --- .../surf-oneblock/build.gradle.kts | 3 +-- .../dev/slne/surf/event/oneblock/PaperMain.kt | 7 ++--- .../event/oneblock/db/IslandRepository.kt | 26 ++++++++++--------- .../event/oneblock/db/table/IslandTable.kt | 5 ++-- .../oneblock/db/table/PlayerStateTable.kt | 5 ++-- .../listener/OneBlockConnectionListener.kt | 19 ++++++++------ 6 files changed, 34 insertions(+), 31 deletions(-) diff --git a/surf-event-events/surf-oneblock/build.gradle.kts b/surf-event-events/surf-oneblock/build.gradle.kts index 298793a..a97862a 100644 --- a/surf-event-events/surf-oneblock/build.gradle.kts +++ b/surf-event-events/surf-oneblock/build.gradle.kts @@ -7,6 +7,7 @@ plugins { surfPaperPluginApi { mainClass("dev.slne.surf.event.oneblock.PaperMain") generateLibraryLoader(false) + withSurfDatabaseR2dbc("1.3.0", "dev.slne.surf.event.oneblock.libs.db") authors.addAll("twisti", "Ammo", "red") @@ -19,8 +20,6 @@ surfPaperPluginApi { } dependencies { - implementation("dev.slne.surf:surf-database-r2dbc:1.0.0-SNAPSHOT") - compileOnly(files("libs/surf-stats-api.jar")) implementation(platform("com.intellectualsites.bom:bom-newest:1.55")) // Ref: https://github.com/IntellectualSites/bom diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt index cd37b4c..fb810c5 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/PaperMain.kt @@ -1,7 +1,6 @@ package dev.slne.surf.event.oneblock import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin -import com.github.shynixn.mccoroutine.folia.launch import dev.slne.surf.database.DatabaseApi import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.SchemaUtils import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction @@ -41,7 +40,7 @@ class PaperMain : SuspendingJavaPlugin() { ConfigMigration.upgradeFile(dataPath / "phases.yml") } - override fun onEnable() { + override suspend fun onEnableAsync() { PhaseConfig OneBlockConnectionListener.register() OneBlockBlockListener.register() @@ -57,9 +56,7 @@ class PaperMain : SuspendingJavaPlugin() { IslandManager.loadIdx() papiHook.register(OneBlockPapiExpansion()) - plugin.launch { - IslandService.fetchIslands() - } + IslandService.fetchIslands() } override suspend fun onDisableAsync() { diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandRepository.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandRepository.kt index 5b53525..44b77eb 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandRepository.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandRepository.kt @@ -44,20 +44,22 @@ object IslandRepository { } suspend fun findAll() = suspendTransaction { - IslandTable.selectAll().map { row -> - IslandDTO( - owner = row[IslandTable.ownerUuid], - oneBlock = Location( - Bukkit.getWorld(row[IslandTable.oneBlockWorld]) ?: error("World not found"), - row[IslandTable.oneBlockX], - row[IslandTable.oneBlockY], - row[IslandTable.oneBlockZ], - ), - totalMined = row[IslandTable.totalMined] - ) - }.toList() + IslandTable.selectAll() + .map { row -> + IslandDTO( + owner = row[IslandTable.ownerUuid], + oneBlock = Location( + Bukkit.getWorld(row[IslandTable.oneBlockWorld]) ?: error("World not found"), + row[IslandTable.oneBlockX], + row[IslandTable.oneBlockY], + row[IslandTable.oneBlockZ], + ), + totalMined = row[IslandTable.totalMined] + ) + }.toList() } + suspend fun saveAll(dtos: List) = suspendTransaction { IslandTable.batchUpsert(dtos, shouldReturnGeneratedValues = false) { dto -> this[IslandTable.ownerUuid] = dto.owner diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/table/IslandTable.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/table/IslandTable.kt index bf38ce2..7a00cb2 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/table/IslandTable.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/table/IslandTable.kt @@ -1,12 +1,13 @@ package dev.slne.surf.event.oneblock.db.table +import dev.slne.surf.database.columns.nativeUuid import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.dao.id.LongIdTable object IslandTable : LongIdTable("event_oneblock_islands") { - val ownerUuid = uuid("owner_uuid").uniqueIndex() + val ownerUuid = nativeUuid("owner_uuid").uniqueIndex() val oneBlockX = double("oneblock_x") val oneBlockY = double("oneblock_y") val oneBlockZ = double("oneblock_z") - val oneBlockWorld = uuid("oneblock_world") + val oneBlockWorld = nativeUuid("oneblock_world") val totalMined = long("total_mined").default(0) } \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/table/PlayerStateTable.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/table/PlayerStateTable.kt index 9b79d0b..7249c3c 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/table/PlayerStateTable.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/table/PlayerStateTable.kt @@ -1,9 +1,10 @@ package dev.slne.surf.event.oneblock.db.table -import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.dao.id.UUIDTable +import dev.slne.surf.database.columns.nativeUuid +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable object PlayerStateTable : UUIDTable("event_oneblock_player_state") { - val playerUuid = uuid("player_uuid").uniqueIndex() + val playerUuid = nativeUuid("player_uuid").uniqueIndex() val relocating = bool("relocating").default(false) val relocateTimestamp = long("relocate_timestamp").default(0L) } \ No newline at end of file diff --git a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockConnectionListener.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockConnectionListener.kt index cb46761..2220fa2 100644 --- a/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockConnectionListener.kt +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/listener/OneBlockConnectionListener.kt @@ -12,11 +12,13 @@ import dev.slne.surf.stats.api.surfStatsApi import dev.slne.surf.surfapi.core.api.util.logger import io.papermc.paper.event.connection.configuration.AsyncPlayerConnectionConfigureEvent import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout import org.bukkit.event.EventHandler import org.bukkit.event.Listener import org.bukkit.event.player.PlayerQuitEvent import java.util.* import java.util.concurrent.ConcurrentHashMap +import kotlin.time.Duration.Companion.seconds @Suppress("UnstableApiUsage") object OneBlockConnectionListener : Listener { @@ -29,14 +31,15 @@ object OneBlockConnectionListener : Listener { try { runBlocking { - PlayerSessionManager.createSession(playerId) - - if (!IslandService.hasIsland(playerId)) { - val created = IslandManager.createIslandForPlayer(playerId) - if (!created) { - event.connection.disconnect(MessageManager.unableToCreateIslandDisconnect) - } else { - tpToIsland.add(playerId) + withTimeout(30.seconds) { + PlayerSessionManager.createSession(playerId) + if (!IslandService.hasIsland(playerId)) { + val created = IslandManager.createIslandForPlayer(playerId) + if (!created) { + event.connection.disconnect(MessageManager.unableToCreateIslandDisconnect) + } else { + tpToIsland.add(playerId) + } } } }