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
//! V806 (16w36a+1) — schematic-relevant subset.
//!
//! Port of DataConverterJava .../versions/V806.java. Ensures potion-type item
//! stacks (potion / splash_potion / lingering_potion / tipped_arrow) carry a
//! `tag.Potion` string, defaulting it to `minecraft:water` when absent. Creates
//! the `tag` compound if missing, matching the Java updater.
//!
//! V806.java only touches ITEM_STACK, so nothing is skipped here.
use crate::nbt::{NbtMap, NbtValue};
use super::super::loss::{report_loss, LossKind, Severity};
use super::super::registry::RegistryBuilder;
use super::super::types::MapExt;
const VERSION: i32 = 806;
/// Item ids whose stacks get a defaulted `tag.Potion` (V806.java:32-35).
const POTION_ITEM_IDS: &[&str] = &[
"minecraft:potion",
"minecraft:splash_potion",
"minecraft:lingering_potion",
"minecraft:tipped_arrow",
];
pub fn register(reg: &mut RegistryBuilder) {
for &id in POTION_ITEM_IDS {
reg.item_stack.add_converter_for_id(
id,
VERSION,
0,
Box::new(|data, _from, _to| {
// Ensure `tag` exists (the Java updater creates it when absent).
if data.get_map_mut("tag").is_none() {
data.set_map("tag", NbtMap::new());
}
let tag = match data.get_map_mut("tag") {
Some(t) => t,
None => return,
};
// Java checks hasKey("Potion", STRING): only a String counts as present.
let has_string_potion = matches!(tag.get("Potion"), Some(NbtValue::String(_)));
if !has_string_potion {
tag.set_string("Potion", "minecraft:water");
}
}),
);
// Reverse: the forward only ADDS a defaulted `tag.Potion = "minecraft:water"`
// when no String `Potion` was present (V806.java:24-26). Pre-806, the absence
// of `Potion` was the implicit water bottle, so the inverse strips the default
// to restore the old "no Potion tag" shape. This is ambiguous: a potion that
// legitimately carries Potion="minecraft:water" in the new format is
// indistinguishable from one the forward defaulted, so dropping it is
// best-effort (Approximated). We leave any other `Potion` value untouched, and
// never remove `tag` itself (it may hold other data).
reg.item_stack.add_reverse_converter_for_id(
id,
VERSION,
0,
Box::new(|data, _from, _to| {
let tag = match data.get_map_mut("tag") {
Some(t) => t,
None => return,
};
if tag.get_string("Potion") == Some("minecraft:water") {
tag.take("Potion");
report_loss(
VERSION,
LossKind::RenameAmbiguous,
Severity::Approximated,
"potion item: dropped defaulted tag.Potion=\"minecraft:water\" \
(cannot distinguish a forward-added default from an \
explicitly-set water bottle)",
);
}
if data.get_map("tag").map(MapExt::is_empty).unwrap_or(false) {
data.take("tag");
}
}),
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dataconverter::registry::convert_item_stack_reverse;
#[test]
fn reverse_water_potion_prunes_empty_tag() {
let mut tag = NbtMap::new();
tag.set_string("Potion", "minecraft:water");
let mut item = NbtMap::new();
item.set_string("id", "minecraft:potion");
item.set_map("tag", tag);
let report = convert_item_stack_reverse(&mut item, 806, 805);
assert!(!item.has_key("tag"));
assert_eq!(report.len(), 1);
assert_eq!(report.loss_count(), 0);
}
}