From 7be6993735a6211dfa7333a816aca59eb07cc2ba Mon Sep 17 00:00:00 2001 From: Spectre Date: Thu, 21 Aug 2025 17:34:53 +0200 Subject: [PATCH] add default value --- Cargo.toml | 3 +- src/level.rs | 191 ++++++++++++++++++++++++++++++++++++++------------ src/player.rs | 60 ++++++++++++++-- src/test.rs | 124 ++------------------------------ 4 files changed, 212 insertions(+), 166 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d22095b..014ddab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,11 @@ version = "0.1.0" edition = "2024" [dependencies] -image = "0.25.6" rand = "0.9.2" rand_chacha = "0.9.0" nbt = { git = "https://github.com/Cactus-minecraft-server/nbt.git" } [lib] name = "world" path = "src/lib.rs" +[dev-dependencies] +image = "0.25.6" diff --git a/src/level.rs b/src/level.rs index 9b018ec..f13c3fb 100644 --- a/src/level.rs +++ b/src/level.rs @@ -1,7 +1,104 @@ -use nbt::{Tag, write_nbt}; +use nbt::{Tag, Writer, write_nbt}; use std::collections::HashMap; use std::fs::File; +pub struct CustomBossEvents { + boss: Vec, +} + +impl Default for CustomBossEvents { + fn default() -> Self { + Self { + boss: ["".into()].into(), + } + } +} + +pub struct DataPacks { + pub disabled: Vec, + pub enabled: Vec, +} +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, + 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; + +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, + pub bonus_chest: bool, + pub generate_features: bool, + pub seed: i64, +} + +impl Default for WorldGenSettings { + fn default() -> Self { + let mut dim: HashMap = 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; + pub struct LevelDat { pub custom_boss_events: CustomBossEvents, pub data_packs: DataPacks, @@ -9,7 +106,7 @@ pub struct LevelDat { pub game_rules: GameRules, pub version: VersionInfo, pub world_gen_settings: WorldGenSettings, - pub scheduled_events: ScheduledEvents, + pub scheduled_events: Vec, pub server_brands: ServerBrands, pub allow_commands: bool, @@ -48,46 +145,53 @@ pub struct LevelDat { pub wandering_trader_spawn_delay: i32, pub was_modded: bool, } - -pub struct CustomBossEvents {} - -pub struct DataPacks { - pub disabled: Vec, - pub enabled: Vec, +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, + } + } } - -pub struct DragonFight { - pub gateways: Vec, - pub dragon_killed: bool, - pub needs_state_scanning: bool, - pub previously_killed: bool, -} - -pub type GameRules = HashMap; - -pub struct VersionInfo { - pub id: i32, - pub name: String, - pub series: String, - pub snapshot: i8, -} - -pub enum Dimension { - Overworld, - End, - Nether, -} - -pub struct WorldGenSettings { - pub dimensions: HashMap, - pub bonus_chest: bool, - pub generate_features: bool, - pub seed: i64, -} - -pub struct ScheduledEvents {} - -pub type ServerBrands = Vec; fn dim_to_str(d: &Dimension) -> &'static str { match d { Dimension::Overworld => "overworld", @@ -262,7 +366,7 @@ pub fn create_nbt(level: &LevelDat, path: &str) -> std::io::Result<()> { ); ver.insert( "Snapshot".to_string(), - Tag::new_byte("Snapshot", level.version.snapshot), + Tag::new_byte("Snapshot", i8::from(level.version.snapshot)), ); root.insert("Version".to_string(), ver); @@ -380,6 +484,7 @@ pub fn create_nbt(level: &LevelDat, path: &str) -> std::io::Result<()> { // --- write --- let file = File::create(format!("{path}/level.dat"))?; - write_nbt(&root, file)?; + let mut w = Writer::to_gzip(file); + w.write_tag(&root)?; Ok(()) } diff --git a/src/player.rs b/src/player.rs index dbed440..5ace286 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,5 +1,7 @@ -use nbt::{Tag, write_nbt}; +use nbt::{Tag, Writer, write_nbt}; use std::fs::File; + +use crate::level::Dimension; pub struct PlayerData { pub inventory: Vec, pub motion: [f64; 2], @@ -10,7 +12,7 @@ pub struct PlayerData { pub current_impulse_context_reset_grace_time: i32, pub data_version: i32, pub death_time: i16, - pub dimension: String, + pub dimension: Dimension, pub fall_distance: f64, pub fall_flying: bool, pub fire: i16, @@ -37,12 +39,61 @@ pub struct PlayerData { pub xp_total: i32, pub uuid: [i32; 4], } +impl Default for PlayerData { + fn default() -> Self { + Self { + inventory: [].into(), + motion: [0.0, 0.0].into(), + position: [0.0, 0.0, 0.0].into(), + rotation: [0.0, 0.0].into(), + absorbtion_amount: 0.0, + air: 300, + current_impulse_context_reset_grace_time: 0, + data_version: 4440, + death_time: 0, + dimension: Dimension::Overworld, + fall_distance: 0.0, + fall_flying: false, + fire: -20, + food_exhaustion_level: 0.0, + food_level: 20, + food_saturation_level: 5.0, + food_tick_timer: 0, + health: 20.0, + hurt_by_timestamp: 0, + hurt_time: 0, + ignore_fall_damage_from_current_explosion: false, + invulnerable: false, + on_ground: true, + player_game_type: 0, + portal_cooldown: 0, + score: 0, + seen_credits: false, + selected_item_slot: 0, + sleep_timer: 0, + spawn_extra_particles_on_fall: false, + xp_level: 0, + xp_p: 0.0, + xp_seed: 0, + xp_total: 0, + uuid: [0, 0, 0, 0], + } + } +} pub struct Item { pub count: i8, pub slot: i8, pub metadata: i16, pub id: String, } + +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(uuid: &String, player_data: PlayerData, path: String) -> std::io::Result<()> { let mut root = Tag::new_compound(uuid); let mut inventory = Tag::new_compound("inventory"); @@ -85,7 +136,7 @@ pub fn create_nbt(uuid: &String, player_data: PlayerData, path: String) -> std:: ); root.insert( "Dimension".into(), - Tag::new_string("Dimension", player_data.dimension), + Tag::new_string("Dimension", dim_to_str(&player_data.dimension)), ); root.insert( "fall_distance".into(), @@ -183,6 +234,7 @@ pub fn create_nbt(uuid: &String, player_data: PlayerData, path: String) -> std:: ); let file = File::create(format!("{path}/{uuid}.dat"))?; - write_nbt(&root, file)?; + let mut w = Writer::to_gzip(file); + w.write_tag(&root)?; Ok(()) } diff --git a/src/test.rs b/src/test.rs index d36592f..86a36b1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -48,83 +48,13 @@ mod perlin_test { /// Test for level.rs #[cfg(test)] mod level_file_test { - use crate::level::{ - CustomBossEvents, DataPacks, Dimension, DragonFight, LevelDat, ScheduledEvents, - ServerBrands, VersionInfo, WorldGenSettings, create_nbt, - }; - use std::collections::HashMap; + + use crate::level::{LevelDat, create_nbt}; #[test] fn test_creation_of_file() { - let mut game_rules: HashMap = HashMap::new(); - game_rules.insert("doDaylightCycle".into(), "true".into()); - - let mut dimensions: HashMap = HashMap::new(); - dimensions.insert("minecraft:overworld".into(), Dimension::Overworld); - let level = LevelDat { - custom_boss_events: CustomBossEvents {}, - data_packs: DataPacks { - disabled: vec![], - enabled: vec![], - }, - dragon_fight: DragonFight { - gateways: vec![0, 1, 2], - dragon_killed: false, - needs_state_scanning: false, - previously_killed: false, - }, - game_rules, - version: VersionInfo { - id: 3465, - name: "1.20.1".into(), - series: "main".into(), - snapshot: 0, - }, - world_gen_settings: WorldGenSettings { - dimensions, - bonus_chest: false, - generate_features: true, - seed: 1234, - }, - scheduled_events: ScheduledEvents {}, - server_brands: ServerBrands::from(vec!["vanilla".to_string()]), - - allow_commands: true, - - border_center_x: 0.0, - border_center_z: 0.0, - border_damage_per_block: 0.0, - border_safe_zone: 0.0, - border_size: 60_000_000.0, - border_size_lerp_target: 60_000_000.0, - border_size_lerp_time: 0, - border_warning_blocks: 5, - border_warning_time: 15, - - clear_weather_time: 0, - data_version: 3465, - day_time: 0, - difficulty: 2, - difficulty_locked: false, - game_type: 0, - hardcore: false, - initialized: true, - last_played: 0, - level_name: "test".into(), - raining: false, - rain_time: 0, - spawn_angle: 0.0, - spawn_x: 0, - spawn_y: 64, - spawn_z: 0, - thundering: false, - thunder_time: 0, - time: 0, - version_id: 3465, - wandering_trader_spawn_chance: 25, - wandering_trader_spawn_delay: 1200, - was_modded: false, + ..Default::default() }; let result = create_nbt(&level, "target"); @@ -135,60 +65,18 @@ mod level_file_test { /// Test for player.rs #[cfg(test)] mod player_data_test { - use crate::player::{Item, PlayerData, create_nbt}; + use crate::player::{PlayerData, create_nbt}; #[test] fn test_playerdata_creation() { - let item = Item { - count: 1, - slot: 0, - metadata: 0, - id: "minecraft:stone".to_string(), - }; - let data = PlayerData { - inventory: vec![item], - motion: [0.0, 0.0], - position: [0.0, 64.0, 0.0], - rotation: [0.0, 0.0], - absorbtion_amount: 0.0, - air: 300, - current_impulse_context_reset_grace_time: 0, - data_version: 3465, - death_time: 0, - dimension: "minecraft:overworld".to_string(), - fall_distance: 0.0, - fall_flying: false, - fire: -20, - food_exhaustion_level: 0.0, - food_level: 20, - food_saturation_level: 5.0, - food_tick_timer: 0, - health: 20.0, - hurt_by_timestamp: 0, - hurt_time: 0, - ignore_fall_damage_from_current_explosion: false, - invulnerable: false, - on_ground: true, - player_game_type: 0, - portal_cooldown: 0, - score: 0, - seen_credits: false, - selected_item_slot: 0, - sleep_timer: 0, - spawn_extra_particles_on_fall: false, - xp_level: 0, - xp_p: 0.0, - xp_seed: 0, - xp_total: 0, - uuid: [0, 0, 0, 0], + ..Default::default() }; let result = create_nbt( &"8c701aa5-e353-42dd-aa71-95d76b63a5d7".into(), data, "target/".into(), ); - - assert_eq!(result.is_ok(), true); + assert!(result.is_ok()); } }