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
2MB
22K SLoC
mcdata
一个 Rust 库,提供表示各种 Minecraft NBT 结构的特性和类型。
概述
目前,这个包提供了三个特性和一些特性的实现
BlockState
用于方块状态。Entity
用于普通实体。BlockEntity
用于方块实体。
每个这些特性都有一个相应的模块中的“通用”实现。其他实现被锁定在功能后面。
启用 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文件中自动提取的。
- "data-extractor"是一个Java文件,充当Fabric模组,并在运行时使用反射获取一些数据。这包括关于块状态的所有内容,以及一些关于实体(所有实体及其对应类的ID)的内容。
- (名称不佳的)"class-parser"几乎像一个用Kotlin编写的JVM,它从"data-extractor"中获取有关实体的数据,并“解释”Minecraft jars来收集有关它们的NBT结构的信息。这是必要的,因为实体NBT是非结构的,唯一定义结构的是实际代码本身。这显然仍然是一个脆弱的方法,但我能想到的最好的方法。一些事情也非常难以在这个“JVM”中正确支持,主要是Minecraft的
Codec
,它在新的Minecraft更新中越来越多地被使用。这意味着在这个crate中描述NBT结构的所有类型可能并不完全正确。如果您发现任何不一致之处,请提交问题单。
如果您想以任何方式为mcdata
做出贡献,这涉及到更改生成的代码,您可以使用cargo xtask codegen
在运行实际代码生成时运行这些工具。只需确保您已安装并配置了Java 21 JDK、git
和deno
即可。
依赖项
~0.8–1.5MB
~35K SLoC