mirror of
https://github.com/Cactus-minecraft-server/World.git
synced 2025-12-07 10:40:37 +00:00
491 lines
14 KiB
Rust
491 lines
14 KiB
Rust
use nbt::{Tag, Writer};
|
|
use std::collections::HashMap;
|
|
use std::fs::File;
|
|
|
|
pub struct CustomBossEvents {
|
|
boss: Vec<String>,
|
|
}
|
|
|
|
impl Default for CustomBossEvents {
|
|
fn default() -> Self {
|
|
Self {
|
|
boss: ["".into()].into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct DataPacks {
|
|
pub disabled: Vec<String>,
|
|
pub enabled: Vec<String>,
|
|
}
|
|
impl Default for DataPacks {
|
|
fn default() -> Self {
|
|
Self {
|
|
disabled: [
|
|
"minecraft_improvement".into(),
|
|
"redstone_experiments".into(),
|
|
"trade_rebalance".into(),
|
|
]
|
|
.into(),
|
|
enabled: ["vanilla".into()].into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct DragonFight {
|
|
pub gateways: Vec<i32>,
|
|
pub dragon_killed: bool,
|
|
pub needs_state_scanning: bool,
|
|
pub previously_killed: bool,
|
|
}
|
|
impl Default for DragonFight {
|
|
fn default() -> Self {
|
|
Self {
|
|
gateways: [].into(),
|
|
dragon_killed: false,
|
|
needs_state_scanning: true,
|
|
previously_killed: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub type GameRules = HashMap<String, String>;
|
|
|
|
pub struct VersionInfo {
|
|
pub id: i32,
|
|
pub name: String,
|
|
pub series: String,
|
|
pub snapshot: bool,
|
|
}
|
|
impl Default for VersionInfo {
|
|
fn default() -> Self {
|
|
Self {
|
|
id: 4440,
|
|
name: "1.21.8".into(),
|
|
series: "main".into(),
|
|
snapshot: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum Dimension {
|
|
Overworld,
|
|
End,
|
|
Nether,
|
|
}
|
|
|
|
pub struct WorldGenSettings {
|
|
pub dimensions: HashMap<String, Dimension>,
|
|
pub bonus_chest: bool,
|
|
pub generate_features: bool,
|
|
pub seed: i64,
|
|
}
|
|
|
|
impl Default for WorldGenSettings {
|
|
fn default() -> Self {
|
|
let mut dim: HashMap<String, Dimension> = HashMap::new();
|
|
dim.insert("Overworld".into(), Dimension::Overworld);
|
|
dim.insert("Nether".into(), Dimension::Nether);
|
|
dim.insert("The_End".into(), Dimension::End);
|
|
|
|
Self {
|
|
dimensions: dim,
|
|
bonus_chest: false,
|
|
generate_features: true,
|
|
seed: 42,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub type ServerBrands = Vec<String>;
|
|
|
|
pub struct LevelDat {
|
|
pub custom_boss_events: CustomBossEvents,
|
|
pub data_packs: DataPacks,
|
|
pub dragon_fight: DragonFight,
|
|
pub game_rules: GameRules,
|
|
pub version: VersionInfo,
|
|
pub world_gen_settings: WorldGenSettings,
|
|
pub scheduled_events: Vec<String>,
|
|
pub server_brands: ServerBrands,
|
|
|
|
pub allow_commands: bool,
|
|
|
|
pub border_center_x: f64,
|
|
pub border_center_z: f64,
|
|
pub border_damage_per_block: f64,
|
|
pub border_safe_zone: f64,
|
|
pub border_size: f64,
|
|
pub border_size_lerp_target: f64,
|
|
pub border_size_lerp_time: i64,
|
|
pub border_warning_blocks: i32,
|
|
pub border_warning_time: i32,
|
|
|
|
pub clear_weather_time: i32,
|
|
pub data_version: i32,
|
|
pub day_time: i64,
|
|
pub difficulty: i8,
|
|
pub difficulty_locked: bool,
|
|
pub game_type: i32,
|
|
pub hardcore: bool,
|
|
pub initialized: bool,
|
|
pub last_played: i64,
|
|
pub level_name: String,
|
|
pub raining: bool,
|
|
pub rain_time: i32,
|
|
pub spawn_angle: f32,
|
|
pub spawn_x: i32,
|
|
pub spawn_y: i32,
|
|
pub spawn_z: i32,
|
|
pub thundering: bool,
|
|
pub thunder_time: i32,
|
|
pub time: i64,
|
|
pub version_id: i32,
|
|
pub wandering_trader_spawn_chance: i32,
|
|
pub wandering_trader_spawn_delay: i32,
|
|
pub was_modded: bool,
|
|
}
|
|
impl Default for LevelDat {
|
|
fn default() -> Self {
|
|
Self {
|
|
custom_boss_events: CustomBossEvents::default(),
|
|
data_packs: DataPacks::default(),
|
|
dragon_fight: DragonFight::default(),
|
|
game_rules: GameRules::default(),
|
|
version: VersionInfo::default(),
|
|
world_gen_settings: WorldGenSettings::default(),
|
|
scheduled_events: ["".into()].into(),
|
|
server_brands: ServerBrands::default(),
|
|
allow_commands: false,
|
|
border_center_x: 0.0,
|
|
border_center_z: 0.0,
|
|
border_damage_per_block: 0.2,
|
|
border_safe_zone: 5.0,
|
|
border_size: 59999968.0,
|
|
border_size_lerp_target: 59999968.0,
|
|
border_size_lerp_time: 0,
|
|
border_warning_blocks: 15,
|
|
border_warning_time: 15,
|
|
clear_weather_time: 0,
|
|
data_version: 4440,
|
|
day_time: 95,
|
|
difficulty: 1,
|
|
difficulty_locked: false,
|
|
game_type: 0,
|
|
hardcore: false,
|
|
initialized: true,
|
|
last_played: 0,
|
|
level_name: "World".into(),
|
|
raining: false,
|
|
rain_time: 0,
|
|
spawn_angle: 0.0,
|
|
spawn_x: 0,
|
|
spawn_y: 82,
|
|
spawn_z: 0,
|
|
thundering: false,
|
|
thunder_time: 0,
|
|
time: 95,
|
|
version_id: 19133,
|
|
wandering_trader_spawn_chance: 25,
|
|
wandering_trader_spawn_delay: 24000,
|
|
was_modded: false,
|
|
}
|
|
}
|
|
}
|
|
fn dim_to_str(d: &Dimension) -> &'static str {
|
|
match d {
|
|
Dimension::Overworld => "overworld",
|
|
Dimension::End => "the_end",
|
|
Dimension::Nether => "the_nether",
|
|
}
|
|
}
|
|
|
|
pub fn create_nbt(level: &LevelDat, path: &str) -> std::io::Result<()> {
|
|
let mut root = Tag::new_compound("Data");
|
|
|
|
// --- primitives (root) ---
|
|
root.insert(
|
|
"allowCommands".to_string(),
|
|
Tag::new_byte("allowCommands", i8::from(level.allow_commands)),
|
|
);
|
|
|
|
root.insert(
|
|
"BorderCenterX".to_string(),
|
|
Tag::new_double("BorderCenterX", level.border_center_x),
|
|
);
|
|
root.insert(
|
|
"BorderCenterZ".to_string(),
|
|
Tag::new_double("BorderCenterZ", level.border_center_z),
|
|
);
|
|
root.insert(
|
|
"BorderDamagePerBlock".to_string(),
|
|
Tag::new_double("BorderDamagePerBlock", level.border_damage_per_block),
|
|
);
|
|
root.insert(
|
|
"BorderSafeZone".to_string(),
|
|
Tag::new_double("BorderSafeZone", level.border_safe_zone),
|
|
);
|
|
root.insert(
|
|
"BorderSize".to_string(),
|
|
Tag::new_double("BorderSize", level.border_size),
|
|
);
|
|
root.insert(
|
|
"BorderSizeLerpTarget".to_string(),
|
|
Tag::new_double("BorderSizeLerpTarget", level.border_size_lerp_target),
|
|
);
|
|
root.insert(
|
|
"BorderSizeLerpTime".to_string(),
|
|
Tag::new_long("BorderSizeLerpTime", level.border_size_lerp_time),
|
|
);
|
|
root.insert(
|
|
"BorderWarningBlocks".to_string(),
|
|
Tag::new_int("BorderWarningBlocks", level.border_warning_blocks),
|
|
);
|
|
root.insert(
|
|
"BorderWarningTime".to_string(),
|
|
Tag::new_int("BorderWarningTime", level.border_warning_time),
|
|
);
|
|
|
|
root.insert(
|
|
"clearWeatherTime".to_string(),
|
|
Tag::new_int("clearWeatherTime", level.clear_weather_time),
|
|
);
|
|
root.insert(
|
|
"DataVersion".to_string(),
|
|
Tag::new_int("DataVersion", level.data_version),
|
|
);
|
|
root.insert(
|
|
"DayTime".to_string(),
|
|
Tag::new_long("DayTime", level.day_time),
|
|
);
|
|
root.insert(
|
|
"Difficulty".to_string(),
|
|
Tag::new_byte("Difficulty", level.difficulty),
|
|
);
|
|
root.insert(
|
|
"DifficultyLocked".to_string(),
|
|
Tag::new_byte("DifficultyLocked", i8::from(level.difficulty_locked)),
|
|
);
|
|
root.insert(
|
|
"GameType".to_string(),
|
|
Tag::new_int("GameType", level.game_type),
|
|
);
|
|
root.insert(
|
|
"hardcore".to_string(),
|
|
Tag::new_byte("hardcore", i8::from(level.hardcore)),
|
|
);
|
|
root.insert(
|
|
"initialized".to_string(),
|
|
Tag::new_byte("initialized", i8::from(level.initialized)),
|
|
);
|
|
root.insert(
|
|
"LastPlayed".to_string(),
|
|
Tag::new_long("LastPlayed", level.last_played),
|
|
);
|
|
root.insert(
|
|
"LevelName".to_string(),
|
|
Tag::new_string("LevelName", level.level_name.clone()),
|
|
);
|
|
root.insert(
|
|
"raining".to_string(),
|
|
Tag::new_byte("raining", i8::from(level.raining)),
|
|
);
|
|
root.insert(
|
|
"RainTime".to_string(),
|
|
Tag::new_int("RainTime", level.rain_time),
|
|
);
|
|
root.insert(
|
|
"SpawnAngle".to_string(),
|
|
Tag::new_float("SpawnAngle", level.spawn_angle),
|
|
);
|
|
root.insert("SpawnX".to_string(), Tag::new_int("SpawnX", level.spawn_x));
|
|
root.insert("SpawnY".to_string(), Tag::new_int("SpawnY", level.spawn_y));
|
|
root.insert("SpawnZ".to_string(), Tag::new_int("SpawnZ", level.spawn_z));
|
|
root.insert(
|
|
"thundering".to_string(),
|
|
Tag::new_byte("thundering", i8::from(level.thundering)),
|
|
);
|
|
root.insert(
|
|
"ThunderTime".to_string(),
|
|
Tag::new_int("ThunderTime", level.thunder_time),
|
|
);
|
|
root.insert("Time".to_string(), Tag::new_long("Time", level.time));
|
|
root.insert(
|
|
"version".to_string(),
|
|
Tag::new_int("version", level.version_id),
|
|
);
|
|
root.insert(
|
|
"WanderingTraderSpawnChance".to_string(),
|
|
Tag::new_int(
|
|
"WanderingTraderSpawnChance",
|
|
level.wandering_trader_spawn_chance,
|
|
),
|
|
);
|
|
root.insert(
|
|
"WanderingTraderSpawnDelay".to_string(),
|
|
Tag::new_int(
|
|
"WanderingTraderSpawnDelay",
|
|
level.wandering_trader_spawn_delay,
|
|
),
|
|
);
|
|
root.insert(
|
|
"WasModded".to_string(),
|
|
Tag::new_byte("WasModded", i8::from(level.was_modded)),
|
|
);
|
|
|
|
// Mirror of older key from your example, sourced from struct:
|
|
root.insert(
|
|
"RandomSeed".to_string(),
|
|
Tag::new_long("RandomSeed", level.world_gen_settings.seed),
|
|
);
|
|
root.insert(
|
|
"MapFeatures".to_string(),
|
|
Tag::new_byte(
|
|
"MapFeatures",
|
|
i8::from(level.world_gen_settings.generate_features),
|
|
),
|
|
);
|
|
|
|
// --- GameRules ---
|
|
let mut gr = Tag::new_compound("GameRules");
|
|
for (k, v) in &level.game_rules {
|
|
gr.insert(k.clone(), Tag::new_string(k, v.clone()));
|
|
}
|
|
root.insert("GameRules".to_string(), gr);
|
|
|
|
// --- Version (compound) ---
|
|
let mut ver = Tag::new_compound("Version");
|
|
ver.insert("Id".to_string(), Tag::new_int("Id", level.version.id));
|
|
ver.insert(
|
|
"Name".to_string(),
|
|
Tag::new_string("Name", level.version.name.clone()),
|
|
);
|
|
ver.insert(
|
|
"Series".to_string(),
|
|
Tag::new_string("Series", level.version.series.clone()),
|
|
);
|
|
ver.insert(
|
|
"Snapshot".to_string(),
|
|
Tag::new_byte("Snapshot", i8::from(level.version.snapshot)),
|
|
);
|
|
root.insert("Version".to_string(), ver);
|
|
|
|
// --- DataPacks ---
|
|
let mut dp = Tag::new_compound("DataPacks");
|
|
let enabled_list: Vec<Tag> = level
|
|
.data_packs
|
|
.enabled
|
|
.iter()
|
|
.cloned()
|
|
.map(|s| Tag::new_string("", s))
|
|
.collect();
|
|
let disabled_list: Vec<Tag> = level
|
|
.data_packs
|
|
.disabled
|
|
.iter()
|
|
.cloned()
|
|
.map(|s| Tag::new_string("", s))
|
|
.collect();
|
|
dp.insert(
|
|
"Enabled".to_string(),
|
|
Tag::new_list("Enabled", 8, enabled_list),
|
|
);
|
|
dp.insert(
|
|
"Disabled".to_string(),
|
|
Tag::new_list("Disabled", 8, disabled_list),
|
|
);
|
|
|
|
root.insert("DataPacks".to_string(), dp);
|
|
|
|
// --- DragonFight ---
|
|
let mut df = Tag::new_compound("DragonFight");
|
|
let gateways_list: Vec<Tag> = level
|
|
.dragon_fight
|
|
.gateways
|
|
.iter()
|
|
.copied()
|
|
.map(|n| Tag::new_int("", n))
|
|
.collect();
|
|
df.insert(
|
|
"Gateways".to_string(),
|
|
Tag::new_list("Gateways", 3, gateways_list),
|
|
);
|
|
df.insert(
|
|
"DragonKilled".to_string(),
|
|
Tag::new_byte("DragonKilled", i8::from(level.dragon_fight.dragon_killed)),
|
|
);
|
|
df.insert(
|
|
"NeedsStateScanning".to_string(),
|
|
Tag::new_byte(
|
|
"NeedsStateScanning",
|
|
i8::from(level.dragon_fight.needs_state_scanning),
|
|
),
|
|
);
|
|
df.insert(
|
|
"PreviouslyKilled".to_string(),
|
|
Tag::new_byte(
|
|
"PreviouslyKilled",
|
|
i8::from(level.dragon_fight.previously_killed),
|
|
),
|
|
);
|
|
root.insert("DragonFight".to_string(), df);
|
|
|
|
// --- WorldGenSettings ---
|
|
let mut dims = Tag::new_compound("dimensions");
|
|
for (name, dim) in &level.world_gen_settings.dimensions {
|
|
dims.insert(
|
|
name.clone(),
|
|
Tag::new_string(name, dim_to_str(dim).to_string()),
|
|
);
|
|
}
|
|
let mut wgs = Tag::new_compound("WorldGenSettings");
|
|
wgs.insert(
|
|
"seed".to_string(),
|
|
Tag::new_long("seed", level.world_gen_settings.seed),
|
|
);
|
|
wgs.insert(
|
|
"generate_features".to_string(),
|
|
Tag::new_byte(
|
|
"generate_features",
|
|
i8::from(level.world_gen_settings.generate_features),
|
|
),
|
|
);
|
|
wgs.insert(
|
|
"bonus_chest".to_string(),
|
|
Tag::new_byte(
|
|
"bonus_chest",
|
|
i8::from(level.world_gen_settings.bonus_chest),
|
|
),
|
|
);
|
|
wgs.insert("dimensions".to_string(), dims);
|
|
root.insert("WorldGenSettings".to_string(), wgs);
|
|
|
|
// --- ServerBrands ---
|
|
let brands_list: Vec<Tag> = level
|
|
.server_brands
|
|
.iter()
|
|
.cloned()
|
|
.map(|s| Tag::new_string("", s))
|
|
.collect();
|
|
root.insert(
|
|
"ServerBrands".to_string(),
|
|
Tag::new_list("ServerBrands", 8, brands_list),
|
|
);
|
|
|
|
// --- CustomBossEvents / ScheduledEvents (empty compounds for now) ---
|
|
root.insert(
|
|
"CustomBossEvents".to_string(),
|
|
Tag::new_compound("CustomBossEvents"),
|
|
);
|
|
root.insert(
|
|
"ScheduledEvents".to_string(),
|
|
Tag::new_compound("ScheduledEvents"),
|
|
);
|
|
|
|
// --- write ---
|
|
let file = File::create(path)?;
|
|
let mut w = Writer::to_gzip(file);
|
|
w.write_tag(&root)?;
|
|
Ok(())
|
|
}
|