7 个不稳定版本 (3 个破坏性更新)

0.4.0 2024年5月11日
0.3.0 2024年5月11日
0.2.3 2024年5月9日
0.1.0 2024年5月4日

#295 in 编码


用于 rustmatica

GPL-3.0-only

2MB
22K SLoC

mcdata

一个 Rust 库,提供表示各种 Minecraft NBT 结构的特性和类型。

概述

目前,这个包提供了三个特性和一些特性的实现

每个这些特性都有一个相应的模块中的“通用”实现。其他实现被锁定在功能后面。

启用 serde 支持后,这个包中的所有类型都可以完全序列化和反序列化 NBT。推荐的包是 fastnbt,这个包也内部使用于这个包中的类型。例如 entity::GenericEntity 几乎完全由一个 fastnbt::Value 表示。

关于方块实体的警告

如果您打算在litematica文件中使用任何特定版本的BlockEntity类型,请注意,自版本1.18.0-0.9.0以来,litematica中存在一个bug,直到版本1.20.1-0.15.3才得到修复,这导致块实体ID没有被包含在保存的方案中。块实体类型确实支持没有ID的反序列化,但这永远不可能完美。例如,在1.18中,箱子具有相同的NBT结构,但在反序列化时,桶总是会先被测试,因此如果没有ID区分两者,箱子也会被反序列化为桶。在序列化过程中,ID总是会包含在内,因此只需读取和写入NBT,这样的箱子就会变成桶。如果您觉得这是问题,可以考虑使用GenericBlockEntity,它不会与缺失的ID混淆。

示例

块状态
# #[cfg(not(feature = "test"))]
# compile_error!("tests should be run with the 'test' feature enabled");
use mcdata::latest::{BlockState, props};

let banjo = BlockState::NoteBlock {
    instrument: props::NoteBlockInstrument::Banjo,
    note: bounded_integer::BoundedU8::new(10).unwrap(),
    powered: false,
};
let banjo_nbt = fastnbt::nbt!({
    "Name": "minecraft:note_block",
    "Properties": {
        "instrument": "banjo",
        "note": "10",
        "powered": "false",
    },
});
assert_eq!(fastnbt::to_value(&banjo), Ok(banjo_nbt));
实体
# #[cfg(not(feature = "test"))]
# compile_error!("tests should be run with the 'test' feature enabled");
use std::collections::HashMap;
use mcdata::latest::{Entity, entity_types as types, entity_compounds as compounds};

