mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 11:39:39 +00:00 
			
		
		
		
	Implement world reassembly from a Heimdall backup.
This commit is contained in:
		@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -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
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user