|
1 | 1 | //! A read-only, high-level, virtual file API for the RuneScape cache. |
2 | 2 | //! |
3 | | -//! This crate provides high performant data reads into the [Oldschool |
4 | | -//! RuneScape] and [RuneScape 3] cache file systems. It can read the necessary |
5 | | -//! data to synchronize the client's cache with the server. There are also some |
6 | | -//! [loaders](#loaders) that give access to definitions from the cache such as |
7 | | -//! items or npcs. |
| 3 | +//! This crate provides high performant data reads into the [Oldschool RuneScape] and [RuneScape 3] |
| 4 | +//! cache file systems. It can read the necessary data to synchronize the client's cache with the |
| 5 | +//! server. There are also some [loaders](#loaders) that give access to definitions from the cache |
| 6 | +//! such as items or npcs. |
8 | 7 | //! |
9 | | -//! For read-heavy workloads, a writer can be used to prevent continuous buffer |
10 | | -//! allocations. By default every read will allocate a writer with the correct |
11 | | -//! capacity. |
| 8 | +//! For read-heavy workloads, a writer can be used to prevent continuous buffer allocations. By |
| 9 | +//! default every read will allocate a writer with the correct capacity. |
12 | 10 | //! |
13 | | -//! RuneScape's chat system uses huffman coding to compress messages. In order |
14 | | -//! to decompress them this library has a [`Huffman`] implementation. |
| 11 | +//! RuneScape's chat system uses huffman coding to compress messages. In order to decompress them |
| 12 | +//! this library has a [`Huffman`] implementation. |
15 | 13 | //! |
16 | | -//! When a RuneScape client sends game packets the id's are encoded and can be |
17 | | -//! decoded with the [`IsaacRand`] implementation. These id's are encoded by the |
18 | | -//! client in a predictable random order which can be reversed if the server has |
19 | | -//! its own `IsaacRand` with the same encoder/decoder keys. These keys are sent |
20 | | -//! by the client on login and are user specific. It will only send encoded |
| 14 | +//! When a RuneScape client sends game packets the id's are encoded and can be decoded with the |
| 15 | +//! [`IsaacRand`] implementation. These id's are encoded by the client in a predictable random order |
| 16 | +//! which can be reversed if the server has its own `IsaacRand` with the same encoder/decoder keys. |
| 17 | +//! These keys are sent by the client on login and are user specific. It will only send encoded |
21 | 18 | //! packet id's if the packets are game packets. |
22 | 19 | //! |
23 | | -//! Note that this crate is still evolving; both OSRS & RS3 are not fully |
24 | | -//! supported/implemented and will probably contain bugs or miss core features. |
25 | | -//! If you require features or find bugs consider [opening an issue]. |
| 20 | +//! Note that this crate is still evolving; both OSRS & RS3 are not fully supported/implemented and |
| 21 | +//! will probably contain bugs or miss core features. If you require features or find bugs consider |
| 22 | +//! [opening an issue]. |
26 | 23 | //! |
27 | 24 | //! # Safety |
28 | 25 | //! |
29 | | -//! In order to read bytes in a high performant way the cache uses [memmap2]. |
30 | | -//! This can be unsafe because of its potential for _Undefined Behaviour_ when |
31 | | -//! the underlying file is subsequently modified, in or out of process. Using |
32 | | -//! `Mmap` here is safe because the RuneScape cache is a read-only binary file |
33 | | -//! system. The map will remain valid even after the `File` is dropped, it's |
34 | | -//! completely independent of the `File` used to create it. Therefore, the use |
35 | | -//! of unsafe is not propagated outwards. When the `Cache` is dropped memory |
36 | | -//! will be subsequently unmapped. |
| 26 | +//! In order to read bytes in a high performant way the cache uses [memmap2]. This can be unsafe |
| 27 | +//! because of its potential for _Undefined Behaviour_ when the underlying file is subsequently |
| 28 | +//! modified, in or out of process. |
| 29 | +//! |
| 30 | +//! Using `Mmap` here is safe because the RuneScape cache is a read-only binary file system. The map |
| 31 | +//! will remain valid even after the `File` is dropped, it's completely independent of the `File` |
| 32 | +//! used to create it. Therefore, the use of unsafe is not propagated outwards. When the `Cache` is |
| 33 | +//! dropped memory will be subsequently unmapped. |
37 | 34 | //! |
38 | 35 | //! # Features |
39 | 36 | //! |
40 | | -//! The cache's protocol defaults to OSRS. In order to use the RS3 protocol you |
41 | | -//! can enable the `rs3` feature flag. A lot of types derive [serde]'s |
42 | | -//! `Serialize` and `Deserialize`. The `serde-derive` feature flag can be used |
43 | | -//! to enable (de)serialization on any compatible types. |
| 37 | +//! The cache's protocol defaults to OSRS. In order to use the RS3 protocol you can enable the `rs3` |
| 38 | +//! feature flag. A lot of types derive [serde]'s `Serialize` and `Deserialize`. The `serde-derive` |
| 39 | +//! feature flag can be used to enable (de)serialization on any compatible types. |
44 | 40 | //! |
45 | 41 | //! # Quick Start |
46 | 42 | //! |
| 43 | +//! The recommended usage would be to wrap it using |
| 44 | +//! [`std::sync::LazyLock`](https://doc.rust-lang.org/std/sync/struct.LazyLock.html) making it the |
| 45 | +//! easiest way to access cache data from anywhere and at any time. No need for an `Arc` or a |
| 46 | +//! `Mutex` because `Cache` will always be `Send + Sync`. |
| 47 | +//! ```rust |
| 48 | +//! use rscache::Cache; |
| 49 | +//! use std::sync::LazyLock; |
| 50 | +//! |
| 51 | +//! static CACHE: LazyLock<Cache> = LazyLock::new(|| { |
| 52 | +//! Cache::new("./data/osrs_cache") |
| 53 | +//! .expect("cache files to be successfully memory mapped") |
| 54 | +//! }); |
| 55 | +//! |
| 56 | +//! std::thread::spawn(|| -> Result<(), rscache::Error> { |
| 57 | +//! let buffer = CACHE.read(0, 10)?; |
| 58 | +//! Ok(()) |
| 59 | +//! }); |
| 60 | +//! |
| 61 | +//! std::thread::spawn(|| -> Result<(), rscache::Error> { |
| 62 | +//! let buffer = CACHE.read(0, 10)?; |
| 63 | +//! Ok(()) |
| 64 | +//! }); |
| 65 | +//! ``` |
| 66 | +//! |
47 | 67 | //! For an instance that stays local to this thread you can simply use: |
48 | 68 | //! ``` |
49 | 69 | //! use rscache::Cache; |
50 | | -//! |
51 | | -//! let cache = Cache::new("./data/osrs_cache").unwrap(); |
52 | | -//! |
| 70 | +//! |
| 71 | +//! # fn main() -> Result<(), rscache::Error> { |
| 72 | +//! let cache = Cache::new("./data/osrs_cache") |
| 73 | +//! .expect("cache files to be successfully memory mapped"); |
| 74 | +//! |
53 | 75 | //! let index_id = 2; // Config index. |
54 | 76 | //! let archive_id = 10; // Archive containing item definitions. |
55 | | -//! |
56 | | -//! let buffer = cache.read(index_id, archive_id).unwrap(); |
| 77 | +//! |
| 78 | +//! let buffer = cache.read(index_id, archive_id)?; |
| 79 | +//! # Ok(()) |
| 80 | +//! # } |
57 | 81 | //! ``` |
58 | | -//! |
59 | | -//! If you want to share the instance over multiple threads you can do so by |
60 | | -//! wrapping it in an |
| 82 | +//! |
| 83 | +//! If you want to share the instance over multiple threads you can do so by wrapping it in an |
61 | 84 | //! [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) |
62 | 85 | //! ``` |
63 | 86 | //! use rscache::Cache; |
64 | 87 | //! use std::sync::Arc; |
65 | | -//! |
66 | | -//! let cache = Arc::new(Cache::new("./data/osrs_cache").unwrap()); |
| 88 | +//! |
| 89 | +//! let cache = Arc::new(Cache::new("./data/osrs_cache") |
| 90 | +//! .expect("cache files to be successfully memory mapped")); |
67 | 91 | //! |
68 | 92 | //! let c = Arc::clone(&cache); |
69 | | -//! std::thread::spawn(move || { |
70 | | -//! c.read(0, 10).unwrap(); |
| 93 | +//! std::thread::spawn(move || -> Result<(), rscache::Error> { |
| 94 | +//! // use the cloned handle |
| 95 | +//! let buffer = c.read(0, 10)?; |
| 96 | +//! Ok(()) |
71 | 97 | //! }); |
72 | 98 | //! |
73 | | -//! std::thread::spawn(move || { |
74 | | -//! cache.read(0, 10).unwrap(); |
75 | | -//! }); |
76 | | -//! ``` |
77 | | -//! |
78 | | -//! The recommended usage would be to wrap it using |
79 | | -//! [`once_cell`](https://docs.rs/once_cell/latest/once_cell/) making it the |
80 | | -//! easiest way to access cache data from anywhere and at any time. No need for |
81 | | -//! an `Arc` or a `Mutex` because `Cache` will always be `Send` & `Sync`. |
82 | | -//! ``` |
83 | | -//! use rscache::Cache; |
84 | | -//! use once_cell::sync::Lazy; |
85 | | -//! |
86 | | -//! static CACHE: Lazy<Cache> = Lazy::new(|| { |
87 | | -//! Cache::new("./data/osrs_cache").unwrap() |
88 | | -//! }); |
89 | | -//! |
90 | | -//! std::thread::spawn(move || { |
91 | | -//! CACHE.read(0, 10).unwrap(); |
92 | | -//! }); |
93 | | -//! |
94 | | -//! std::thread::spawn(move || { |
95 | | -//! CACHE.read(0, 10).unwrap(); |
| 99 | +//! std::thread::spawn(move || -> Result<(), rscache::Error> { |
| 100 | +//! // use handle directly and take ownership |
| 101 | +//! let buffer = cache.read(0, 10)?; |
| 102 | +//! Ok(()) |
96 | 103 | //! }); |
97 | 104 | //! ``` |
98 | 105 | //! |
99 | 106 | //! # Loaders |
100 | 107 | //! |
101 | | -//! In order to get [definitions](crate::definition) you can look at the |
102 | | -//! [loaders](crate::loader) this library provides. The loaders use the cache as |
103 | | -//! a dependency to parse in their data and cache the relevant definitions |
104 | | -//! internally. The loader module also tells you how to make a loader if this |
105 | | -//! crate doesn't (yet) provide it. |
| 108 | +//! In order to get [definitions](crate::definition) you can look at the [loaders](crate::loader) |
| 109 | +//! this library provides. The loaders use the cache as a dependency to parse in their data and |
| 110 | +//! cache the relevant definitions internally. The loader module also tells you how to make a loader |
| 111 | +//! if this crate doesn't (yet) provide it. |
106 | 112 | //! |
107 | | -//! Note: Some loaders cache these definitions lazily because of either the size |
108 | | -//! of the data or the performance. The map loader for example is both slow and |
109 | | -//! large so caching is by default lazy. Lazy loaders require mutability. |
| 113 | +//! Note: Some loaders cache these definitions lazily because of either the size of the data or the |
| 114 | +//! performance. The map loader for example is both slow and large so caching is by default lazy. |
| 115 | +//! Lazy loaders require mutability. |
110 | 116 | //! |
111 | 117 | //! [Oldschool RuneScape]: https://oldschool.runescape.com/ |
112 | 118 | //! [RuneScape 3]: https://www.runescape.com/ |
|
0 commit comments