Implement world reassembly from a Heimdall backup.

This commit is contained in:
2023-02-07 09:01:43 -05:00
parent 192c6cb511
commit e8084d7283
11 changed files with 209 additions and 6 deletions

View File

@ -10,6 +10,7 @@ import gay.pizza.foundation.heimdall.plugin.buffer.BufferFlushThread
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer
import gay.pizza.foundation.heimdall.plugin.event.*
import gay.pizza.foundation.heimdall.plugin.export.ExportAllChunksCommand
import gay.pizza.foundation.heimdall.plugin.load.ImportWorldLoadCommand
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
import org.bukkit.event.EventHandler
@ -45,6 +46,9 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
val exportChunksCommand = getCommand("export_all_chunks") ?: throw Exception("Failed to get export_all_chunks command")
exportChunksCommand.setExecutor(ExportAllChunksCommand(this))
val importWorldLoadCommand = getCommand("import_world_load") ?: throw Exception("Failed to get import_world_load command")
importWorldLoadCommand.setExecutor(ImportWorldLoadCommand(this))
val foundation = FoundationCoreLoader.get(server)
val configPath = copyDefaultConfig<FoundationHeimdallPlugin>(
slF4JLogger,

View File

@ -0,0 +1,37 @@
package gay.pizza.foundation.heimdall.plugin.load
import gay.pizza.foundation.heimdall.load.WorldLoadFormat
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.plugin.Plugin
import java.nio.file.Paths
import kotlin.io.path.exists
import kotlin.io.path.inputStream
class ImportWorldLoadCommand(private val plugin: Plugin) : CommandExecutor {
override fun onCommand(
sender: CommandSender,
command: Command,
label: String,
args: Array<out String>
): Boolean {
if (args.size != 1) {
sender.sendMessage("Usage: import_world_load <path>")
return true
}
val pathString = args[0]
val path = Paths.get(pathString)
if (!path.exists()) {
sender.sendMessage("Path '${path}' not found.")
}
val format = Json.decodeFromStream(WorldLoadFormat.serializer(), path.inputStream())
val reassembler = WorldReassembler(plugin, sender.server, format) { message ->
sender.sendMessage(message)
}
reassembler.loadInBackground()
return true
}
}

View File

@ -0,0 +1,67 @@
package gay.pizza.foundation.heimdall.plugin.load
import gay.pizza.foundation.heimdall.load.WorldLoadFormat
import gay.pizza.foundation.heimdall.load.WorldLoadWorld
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.Server
import org.bukkit.plugin.Plugin
import org.bukkit.scheduler.BukkitRunnable
import java.util.concurrent.atomic.AtomicLong
class WorldReassembler(val plugin: Plugin, val server: Server, val format: WorldLoadFormat, val feedback: (String) -> Unit) {
fun loadInBackground() {
server.scheduler.runTaskAsynchronously(plugin) { ->
for (world in server.worlds) {
val id = world.uid
var load: WorldLoadWorld? = format.worlds[id.toString().lowercase()]
if (load == null) {
load = format.worlds.values.firstOrNull { it.name == world.name }
}
if (load == null) {
feedback("Unable to match world ${world.uid} (${world.name}) to a loadable world, skipping")
continue
}
val blocksToMake = mutableListOf<Pair<Location, Material>>()
for ((x, zBlocks) in load.blocks) {
for ((z, yBlocks) in zBlocks) {
for ((y, block) in yBlocks) {
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 material)
}
}
}
blocksToMake.sortBy { it.first.x }
feedback("Will place ${blocksToMake.size} blocks in ${world.name}")
val count = AtomicLong()
var ticks = 0L
blocksToMake.chunked(1000) { section ->
val copy = section.toList()
val runnable = object : BukkitRunnable() {
override fun run() {
for ((location, material) in copy) {
val block = world.getBlockAt(location)
block.type = material
count.incrementAndGet()
}
feedback("Placed ${count.get()} blocks in ${world.name}")
}
}
runnable.runTaskLater(plugin, ticks)
ticks += 10
}
}
}
}
}

View File

@ -13,3 +13,7 @@ commands:
description: Export All Chunks
usage: /export_all_chunks
permission: heimdall.command.export_all_chunks
import_world_load:
description: Import World Load
usage: /import_world_load
permission: heimdall.command.import_world_load