From 3d4862adc00d269caea106db213a9cff96385783 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sun, 5 Mar 2023 17:33:54 -0800 Subject: [PATCH] More stuff. --- .../heimdall/load/ExportedBlockTable.kt | 19 +++++ .../heimdall/load/WorldLoadCompactWorld.kt | 69 +++++++++++++++++++ .../heimdall/load/WorldLoadFormat.kt | 2 + .../heimdall/load/WorldLoadSimpleWorld.kt | 64 +++++++++++++++++ .../heimdall/load/WorldLoadWorld.kt | 10 +-- .../foundation/common/BaseFoundationPlugin.kt | 21 ++++++ .../bifrost/FoundationBifrostPlugin.kt | 4 +- .../foundation/chaos/FoundationChaosPlugin.kt | 7 +- .../foundation/chaos/modules/ChaosModules.kt | 5 +- .../foundation/chaos/modules/PlayerSwap.kt | 30 ++++++++ foundation-core/build.gradle.kts | 1 + .../foundation/core/abstraction/Feature.kt | 16 ----- .../core/abstraction/FoundationPlugin.kt | 4 +- .../core/features/backup/BackupFeature.kt | 4 +- .../core/features/player/PlayerFeature.kt | 16 ++--- .../core/features/stats/StatsFeature.kt | 4 +- .../core/features/update/UpdateFeature.kt | 2 +- .../core/features/world/WorldFeature.kt | 4 +- .../plugin/FoundationHeimdallPlugin.kt | 8 ++- .../heimdall/plugin/export/ChunkExporter.kt | 6 +- .../plugin/export/ExportWorldLoadCommand.kt | 26 +++++++ .../plugin/export/WorldLoadExporter.kt | 48 +++++++++++++ .../heimdall/plugin/load/WorldReassembler.kt | 20 +++--- .../src/main/resources/plugin.yml | 4 ++ .../tool/commands/GenerateWorldLoadFile.kt | 20 ++++-- 25 files changed, 345 insertions(+), 69 deletions(-) create mode 100644 common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/ExportedBlockTable.kt create mode 100644 common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadCompactWorld.kt create mode 100644 common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadSimpleWorld.kt create mode 100644 common-plugin/src/main/kotlin/gay/pizza/foundation/common/BaseFoundationPlugin.kt create mode 100644 foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/modules/PlayerSwap.kt create mode 100644 foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/ExportWorldLoadCommand.kt create mode 100644 foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/WorldLoadExporter.kt diff --git a/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/ExportedBlockTable.kt b/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/ExportedBlockTable.kt new file mode 100644 index 0000000..f00be9c --- /dev/null +++ b/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/ExportedBlockTable.kt @@ -0,0 +1,19 @@ +package gay.pizza.foundation.heimdall.load + +import gay.pizza.foundation.heimdall.export.ExportedBlock + +class ExportedBlockTable { + private val internalBlocks = mutableListOf() + + val blocks: List + get() = internalBlocks + + fun index(block: ExportedBlock): Int { + val existing = internalBlocks.indexOf(block) + if (existing >= 0) { + return existing + } + internalBlocks.add(block) + return internalBlocks.size - 1 + } +} diff --git a/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadCompactWorld.kt b/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadCompactWorld.kt new file mode 100644 index 0000000..e71139f --- /dev/null +++ b/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadCompactWorld.kt @@ -0,0 +1,69 @@ +package gay.pizza.foundation.heimdall.load + +import kotlinx.serialization.Serializable +import kotlin.math.absoluteValue + +@Serializable +data class OffsetList, T>( + val offset: Int, + val data: L +) { + fun toMap(toKey: (Int) -> K): Map { + val map = mutableMapOf() + for ((index, value) in data.withIndex()) { + val real = index + offset + val key = toKey(real) + map[key] = value + } + return map + } + + fun map(value: (T) -> R): ImmutableOffsetList = + ImmutableOffsetList(offset, MutableList(data.size) { index -> value(data[index]) }) + + fun eachRealIndex(block: (Int, T) -> Unit) { + for ((fakeIndex, value) in data.withIndex()) { + val realIndex = fakeIndex + offset + block(realIndex, value) + } + } + + companion object { + fun transform( + map: Map, + minAndTotal: (Map) -> Pair, + keyToInt: (K) -> Int, + valueTransform: (T) -> V + ): ImmutableOffsetList { + val (min, total) = minAndTotal(map) + val offset = if (min < 0) min.absoluteValue else 0 + val list = MutableList(total) { null } + for ((key, value) in map) { + val pkey = keyToInt(key) + val rkey = pkey + offset + list[rkey] = valueTransform(value) + } + return OffsetList(if (min < 0) min else 0, list) + } + } +} + +typealias ImmutableOffsetList = OffsetList, T> + +@Serializable +class WorldLoadCompactWorld( + override val name: String, + val data: ImmutableOffsetList?>?> +) : WorldLoadWorld() { + override fun crawl(block: (Long, Long, Long, Int) -> Unit) { + data.eachRealIndex { x, zList -> + zList?.eachRealIndex { z, yList -> + yList?.eachRealIndex { y, index -> + if (index != null) { + block(x.toLong(), z.toLong(), y.toLong(), index) + } + } + } + } + } +} diff --git a/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadFormat.kt b/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadFormat.kt index 55648e4..de7b692 100644 --- a/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadFormat.kt +++ b/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadFormat.kt @@ -1,8 +1,10 @@ package gay.pizza.foundation.heimdall.load +import gay.pizza.foundation.heimdall.export.ExportedBlock import kotlinx.serialization.Serializable @Serializable class WorldLoadFormat( + val blockLookupTable: List, val worlds: Map ) diff --git a/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadSimpleWorld.kt b/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadSimpleWorld.kt new file mode 100644 index 0000000..6b6e73e --- /dev/null +++ b/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadSimpleWorld.kt @@ -0,0 +1,64 @@ +package gay.pizza.foundation.heimdall.load + +import kotlinx.serialization.Serializable +import kotlin.math.absoluteValue + +@Serializable +class WorldLoadSimpleWorld( + override val name: String, + val blocks: Map>> +) : WorldLoadWorld() { + fun compact(): WorldLoadCompactWorld { + val list = OffsetList.transform( + blocks, + minAndTotal = ::minAndTotal, + keyToInt = Long::toInt, + valueTransform = { zValue -> + OffsetList.transform( + zValue, + minAndTotal = ::minAndTotal, + keyToInt = Long::toInt, + valueTransform = { yValue -> + OffsetList.transform( + yValue, + minAndTotal = ::minAndTotal, + keyToInt = Long::toInt, + valueTransform = { it } + ) + } + ) + }) + return WorldLoadCompactWorld(name, list) + } + + private fun minAndTotal(map: Map): Pair { + val keys = map.keys + + if (keys.isEmpty()) { + return 0 to 0 + } + + val min = keys.min() + val max = keys.max() + var total = 1L + + if (max > 0) { + total += max + } + + if (min < 0) { + total += min.absoluteValue + } + return min.toInt() to total.toInt() + } + + override fun crawl(block: (Long, Long, Long, Int) -> Unit) { + for ((x, zBlocks) in blocks) { + for ((z, yBlocks) in zBlocks) { + for ((y, index) in yBlocks) { + block(x, z, y, index) + } + } + } + } +} diff --git a/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadWorld.kt b/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadWorld.kt index 46c80a0..24412cb 100644 --- a/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadWorld.kt +++ b/common-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/load/WorldLoadWorld.kt @@ -1,10 +1,10 @@ package gay.pizza.foundation.heimdall.load -import gay.pizza.foundation.heimdall.export.ExportedBlock import kotlinx.serialization.Serializable @Serializable -class WorldLoadWorld( - val name: String, - val blocks: Map>> -) +sealed class WorldLoadWorld { + abstract val name: String + + abstract fun crawl(block: (Long, Long, Long, Int) -> Unit) +} diff --git a/common-plugin/src/main/kotlin/gay/pizza/foundation/common/BaseFoundationPlugin.kt b/common-plugin/src/main/kotlin/gay/pizza/foundation/common/BaseFoundationPlugin.kt new file mode 100644 index 0000000..8037815 --- /dev/null +++ b/common-plugin/src/main/kotlin/gay/pizza/foundation/common/BaseFoundationPlugin.kt @@ -0,0 +1,21 @@ +package gay.pizza.foundation.common + +import org.bukkit.command.CommandExecutor +import org.bukkit.command.TabCompleter +import org.bukkit.plugin.java.JavaPlugin + +abstract class BaseFoundationPlugin : JavaPlugin() { + fun registerCommandExecutor(name: String, executor: CommandExecutor) { + registerCommandExecutor(listOf(name), executor) + } + + fun registerCommandExecutor(names: List, executor: CommandExecutor) { + for (name in names) { + val command = getCommand(name) ?: throw Exception("Failed to get $name command") + command.setExecutor(executor) + if (executor is TabCompleter) { + command.tabCompleter = executor + } + } + } +} diff --git a/foundation-bifrost/src/main/kotlin/gay/pizza/foundation/bifrost/FoundationBifrostPlugin.kt b/foundation-bifrost/src/main/kotlin/gay/pizza/foundation/bifrost/FoundationBifrostPlugin.kt index ed37f78..6e7cdbb 100644 --- a/foundation-bifrost/src/main/kotlin/gay/pizza/foundation/bifrost/FoundationBifrostPlugin.kt +++ b/foundation-bifrost/src/main/kotlin/gay/pizza/foundation/bifrost/FoundationBifrostPlugin.kt @@ -2,6 +2,7 @@ package gay.pizza.foundation.bifrost import com.charleskorn.kaml.Yaml import gay.pizza.foundation.bifrost.model.BifrostConfig +import gay.pizza.foundation.common.BaseFoundationPlugin import gay.pizza.foundation.common.FoundationCoreLoader import gay.pizza.foundation.shared.* import io.papermc.paper.event.player.AsyncChatEvent @@ -22,14 +23,13 @@ import org.bukkit.event.entity.PlayerDeathEvent import org.bukkit.event.player.PlayerAdvancementDoneEvent import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerQuitEvent -import org.bukkit.plugin.java.JavaPlugin import java.awt.Color import kotlin.io.path.inputStream import net.dv8tion.jda.api.hooks.EventListener as DiscordEventListener import org.bukkit.event.Listener as BukkitEventListener @PluginMainClass -class FoundationBifrostPlugin : JavaPlugin(), DiscordEventListener, BukkitEventListener { +class FoundationBifrostPlugin : BaseFoundationPlugin(), DiscordEventListener, BukkitEventListener { private lateinit var config: BifrostConfig private var jda: JDA? = null private var isDev = false diff --git a/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/FoundationChaosPlugin.kt b/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/FoundationChaosPlugin.kt index 60dc5af..4415eb3 100644 --- a/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/FoundationChaosPlugin.kt +++ b/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/FoundationChaosPlugin.kt @@ -2,14 +2,14 @@ package gay.pizza.foundation.chaos import com.charleskorn.kaml.Yaml import gay.pizza.foundation.chaos.model.ChaosConfig +import gay.pizza.foundation.common.BaseFoundationPlugin import gay.pizza.foundation.common.FoundationCoreLoader import gay.pizza.foundation.shared.PluginMainClass import gay.pizza.foundation.shared.copyDefaultConfig -import org.bukkit.plugin.java.JavaPlugin import kotlin.io.path.inputStream @PluginMainClass -class FoundationChaosPlugin : JavaPlugin() { +class FoundationChaosPlugin : BaseFoundationPlugin() { lateinit var config: ChaosConfig val controller by lazy { @@ -24,7 +24,6 @@ class FoundationChaosPlugin : JavaPlugin() { "chaos.yaml" ) config = Yaml.default.decodeFromStream(ChaosConfig.serializer(), configPath.inputStream()) - val chaosCommand = getCommand("chaos")!! - chaosCommand.setExecutor(ChaosToggleCommand()) + registerCommandExecutor("chaos", ChaosToggleCommand()) } } diff --git a/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/modules/ChaosModules.kt b/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/modules/ChaosModules.kt index b9c13db..2fd69d2 100644 --- a/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/modules/ChaosModules.kt +++ b/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/modules/ChaosModules.kt @@ -8,6 +8,7 @@ object ChaosModules { TeleportAllEntitiesNearestPlayer(plugin), KillRandomPlayer(plugin), TntAllPlayers(plugin), - MegaTnt(plugin) - ) + MegaTnt(plugin), + PlayerSwap(plugin) + ).shuffled() } diff --git a/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/modules/PlayerSwap.kt b/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/modules/PlayerSwap.kt new file mode 100644 index 0000000..3c6d0f4 --- /dev/null +++ b/foundation-chaos/src/main/kotlin/gay/pizza/foundation/chaos/modules/PlayerSwap.kt @@ -0,0 +1,30 @@ +package gay.pizza.foundation.chaos.modules + +import org.bukkit.Location +import org.bukkit.entity.Player +import org.bukkit.plugin.Plugin + +class PlayerSwap(val plugin: Plugin) : ChaosModule { + override fun id(): String = "player-swap" + override fun name(): String = "Player Swap" + override fun what(): String = "Randomly swaps player positions." + + override fun activate() { + for (world in plugin.server.worlds) { + if (world.playerCount <= 0) { + continue + } + + val players = world.players + val map = mutableMapOf() + for (player in players) { + val next = players.filter { it != player }.randomOrNull() ?: continue + map[player] = next.location.clone() + } + + for ((player, next) in map) { + player.teleport(next) + } + } + } +} \ No newline at end of file diff --git a/foundation-core/build.gradle.kts b/foundation-core/build.gradle.kts index d19836a..774eaeb 100644 --- a/foundation-core/build.gradle.kts +++ b/foundation-core/build.gradle.kts @@ -4,6 +4,7 @@ plugins { dependencies { api(project(":common-all")) + api(project(":common-plugin")) implementation(project(":foundation-shared")) implementation(libs.aws.sdk.s3) diff --git a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/abstraction/Feature.kt b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/abstraction/Feature.kt index d99e31f..0ae7c20 100644 --- a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/abstraction/Feature.kt +++ b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/abstraction/Feature.kt @@ -1,8 +1,6 @@ package gay.pizza.foundation.core.abstraction import gay.pizza.foundation.core.FoundationCorePlugin -import org.bukkit.command.CommandExecutor -import org.bukkit.command.TabCompleter import org.bukkit.event.Listener import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -16,18 +14,4 @@ abstract class Feature : CoreFeature, KoinComponent, Listener { override fun enable() {} override fun disable() {} override fun module() = module {} - - protected fun registerCommandExecutor(name: String, executor: CommandExecutor) { - registerCommandExecutor(listOf(name), executor) - } - - protected fun registerCommandExecutor(names: List, executor: CommandExecutor) { - for (name in names) { - val command = plugin.getCommand(name) ?: throw Exception("Failed to get $name command") - command.setExecutor(executor) - if (executor is TabCompleter) { - command.tabCompleter = executor - } - } - } } diff --git a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/abstraction/FoundationPlugin.kt b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/abstraction/FoundationPlugin.kt index 7b0c881..6cb867b 100644 --- a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/abstraction/FoundationPlugin.kt +++ b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/abstraction/FoundationPlugin.kt @@ -1,13 +1,13 @@ package gay.pizza.foundation.core.abstraction -import org.bukkit.plugin.java.JavaPlugin +import gay.pizza.foundation.common.BaseFoundationPlugin import org.koin.core.KoinApplication import org.koin.core.context.startKoin import org.koin.core.context.stopKoin import org.koin.core.module.Module import org.koin.dsl.module -abstract class FoundationPlugin : JavaPlugin() { +abstract class FoundationPlugin : BaseFoundationPlugin() { private lateinit var pluginModule: Module private lateinit var pluginApplication: KoinApplication private lateinit var features: List diff --git a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/backup/BackupFeature.kt b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/backup/BackupFeature.kt index 5867686..c94552b 100644 --- a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/backup/BackupFeature.kt +++ b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/backup/BackupFeature.kt @@ -1,11 +1,11 @@ package gay.pizza.foundation.core.features.backup import com.charleskorn.kaml.Yaml -import gay.pizza.foundation.shared.copyDefaultConfig import gay.pizza.foundation.core.FoundationCorePlugin import gay.pizza.foundation.core.abstraction.Feature import gay.pizza.foundation.core.features.scheduler.cancel import gay.pizza.foundation.core.features.scheduler.cron +import gay.pizza.foundation.shared.copyDefaultConfig import org.koin.core.component.inject import org.koin.dsl.module import software.amazon.awssdk.auth.credentials.AwsSessionCredentials @@ -25,7 +25,7 @@ class BackupFeature : Feature() { val backupPath = plugin.pluginDataPath.resolve(BACKUPS_DIRECTORY) backupPath.toFile().mkdir() - registerCommandExecutor("fbackup", BackupCommand(plugin, backupPath, config, s3Client)) + plugin.registerCommandExecutor("fbackup", BackupCommand(plugin, backupPath, config, s3Client)) if (config.schedule.cron.isNotEmpty()) { // Assume the user never wants to modify the second. I'm not sure why this is enforced in Quartz. diff --git a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/player/PlayerFeature.kt b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/player/PlayerFeature.kt index 061c1e2..7481810 100644 --- a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/player/PlayerFeature.kt +++ b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/player/PlayerFeature.kt @@ -4,9 +4,9 @@ import com.charleskorn.kaml.Yaml import com.google.common.cache.Cache import com.google.common.cache.CacheBuilder import com.google.common.cache.RemovalCause -import gay.pizza.foundation.shared.copyDefaultConfig import gay.pizza.foundation.core.FoundationCorePlugin import gay.pizza.foundation.core.abstraction.Feature +import gay.pizza.foundation.shared.copyDefaultConfig import net.kyori.adventure.text.Component import org.bukkit.GameMode import org.bukkit.event.EventHandler @@ -42,13 +42,13 @@ class PlayerFeature : Feature() { playerActivity.cleanUp() }, 20, 100) - registerCommandExecutor(listOf("survival", "s"), GamemodeCommand(GameMode.SURVIVAL)) - registerCommandExecutor(listOf("creative", "c"), GamemodeCommand(GameMode.CREATIVE)) - registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE)) - registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR)) - registerCommandExecutor(listOf("localweather", "lw"), LocalWeatherCommand()) - registerCommandExecutor(listOf("goose", "the_most_wonderful_kitty_ever"), GooseCommand()) - registerCommandExecutor(listOf("megatnt"), MegaTntCommand()) + plugin.registerCommandExecutor(listOf("survival", "s"), GamemodeCommand(GameMode.SURVIVAL)) + plugin.registerCommandExecutor(listOf("creative", "c"), GamemodeCommand(GameMode.CREATIVE)) + plugin.registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE)) + plugin.registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR)) + plugin.registerCommandExecutor(listOf("localweather", "lw"), LocalWeatherCommand()) + plugin.registerCommandExecutor(listOf("goose", "the_most_wonderful_kitty_ever"), GooseCommand()) + plugin.registerCommandExecutor(listOf("megatnt"), MegaTntCommand()) } override fun module() = org.koin.dsl.module { diff --git a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/stats/StatsFeature.kt b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/stats/StatsFeature.kt index 6e2e23c..9ca80a8 100644 --- a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/stats/StatsFeature.kt +++ b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/stats/StatsFeature.kt @@ -19,8 +19,8 @@ class StatsFeature : Feature() { override fun enable() { chatLogStore = persistence.value.store("chat-logs") - registerCommandExecutor(listOf("leaderboard", "lb"), LeaderboardCommand()) - registerCommandExecutor("pstore", PersistentStoreCommand(this)) + plugin.registerCommandExecutor(listOf("leaderboard", "lb"), LeaderboardCommand()) + plugin.registerCommandExecutor("pstore", PersistentStoreCommand(this)) } @EventHandler diff --git a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/update/UpdateFeature.kt b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/update/UpdateFeature.kt index b139ce0..493e054 100644 --- a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/update/UpdateFeature.kt +++ b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/update/UpdateFeature.kt @@ -4,6 +4,6 @@ import gay.pizza.foundation.core.abstraction.Feature class UpdateFeature : Feature() { override fun enable() { - registerCommandExecutor("fupdate", UpdateCommand()) + plugin.registerCommandExecutor("fupdate", UpdateCommand()) } } diff --git a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/world/WorldFeature.kt b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/world/WorldFeature.kt index 09e550c..1fe609b 100644 --- a/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/world/WorldFeature.kt +++ b/foundation-core/src/main/kotlin/gay/pizza/foundation/core/features/world/WorldFeature.kt @@ -4,7 +4,7 @@ import gay.pizza.foundation.core.abstraction.Feature class WorldFeature : Feature() { override fun enable() { - registerCommandExecutor("setspawn", SetSpawnCommand()) - registerCommandExecutor("spawn", SpawnCommand()) + plugin.registerCommandExecutor("setspawn", SetSpawnCommand()) + plugin.registerCommandExecutor("spawn", SpawnCommand()) } } diff --git a/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/FoundationHeimdallPlugin.kt b/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/FoundationHeimdallPlugin.kt index 79c1777..a96c8de 100644 --- a/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/FoundationHeimdallPlugin.kt +++ b/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/FoundationHeimdallPlugin.kt @@ -3,6 +3,7 @@ package gay.pizza.foundation.heimdall.plugin import com.charleskorn.kaml.Yaml import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariDataSource +import gay.pizza.foundation.common.BaseFoundationPlugin import gay.pizza.foundation.common.FoundationCoreLoader import gay.pizza.foundation.heimdall.plugin.buffer.BufferFlushThread import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer @@ -14,14 +15,13 @@ import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig import gay.pizza.foundation.shared.PluginMainClass import gay.pizza.foundation.shared.copyDefaultConfig import org.bukkit.event.Listener -import org.bukkit.plugin.java.JavaPlugin import org.jetbrains.exposed.sql.Database import org.postgresql.Driver import java.time.Duration import kotlin.io.path.inputStream @PluginMainClass -class FoundationHeimdallPlugin : JavaPlugin(), Listener { +class FoundationHeimdallPlugin : BaseFoundationPlugin(), Listener { private lateinit var config: HeimdallConfig private lateinit var pool: HikariDataSource internal var db: Database? = null @@ -36,6 +36,10 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener { throw Exception("Failed to get export_all_chunks command") exportChunksCommand.setExecutor(ExportAllChunksCommand(this)) + registerCommandExecutor("export_all_chunks", ExportAllChunksCommand(this)) + registerCommandExecutor("export_world_load", ExportAllChunksCommand(this)) + registerCommandExecutor("import_world_load", ExportAllChunksCommand(this)) + val importWorldLoadCommand = getCommand("import_world_load") ?: throw Exception("Failed to get import_world_load command") importWorldLoadCommand.setExecutor(ImportWorldLoadCommand(this)) diff --git a/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/ChunkExporter.kt b/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/ChunkExporter.kt index 8f883e2..90f6306 100644 --- a/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/ChunkExporter.kt +++ b/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/ChunkExporter.kt @@ -13,10 +13,6 @@ import java.io.File import java.util.zip.GZIPOutputStream class ChunkExporter(private val plugin: Plugin) { - private val json = Json { - ignoreUnknownKeys = true - } - fun exportLoadedChunksAsync(world: World) { exportChunkListAsync(world, world.loadedChunks.toList()) } @@ -56,7 +52,7 @@ class ChunkExporter(private val plugin: Plugin) { val fileOutputStream = file.outputStream() val gzipOutputStream = GZIPOutputStream(fileOutputStream) - json.encodeToStream(ExportedChunk.serializer(), chunk, gzipOutputStream) + Json.encodeToStream(ExportedChunk.serializer(), chunk, gzipOutputStream) gzipOutputStream.close() } diff --git a/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/ExportWorldLoadCommand.kt b/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/ExportWorldLoadCommand.kt new file mode 100644 index 0000000..a407cde --- /dev/null +++ b/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/ExportWorldLoadCommand.kt @@ -0,0 +1,26 @@ +package gay.pizza.foundation.heimdall.plugin.export + +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender +import org.bukkit.plugin.Plugin + +class ExportWorldLoadCommand(private val plugin: Plugin) : CommandExecutor { + override fun onCommand( + sender: CommandSender, + command: Command, + label: String, + args: Array + ): Boolean { + sender.sendMessage("Exporting all worlds...") + plugin.slF4JLogger.info("Exporting all worlds") + val export = WorldLoadExporter() + for (world in sender.server.worlds) { + export.exportLoadedChunks(world) + } + export.save() + sender.sendMessage("Exported all worlds...") + plugin.slF4JLogger.info("Exported all worlds") + return true + } +} diff --git a/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/WorldLoadExporter.kt b/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/WorldLoadExporter.kt new file mode 100644 index 0000000..2b707da --- /dev/null +++ b/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/export/WorldLoadExporter.kt @@ -0,0 +1,48 @@ +package gay.pizza.foundation.heimdall.plugin.export + +import gay.pizza.foundation.heimdall.export.ExportedBlock +import gay.pizza.foundation.heimdall.load.ExportedBlockTable +import gay.pizza.foundation.heimdall.load.WorldLoadFormat +import gay.pizza.foundation.heimdall.load.WorldLoadSimpleWorld +import gay.pizza.foundation.heimdall.load.WorldLoadWorld +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToStream +import org.bukkit.World +import java.nio.file.Paths +import kotlin.io.path.outputStream + +class WorldLoadExporter { + private val blockTable = ExportedBlockTable() + private val worlds = mutableMapOf() + + fun exportLoadedChunks(world: World) { + val data = mutableMapOf>>() + for (chunk in world.loadedChunks) { + val snapshot = chunk.chunkSnapshot + val yRange = world.minHeight until world.maxHeight + val chunkRange = 0..15 + for (x in chunkRange) { + for (z in chunkRange) { + for (y in yRange) { + val blockInfo = snapshot.getBlockData(x, y, z) + val block = ExportedBlock(blockInfo.material.key.toString(), blockInfo.asString) + data.getOrPut(x.toLong()) { + mutableMapOf() + }.getOrPut(z.toLong()) { + mutableMapOf() + }[y.toLong()] = blockTable.index(block) + } + } + } + } + worlds[world.name] = WorldLoadSimpleWorld(world.name, data).compact() + } + + fun save() { + val format = WorldLoadFormat(blockTable.blocks, worlds) + val path = Paths.get("world.load.json") + path.outputStream().use { stream -> + Json.encodeToStream(WorldLoadFormat.serializer(), format, stream) + } + } +} diff --git a/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/load/WorldReassembler.kt b/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/load/WorldReassembler.kt index 3caf325..a480c7b 100644 --- a/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/load/WorldReassembler.kt +++ b/foundation-heimdall/src/main/kotlin/gay/pizza/foundation/heimdall/plugin/load/WorldReassembler.kt @@ -27,20 +27,18 @@ class WorldReassembler(val plugin: Plugin, val server: Server, val format: World val blocksToMake = mutableListOf>() - for ((x, zBlocks) in load.blocks) { - for ((z, yBlocks) in zBlocks) { - for ((y, block) in yBlocks) { - val material: Material? = Material.matchMaterial(block.type) + load.crawl { x, z, y, blockIndex -> + val block = format.blockLookupTable[blockIndex] + val material: Material? = Material.matchMaterial(block.type) - if (material == null) { - feedback("Unknown Material '${block.type}' at $x $y $z") - continue - } - - blocksToMake.add(Location(world, x.toDouble(), y.toDouble(), z.toDouble()) to block) - } + if (material == null) { + feedback("Unknown Material '${block.type}' at $x $y $z") + return@crawl } + + blocksToMake.add(Location(world, x.toDouble(), y.toDouble(), z.toDouble()) to block) } + blocksToMake.sortBy { it.first.x } feedback("Will place ${blocksToMake.size} blocks in ${world.name}") diff --git a/foundation-heimdall/src/main/resources/plugin.yml b/foundation-heimdall/src/main/resources/plugin.yml index 70f4079..fcba220 100644 --- a/foundation-heimdall/src/main/resources/plugin.yml +++ b/foundation-heimdall/src/main/resources/plugin.yml @@ -13,6 +13,10 @@ commands: description: Export All Chunks usage: /export_all_chunks permission: heimdall.command.export_all_chunks + export_world_load: + description: Export World Load + usage: /export_world_load + permission: heimdall.command.export_world_load import_world_load: description: Import World Load usage: /import_world_load diff --git a/tool-gjallarhorn/src/main/kotlin/gay/pizza/foundation/heimdall/tool/commands/GenerateWorldLoadFile.kt b/tool-gjallarhorn/src/main/kotlin/gay/pizza/foundation/heimdall/tool/commands/GenerateWorldLoadFile.kt index 15ce2b2..26501a8 100644 --- a/tool-gjallarhorn/src/main/kotlin/gay/pizza/foundation/heimdall/tool/commands/GenerateWorldLoadFile.kt +++ b/tool-gjallarhorn/src/main/kotlin/gay/pizza/foundation/heimdall/tool/commands/GenerateWorldLoadFile.kt @@ -5,8 +5,9 @@ import com.github.ajalt.clikt.core.requireObject import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.types.path import gay.pizza.foundation.heimdall.export.ExportedBlock +import gay.pizza.foundation.heimdall.load.ExportedBlockTable import gay.pizza.foundation.heimdall.load.WorldLoadFormat -import gay.pizza.foundation.heimdall.load.WorldLoadWorld +import gay.pizza.foundation.heimdall.load.WorldLoadSimpleWorld import gay.pizza.foundation.heimdall.table.WorldChangeTable import gay.pizza.foundation.heimdall.tool.state.BlockChangelog import gay.pizza.foundation.heimdall.tool.state.BlockLogTracker @@ -24,23 +25,32 @@ class GenerateWorldLoadFile : CliktCommand(name = "generate-world-load", help = val path by argument("load-format-file").path() override fun run() { - val worlds = mutableMapOf() + val worlds = mutableMapOf() val worldChangelogs = BlockChangelog.query(db).splitBy { it.world } val worldNames = transaction(db) { WorldChangeTable.selectAll() .associate { it[WorldChangeTable.toWorld] to it[WorldChangeTable.toWorldName] } } + + val blockTable = ExportedBlockTable() + for ((id, changelog) in worldChangelogs) { val tracker = BlockLogTracker() tracker.replay(changelog) val sparse = tracker.buildBlockMap { ExportedBlock(it.type, it.data) } val blocks = sparse.blocks - worlds[id.toString().lowercase()] = WorldLoadWorld( + worlds[id.toString().lowercase()] = WorldLoadSimpleWorld( worldNames[id] ?: "unknown_$id", - blocks + blocks.mapValues { levelOne -> + levelOne.value.mapValues { levelTwo -> + levelTwo.value.mapValues { entry -> + blockTable.index(entry.value) + } + } + } ) } - val format = WorldLoadFormat(worlds) + val format = WorldLoadFormat(blockTable.blocks, worlds) path.deleteIfExists() Json.encodeToStream(format, path.outputStream()) }