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/build.gradle.kts b/surf-event-events/surf-oneblock/build.gradle.kts index 007dff7..a97862a 100644 --- a/surf-event-events/surf-oneblock/build.gradle.kts +++ b/surf-event-events/surf-oneblock/build.gradle.kts @@ -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") } \ No newline at end of file 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 0000000..e6a9da8 Binary files /dev/null and b/surf-event-events/surf-oneblock/libs/surf-stats-api.jar differ 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..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 @@ -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 @@ -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() @@ -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() 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..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 @@ -12,10 +19,38 @@ 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) { 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/IslandRepository.kt b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/db/IslandRepository.kt index 50852c7..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 @@ -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 @@ -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) = suspendTransaction { IslandTable.batchUpsert(dtos, shouldReturnGeneratedValues = false) { dto -> this[IslandTable.ownerUuid] = dto.owner @@ -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 } } + } \ 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..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 @@ -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 } @@ -79,6 +111,10 @@ object IslandService { } } + fun anyNearIslands(location: Location): Boolean = all().any { island -> + island.oneBlock.distanceSquared(location) < 10 + } + 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/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/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 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..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 @@ -1,17 +1,24 @@ 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 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 { @@ -24,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) + } } } } @@ -53,4 +61,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 a2fec5c..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,10 +17,10 @@ object OneBlockSaveListener : Listener { if (event.world != overworld) return plugin.launch { + ProgressService.flushStats() IslandService.flushAll() GlobalGoals.flush() 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 } 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..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,8 @@ 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 import dev.slne.surf.surfapi.bukkit.api.hook.papi.expansion.PapiExpansion @@ -9,7 +12,10 @@ class OneBlockPapiExpansion : PapiExpansion( LevelPlaceholder(), TotalBlocksGlobalPlaceholder(), PlayerNamePlaceholder(), - TotalBlocksPlaceholder() + TotalBlocksPlaceholder(), + LeaderboardPlacePlaceholder, + 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/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/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..680cc68 --- /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("isTopTen") { + override fun parse( + player: OfflinePlayer, + args: List + ): String { + val uuid = player.uniqueId + val ownPlace = ValueHolder.getPlace(uuid) ?: return "scoreboard02" + + if (ownPlace <= 10) { + return "scoreboard01" + } + return "scoreboard02" + } +} \ 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..fc64a45 --- /dev/null +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/papi/leaderboard/leaderboard-util.kt @@ -0,0 +1,31 @@ +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.* + +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 LegacyComponentSerializer.legacySection() + .serialize(miniMessage.deserialize(player.name ?: "Fehler")) + } + + val primaryGroup = user.primaryGroup + + val group = luckPerms.groupManager.getGroup(primaryGroup) ?: return player.name ?: "Fehler" + val prefix = group.cachedData.metaData.prefix + + return LegacyComponentSerializer.legacySection() + .serialize(miniMessage.deserialize("$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 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..c34ce85 --- /dev/null +++ b/surf-event-events/surf-oneblock/src/main/kotlin/dev/slne/surf/event/oneblock/progress/ConfigMigration.kt @@ -0,0 +1,81 @@ +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 entries: List> = + parentsNode.childrenList().mapNotNull { child -> + child.string?.let { return@mapNotNull it to 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 + } + .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..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 @@ -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) @@ -215,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 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 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), 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..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 @@ -1,6 +1,5 @@ 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 @@ -15,7 +14,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 +56,39 @@ 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 - } + suspend fun relocate(location: Location): RelocateResult { + val island = IslandService.getIsland(uuid) ?: error("Island not found for player $uuid") - if (!isInRangeOfOneBlock(player)) { - return RelocateResult.NOT_IN_RANGE + if (location.y >= config.islandPlacement.maxY) { + return RelocateResult.TO_HIGH } - if (isOnRelocationCooldown()) { - return RelocateResult.COOLDOWN + if (location.y <= config.islandPlacement.minY) { + return RelocateResult.TO_LOW } - state.relocating = true - flushState() - - return RelocateResult.START_RELOCATING - } - - suspend fun finishRelocate(player: Player, loc: 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,44 +97,26 @@ 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({ + LOCATION_OCCUPIED({ appendErrorPrefix() - error("Der ausgewählte Ort ist zu weit von dir entfernt.") + error("Der Zielort ist ungültig oder bereits belegt.") }), - ALREADY_RELOCATING({ + TO_HIGH({ appendErrorPrefix() - error("Du befindest dich bereits im Umzugsmodus.") + error("Der Zielort ist zu hoch. Bitte wähle einen niedrigeren Ort.") }), - LOCATION_OCCUPIED({ + TO_LOW({ appendErrorPrefix() - error("Der Zielort ist ungültig oder bereits belegt.") + error("Der Zielort ist zu niedrig. Bitte wähle einen höheren Ort.") }); val message = buildText(message) override fun asComponent() = message + + fun isSuccess() = this == RELOCATED } } \ No newline at end of file