More stuff.

This commit is contained in:
Alex Zenla 2023-03-05 17:33:54 -08:00
parent c973a1a3c6
commit 3d4862adc0
Signed by: alex
GPG Key ID: C0780728420EBFE5
25 changed files with 345 additions and 69 deletions

View File

@ -0,0 +1,19 @@
package gay.pizza.foundation.heimdall.load
import gay.pizza.foundation.heimdall.export.ExportedBlock
class ExportedBlockTable {
private val internalBlocks = mutableListOf<ExportedBlock>()
val blocks: List<ExportedBlock>
get() = internalBlocks
fun index(block: ExportedBlock): Int {
val existing = internalBlocks.indexOf(block)
if (existing >= 0) {
return existing
}
internalBlocks.add(block)
return internalBlocks.size - 1
}
}

View File

@ -0,0 +1,69 @@
package gay.pizza.foundation.heimdall.load
import kotlinx.serialization.Serializable
import kotlin.math.absoluteValue
@Serializable
data class OffsetList<L: List<T>, T>(
val offset: Int,
val data: L
) {
fun <K> toMap(toKey: (Int) -> K): Map<K, T> {
val map = mutableMapOf<K, T>()
for ((index, value) in data.withIndex()) {
val real = index + offset
val key = toKey(real)
map[key] = value
}
return map
}
fun <R> map(value: (T) -> R): ImmutableOffsetList<R> =
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 <K, T, V> transform(
map: Map<K, T>,
minAndTotal: (Map<K, T>) -> Pair<Int, Int>,
keyToInt: (K) -> Int,
valueTransform: (T) -> V
): ImmutableOffsetList<V?> {
val (min, total) = minAndTotal(map)
val offset = if (min < 0) min.absoluteValue else 0
val list = MutableList<V?>(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<T> = OffsetList<List<T>, T>
@Serializable
class WorldLoadCompactWorld(
override val name: String,
val data: ImmutableOffsetList<ImmutableOffsetList<ImmutableOffsetList<Int?>?>?>
) : 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)
}
}
}
}
}
}

View File

@ -1,8 +1,10 @@
package gay.pizza.foundation.heimdall.load package gay.pizza.foundation.heimdall.load
import gay.pizza.foundation.heimdall.export.ExportedBlock
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
class WorldLoadFormat( class WorldLoadFormat(
val blockLookupTable: List<ExportedBlock>,
val worlds: Map<String, WorldLoadWorld> val worlds: Map<String, WorldLoadWorld>
) )

View File

@ -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<Long, Map<Long, Map<Long, Int>>>
) : 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 <T> minAndTotal(map: Map<Long, T>): Pair<Int, Int> {
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)
}
}
}
}
}

View File

@ -1,10 +1,10 @@
package gay.pizza.foundation.heimdall.load package gay.pizza.foundation.heimdall.load
import gay.pizza.foundation.heimdall.export.ExportedBlock
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
class WorldLoadWorld( sealed class WorldLoadWorld {
val name: String, abstract val name: String
val blocks: Map<Long, Map<Long, Map<Long, ExportedBlock>>>
) abstract fun crawl(block: (Long, Long, Long, Int) -> Unit)
}

View File

@ -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<String>, 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
}
}
}
}

View File

