osrscache/loader/
osrs.rs

1use std::collections::{
2    hash_map::{self, Entry},
3    HashMap,
4};
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9use crate::{
10    definition::osrs::{
11        Definition, FetchDefinition, InventoryDefinition, ItemDefinition, LocationDefinition,
12        MapDefinition, NpcDefinition, ObjectDefinition, VarbitDefinition,
13    },
14    Cache,
15};
16
17/// Loads all item definitions from the current cache.
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19#[derive(Clone, Eq, PartialEq, Debug, Default)]
20pub struct ItemLoader(HashMap<u16, ItemDefinition>);
21
22impl_osrs_loader!(ItemLoader, ItemDefinition, index_id: 2, archive_id: 10);
23
24/// Loads all npc definitions from the current cache.
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26#[derive(Clone, Eq, PartialEq, Debug, Default)]
27pub struct NpcLoader(HashMap<u16, NpcDefinition>);
28
29impl_osrs_loader!(NpcLoader, NpcDefinition, index_id: 2, archive_id: 9);
30
31/// Loads all object definitions from the current cache.
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33#[derive(Clone, Eq, PartialEq, Debug, Default)]
34pub struct ObjectLoader(HashMap<u16, ObjectDefinition>);
35
36impl_osrs_loader!(ObjectLoader, ObjectDefinition, index_id: 2, archive_id: 6);
37
38/// Loads all inventory definitions from the current cache.
39#[derive(Clone, Eq, PartialEq, Debug, Default)]
40#[cfg_attr(feature = "serde-derive", derive(Serialize, Deserialize))]
41pub struct InventoryLoader(HashMap<u16, InventoryDefinition>);
42
43impl_osrs_loader!(InventoryLoader, InventoryDefinition, index_id: 2, archive_id: 5);
44
45/// Loads all varbit definitions from the current cache.
46#[derive(Clone, Eq, PartialEq, Debug, Default)]
47#[cfg_attr(feature = "serde-derive", derive(Serialize, Deserialize))]
48pub struct VarbitLoader(HashMap<u16, VarbitDefinition>);
49
50impl_osrs_loader!(VarbitLoader, VarbitDefinition, index_id: 2, archive_id: 14);
51
52/// Loads maps definitions lazily from the current cache.
53#[derive(Debug)]
54pub struct MapLoader<'cache> {
55    cache: &'cache Cache,
56    maps: HashMap<u16, MapDefinition>,
57}
58
59impl<'cache> MapLoader<'cache> {
60    /// Make a new `MapLoader`.
61    ///
62    /// This takes a `Cache` by references with a `'cache` lifetime.
63    /// All the map definitions are loaded lazily where the `&'cache Cache` is used
64    /// to cache them internally on load.
65    pub fn new(cache: &'cache Cache) -> Self {
66        Self {
67            cache,
68            maps: HashMap::new(),
69        }
70    }
71
72    pub fn load(&mut self, id: u16) -> crate::Result<&MapDefinition> {
73        if let Entry::Vacant(entry) = self.maps.entry(id) {
74            let x = id >> 8;
75            let y = id & 0xFF;
76
77            let map_archive = self.cache.archive_by_name(5, format!("m{}_{}", x, y))?;
78            let buffer = self.cache.read_archive(map_archive)?.decode()?;
79
80            entry.insert(MapDefinition::new(id, &buffer)?);
81        }
82
83        Ok(&self.maps[&id])
84    }
85}
86
87/// Loads location definitions lazily from the current cache.
88#[derive(Debug)]
89pub struct LocationLoader<'cache> {
90    cache: &'cache Cache,
91    locations: HashMap<u16, LocationDefinition>,
92}
93
94impl<'cache> LocationLoader<'cache> {
95    /// Make a new `LocationLoader`.
96    ///
97    /// This takes a `Cache` by references with a `'cache` lifetime.
98    /// All the location definitions are loaded lazily where the `&'cache Cache` is used
99    /// to cache them internally on load.
100    pub fn new(cache: &'cache Cache) -> Self {
101        Self {
102            cache,
103            locations: HashMap::new(),
104        }
105    }
106
107    /// Loads the location data for a particular region.
108    ///
109    /// Also takes a `keys: [u32; 4]` because the location archive is encrypted
110    /// with XTEA. The buffer is automatically decoded with the given keys.
111    pub fn load(&mut self, id: u16, keys: &[u32; 4]) -> crate::Result<&LocationDefinition> {
112        if let Entry::Vacant(entry) = self.locations.entry(id) {
113            let x = id >> 8;
114            let y = id & 0xFF;
115
116            let loc_archive = self.cache.archive_by_name(5, format!("l{}_{}", x, y))?;
117            let buffer = self
118                .cache
119                .read_archive(loc_archive)?
120                .with_xtea_keys(*keys)
121                .decode()?;
122
123            entry.insert(LocationDefinition::new(id, &buffer)?);
124        }
125
126        Ok(&self.locations[&id])
127    }
128}
129
130#[cfg(test)]
131mod items {
132    use super::ItemLoader;
133    use crate::test_util;
134
135    fn item_loader() -> crate::Result<ItemLoader> {
136        ItemLoader::new(&test_util::osrs_cache()?)
137    }
138
139    #[test]
140    fn blue_partyhat() -> crate::Result<()> {
141        let item_loader = item_loader()?;
142        let item = item_loader.load(1042).unwrap();
143
144        assert_eq!(item.name, "Blue partyhat");
145        assert!(!item.stackable);
146        assert!(!item.members_only);
147
148        Ok(())
149    }
150
151    #[test]
152    fn magic_logs() -> crate::Result<()> {
153        let item_loader = item_loader()?;
154        let item = item_loader.load(1513).unwrap();
155
156        assert_eq!(item.name, "Magic logs");
157        assert!(!item.stackable);
158        assert!(item.members_only);
159
160        Ok(())
161    }
162
163    #[test]
164    fn noted() -> crate::Result<()> {
165        let item_loader = item_loader()?;
166        let item = item_loader.load(1512).unwrap();
167
168        assert!(item.stackable);
169        assert!(!item.members_only);
170
171        Ok(())
172    }
173
174    #[test]
175    fn non_existent() -> crate::Result<()> {
176        let item_loader = item_loader()?;
177
178        assert!(item_loader.load(65_535).is_none());
179
180        Ok(())
181    }
182}
183
184#[cfg(test)]
185mod npcs {
186    use super::NpcLoader;
187    use crate::test_util;
188
189    fn npc_loader() -> crate::Result<NpcLoader> {
190        NpcLoader::new(&test_util::osrs_cache()?)
191    }
192
193    #[test]
194    fn woodsman_tutor() -> crate::Result<()> {
195        let npc_loader = npc_loader()?;
196        let npc = npc_loader.load(3226).unwrap();
197
198        assert_eq!(npc.name, "Woodsman tutor");
199        assert!(npc.interactable);
200
201        Ok(())
202    }
203
204    #[test]
205    fn last_valid_npc() -> crate::Result<()> {
206        let npc_loader = npc_loader()?;
207        let npc = npc_loader.load(8691).unwrap();
208
209        assert_eq!(npc.name, "Ancient Fungi");
210        assert!(npc.interactable);
211
212        Ok(())
213    }
214
215    #[test]
216    fn non_existent() -> crate::Result<()> {
217        let npc_loader = npc_loader()?;
218
219        assert!(npc_loader.load(65_535).is_none());
220
221        Ok(())
222    }
223}
224
225#[cfg(test)]
226mod objects {
227    use super::ObjectLoader;
228    use crate::test_util;
229
230    fn obj_loader() -> crate::Result<ObjectLoader> {
231        ObjectLoader::new(&test_util::osrs_cache()?)
232    }
233
234    #[test]
235    fn law_rift() -> crate::Result<()> {
236        let obj_loader = obj_loader()?;
237        let obj = obj_loader.load(25034).unwrap();
238
239        assert_eq!(obj.name, "Law rift");
240        assert_eq!(obj.animation_id, 2178);
241        assert!(obj.solid);
242        assert!(!obj.obstruct_ground);
243
244        Ok(())
245    }
246
247    #[test]
248    fn furnace() -> crate::Result<()> {
249        let obj_loader = obj_loader()?;
250        let obj = obj_loader.load(2030).unwrap();
251
252        assert_eq!(obj.name, "Furnace");
253        assert!(obj.solid);
254        assert!(!obj.obstruct_ground);
255
256        Ok(())
257    }
258
259    #[test]
260    fn bank_table() -> crate::Result<()> {
261        let obj_loader = obj_loader()?;
262        let obj = obj_loader.load(590).unwrap();
263
264        assert_eq!(obj.name, "Bank table");
265        assert_eq!(obj.supports_items, Some(1));
266        assert!(obj.solid);
267        assert!(!obj.obstruct_ground);
268
269        Ok(())
270    }
271
272    #[test]
273    fn dungeon_door() -> crate::Result<()> {
274        let obj_loader = obj_loader()?;
275        let obj = obj_loader.load(1725).unwrap();
276
277        assert_eq!(obj.name, "Dungeon door");
278        assert_eq!(obj.wall_or_door, Some(1));
279        assert_eq!(obj.supports_items, Some(0));
280        assert!(obj.solid);
281        assert!(!obj.obstruct_ground);
282
283        Ok(())
284    }
285}
286
287#[cfg(test)]
288mod locations {
289    use super::LocationLoader;
290    use crate::test_util;
291
292    #[test]
293    fn lumbridge() -> crate::Result<()> {
294        let cache = test_util::osrs_cache()?;
295
296        let keys: [u32; 4] = [1766500218, 1050654932, 397022681, 1618041309];
297
298        let mut location_loader = LocationLoader::new(&cache);
299        let location_def = location_loader.load(12850, &keys)?;
300
301        assert_eq!(location_def.region_x, 50);
302        assert_eq!(location_def.region_y, 50);
303        assert_eq!(location_def.region_base_coords(), (3200, 3200));
304        assert_eq!(location_def.data.len(), 4730);
305
306        Ok(())
307    }
308}
309
310#[cfg(test)]
311mod maps {
312    use super::MapLoader;
313    use crate::test_util;
314
315    #[test]
316    fn lumbridge() -> crate::Result<()> {
317        let cache = test_util::osrs_cache()?;
318
319        let mut map_loader = MapLoader::new(&cache);
320        let map_def = map_loader.load(12850).unwrap();
321
322        assert_eq!(map_def.region_x, 50);
323        assert_eq!(map_def.region_y, 50);
324        assert_eq!(map_def.region_base_coords(), (3200, 3200));
325
326        Ok(())
327    }
328}
329
330#[cfg(test)]
331mod inventory {
332    use super::InventoryLoader;
333    use crate::test_util;
334
335    #[test]
336    fn load_player_backpack() -> crate::Result<()> {
337        let cache = test_util::osrs_cache()?;
338        let inventory_loader = InventoryLoader::new(&cache)?;
339
340        let inventory = inventory_loader.load(93).unwrap();
341
342        assert_eq!(28, inventory.capacity.unwrap());
343
344        Ok(())
345    }
346}
347
348#[cfg(test)]
349mod varbits {
350    use super::VarbitLoader;
351    use crate::test_util;
352
353    #[test]
354    fn load_sample_varbit() -> crate::Result<()> {
355        let cache = test_util::osrs_cache()?;
356        let varbit_loader = VarbitLoader::new(&cache)?;
357
358        let chatbox_varbit = varbit_loader.load(8119).unwrap();
359
360        assert_eq!(1737, chatbox_varbit.varp_id);
361        assert_eq!(31, chatbox_varbit.least_significant_bit);
362        assert_eq!(31, chatbox_varbit.most_significant_bit);
363
364        Ok(())
365    }
366}