Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
888a6db
feat: add ParentPhase class and update phase parents to use it
ammodev Feb 19, 2026
ac5ca0a
feat: update version to 1.21.11-1.0.5-SNAPSHOT and enhance relocation…
TheBjoRedCraft Feb 19, 2026
4771a6a
feat: add relocation height validation to PlayerSession
TheBjoRedCraft Feb 19, 2026
9de077a
fix: remove unnecessary newline and simplify player spawn condition
TheBjoRedCraft Feb 19, 2026
4873cb0
Merge remote-tracking branch 'refs/remotes/origin/chore/add-parent-we…
TheBjoRedCraft Feb 19, 2026
427ceb2
feat: add leaderboard placeholders and integrate LuckPerms for player…
TheBjoRedCraft Feb 19, 2026
917eed0
feat: implement island reset functionality and add leaderboard placeh…
TheBjoRedCraft Feb 19, 2026
3b4d57d
Revert "feat: add ParentPhase class and update phase parents to use it"
TheBjoRedCraft Feb 19, 2026
120e867
fix: adjust distance threshold for nearby islands check
TheBjoRedCraft Feb 19, 2026
51cd0ec
feat: rename placeholder for top ten leaderboard check
TheBjoRedCraft Feb 19, 2026
f11ffc4
fix: correct German grammar in relocation error messages
TheBjoRedCraft Feb 19, 2026
679d3a8
feat: update leaderboard placeholders for top ten and own place checks
TheBjoRedCraft Feb 19, 2026
da75a83
feat: enhance player name serialization with miniMessage support
TheBjoRedCraft Feb 19, 2026
a012c1e
feat: implement player stats flushing on quit and save events
TheBjoRedCraft Feb 20, 2026
13498cb
feat: implement configuration migration for phase parents to enhance …
twisti-dev Feb 20, 2026
d885e1c
feat: enhance phase configuration loading with error handling and ref…
twisti-dev Feb 20, 2026
61d707b
feat: update island structure placement logic to use region dispatche…
twisti-dev Feb 20, 2026
fbe0774
feat: update database column types to use nativeUuid for improved com…
twisti-dev Feb 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
version=1.21.11-1.0.5-SNAPSHOT
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -31,6 +33,7 @@ class PaperMain : SuspendingJavaPlugin() {
}

override suspend fun onDisableAsync() {
eventServerAccess.setEventServerState(EventServerState.UNKNOWN)
redisLoader.disconnect()
}

Expand Down
6 changes: 5 additions & 1 deletion surf-event-events/surf-oneblock/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@ 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")

serverDependencies {
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 }

compileOnly("de.oliver:FancyHolograms:2.7.0")
compileOnly("net.luckperms:api:5.4")
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -15,11 +14,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.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 kotlin.io.path.div

class PaperMain : SuspendingJavaPlugin() {
private lateinit var databaseApi: DatabaseApi
Expand All @@ -33,9 +36,12 @@ class PaperMain : SuspendingJavaPlugin() {
PlayerStateTable
)
}

ConfigMigration.upgradeFile(dataPath / "phases.yml")
}

override fun onEnable() {
override suspend fun onEnableAsync() {
PhaseConfig
OneBlockConnectionListener.register()
OneBlockBlockListener.register()
OneBlockSaveListener.register()
Expand All @@ -50,12 +56,11 @@ class PaperMain : SuspendingJavaPlugin() {
IslandManager.loadIdx()
papiHook.register(OneBlockPapiExpansion())

plugin.launch {
IslandService.fetchIslands()
}
IslandService.fetchIslands()
}

override suspend fun onDisableAsync() {
ProgressService.flushStats()
IslandManager.saveIdx()
IslandService.flushAll()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,56 @@
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

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)

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.")
}
}
}
}
}
}
}

private fun reload(sender: CommandSender) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<CommandSender> { sender ->
if (sender is Player) {
PlayerSession[sender.uniqueId].isRelocating
} else {
false
}
}
private val lastTimeRelocated = ConcurrentHashMap<UUID, Long>()
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")
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -43,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<IslandDTO>) = suspendTransaction {
IslandTable.batchUpsert(dtos, shouldReturnGeneratedValues = false) { dto ->
this[IslandTable.ownerUuid] = dto.owner
Expand All @@ -69,20 +72,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
}
}

}
Loading