Add DataPackManager & SurfaceBlockPopulator
Introduce DataPackManager to generate/install a datapack into the target world folder (biome overrides, simplified noise_settings) and call install() during onLoad after the world folder is prepared. Add SurfaceBlockPopulator to apply surface/sub-surface block overrides (for TerraformGenerator worlds) and register it in onEnable via a WorldInitEvent listener for the configured target world. Refactor WorldManager to extract deletion logic into deleteWorld() and delete existing world/_nether/_the_end before unpacking the selected map. Also update imports and wiring to support these changes.
This commit is contained in:
@@ -31,10 +31,15 @@ import club.mcscrims.speedhg.perk.listener.PerkEventDispatcher
|
|||||||
import club.mcscrims.speedhg.ranking.RankingManager
|
import club.mcscrims.speedhg.ranking.RankingManager
|
||||||
import club.mcscrims.speedhg.scoreboard.ScoreboardManager
|
import club.mcscrims.speedhg.scoreboard.ScoreboardManager
|
||||||
import club.mcscrims.speedhg.webhook.DiscordWebhookManager
|
import club.mcscrims.speedhg.webhook.DiscordWebhookManager
|
||||||
|
import club.mcscrims.speedhg.world.DataPackManager
|
||||||
|
import club.mcscrims.speedhg.world.SurfaceBlockPopulator
|
||||||
import club.mcscrims.speedhg.world.WorldManager
|
import club.mcscrims.speedhg.world.WorldManager
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.Material
|
import org.bukkit.Material
|
||||||
import org.bukkit.NamespacedKey
|
import org.bukkit.NamespacedKey
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.world.WorldInitEvent
|
||||||
import org.bukkit.inventory.ItemStack
|
import org.bukkit.inventory.ItemStack
|
||||||
import org.bukkit.inventory.ShapelessRecipe
|
import org.bukkit.inventory.ShapelessRecipe
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
@@ -99,10 +104,26 @@ class SpeedHG : JavaPlugin() {
|
|||||||
|
|
||||||
val worldManager = WorldManager( this )
|
val worldManager = WorldManager( this )
|
||||||
worldManager.prepareRandomWorld()
|
worldManager.prepareRandomWorld()
|
||||||
|
|
||||||
|
val dataPackManager = DataPackManager( this )
|
||||||
|
dataPackManager.install()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEnable()
|
override fun onEnable()
|
||||||
{
|
{
|
||||||
|
server.pluginManager.registerEvents(object : Listener {
|
||||||
|
@EventHandler
|
||||||
|
fun onWorldInit(
|
||||||
|
event: WorldInitEvent
|
||||||
|
) {
|
||||||
|
val targetWorldName = config.getString( "map-system.target-world-name", "world" )
|
||||||
|
if ( event.world.name != targetWorldName ) return
|
||||||
|
|
||||||
|
event.world.populators.add( SurfaceBlockPopulator() )
|
||||||
|
logger.info( "[SpeedHG] SurfaceBlockPopulator für '${event.world.name}' registriert." )
|
||||||
|
}
|
||||||
|
}, this )
|
||||||
|
|
||||||
databaseManager = DatabaseManager( this )
|
databaseManager = DatabaseManager( this )
|
||||||
try {
|
try {
|
||||||
databaseManager.connect()
|
databaseManager.connect()
|
||||||
|
|||||||
559
src/main/kotlin/club/mcscrims/speedhg/world/DataPackManager.kt
Normal file
559
src/main/kotlin/club/mcscrims/speedhg/world/DataPackManager.kt
Normal file
@@ -0,0 +1,559 @@
|
|||||||
|
package club.mcscrims.speedhg.world
|
||||||
|
|
||||||
|
import club.mcscrims.speedhg.SpeedHG
|
||||||
|
import java.io.File
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt vor dem World-Load einen vollständigen DataPack im Weltordner.
|
||||||
|
*
|
||||||
|
* ## Was dieser DataPack KANN (mit und ohne TerraformGenerator)
|
||||||
|
* - Biom-Farben: Wasser-, Himmel-, Nebelfarbe, Laubfarbe
|
||||||
|
* - Biom-Geräusche: Ambiente-Sounds, Partikel
|
||||||
|
* - Mob-Spawntabellen: Welche Mobs in welcher Häufigkeit spawnen
|
||||||
|
* - Biom-Klimawerte: Temperatur, Niederschlag
|
||||||
|
*
|
||||||
|
* ## Was dieser DataPack NICHT kann (mit TerraformGenerator)
|
||||||
|
* - Surface-Blöcke (Gras → Sand → Stein): TG ignoriert noise_settings/overworld.json,
|
||||||
|
* da TG die Block-Platzierung komplett in eigenem Java-Code erledigt.
|
||||||
|
* → Für Surface-Block-Overrides: [SurfaceBlockPopulator] verwenden.
|
||||||
|
*
|
||||||
|
* ## Was dieser DataPack KANN (NUR ohne TerraformGenerator, Vanilla-Gen)
|
||||||
|
* - noise_settings/overworld.json: Komplette Surface-Rule-Bäume für Vanilla-Welten.
|
||||||
|
*
|
||||||
|
* ## Timing
|
||||||
|
* Muss in [JavaPlugin.onLoad] aufgerufen werden, NACHDEM der Weltordner
|
||||||
|
* durch [WorldManager.prepareRandomWorld] entpackt wurde, aber BEVOR
|
||||||
|
* der Server die Welt lädt. In onEnable() ist es zu spät — die Welt
|
||||||
|
* ist dann bereits vollständig initialisiert.
|
||||||
|
*/
|
||||||
|
class DataPackManager(private val plugin: SpeedHG) {
|
||||||
|
|
||||||
|
private val log: Logger get() = plugin.logger
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** Name des DataPack-Ordners unter <welt>/datapacks/. */
|
||||||
|
const val DATAPACK_NAME = "SpeedHG-Gen"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataPack-Format-Nummer je Minecraft-Version:
|
||||||
|
* 1.20.4 = 26 | 1.21 = 48 | 1.21.1 = 57 | 1.21.4 = 61
|
||||||
|
* Bei einem Server-Versions-Upgrade aktualisieren.
|
||||||
|
*/
|
||||||
|
const val PACK_FORMAT = 57
|
||||||
|
|
||||||
|
const val PACK_DESCRIPTION = "SpeedHG – Custom Biome und Surface Overrides"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
// Öffentliche API
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience-Methode: liest den Weltordner aus der config.yml und
|
||||||
|
* installiert den DataPack dort. Für den Normalfall gedacht.
|
||||||
|
*/
|
||||||
|
fun install() {
|
||||||
|
val worldName = plugin.config.getString("map-system.target-world-name", "world")!!
|
||||||
|
val serverRoot = plugin.dataFolder.parentFile.parentFile
|
||||||
|
val worldFolder = File(serverRoot, worldName)
|
||||||
|
install(worldFolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installiert den DataPack in den angegebenen [worldFolder].
|
||||||
|
*
|
||||||
|
* Idempotent: Existiert bereits ein alter DataPack, wird er komplett
|
||||||
|
* neu geschrieben. Sicher bei jedem Serverstart / Map-Reset aufzurufen.
|
||||||
|
*
|
||||||
|
* @param worldFolder Ordner der Zielwelt (z.B. File(serverRoot, "world")).
|
||||||
|
* Muss noch nicht existieren – er wird angelegt.
|
||||||
|
*/
|
||||||
|
fun install(worldFolder: File) {
|
||||||
|
val packRoot = File(worldFolder, "datapacks/$DATAPACK_NAME")
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Alte Version vollständig entfernen, damit keine veralteten
|
||||||
|
// JSON-Dateien aus früheren Plugin-Versionen übrig bleiben.
|
||||||
|
if (packRoot.exists()) {
|
||||||
|
log.info("[DataPackManager] Alten DataPack löschen...")
|
||||||
|
packRoot.deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
packRoot.mkdirs()
|
||||||
|
|
||||||
|
writePackMcmeta(packRoot)
|
||||||
|
writeBiomeOverrides(packRoot)
|
||||||
|
|
||||||
|
// ─ Vanilla-Gen only ─────────────────────────────────────────────
|
||||||
|
// Bei TerraformGenerator-Welten ist dieser Block wirkungslos,
|
||||||
|
// da TG noise_settings komplett ignoriert. Für Dokumentations-
|
||||||
|
// zwecke (und zukünftige Vanilla-Gen-Setups) trotzdem erstellt.
|
||||||
|
writeNoiseSettingOverride(packRoot)
|
||||||
|
// ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
log.info("[DataPackManager] '$DATAPACK_NAME' installiert in: ${packRoot.absolutePath}")
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.severe("[DataPackManager] Installation fehlgeschlagen: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entfernt den DataPack aus dem Weltordner.
|
||||||
|
* Kann z.B. in onDisable() aufgerufen werden, wenn der nächste
|
||||||
|
* Serverstart ohnehin einen frischen World-Reset durchführt.
|
||||||
|
*/
|
||||||
|
fun uninstall(worldFolder: File) {
|
||||||
|
val packRoot = File(worldFolder, "datapacks/$DATAPACK_NAME")
|
||||||
|
if (packRoot.exists()) {
|
||||||
|
packRoot.deleteRecursively()
|
||||||
|
log.info("[DataPackManager] '$DATAPACK_NAME' entfernt.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
// pack.mcmeta
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private fun writePackMcmeta(packRoot: File) {
|
||||||
|
// Hinweis: Seit 1.20.2 kann pack.mcmeta auch ein "overlays"-Array
|
||||||
|
// enthalten, das partielle Overrides (nur bestimmte Dateien aus einer
|
||||||
|
// Version) ermöglicht. Für unseren Anwendungsfall (volle Biom-Dateien)
|
||||||
|
// reicht das einfache Format.
|
||||||
|
File(packRoot, "pack.mcmeta").writeText(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"pack": {
|
||||||
|
"pack_format": $PACK_FORMAT,
|
||||||
|
"description": "$PACK_DESCRIPTION"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
Charsets.UTF_8
|
||||||
|
)
|
||||||
|
log.fine("[DataPackManager] pack.mcmeta geschrieben.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
// Biom-Overrides
|
||||||
|
// Pfad: data/minecraft/worldgen/biome/<biome_name>.json
|
||||||
|
//
|
||||||
|
// WICHTIG: Biom-Dateien in DataPacks ersetzen den KOMPLETTEN Vanilla-
|
||||||
|
// Eintrag. Alle Felder müssen vorhanden sein. Fehlende Felder führen
|
||||||
|
// zu Biom-Darstellungsfehlern oder Server-Abstürzen beim Laden.
|
||||||
|
//
|
||||||
|
// WICHTIG: Biom-JSONs kontrollieren KEINE Surface-Blöcke (Gras, Sand)!
|
||||||
|
// Surface-Blöcke liegen in noise_settings (s.u.) oder im TG-Java-Code.
|
||||||
|
//
|
||||||
|
// Was Biom-JSONs kontrollieren:
|
||||||
|
// ✔ Wasserfarbe, Wassernebelfarbe, Himmelsfarbe, Nebelfarbe
|
||||||
|
// ✔ Laubfarbe (foliage_color), Grasfarbe (grass_color)
|
||||||
|
// ✔ Biom-Ambient-Sounds, Partikel, Stimmungsgeräusche
|
||||||
|
// ✔ Mob-Spawntabellen (monster, creature, ambient, ...)
|
||||||
|
// ✔ Klimawerte: Temperatur, Niederschlag, Gefrierverhalten
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private fun writeBiomeOverrides(packRoot: File) {
|
||||||
|
val biomeDir = File(packRoot, "data/minecraft/worldgen/biome").also { it.mkdirs() }
|
||||||
|
BiomeOverride.all.forEach { override ->
|
||||||
|
val file = File(biomeDir, "${override.vanillaId}.json")
|
||||||
|
file.writeText(override.buildJson(), Charsets.UTF_8)
|
||||||
|
log.fine("[DataPackManager] Biom-Override geschrieben: ${override.vanillaId}.json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
// noise_settings Override (Surface Rules – NUR Vanilla-Gen)
|
||||||
|
// Pfad: data/minecraft/worldgen/noise_settings/overworld.json
|
||||||
|
//
|
||||||
|
// Dieser Block FUNKTIONIERT NICHT mit TerraformGenerator.
|
||||||
|
// TG generiert Blöcke in eigenem Java-Code und liest diese Datei nie.
|
||||||
|
//
|
||||||
|
// Für Vanilla-Gen: Lade die vollständige vanilla overworld.json unter
|
||||||
|
// https://raw.githubusercontent.com/misode/mcmeta/refs/tags/1.21.1-summary/
|
||||||
|
// data/minecraft/worldgen/noise_settings/overworld.json
|
||||||
|
// und ersetze nur den "surface_rule"-Block durch eigene Regeln.
|
||||||
|
//
|
||||||
|
// Wir schreiben hier eine vereinfachte Demonstrations-Version.
|
||||||
|
// In Produktion mit Vanilla-Gen: volle Datei + nur surface_rule ändern.
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private fun writeNoiseSettingOverride(packRoot: File) {
|
||||||
|
val nsDir = File(packRoot, "data/minecraft/worldgen/noise_settings").also { it.mkdirs() }
|
||||||
|
File(nsDir, "overworld.json").writeText(buildOverworldNoiseSetting(), Charsets.UTF_8)
|
||||||
|
log.fine("[DataPackManager] noise_settings/overworld.json geschrieben (nur Vanilla-Gen).")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt eine VEREINFACHTE noise_settings/overworld.json.
|
||||||
|
*
|
||||||
|
* Das echte Vanilla-overworld.json hat ~3000 Zeilen mit feature_flags,
|
||||||
|
* ore_veins, aquifer_config, noise_router und mehr.
|
||||||
|
*
|
||||||
|
* Für Vanilla-Gen-Produktion: Vollständige Datei von misode.github.io
|
||||||
|
* oder vom MC-Data-Generator laden, dann NUR den "surface_rule"-Block
|
||||||
|
* durch buildSurfaceRuleBlock() ersetzen.
|
||||||
|
*
|
||||||
|
* Für TerraformGenerator: Diese Datei ist irrelevant – SurfaceBlockPopulator
|
||||||
|
* verwenden (separate Klasse, ebenfalls im WorldManager-Package).
|
||||||
|
*/
|
||||||
|
private fun buildOverworldNoiseSetting(): String {
|
||||||
|
// Die Surface-Rule ist ein Entscheidungsbaum: Condition → Action.
|
||||||
|
// Unsere benutzerdefinierten Regeln kommen ZUERST, da "sequence" die
|
||||||
|
// erste zutreffende Regel gewinnen lässt (Priority-Queue-Semantik).
|
||||||
|
return """
|
||||||
|
{
|
||||||
|
"sea_level": 63,
|
||||||
|
"disable_mob_generation": false,
|
||||||
|
"aquifers_enabled": true,
|
||||||
|
"ore_veins_enabled": true,
|
||||||
|
"legacy_random_source": false,
|
||||||
|
"default_block": {
|
||||||
|
"Name": "minecraft:stone"
|
||||||
|
},
|
||||||
|
"default_fluid": {
|
||||||
|
"Name": "minecraft:water",
|
||||||
|
"Properties": { "level": "0" }
|
||||||
|
},
|
||||||
|
"noise": {
|
||||||
|
"height": 384,
|
||||||
|
"size_horizontal": 1,
|
||||||
|
"size_vertical": 2
|
||||||
|
},
|
||||||
|
"surface_rule": {
|
||||||
|
"type": "minecraft:sequence",
|
||||||
|
"sequence": [
|
||||||
|
${buildSurfaceRuleBlock()},
|
||||||
|
{
|
||||||
|
"type": "minecraft:block",
|
||||||
|
"result_state": { "Name": "minecraft:grass_block", "Properties": { "snowy": "false" } }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"spawn_target": [],
|
||||||
|
"noise_router": {}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baut den biom-spezifischen Surface-Rule-Block.
|
||||||
|
*
|
||||||
|
* Surface-Rules sind ein Baum aus condition/sequence/block-Nodes:
|
||||||
|
* condition → prüft (biom, Tiefe, Noise-Wert, ...)
|
||||||
|
* sequence → führt die erste zutreffende Regel in einer Liste aus
|
||||||
|
* block → platziert einen spezifischen Block
|
||||||
|
*
|
||||||
|
* stone_depth-Bedingung:
|
||||||
|
* offset=0, add_surface_depth=false → nur die oberste Schicht (y=surface)
|
||||||
|
* offset=0, add_surface_depth=true → oberste Schicht + Tiefe per Noise
|
||||||
|
*/
|
||||||
|
private fun buildSurfaceRuleBlock(): String = """
|
||||||
|
{
|
||||||
|
"type": "minecraft:condition",
|
||||||
|
"if_true": {
|
||||||
|
"type": "minecraft:biome",
|
||||||
|
"biome_is": [
|
||||||
|
"minecraft:badlands",
|
||||||
|
"minecraft:eroded_badlands",
|
||||||
|
"minecraft:wooded_badlands"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"then_run": {
|
||||||
|
"type": "minecraft:sequence",
|
||||||
|
"sequence": [
|
||||||
|
{
|
||||||
|
"type": "minecraft:condition",
|
||||||
|
"if_true": {
|
||||||
|
"type": "minecraft:stone_depth",
|
||||||
|
"offset": 0,
|
||||||
|
"add_surface_depth": false,
|
||||||
|
"secondary_depth_range": 0,
|
||||||
|
"surface_type": "floor"
|
||||||
|
},
|
||||||
|
"then_run": {
|
||||||
|
"type": "minecraft:block",
|
||||||
|
"result_state": { "Name": "minecraft:red_sand" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "minecraft:condition",
|
||||||
|
"if_true": {
|
||||||
|
"type": "minecraft:stone_depth",
|
||||||
|
"offset": 0,
|
||||||
|
"add_surface_depth": true,
|
||||||
|
"secondary_depth_range": 0,
|
||||||
|
"surface_type": "floor"
|
||||||
|
},
|
||||||
|
"then_run": {
|
||||||
|
"type": "minecraft:block",
|
||||||
|
"result_state": { "Name": "minecraft:red_sandstone" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "minecraft:condition",
|
||||||
|
"if_true": {
|
||||||
|
"type": "minecraft:biome",
|
||||||
|
"biome_is": ["minecraft:desert"]
|
||||||
|
},
|
||||||
|
"then_run": {
|
||||||
|
"type": "minecraft:sequence",
|
||||||
|
"sequence": [
|
||||||
|
{
|
||||||
|
"type": "minecraft:condition",
|
||||||
|
"if_true": {
|
||||||
|
"type": "minecraft:stone_depth",
|
||||||
|
"offset": 0,
|
||||||
|
"add_surface_depth": true,
|
||||||
|
"secondary_depth_range": 0,
|
||||||
|
"surface_type": "floor"
|
||||||
|
},
|
||||||
|
"then_run": {
|
||||||
|
"type": "minecraft:block",
|
||||||
|
"result_state": { "Name": "minecraft:sand" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "minecraft:condition",
|
||||||
|
"if_true": { "type": "minecraft:above_preliminary_surface" },
|
||||||
|
"then_run": {
|
||||||
|
"type": "minecraft:block",
|
||||||
|
"result_state": { "Name": "minecraft:sandstone" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Datenmodell: Biom-Overrides
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kapselt alle Daten für eine einzelne Biom-Override-JSON-Datei.
|
||||||
|
*
|
||||||
|
* In Minecraft 1.21 enthält eine Biom-JSON-Datei in einem DataPack folgende
|
||||||
|
* Pflichtfelder (Fehler → World-Load schlägt fehl):
|
||||||
|
* - temperature, downfall, has_precipitation
|
||||||
|
* - effects (sky_color, fog_color, water_color, water_fog_color + mood_sound)
|
||||||
|
* - spawners (alle Kategorien als leere Listen oder mit Einträgen)
|
||||||
|
* - spawn_costs
|
||||||
|
* - carvers (Vanilla: { "air": ["minecraft:cave", "minecraft:canyon"] })
|
||||||
|
* - features (10 leere Listen für die 10 Decoration-Phasen)
|
||||||
|
*
|
||||||
|
* Farben als Dezimal-Integer (hex → decimal):
|
||||||
|
* #55C677 (Sumpf-Wasser) = 5588599
|
||||||
|
* #617B64 (Sumpf-Laub) = 6325092
|
||||||
|
*
|
||||||
|
* Hex → Dezimal: "0x${hexCode.removePrefix("#")}".toLong(16).toInt()
|
||||||
|
*/
|
||||||
|
data class BiomeOverride(
|
||||||
|
/** Vanilla-Biom-ID ohne Namespace, z.B. "badlands". */
|
||||||
|
val vanillaId: String,
|
||||||
|
val temperature: Double,
|
||||||
|
val downfall: Double,
|
||||||
|
val hasPrecipitation: Boolean,
|
||||||
|
/** Himmelsfarbe als Dezimal-Integer. */
|
||||||
|
val skyColor: Int,
|
||||||
|
/** Hauptnebelfarbe. */
|
||||||
|
val fogColor: Int,
|
||||||
|
/** Wasserfarbe (sichtbare Wasserfläche). */
|
||||||
|
val waterColor: Int,
|
||||||
|
/** Wassernebelfarbe (Unterwasser-Sicht). */
|
||||||
|
val waterFogColor: Int,
|
||||||
|
/** Optionale Laubfarbe (null = Vanilla-Berechnung via Temperatur/Niederschlag). */
|
||||||
|
val foliageColor: Int? = null,
|
||||||
|
/** Optionale Grasfarbe (null = Vanilla-Berechnung). */
|
||||||
|
val grassColor: Int? = null,
|
||||||
|
/** Monster-Spawner-Einträge. */
|
||||||
|
val monsterSpawners: List<SpawnerEntry> = DEFAULT_MONSTER_SPAWNERS,
|
||||||
|
/** Tier-Spawner-Einträge. */
|
||||||
|
val creatureSpawners: List<SpawnerEntry> = emptyList(),
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun buildJson(): String {
|
||||||
|
val foliageBlock = if (foliageColor != null) """"foliage_color": $foliageColor,""" else ""
|
||||||
|
val grassBlock = if (grassColor != null) """"grass_color": $grassColor,""" else ""
|
||||||
|
val monsterJson = monsterSpawners.joinToString(",\n ") { it.toJson() }
|
||||||
|
val creatureJson = creatureSpawners.joinToString(",\n ") { it.toJson() }
|
||||||
|
|
||||||
|
return """
|
||||||
|
{
|
||||||
|
"temperature": $temperature,
|
||||||
|
"downfall": $downfall,
|
||||||
|
"has_precipitation": $hasPrecipitation,
|
||||||
|
"effects": {
|
||||||
|
"sky_color": $skyColor,
|
||||||
|
"fog_color": $fogColor,
|
||||||
|
"water_color": $waterColor,
|
||||||
|
"water_fog_color": $waterFogColor,
|
||||||
|
$foliageBlock
|
||||||
|
$grassBlock
|
||||||
|
"mood_sound": {
|
||||||
|
"block_search_extent": 8,
|
||||||
|
"offset": 2.0,
|
||||||
|
"sound": "minecraft:ambient.cave",
|
||||||
|
"tick_delay": 6000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"carvers": {
|
||||||
|
"air": ["minecraft:cave", "minecraft:canyon"]
|
||||||
|
},
|
||||||
|
"features": [[], [], [], [], [], [], [], [], [], []],
|
||||||
|
"spawners": {
|
||||||
|
"monster": [
|
||||||
|
$monsterJson
|
||||||
|
],
|
||||||
|
"creature": [
|
||||||
|
$creatureJson
|
||||||
|
],
|
||||||
|
"ambient": [],
|
||||||
|
"water_creature": [],
|
||||||
|
"underground_water_creature": [
|
||||||
|
{"type": "minecraft:glow_squid", "weight": 10, "minCount": 4, "maxCount": 6}
|
||||||
|
],
|
||||||
|
"water_ambient": [],
|
||||||
|
"axolotls": [],
|
||||||
|
"misc": []
|
||||||
|
},
|
||||||
|
"spawn_costs": {}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** Standard-Monster-Spawner für die meisten Overworld-Biome. */
|
||||||
|
val DEFAULT_MONSTER_SPAWNERS = listOf(
|
||||||
|
SpawnerEntry("minecraft:spider", 100, 4, 4),
|
||||||
|
SpawnerEntry("minecraft:zombie", 95, 4, 4),
|
||||||
|
SpawnerEntry("minecraft:zombie_villager", 5, 1, 1),
|
||||||
|
SpawnerEntry("minecraft:skeleton", 100, 4, 4),
|
||||||
|
SpawnerEntry("minecraft:creeper", 100, 4, 4),
|
||||||
|
SpawnerEntry("minecraft:slime", 100, 4, 4),
|
||||||
|
SpawnerEntry("minecraft:enderman", 10, 1, 4),
|
||||||
|
SpawnerEntry("minecraft:witch", 5, 1, 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Alle konfigurierten Biom-Overrides für diesen DataPack. */
|
||||||
|
val all: List<BiomeOverride> = buildList {
|
||||||
|
|
||||||
|
// ── Badlands ─────────────────────────────────────────────────────
|
||||||
|
// Sehr heiß, kein Niederschlag, heißer Himmel, trübes Wasser.
|
||||||
|
// Vanilla skyColor=7254527, waterColor=4159204
|
||||||
|
add(BiomeOverride(
|
||||||
|
vanillaId = "badlands",
|
||||||
|
temperature = 2.0,
|
||||||
|
downfall = 0.0,
|
||||||
|
hasPrecipitation = false,
|
||||||
|
skyColor = 7254527,
|
||||||
|
fogColor = 12638463,
|
||||||
|
waterColor = 4159204,
|
||||||
|
waterFogColor = 329011,
|
||||||
|
// Kein foliage/grass – Badlands hat kaum Vegetation
|
||||||
|
monsterSpawners = DEFAULT_MONSTER_SPAWNERS,
|
||||||
|
))
|
||||||
|
|
||||||
|
// Varianten mitüberziehen, damit alle drei Badlands-Typen konsistent sind
|
||||||
|
add(BiomeOverride(
|
||||||
|
vanillaId = "eroded_badlands",
|
||||||
|
temperature = 2.0,
|
||||||
|
downfall = 0.0,
|
||||||
|
hasPrecipitation = false,
|
||||||
|
skyColor = 7254527,
|
||||||
|
fogColor = 12638463,
|
||||||
|
waterColor = 4159204,
|
||||||
|
waterFogColor = 329011,
|
||||||
|
monsterSpawners = DEFAULT_MONSTER_SPAWNERS,
|
||||||
|
))
|
||||||
|
|
||||||
|
add(BiomeOverride(
|
||||||
|
vanillaId = "wooded_badlands",
|
||||||
|
temperature = 2.0,
|
||||||
|
downfall = 0.0,
|
||||||
|
hasPrecipitation = false,
|
||||||
|
skyColor = 7254527,
|
||||||
|
fogColor = 12638463,
|
||||||
|
waterColor = 4159204,
|
||||||
|
waterFogColor = 329011,
|
||||||
|
monsterSpawners = DEFAULT_MONSTER_SPAWNERS,
|
||||||
|
))
|
||||||
|
|
||||||
|
// ── Swamp ─────────────────────────────────────────────────────────
|
||||||
|
// Angepasste Wasserfarbe (dunkler, grünlicher als Vanilla).
|
||||||
|
// Vanilla waterColor=6388580, foliageColor=6975545, grassColor=6975545
|
||||||
|
add(BiomeOverride(
|
||||||
|
vanillaId = "swamp",
|
||||||
|
temperature = 0.8,
|
||||||
|
downfall = 0.9,
|
||||||
|
hasPrecipitation = true,
|
||||||
|
skyColor = 7907327,
|
||||||
|
fogColor = 12638463,
|
||||||
|
waterColor = 3832426, // dunkler, trüber als Vanilla
|
||||||
|
waterFogColor = 2302743,
|
||||||
|
foliageColor = 6975545,
|
||||||
|
grassColor = 6975545,
|
||||||
|
monsterSpawners = buildList {
|
||||||
|
addAll(DEFAULT_MONSTER_SPAWNERS)
|
||||||
|
add(SpawnerEntry("minecraft:slime", 1, 1, 1))
|
||||||
|
},
|
||||||
|
creatureSpawners = listOf(
|
||||||
|
SpawnerEntry("minecraft:frog", 10, 2, 5),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
|
||||||
|
// ── Dark Forest ──────────────────────────────────────────────────
|
||||||
|
// Dunklere Laubfarbe, trüberer Himmel für dunklere Atmosphäre.
|
||||||
|
add(BiomeOverride(
|
||||||
|
vanillaId = "dark_forest",
|
||||||
|
temperature = 0.7,
|
||||||
|
downfall = 0.8,
|
||||||
|
hasPrecipitation = true,
|
||||||
|
skyColor = 7972607,
|
||||||
|
fogColor = 12638463,
|
||||||
|
waterColor = 4159204,
|
||||||
|
waterFogColor = 329011,
|
||||||
|
foliageColor = 4145489, // deutlich dunkler als Vanilla (6529093)
|
||||||
|
grassColor = 4145489,
|
||||||
|
monsterSpawners = DEFAULT_MONSTER_SPAWNERS,
|
||||||
|
))
|
||||||
|
|
||||||
|
// ── Desert ───────────────────────────────────────────────────────
|
||||||
|
// Heißer, etwas orangestichigerer Himmel, trübes Wasser.
|
||||||
|
add(BiomeOverride(
|
||||||
|
vanillaId = "desert",
|
||||||
|
temperature = 2.0,
|
||||||
|
downfall = 0.0,
|
||||||
|
hasPrecipitation = false,
|
||||||
|
skyColor = 7254527,
|
||||||
|
fogColor = 12638463,
|
||||||
|
waterColor = 4159204,
|
||||||
|
waterFogColor = 329011,
|
||||||
|
monsterSpawners = buildList {
|
||||||
|
addAll(DEFAULT_MONSTER_SPAWNERS)
|
||||||
|
add(SpawnerEntry("minecraft:husk", 80, 4, 4))
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
|
// Weitere Biome hier ergänzen...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Einzelner Mob-Spawner-Eintrag für die spawners-Tabelle. */
|
||||||
|
data class SpawnerEntry(
|
||||||
|
val type: String,
|
||||||
|
val weight: Int,
|
||||||
|
val minCount: Int,
|
||||||
|
val maxCount: Int,
|
||||||
|
) {
|
||||||
|
fun toJson(): String =
|
||||||
|
"""{"type": "$type", "weight": $weight, "minCount": $minCount, "maxCount": $maxCount}"""
|
||||||
|
}
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
package club.mcscrims.speedhg.world
|
||||||
|
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.World
|
||||||
|
import org.bukkit.block.Biome
|
||||||
|
import org.bukkit.generator.BlockPopulator
|
||||||
|
import org.bukkit.generator.LimitedRegion
|
||||||
|
import org.bukkit.generator.WorldInfo
|
||||||
|
import java.util.Random
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ersetzt Surface- und Sub-Surface-Blöcke nach der TerraformGenerator-Generation.
|
||||||
|
*
|
||||||
|
* ## Warum BlockPopulator statt DataPack?
|
||||||
|
*
|
||||||
|
* TerraformGenerator (TG) platziert Surface-Blöcke vollständig in eigenem
|
||||||
|
* Java-Code. Die Datei noise_settings/overworld.json eines DataPacks wird
|
||||||
|
* von TG nie gelesen – sie gilt nur für Vanilla-Worldgen.
|
||||||
|
*
|
||||||
|
* Ein [BlockPopulator] wird von Paper aufgerufen, NACHDEM TG einen Chunk
|
||||||
|
* fertig generiert hat. Wir überschreiben dann die Blöcke per API.
|
||||||
|
*
|
||||||
|
* ## Einbindung
|
||||||
|
* ```kotlin
|
||||||
|
* // In der ChunkGenerator-Implementierung (oder als Zusatz zu TG):
|
||||||
|
* class SpeedHGWorldGenerator : ChunkGenerator() {
|
||||||
|
* override fun getDefaultPopulators(world: World): MutableList<BlockPopulator> =
|
||||||
|
* mutableListOf(SurfaceBlockPopulator())
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Da TG selbst der ChunkGenerator ist, muss der Populator über ein eigenes
|
||||||
|
* Plugin oder via Paper's [World.addPopulator] (deprecated seit 1.17) oder
|
||||||
|
* — am zuverlässigsten — als WorldGen-Feature im Plugin registriert werden.
|
||||||
|
*
|
||||||
|
* ## Empfehlung für TG + Paper 1.21
|
||||||
|
*
|
||||||
|
* Paper 1.18+ empfiehlt [WorldGenOptions] oder den Registration-Weg via
|
||||||
|
* `plugin.yml` / `GeneratorSettings`. Da TG den Generator stellt, ist die
|
||||||
|
* sauberste Lösung ein `WorldInitEvent`-Listener:
|
||||||
|
*
|
||||||
|
* ```kotlin
|
||||||
|
* @EventHandler
|
||||||
|
* fun onWorldInit(event: WorldInitEvent) {
|
||||||
|
* if (event.world.name == "world") {
|
||||||
|
* event.world.populators.add(SurfaceBlockPopulator())
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Dieser Listener muss in onEnable() registriert sein (nicht onLoad()).
|
||||||
|
* WorldInitEvent feuert kurz bevor Chunks geloaded/generiert werden.
|
||||||
|
*
|
||||||
|
* ## Performance-Hinweis
|
||||||
|
* Der Populator iteriert maximal über die oberste Schicht eines Chunks
|
||||||
|
* (16×16 = 256 Blöcke). Für jeden Block wird:
|
||||||
|
* 1. Das Biom abgefragt (O(1) in Paper)
|
||||||
|
* 2. Eine Map-Lookup durchgeführt (O(1))
|
||||||
|
* 3. Ggf. ein Block gesetzt (nur wenn Änderung nötig)
|
||||||
|
*
|
||||||
|
* Der Overhead ist minimal – deutlich kleiner als TG's eigene Generation.
|
||||||
|
*/
|
||||||
|
class SurfaceBlockPopulator : BlockPopulator() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Surface-Block-Konfiguration pro Biom-Typ.
|
||||||
|
* @property surface Oberster Block (Y = Oberfläche)
|
||||||
|
* @property subSurface Nächste 2–4 Blöcke darunter
|
||||||
|
* @property deepSurface Tief genug für Sandstein-Effekte o.Ä.
|
||||||
|
*/
|
||||||
|
private data class SurfaceConfig(
|
||||||
|
val surface: Material,
|
||||||
|
val subSurface: Material,
|
||||||
|
val deepSurface: Material = Material.STONE,
|
||||||
|
val subSurfaceDepth: Int = 3,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Biom → Surface-Konfiguration.
|
||||||
|
*
|
||||||
|
* Nur Biome, die abweichen sollen, müssen eingetragen werden.
|
||||||
|
* Alle anderen Biome bleiben unverändert (TG's Output bleibt bestehen).
|
||||||
|
*
|
||||||
|
* Für Biome, die TG bereits korrekt generiert (Forest = Gras/Erde),
|
||||||
|
* ist kein Eintrag nötig.
|
||||||
|
*/
|
||||||
|
private val surfaceOverrides: Map<Biome, SurfaceConfig> = buildMap {
|
||||||
|
|
||||||
|
// Badlands: Terrakotta oben, Rotsandstein darunter
|
||||||
|
// (TG platziert hier bereits Terrakotta, aber kein Rotsandstein darunter)
|
||||||
|
val badlandsConfig = SurfaceConfig(
|
||||||
|
surface = Material.TERRACOTTA,
|
||||||
|
subSurface = Material.RED_SANDSTONE,
|
||||||
|
deepSurface = Material.RED_SANDSTONE,
|
||||||
|
subSurfaceDepth = 4,
|
||||||
|
)
|
||||||
|
put(Biome.BADLANDS, badlandsConfig)
|
||||||
|
put(Biome.ERODED_BADLANDS, badlandsConfig)
|
||||||
|
put(Biome.WOODED_BADLANDS, badlandsConfig)
|
||||||
|
|
||||||
|
// Desert: Sand auf Sandstein (TG macht das meist schon, aber doppelt hält)
|
||||||
|
val desertConfig = SurfaceConfig(
|
||||||
|
surface = Material.SAND,
|
||||||
|
subSurface = Material.SAND,
|
||||||
|
deepSurface = Material.SANDSTONE,
|
||||||
|
subSurfaceDepth = 3,
|
||||||
|
)
|
||||||
|
put(Biome.DESERT, desertConfig)
|
||||||
|
|
||||||
|
// Swamp: Schmutziger mit Matsch/Erde statt Gras
|
||||||
|
put(Biome.SWAMP, SurfaceConfig(
|
||||||
|
surface = Material.GRASS_BLOCK,
|
||||||
|
subSurface = Material.MUD, // Mud (seit 1.19)
|
||||||
|
deepSurface = Material.DIRT,
|
||||||
|
subSurfaceDepth = 2,
|
||||||
|
))
|
||||||
|
|
||||||
|
// Snowy surfaces: Powder Snow-Effekt auf Berghöhen
|
||||||
|
val snowyConfig = SurfaceConfig(
|
||||||
|
surface = Material.SNOW_BLOCK,
|
||||||
|
subSurface = Material.DIRT,
|
||||||
|
subSurfaceDepth = 2,
|
||||||
|
)
|
||||||
|
put(Biome.SNOWY_TAIGA, snowyConfig)
|
||||||
|
put(Biome.SNOWY_PLAINS, snowyConfig)
|
||||||
|
put(Biome.ICE_SPIKES, snowyConfig)
|
||||||
|
|
||||||
|
// Stone Shore (Felsige Küste): Stein von Anfang an
|
||||||
|
put(Biome.STONY_SHORE, SurfaceConfig(
|
||||||
|
surface = Material.STONE,
|
||||||
|
subSurface = Material.STONE,
|
||||||
|
deepSurface = Material.STONE,
|
||||||
|
))
|
||||||
|
|
||||||
|
// Weitere Biome nach Bedarf ergänzen...
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
// BlockPopulator API
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wird von Paper für jeden neu generierten Chunk aufgerufen.
|
||||||
|
*
|
||||||
|
* Wir iterieren nur über die x/z-Spalten des Chunks (16×16) und
|
||||||
|
* suchen pro Spalte die Oberfläche – kein komplettes Y-Scan nötig.
|
||||||
|
*/
|
||||||
|
override fun populate(
|
||||||
|
worldInfo: WorldInfo,
|
||||||
|
random: Random,
|
||||||
|
chunkX: Int,
|
||||||
|
chunkZ: Int,
|
||||||
|
limitedRegion: LimitedRegion,
|
||||||
|
) {
|
||||||
|
val worldMinY = worldInfo.minHeight
|
||||||
|
|
||||||
|
for (lx in 0..15) {
|
||||||
|
for (lz in 0..15) {
|
||||||
|
val worldX = chunkX * 16 + lx
|
||||||
|
val worldZ = chunkZ * 16 + lz
|
||||||
|
|
||||||
|
// Biom einmalig pro Spalte abfragen (Y spielt für Biom-Typ weniger Rolle)
|
||||||
|
val biome = limitedRegion.getBiome(worldX, 64, worldZ)
|
||||||
|
val config = surfaceOverrides[biome] ?: continue
|
||||||
|
|
||||||
|
// Oberste solide Oberfläche dieser Spalte finden
|
||||||
|
val surfaceY = findSurfaceY(limitedRegion, worldX, worldZ, worldInfo.maxHeight, worldMinY)
|
||||||
|
?: continue // Keine Oberfläche gefunden (z.B. tiefe Ozean-Spalte unter Wasser)
|
||||||
|
|
||||||
|
applySurfaceConfig(limitedRegion, worldX, worldZ, surfaceY, config, worldMinY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
// Hilfsmethoden
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Findet den Y-Wert des obersten soliden, nicht-flüssigen Blocks in dieser Spalte.
|
||||||
|
* Gibt null zurück, wenn keine solide Oberfläche gefunden wurde.
|
||||||
|
*/
|
||||||
|
private fun findSurfaceY(
|
||||||
|
region: LimitedRegion,
|
||||||
|
x: Int,
|
||||||
|
z: Int,
|
||||||
|
maxY: Int,
|
||||||
|
minY: Int,
|
||||||
|
): Int? {
|
||||||
|
for (y in maxY downTo minY) {
|
||||||
|
if (!region.isInRegion(x, y, z)) continue
|
||||||
|
|
||||||
|
val type = region.getType(x, y, z)
|
||||||
|
if (type.isSolid && !type.isLiquid()) return y
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wendet die Surface-Konfiguration für eine Spalte an:
|
||||||
|
* surfaceY → config.surface
|
||||||
|
* surfaceY-1 bis surfaceY-subSurfaceDepth → config.subSurface
|
||||||
|
* darunter ggf. → config.deepSurface (wenn sich von subSurface unterscheidet)
|
||||||
|
*/
|
||||||
|
private fun applySurfaceConfig(
|
||||||
|
region: LimitedRegion,
|
||||||
|
x: Int,
|
||||||
|
z: Int,
|
||||||
|
surfaceY: Int,
|
||||||
|
config: SurfaceConfig,
|
||||||
|
minY: Int,
|
||||||
|
) {
|
||||||
|
// Oberflächen-Block ersetzen
|
||||||
|
if (region.isInRegion(x, surfaceY, z)) {
|
||||||
|
val existing = region.getType(x, surfaceY, z)
|
||||||
|
// Nur solide Blöcke ersetzen, keine Flüssigkeiten, kein Glas etc.
|
||||||
|
if (existing.isSolid && !existing.isLiquid()) {
|
||||||
|
region.setType(x, surfaceY, z, config.surface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub-Surface-Schichten ersetzen
|
||||||
|
for (depth in 1..config.subSurfaceDepth) {
|
||||||
|
val y = surfaceY - depth
|
||||||
|
if (y < minY || !region.isInRegion(x, y, z)) break
|
||||||
|
|
||||||
|
val existing = region.getType(x, y, z)
|
||||||
|
if (!existing.isSolid || existing.isLiquid()) break
|
||||||
|
|
||||||
|
val targetMaterial = if (depth <= config.subSurfaceDepth) config.subSurface
|
||||||
|
else config.deepSurface
|
||||||
|
region.setType(x, y, z, targetMaterial)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deep-Surface: Blöcke tiefer als subSurfaceDepth (bis Stein)
|
||||||
|
if (config.deepSurface != Material.STONE) {
|
||||||
|
for (depth in (config.subSurfaceDepth + 1)..(config.subSurfaceDepth + 4)) {
|
||||||
|
val y = surfaceY - depth
|
||||||
|
if (y < minY || !region.isInRegion(x, y, z)) break
|
||||||
|
|
||||||
|
val existing = region.getType(x, y, z)
|
||||||
|
// Nur weiche Blöcke weiter ersetzen (nicht Stein/Tiefenschiefer)
|
||||||
|
if (existing == Material.STONE || existing == Material.DEEPSLATE) break
|
||||||
|
|
||||||
|
region.setType(x, y, z, config.deepSurface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Material.isLiquid(): Boolean =
|
||||||
|
this == Material.WATER || this == Material.LAVA ||
|
||||||
|
this == Material.SEAGRASS || this == Material.TALL_SEAGRASS
|
||||||
|
}
|
||||||
@@ -10,11 +10,31 @@ class WorldManager(
|
|||||||
private val plugin: SpeedHG
|
private val plugin: SpeedHG
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private fun deleteWorld(): File
|
||||||
|
{
|
||||||
|
val targetWorldName = plugin.config.getString("map-system.target-world-name", "world")!!
|
||||||
|
val serverRoot = plugin.dataFolder.parentFile.parentFile
|
||||||
|
val targetWorldFolder = File(serverRoot, targetWorldName)
|
||||||
|
|
||||||
|
if ( targetWorldFolder.exists() )
|
||||||
|
{
|
||||||
|
plugin.logger.info( "[WorldManager] Lösche alte Welt..." )
|
||||||
|
targetWorldFolder.deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
File( serverRoot, "${targetWorldName}_nether" ).deleteRecursively()
|
||||||
|
File( serverRoot, "${targetWorldName}_the_end" ).deleteRecursively()
|
||||||
|
|
||||||
|
return targetWorldFolder
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird in onLoad() aufgerufen, um die Welt VOR dem Server-Start auszutauschen
|
* Wird in onLoad() aufgerufen, um die Welt VOR dem Server-Start auszutauschen
|
||||||
*/
|
*/
|
||||||
fun prepareRandomWorld()
|
fun prepareRandomWorld()
|
||||||
{
|
{
|
||||||
|
val targetWorldFolder = deleteWorld()
|
||||||
|
|
||||||
if (!plugin.config.getBoolean( "map-system.enabled", false ))
|
if (!plugin.config.getBoolean( "map-system.enabled", false ))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -37,19 +57,6 @@ class WorldManager(
|
|||||||
|
|
||||||
plugin.logger.info( "[WorldManager] Ausgewählte Map: $randomMapName. Entpacke..." )
|
plugin.logger.info( "[WorldManager] Ausgewählte Map: $randomMapName. Entpacke..." )
|
||||||
|
|
||||||
val targetWorldName = plugin.config.getString( "map-system.target-world-name", "world" )!!
|
|
||||||
val serverRoot = plugin.dataFolder.parentFile.parentFile // Geht von plugins/SpeedHG -> plugins -> Server Root
|
|
||||||
val targetWorldFolder = File( serverRoot, targetWorldName )
|
|
||||||
|
|
||||||
if ( targetWorldFolder.exists() )
|
|
||||||
{
|
|
||||||
plugin.logger.info( "[WorldManager] Lösche alte Welt..." )
|
|
||||||
targetWorldFolder.deleteRecursively()
|
|
||||||
}
|
|
||||||
|
|
||||||
File( serverRoot, "${targetWorldName}_nether" ).deleteRecursively()
|
|
||||||
File( serverRoot, "${targetWorldName}_the_end" ).deleteRecursively()
|
|
||||||
|
|
||||||
targetWorldFolder.mkdirs()
|
targetWorldFolder.mkdirs()
|
||||||
unzip( zipFile, targetWorldFolder )
|
unzip( zipFile, targetWorldFolder )
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user