world_transmuter_engine/
utils.rs

1use crate::{
2    AbstractDynamicDataType, AbstractMapDataType, AbstractValueDataType, DataVersion, JCompound,
3    JList, JValue, MapDataWalker,
4};
5use java_string::{JavaStr, JavaString};
6use log::warn;
7
8pub struct DataWalkerObjectListPaths<T>
9where
10    T: AbstractValueDataType,
11{
12    typ: T,
13    paths: Vec<String>,
14}
15
16impl<T> DataWalkerObjectListPaths<T>
17where
18    T: AbstractValueDataType,
19{
20    pub fn new(typ: T, path: impl Into<String>) -> Self {
21        Self::new_multi(typ, vec![path.into()])
22    }
23
24    pub fn new_multi(typ: T, paths: Vec<String>) -> Self {
25        Self { typ, paths }
26    }
27}
28
29impl<T> MapDataWalker for DataWalkerObjectListPaths<T>
30where
31    T: AbstractValueDataType,
32{
33    fn walk(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
34        for path in &self.paths {
35            convert_object_list_in_map(&self.typ, data, path, from_version, to_version);
36        }
37    }
38}
39
40pub struct DataWalkerMapListPaths<T>
41where
42    T: AbstractMapDataType,
43{
44    typ: T,
45    paths: Vec<String>,
46}
47
48impl<T> DataWalkerMapListPaths<T>
49where
50    T: AbstractMapDataType,
51{
52    pub fn new(typ: T, path: impl Into<String>) -> Self {
53        Self::new_multi(typ, vec![path.into()])
54    }
55
56    pub fn new_multi(typ: T, paths: Vec<String>) -> Self {
57        Self { typ, paths }
58    }
59}
60
61impl<T> MapDataWalker for DataWalkerMapListPaths<T>
62where
63    T: AbstractMapDataType,
64{
65    fn walk(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
66        for path in &self.paths {
67            convert_map_list_in_map(&self.typ, data, path, from_version, to_version);
68        }
69    }
70}
71
72pub struct DataWalkerDynamicListPaths<T>
73where
74    T: AbstractDynamicDataType,
75{
76    typ: T,
77    paths: Vec<String>,
78}
79
80impl<T> DataWalkerDynamicListPaths<T>
81where
82    T: AbstractDynamicDataType,
83{
84    pub fn new(typ: T, path: impl Into<String>) -> Self {
85        Self::new_multi(typ, vec![path.into()])
86    }
87
88    pub fn new_multi(typ: T, paths: Vec<String>) -> Self {
89        Self { typ, paths }
90    }
91}
92
93impl<T> MapDataWalker for DataWalkerDynamicListPaths<T>
94where
95    T: AbstractDynamicDataType,
96{
97    fn walk(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
98        for path in &self.paths {
99            convert_dynamic_list_in_map(&self.typ, data, path, from_version, to_version);
100        }
101    }
102}
103
104pub struct DataWalkerObjectTypePaths<T>
105where
106    T: AbstractValueDataType,
107{
108    typ: T,
109    paths: Vec<String>,
110}
111
112impl<T> DataWalkerObjectTypePaths<T>
113where
114    T: AbstractValueDataType,
115{
116    pub fn new(typ: T, path: impl Into<String>) -> Self {
117        Self::new_multi(typ, vec![path.into()])
118    }
119
120    pub fn new_multi(typ: T, paths: Vec<String>) -> Self {
121        Self { typ, paths }
122    }
123}
124
125impl<T> MapDataWalker for DataWalkerObjectTypePaths<T>
126where
127    T: AbstractValueDataType,
128{
129    fn walk(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
130        for path in &self.paths {
131            convert_object_in_map(&self.typ, data, path, from_version, to_version);
132        }
133    }
134}
135
136pub struct DataWalkerMapTypePaths<T>
137where
138    T: AbstractMapDataType,
139{
140    typ: T,
141    paths: Vec<String>,
142}
143
144impl<T> DataWalkerMapTypePaths<T>
145where
146    T: AbstractMapDataType,
147{
148    pub fn new(typ: T, path: impl Into<String>) -> Self {
149        Self::new_multi(typ, vec![path.into()])
150    }
151
152    pub fn new_multi(typ: T, paths: Vec<String>) -> Self {
153        Self { typ, paths }
154    }
155}
156
157impl<T> MapDataWalker for DataWalkerMapTypePaths<T>
158where
159    T: AbstractMapDataType,
160{
161    fn walk(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
162        for path in &self.paths {
163            convert_map_in_map(&self.typ, data, path, from_version, to_version);
164        }
165    }
166}
167
168pub struct DataWalkerDynamicTypePaths<T>
169where
170    T: AbstractDynamicDataType,
171{
172    typ: T,
173    paths: Vec<String>,
174}
175
176impl<T> DataWalkerDynamicTypePaths<T>
177where
178    T: AbstractDynamicDataType,
179{
180    pub fn new(typ: T, path: impl Into<String>) -> Self {
181        Self::new_multi(typ, vec![path.into()])
182    }
183
184    pub fn new_multi(typ: T, paths: Vec<String>) -> Self {
185        Self { typ, paths }
186    }
187}
188
189impl<T> MapDataWalker for DataWalkerDynamicTypePaths<T>
190where
191    T: AbstractDynamicDataType,
192{
193    fn walk(&self, data: &mut JCompound, from_version: DataVersion, to_version: DataVersion) {
194        for path in &self.paths {
195            convert_dynamic_in_map(&self.typ, data, path, from_version, to_version);
196        }
197    }
198}
199
200pub fn convert_map_in_map<T>(
201    data_type: T,
202    data: &mut JCompound,
203    path: &(impl AsRef<JavaStr> + ?Sized),
204    from_version: DataVersion,
205    to_version: DataVersion,
206) where
207    T: AbstractMapDataType,
208{
209    if let Some(valence_nbt::Value::Compound(map)) = data.get_mut(path.as_ref()) {
210        data_type.convert(map, from_version, to_version);
211    }
212}
213
214pub fn convert_map_list_in_map<T>(
215    data_type: T,
216    data: &mut JCompound,
217    path: &(impl AsRef<JavaStr> + ?Sized),
218    from_version: DataVersion,
219    to_version: DataVersion,
220) where
221    T: AbstractMapDataType,
222{
223    if let Some(valence_nbt::Value::List(valence_nbt::List::Compound(list))) =
224        data.get_mut(path.as_ref())
225    {
226        for map in list {
227            data_type.convert(map, from_version, to_version);
228        }
229    }
230}
231
232pub fn convert_object_in_map<T>(
233    data_type: T,
234    data: &mut JCompound,
235    path: &(impl AsRef<JavaStr> + ?Sized),
236    from_version: DataVersion,
237    to_version: DataVersion,
238) where
239    T: AbstractValueDataType,
240{
241    if let Some(obj) = data.get_mut(path.as_ref()) {
242        data_type.convert(&mut obj.as_value_mut(), from_version, to_version);
243    }
244}
245
246pub fn convert_object_list<T>(
247    data_type: T,
248    data: &mut JList,
249    from_version: DataVersion,
250    to_version: DataVersion,
251) where
252    T: AbstractValueDataType,
253{
254    for mut obj in data.iter_mut() {
255        data_type.convert(&mut obj, from_version, to_version);
256    }
257}
258
259pub fn convert_object_list_in_map<T>(
260    data_type: T,
261    data: &mut JCompound,
262    path: &(impl AsRef<JavaStr> + ?Sized),
263    from_version: DataVersion,
264    to_version: DataVersion,
265) where
266    T: AbstractValueDataType,
267{
268    if let Some(valence_nbt::Value::List(list)) = data.get_mut(path.as_ref()) {
269        for mut obj in list.iter_mut() {
270            data_type.convert(&mut obj, from_version, to_version);
271        }
272    }
273}
274
275pub fn convert_dynamic_in_map<T>(
276    data_type: T,
277    data: &mut JCompound,
278    path: &(impl AsRef<JavaStr> + ?Sized),
279    from_version: DataVersion,
280    to_version: DataVersion,
281) where
282    T: AbstractDynamicDataType,
283{
284    if let Some(value) = data.get_mut(path.as_ref()) {
285        data_type.convert(value, from_version, to_version);
286    }
287}
288
289pub fn convert_dynamic_list_in_map<T>(
290    data_type: T,
291    data: &mut JCompound,
292    path: &(impl AsRef<JavaStr> + ?Sized),
293    from_version: DataVersion,
294    to_version: DataVersion,
295) where
296    T: AbstractDynamicDataType,
297{
298    fn convert_list_inner<T: AbstractDynamicDataType, E: Into<JValue>>(
299        data_type: T,
300        in_list: &mut Vec<E>,
301        from_version: DataVersion,
302        to_version: DataVersion,
303    ) -> JList {
304        let mut result = JList::new();
305        let mut all_success = true;
306        for element in in_list.drain(..) {
307            let mut element = element.into();
308            data_type.convert(&mut element, from_version, to_version);
309            all_success &= result.try_push(element)
310        }
311        if !all_success {
312            warn!("Result of list conversion was not homogenous");
313        }
314        result
315    }
316
317    let Some(valence_nbt::Value::List(list)) = data.get_mut(path.as_ref()) else {
318        return;
319    };
320    *list = match list {
321        valence_nbt::List::End => JList::End,
322        valence_nbt::List::Byte(bytes) => {
323            convert_list_inner(data_type, bytes, from_version, to_version)
324        }
325        valence_nbt::List::Short(shorts) => {
326            convert_list_inner(data_type, shorts, from_version, to_version)
327        }
328        valence_nbt::List::Int(ints) => {
329            convert_list_inner(data_type, ints, from_version, to_version)
330        }
331        valence_nbt::List::Long(longs) => {
332            convert_list_inner(data_type, longs, from_version, to_version)
333        }
334        valence_nbt::List::Float(floats) => {
335            convert_list_inner(data_type, floats, from_version, to_version)
336        }
337        valence_nbt::List::Double(doubles) => {
338            convert_list_inner(data_type, doubles, from_version, to_version)
339        }
340        valence_nbt::List::ByteArray(byte_arrays) => {
341            convert_list_inner(data_type, byte_arrays, from_version, to_version)
342        }
343        valence_nbt::List::String(strings) => {
344            convert_list_inner(data_type, strings, from_version, to_version)
345        }
346        valence_nbt::List::List(lists) => {
347            convert_list_inner(data_type, lists, from_version, to_version)
348        }
349        valence_nbt::List::Compound(compounds) => {
350            convert_list_inner(data_type, compounds, from_version, to_version)
351        }
352        valence_nbt::List::IntArray(int_arrays) => {
353            convert_list_inner(data_type, int_arrays, from_version, to_version)
354        }
355        valence_nbt::List::LongArray(long_arrays) => {
356            convert_list_inner(data_type, long_arrays, from_version, to_version)
357        }
358    }
359}
360
361pub fn convert_values_in_map<T>(
362    data_type: T,
363    data: &mut JCompound,
364    path: &(impl AsRef<JavaStr> + ?Sized),
365    from_version: DataVersion,
366    to_version: DataVersion,
367) where
368    T: AbstractMapDataType,
369{
370    if let Some(valence_nbt::Value::Compound(map)) = data.get_mut(path.as_ref()) {
371        convert_values(data_type, map, from_version, to_version);
372    }
373}
374
375pub fn convert_values<T>(
376    data_type: T,
377    data: &mut JCompound,
378    from_version: DataVersion,
379    to_version: DataVersion,
380) where
381    T: AbstractMapDataType,
382{
383    for obj in data.values_mut() {
384        if let valence_nbt::Value::Compound(map) = obj {
385            data_type.convert(map, from_version, to_version);
386        }
387    }
388}
389
390#[inline]
391pub fn rename_key(map: &mut JCompound, from: impl AsRef<JavaStr>, to: impl Into<JavaString>) {
392    if let Some(value) = map.remove(from.as_ref()) {
393        map.insert(to.into(), value);
394    }
395}
396
397pub fn rename_keys(map: &mut JCompound, renamer: impl Fn(&JavaStr) -> Option<JavaString>) {
398    let renames: Vec<_> = map
399        .keys()
400        .filter_map(|key| renamer(key).map(|new_key| (key.clone(), new_key)))
401        .collect();
402    let renamed: Vec<_> = renames
403        .into_iter()
404        .map(|(from, to)| (to, map.remove(&from[..]).unwrap()))
405        .collect();
406    for (key, value) in renamed {
407        map.insert(key, value);
408    }
409}
410
411pub fn get_mut_multi<'a, const N: usize>(
412    map: &'a mut JCompound,
413    keys: [&str; N],
414) -> [Option<&'a mut JValue>; N] {
415    #[cold]
416    #[inline(never)]
417    fn non_unique_keys(keys: &[&str]) -> ! {
418        panic!("keys are not all unique: {keys:?}")
419    }
420
421    if N > 1 {
422        for i in 0..N - 1 {
423            for j in i + 1..N {
424                if keys[i] == keys[j] {
425                    non_unique_keys(&keys);
426                }
427            }
428        }
429    }
430
431    keys.map(|key| {
432        map.get_mut(key).map(|value| {
433            // SAFETY: we just checked that all keys are unique, so these mutable references are all different values in the map, so they can coexist.
434            unsafe { &mut *(value as *mut _) }
435        })
436    })
437}