1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! V3803 (23w51b + 1) — schematic-relevant subset of `V3803.java`.
//!
//! A single enchantment id rename (`minecraft:sweeping` ->
//! `minecraft:sweeping_edge`) applied to ITEM_STACK via
//! `ConverterEnchantmentsRename` (V3803.java:16-21).
//!
//! `ConverterEnchantmentsRename` renames the `id` field of each map in the
//! `tag.Enchantments` and `tag.StoredEnchantments` lists. Critically, the input
//! id is first passed through `NamespaceUtil.correctNamespace` before the lookup,
//! so a legacy unnamespaced `sweeping` still matches. The engine has no exported
//! `correctNamespace`, so the parse rule is inlined here (mirrors
//! `helpers::correct_namespace_or_null`).
//!
//! VERSION = MCVersions.V23W51B (3802) + 1 = 3803.
use crate::nbt::NbtMap;
use super::super::registry::RegistryBuilder;
use super::super::types::{MapExt, ValueExt};
const VERSION: i32 = 3803;
/// `(old, new)` enchantment renames (V3803.java:16-18). Matched against the
/// namespace-corrected id.
const ENCHANTMENT_RENAMES: &[(&str, &str)] = &[("minecraft:sweeping", "minecraft:sweeping_edge")];
/// Port of `NamespaceUtil.correctNamespace`: default-namespace an unnamespaced,
/// parseable resource location; otherwise return the input unchanged.
fn correct_namespace(value: &str) -> String {
if value.contains(':') {
return value.to_string();
}
// Only a valid path (no illegal chars) gets the implicit `minecraft:`
// namespace; anything else parses as-null and is returned unchanged.
let valid = !value.is_empty()
&& value
.bytes()
.all(|b| matches!(b, b'a'..=b'z' | b'0'..=b'9' | b'.' | b'_' | b'-' | b'/'));
if valid {
format!("minecraft:{value}")
} else {
value.to_string()
}
}
/// `renamer.apply(correctNamespace(input))` (ConverterEnchantmentsRename:19-22).
fn rename(id: &str) -> Option<&'static str> {
let corrected = correct_namespace(id);
ENCHANTMENT_RENAMES
.iter()
.find(|(old, _)| *old == corrected)
.map(|(_, new)| *new)
}
/// Inverse of `rename`: map the new id back to the old id. The new id
/// (`minecraft:sweeping_edge`) uniquely encodes the old id, so this is exact.
fn rename_reverse(id: &str) -> Option<&'static str> {
let corrected = correct_namespace(id);
ENCHANTMENT_RENAMES
.iter()
.find(|(_, new)| *new == corrected)
.map(|(old, _)| *old)
}
/// `RenameHelper.renameListMapItems(tag, listPath, "id", renamer)`.
fn rename_list_map_ids(tag: &mut NbtMap, list_path: &str) {
if let Some(list) = tag.get_list_mut(list_path) {
for el in list.iter_mut() {
if let Some(map) = el.as_compound_mut() {
if let Some(id) = map.get_string("id") {
if let Some(new) = rename(id) {
map.set_string("id", new);
}
}
}
}
}
}
/// Reverse of `rename_list_map_ids`: rename `id` fields back to their old ids.
fn rename_list_map_ids_reverse(tag: &mut NbtMap, list_path: &str) {
if let Some(list) = tag.get_list_mut(list_path) {
for el in list.iter_mut() {
if let Some(map) = el.as_compound_mut() {
if let Some(id) = map.get_string("id") {
if let Some(old) = rename_reverse(id) {
map.set_string("id", old);
}
}
}
}
}
}
pub fn register(reg: &mut RegistryBuilder) {
reg.item_stack.add_structure_converter(
VERSION,
0,
Box::new(|data: &mut NbtMap, _from, _to| {
let tag = match data.get_map_mut("tag") {
Some(t) => t,
None => return,
};
rename_list_map_ids(tag, "Enchantments");
rename_list_map_ids(tag, "StoredEnchantments");
}),
);
// Reverse: `minecraft:sweeping_edge` -> `minecraft:sweeping`. Lossless
// rename — the new id uniquely encodes the old one (bucket A).
reg.item_stack.add_reverse_converter(
VERSION,
0,
Box::new(|data: &mut NbtMap, _from, _to| {
let tag = match data.get_map_mut("tag") {
Some(t) => t,
None => return,
};
rename_list_map_ids_reverse(tag, "Enchantments");
rename_list_map_ids_reverse(tag, "StoredEnchantments");
}),
);
}