@ -2,6 +2,7 @@ package gay.pizza.foundation.bifrost
import com.charleskorn.kaml.Yaml import com.charleskorn.kaml.Yaml
import gay.pizza.foundation.bifrost.model.BifrostConfig import gay.pizza.foundation.bifrost.model.BifrostConfig
import gay.pizza.foundation.common.BaseFoundationPlugin
import gay.pizza.foundation.common.FoundationCoreLoader import gay.pizza.foundation.common.FoundationCoreLoader
import gay.pizza.foundation.shared.* import gay.pizza.foundation.shared.*
import io.papermc.paper.event.player.AsyncChatEvent 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.PlayerAdvancementDoneEvent
import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.plugin.java.JavaPlugin
import java.awt.Color import java.awt.Color
import kotlin.io.path.inputStream import kotlin.io.path.inputStream
import net.dv8tion.jda.api.hooks.EventListener as DiscordEventListener import net.dv8tion.jda.api.hooks.EventListener as DiscordEventListener
import org.bukkit.event.Listener as BukkitEventListener import org.bukkit.event.Listener as BukkitEventListener
@PluginMainClass @PluginMainClass
class FoundationBifrostPlugin : JavaPlugin(), DiscordEventListener, BukkitEventListener { class FoundationBifrostPlugin : BaseFoundationPlugin(), DiscordEventListener, BukkitEventListener {
private lateinit var config: BifrostConfig private lateinit var config: BifrostConfig
private var jda: JDA? = null private var jda: JDA? = null
private var isDev = false private var isDev = false

View File

@ -2,14 +2,14 @@ package gay.pizza.foundation.chaos
import com.charleskorn.kaml.Yaml import com.charleskorn.kaml.Yaml
import gay.pizza.foundation.chaos.model.ChaosConfig import gay.pizza.foundation.chaos.model.ChaosConfig
import gay.pizza.foundation.common.BaseFoundationPlugin
import gay.pizza.foundation.common.FoundationCoreLoader import gay.pizza.foundation.common.FoundationCoreLoader
import gay.pizza.foundation.shared.PluginMainClass import gay.pizza.foundation.shared.PluginMainClass
import gay.pizza.foundation.shared.copyDefaultConfig import gay.pizza.foundation.shared.copyDefaultConfig
import org.bukkit.plugin.java.JavaPlugin
import kotlin.io.path.inputStream import kotlin.io.path.inputStream
@PluginMainClass @PluginMainClass
class FoundationChaosPlugin : JavaPlugin() { class FoundationChaosPlugin : BaseFoundationPlugin() {
lateinit var config: ChaosConfig lateinit var config: ChaosConfig
val controller by lazy { val controller by lazy {
@ -24,7 +24,6 @@ class FoundationChaosPlugin : JavaPlugin() {
"chaos.yaml" "chaos.yaml"
) )
config = Yaml.default.decodeFromStream(ChaosConfig.serializer(), configPath.inputStream()) config = Yaml.default.decodeFromStream(ChaosConfig.serializer(), configPath.inputStream())
val chaosCommand = getCommand("chaos")!! registerCommandExecutor("chaos", ChaosToggleCommand())
chaosCommand.setExecutor(ChaosToggleCommand())
} }
} }

View File

@ -8,6 +8,7 @@ object ChaosModules {
TeleportAllEntitiesNearestPlayer(plugin), TeleportAllEntitiesNearestPlayer(plugin),
KillRandomPlayer(plugin), KillRandomPlayer(plugin),
TntAllPlayers(plugin), TntAllPlayers(plugin),
MegaTnt(plugin) MegaTnt(plugin),
) PlayerSwap(plugin)
).shuffled()
} }

View File

@ -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<Player, Location>()
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)
}
}
}
}

View File

@ -4,6 +4,7 @@ plugins {
dependencies { dependencies {
api(project(":common-all")) api(project(":common-all"))
api(project(":common-plugin"))
implementation(project(":foundation-shared")) implementation(project(":foundation-shared"))
implementation(libs.aws.sdk.s3) implementation(libs.aws.sdk.s3)

View File

@ -1,8 +1,6 @@
package gay.pizza.foundation.core.abstraction package gay.pizza.foundation.core.abstraction
import gay.pizza.foundation.core.FoundationCorePlugin import gay.pizza.foundation.core.FoundationCorePlugin
import org.bukkit.command.CommandExecutor
import org.bukkit.command.TabCompleter
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
@ -16,18 +14,4 @@ abstract class Feature : CoreFeature, KoinComponent, Listener {
override fun enable() {} override fun enable() {}
override fun disable() {} override fun disable() {}
override fun module() = module {} override fun module() = module {}
protected fun registerCommandExecutor(name: String, executor: CommandExecutor) {
registerCommandExecutor(listOf(name), executor)
}
protected fun registerCommandExecutor(names: List<String>, 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
}
}
}
} }

View File