let axolotl = Entity::Axolotl(types::Axolotl {
    from_bucket: false,
    variant: 0,
    parent: types::Animal {
        in_love: 0,
        love_cause: None,
        parent: types::AgeableMob {
            age: 0,
            forced_age: 0,
            parent: types::PathfinderMob {
                parent: types::Mob {
                    armor_drop_chances: vec![0.085; 4],
                    armor_items: vec![HashMap::new(); 4],
                    can_pick_up_loot: false,
                    death_loot_table: None,
                    body_armor_drop_chance: None,
                    death_loot_table_seed: None,
                    hand_drop_chances: vec![0.085; 2],
                    hand_items: vec![HashMap::new(); 2],
                    left_handed: false,
                    no_ai: None,
                    persistence_required: false,
                    body_armor_item: None,
                    leash: None,
                    parent: types::LivingEntity {
                        absorption_amount: 0.,
                        attributes: vec![compounds::AttributeInstance_save {
                            base: 1.,
                            modifiers: None,
                            name: "minecraft:generic.movement_speed".into(),
                        }],
                        brain: Some(fastnbt::nbt!({ "memories": {} })),
                        death_time: 0,
                        fall_flying: false,
                        health: 14.,
                        hurt_by_timestamp: 0,
                        hurt_time: 0,
                        sleeping_x: None,
                        sleeping_y: None,
                        sleeping_z: None,
                        active_effects: None,
                        parent: types::Entity {
                            air: 6000,
                            custom_name: None,
                            custom_name_visible: None,
                            fall_distance: 0.,
                            fire: -1,
                            glowing: None,
                            has_visual_fire: None,
                            invulnerable: false,
                            motion: vec![0.; 3],
                            no_gravity: None,
                            on_ground: false,
                            passengers: None,
                            portal_cooldown: 0,
                            pos: vec![-0.5, 0., 1.5],
                            rotation: vec![-107.68715, 0.],
                            silent: None,
                            tags: None,
                            ticks_frozen: None,
                            uuid: 307716075036743941152627606223512221703,
                        },
                    },
                },
            },
        },
    },
});
let axolotl_nbt = fastnbt::nbt!({
    "id": "minecraft:axolotl",
    "FromBucket": false,
    "Variant": 0,
    "InLove": 0,
    "Age": 0,
    "ForcedAge": 0,
    "ArmorDropChances": vec![0.085_f32; 4],
    "ArmorItems": [{}, {}, {}, {}],
    "CanPickUpLoot": false,
    "HandDropChances": vec![0.085_f32; 2],
    "HandItems": [{}, {}],
    "LeftHanded": false,
    "PersistenceRequired": false,
    "AbsorptionAmount": 0_f32,
    "Attributes": [{
        "Base": 1.,
        "Name": "minecraft:generic.movement_speed",
    }],
    "Brain": { "memories": {} },
    "DeathTime": 0_i16,
    "FallFlying": false,
    "Health": 14_f32,
    "HurtByTimestamp": 0,
    "HurtTime": 0_i16,
    "Air": 6000_i16,
    "FallDistance": 0_f32,
    "Fire": -1_i16,
    "Invulnerable": false,
    "Motion": vec![0.; 3],
    "OnGround": false,
    "PortalCooldown": 0,
    "Pos": [-0.5, 0., 1.5],
    "Rotation": [-107.68715_f32, 0_f32],
    "UUID": [I; -411044392, 312166398, -1883713137, 1472542727],
});
assert_eq!(fastnbt::to_value(&axolotl), Ok(axolotl_nbt));
块实体
# #[cfg(not(feature = "test"))]
# compile_error!("tests should be run with the 'test' feature enabled");
use mcdata::latest::{BlockEntity, block_entity_types as types};

let command_block = BlockEntity::CommandBlock(types::CommandBlockEntity {
    command: "/say hi".into(),
    custom_name: None,
    last_execution: None,
    last_output: None,
    success_count: 2,
    track_output: true,
    update_last_execution: true,
    auto: false,
    condition_met: false,
    powered: false,
    parent: types::BlockEntity {
        x: 0,
        y: 10,
        z: -5,
    },
});
let command_block_nbt = fastnbt::nbt!({
    "id": "minecraft:command_block",
    "Command": "/say hi",
    "SuccessCount": 2,
    "TrackOutput": true,
    "UpdateLastExecution": true,
    "auto": false,
    "conditionMet": false,
    "powered": false,
    "x": 0,
    "y": 10,
    "z": -5,
});
assert_eq!(fastnbt::to_value(&command_block), Ok(command_block_nbt));

这些数据从何而来?

您可以想象,我并没有手动输入所有这些数据。它是通过使用两种自定义工具从Minecraft jar文件中自动提取的。

  1. "data-extractor"是一个Java文件,充当Fabric模组,并在运行时使用反射获取一些数据。这包括关于块状态的所有内容,以及一些关于实体(所有实体及其对应类的ID)的内容。
  2. (名称不佳的)"class-parser"几乎像一个用Kotlin编写的JVM,它从"data-extractor"中获取有关实体的数据,并“解释”Minecraft jars来收集有关它们的NBT结构的信息。这是必要的,因为实体NBT是非结构的,唯一定义结构的是实际代码本身。这显然仍然是一个脆弱的方法,但我能想到的最好的方法。一些事情也非常难以在这个“JVM”中正确支持,主要是Minecraft的Codec,它在新的Minecraft更新中越来越多地被使用。这意味着在这个crate中描述NBT结构的所有类型可能并不完全正确。如果您发现任何不一致之处,请提交问题单

如果您想以任何方式为mcdata做出贡献,这涉及到更改生成的代码,您可以使用cargo xtask codegen在运行实际代码生成时运行这些工具。只需确保您已安装并配置了Java 21 JDK、gitdeno即可。

依赖项

~0.8–1.5MB
~35K SLoC