@ -1,13 +1,13 @@
package gay.pizza.foundation.core.abstraction 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.KoinApplication
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin import org.koin.core.context.stopKoin
import org.koin.core.module.Module import org.koin.core.module.Module
import org.koin.dsl.module import org.koin.dsl.module
abstract class FoundationPlugin : JavaPlugin() { abstract class FoundationPlugin : BaseFoundationPlugin() {
private lateinit var pluginModule: Module private lateinit var pluginModule: Module
private lateinit var pluginApplication: KoinApplication private lateinit var pluginApplication: KoinApplication
private lateinit var features: List<CoreFeature> private lateinit var features: List<CoreFeature>

View File

@ -1,11 +1,11 @@
package gay.pizza.foundation.core.features.backup package gay.pizza.foundation.core.features.backup
import com.charleskorn.kaml.Yaml import com.charleskorn.kaml.Yaml
import gay.pizza.foundation.shared.copyDefaultConfig
import gay.pizza.foundation.core.FoundationCorePlugin import gay.pizza.foundation.core.FoundationCorePlugin
import gay.pizza.foundation.core.abstraction.Feature import gay.pizza.foundation.core.abstraction.Feature
import gay.pizza.foundation.core.features.scheduler.cancel import gay.pizza.foundation.core.features.scheduler.cancel
import gay.pizza.foundation.core.features.scheduler.cron import gay.pizza.foundation.core.features.scheduler.cron
import gay.pizza.foundation.shared.copyDefaultConfig
import org.koin.core.component.inject import org.koin.core.component.inject
import org.koin.dsl.module import org.koin.dsl.module
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials import software.amazon.awssdk.auth.credentials.AwsSessionCredentials
@ -25,7 +25,7 @@ class BackupFeature : Feature() {
val backupPath = plugin.pluginDataPath.resolve(BACKUPS_DIRECTORY) val backupPath = plugin.pluginDataPath.resolve(BACKUPS_DIRECTORY)
backupPath.toFile().mkdir() backupPath.toFile().mkdir()
registerCommandExecutor("fbackup", BackupCommand(plugin, backupPath, config, s3Client)) plugin.registerCommandExecutor("fbackup", BackupCommand(plugin, backupPath, config, s3Client))
if (config.schedule.cron.isNotEmpty()) { if (config.schedule.cron.isNotEmpty()) {
// Assume the user never wants to modify the second. I'm not sure why this is enforced in Quartz. // Assume the user never wants to modify the second. I'm not sure why this is enforced in Quartz.

View File

@ -4,9 +4,9 @@ import com.charleskorn.kaml.Yaml
import com.google.common.cache.Cache import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder import com.google.common.cache.CacheBuilder
import com.google.common.cache.RemovalCause import com.google.common.cache.RemovalCause
import gay.pizza.foundation.shared.copyDefaultConfig
import gay.pizza.foundation.core.FoundationCorePlugin import gay.pizza.foundation.core.FoundationCorePlugin
import gay.pizza.foundation.core.abstraction.Feature import gay.pizza.foundation.core.abstraction.Feature
import gay.pizza.foundation.shared.copyDefaultConfig
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import org.bukkit.GameMode import org.bukkit.GameMode
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
@ -42,13 +42,13 @@ class PlayerFeature : Feature() {
playerActivity.cleanUp() playerActivity.cleanUp()
}, 20, 100) }, 20, 100)
registerCommandExecutor(listOf("survival", "s"), GamemodeCommand(GameMode.SURVIVAL)) plugin.registerCommandExecutor(listOf("survival", "s"), GamemodeCommand(GameMode.SURVIVAL))
registerCommandExecutor(listOf("creative", "c"), GamemodeCommand(GameMode.CREATIVE)) plugin.registerCommandExecutor(listOf("creative", "c"), GamemodeCommand(GameMode.CREATIVE))
registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE)) plugin.registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE))
registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR)) plugin.registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR))
registerCommandExecutor(listOf("localweather", "lw"), LocalWeatherCommand()) plugin.registerCommandExecutor(listOf("localweather", "lw"), LocalWeatherCommand())
registerCommandExecutor(listOf("goose", "the_most_wonderful_kitty_ever"), GooseCommand()) plugin.registerCommandExecutor(listOf("goose", "the_most_wonderful_kitty_ever"), GooseCommand())
registerCommandExecutor(listOf("megatnt"), MegaTntCommand()) plugin.registerCommandExecutor(listOf("megatnt"), MegaTntCommand())
} }
override fun module() = org.koin.dsl.module { override fun module() = org.koin.dsl.module {

View File

@ -19,8 +19,8 @@ class StatsFeature : Feature() {
override fun enable() { override fun enable() {
chatLogStore = persistence.value.store("chat-logs") chatLogStore = persistence.value.store("chat-logs")
registerCommandExecutor(listOf("leaderboard", "lb"), LeaderboardCommand()) plugin.registerCommandExecutor(listOf("leaderboard", "lb"), LeaderboardCommand())
registerCommandExecutor("pstore", PersistentStoreCommand(this)) plugin.registerCommandExecutor("pstore", PersistentStoreCommand(this))
} }
@EventHandler @EventHandler

View File

@ -4,6 +4,6 @@ import gay.pizza.foundation.core.abstraction.Feature
class UpdateFeature : Feature() { class UpdateFeature : Feature() {
override fun enable() { override fun enable() {
registerCommandExecutor("fupdate", UpdateCommand()) plugin.registerCommandExecutor("fupdate", UpdateCommand())
} }
} }

View File

@ -4,7 +4,7 @@ import gay.pizza.foundation.core.abstraction.Feature
class WorldFeature : Feature() { class WorldFeature : Feature() {
override fun enable() { override fun enable() {
registerCommandExecutor("setspawn", SetSpawnCommand()) plugin.registerCommandExecutor("setspawn", SetSpawnCommand())
registerCommandExecutor("spawn", SpawnCommand()) plugin.registerCommandExecutor("spawn", SpawnCommand())
} }
} }

View File

@ -3,6 +3,7 @@ package gay.pizza.foundation.heimdall.plugin
import com.charleskorn.kaml.Yaml import com.charleskorn.kaml.Yaml
import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource import com.zaxxer.hikari.HikariDataSource
import gay.pizza.foundation.common.BaseFoundationPlugin
import gay.pizza.foundation.common.FoundationCoreLoader import gay.pizza.foundation.common.FoundationCoreLoader
import gay.pizza.foundation.heimdall.plugin.buffer.BufferFlushThread import gay.pizza.foundation.heimdall.plugin.buffer.BufferFlushThread
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer 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.PluginMainClass
import gay.pizza.foundation.shared.copyDefaultConfig import gay.pizza.foundation.shared.copyDefaultConfig
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.plugin.java.JavaPlugin
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.postgresql.Driver import org.postgresql.Driver
import java.time.Duration import java.time.Duration
import kotlin.io.path.inputStream import kotlin.io.path.inputStream
@PluginMainClass @PluginMainClass
class FoundationHeimdallPlugin : JavaPlugin(), Listener { class FoundationHeimdallPlugin : BaseFoundationPlugin(), Listener {
private lateinit var config: HeimdallConfig private lateinit var config: HeimdallConfig
private lateinit var pool: HikariDataSource private lateinit var pool: HikariDataSource
internal var db: Database? = null internal var db: Database? = null
@ -36,6 +36,10 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
throw Exception("Failed to get export_all_chunks command") throw Exception("Failed to get export_all_chunks command")
exportChunksCommand.setExecutor(ExportAllChunksCommand(this)) 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") ?: val importWorldLoadCommand = getCommand("import_world_load") ?:
throw Exception("Failed to get import_world_load command") throw Exception("Failed to get import_world_load command")
importWorldLoadCommand.setExecutor(ImportWorldLoadCommand(this)) importWorldLoadCommand.setExecutor(ImportWorldLoadCommand(this))

View File

@ -13,10 +13,6 @@ import java.io.File
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
class ChunkExporter(private val plugin: Plugin) { class ChunkExporter(private val plugin: Plugin) {
private val json = Json {
ignoreUnknownKeys = true
}
fun exportLoadedChunksAsync(world: World) { fun exportLoadedChunksAsync(world: World) {
exportChunkListAsync(world, world.loadedChunks.toList()) exportChunkListAsync(world, world.loadedChunks.toList())
} }
@ -56,7 +52,7 @@ class ChunkExporter(private val plugin: Plugin) {
val fileOutputStream = file.outputStream() val fileOutputStream = file.outputStream()
val gzipOutputStream = GZIPOutputStream(fileOutputStream) val gzipOutputStream = GZIPOutputStream(fileOutputStream)
json.encodeToStream(ExportedChunk.serializer(), chunk, gzipOutputStream) Json.encodeToStream(ExportedChunk.serializer(), chunk, gzipOutputStream)
gzipOutputStream.close() gzipOutputStream.close()
} }

View File

@ -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<out String>
): 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
}
}

View File

@ -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<String, WorldLoadWorld>()
fun exportLoadedChunks(world: World) {
val data = mutableMapOf<Long, MutableMap<Long, MutableMap<Long, Int>>>()
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)
}
}
}

View File

@ -27,20 +27,18 @@ class WorldReassembler(val plugin: Plugin, val server: Server, val format: World
val blocksToMake = mutableListOf<Pair<Location, ExportedBlock>>() val blocksToMake = mutableListOf<Pair<Location, ExportedBlock>>()
for ((x, zBlocks) in load.blocks) { load.crawl { x, z, y, blockIndex ->
for ((z, yBlocks) in zBlocks) { val block = format.blockLookupTable[blockIndex]
for ((y, block) in yBlocks) { val material: Material? = Material.matchMaterial(block.type)
val material: Material? = Material.matchMaterial(block.type)
if (material == null) { if (material == null) {
feedback("Unknown Material '${block.type}' at $x $y $z") feedback("Unknown Material '${block.type}' at $x $y $z")
continue return@crawl
}
blocksToMake.add(Location(world, x.toDouble(), y.toDouble(), z.toDouble()) to block)
}
} }
blocksToMake.add(Location(world, x.toDouble(), y.toDouble(), z.toDouble()) to block)
} }
blocksToMake.sortBy { it.first.x } blocksToMake.sortBy { it.first.x }
feedback("Will place ${blocksToMake.size} blocks in ${world.name}") feedback("Will place ${blocksToMake.size} blocks in ${world.name}")

View File

@ -13,6 +13,10 @@ commands:
description: Export All Chunks description: Export All Chunks
usage: /export_all_chunks usage: /export_all_chunks
permission: heimdall.command.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: import_world_load:
description: Import World Load description: Import World Load
usage: /import_world_load usage: /import_world_load

View File

@ -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.arguments.argument
import com.github.ajalt.clikt.parameters.types.path import com.github.ajalt.clikt.parameters.types.path
import gay.pizza.foundation.heimdall.export.ExportedBlock 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.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.table.WorldChangeTable
import gay.pizza.foundation.heimdall.tool.state.BlockChangelog import gay.pizza.foundation.heimdall.tool.state.BlockChangelog
import gay.pizza.foundation.heimdall.tool.state.BlockLogTracker 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() val path by argument("load-format-file").path()
override fun run() { override fun run() {
val worlds = mutableMapOf<String, WorldLoadWorld>() val worlds = mutableMapOf<String, WorldLoadSimpleWorld>()
val worldChangelogs = BlockChangelog.query(db).splitBy { it.world } val worldChangelogs = BlockChangelog.query(db).splitBy { it.world }
val worldNames = transaction(db) { val worldNames = transaction(db) {
WorldChangeTable.selectAll() WorldChangeTable.selectAll()
.associate { it[WorldChangeTable.toWorld] to it[WorldChangeTable.toWorldName] } .associate { it[WorldChangeTable.toWorld] to it[WorldChangeTable.toWorldName] }
} }
val blockTable = ExportedBlockTable()
for ((id, changelog) in worldChangelogs) { for ((id, changelog) in worldChangelogs) {
val tracker = BlockLogTracker() val tracker = BlockLogTracker()
tracker.replay(changelog) tracker.replay(changelog)
val sparse = tracker.buildBlockMap { ExportedBlock(it.type, it.data) } val sparse = tracker.buildBlockMap { ExportedBlock(it.type, it.data) }
val blocks = sparse.blocks val blocks = sparse.blocks
worlds[id.toString().lowercase()] = WorldLoadWorld( worlds[id.toString().lowercase()] = WorldLoadSimpleWorld(
worldNames[id] ?: "unknown_$id", 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() path.deleteIfExists()
Json.encodeToStream(format, path.outputStream()) Json.encodeToStream(format, path.outputStream())
} }