Skip to content

Commit 4178621

Browse files
authored
Registry system added
2 parents 9ac6c2e + 2129aa0 commit 4178621

File tree

4 files changed

+161
-2
lines changed

4 files changed

+161
-2
lines changed

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ members = [
3434
"src/lib/world",
3535
"src/lib/world_gen",
3636
"src/lib/utils/threadpool",
37+
"src/lib/registry",
3738
]
3839

3940
#================== Lints ==================#
4041
[workspace.lints.rust]
41-
unsafe_code = "deny"
42+
unsafe_code = "allow"
4243
unused_unsafe = "deny"
4344
#unsafe_op_in_unsafe_fn = "deny"
4445
#unused_crate_dependencies = "deny"
@@ -96,13 +97,14 @@ ferrumc-net-codec = { path = "src/lib/net/crates/codec" }
9697
ferrumc-net-encryption = { path = "src/lib/net/crates/encryption" }
9798
ferrumc-plugins = { path = "src/lib/plugins" }
9899
ferrumc-profiling = { path = "src/lib/utils/profiling" }
100+
ferrumc-registry = { path = "src/lib/registry" }
99101
ferrumc-state = { path = "src/lib/core/state" }
100102
ferrumc-storage = { path = "src/lib/storage" }
101103
ferrumc-text = { path = "src/lib/text" }
104+
ferrumc-threadpool = { path = "src/lib/utils/threadpool" }
102105
ferrumc-utils = { path = "src/lib/utils" }
103106
ferrumc-world = { path = "src/lib/world" }
104107
ferrumc-world-gen = { path = "src/lib/world_gen" }
105-
ferrumc-threadpool = { path = "src/lib/utils/threadpool" }
106108

107109

108110
# Asynchronous
@@ -145,6 +147,8 @@ bitcode_derive = "0.6.5"
145147
toml = "0.9.4"
146148
craftflow-nbt = "2.1.0"
147149
figment = { version = "0.10.19", features = ["toml", "env"] }
150+
simd-json = "0.15.1"
151+
sonic-rs = "0.5.3"
148152

149153
# Bit manipulation
150154
byteorder = "1.5.0"

src/lib/registry/Cargo.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "ferrumc-registry"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
simd-json = { workspace = true }
8+
once_cell = { workspace = true }
9+
10+
[lints]
11+
workspace = true
12+
13+
14+
[dev-dependencies]
15+
criterion = { workspace = true }
16+
17+
[[bench]]
18+
name = "registry_bench"
19+
harness = false
20+
path = "benches/registry_lookup.rs"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
2+
use std::hint::black_box;
3+
4+
fn bench_lookup(c: &mut Criterion) {
5+
// run once to load the registry
6+
7+
black_box(ferrumc_registry::lookup(
8+
"minecraft:item/entries/minecraft:cobblestone/protocol_id",
9+
));
10+
11+
let mut group = c.benchmark_group("registry_lookup");
12+
13+
group.throughput(Throughput::Elements(1));
14+
15+
group.bench_function("lookup_cobblestone", |b| {
16+
b.iter(|| {
17+
// black_box to prevent compiler optimizations
18+
black_box(ferrumc_registry::lookup(
19+
"minecraft:item/entries/minecraft:cobblestone/protocol_id",
20+
))
21+
})
22+
});
23+
24+
group.finish()
25+
}
26+
27+
criterion_group!(benches, bench_lookup);
28+
criterion_main!(benches);

src/lib/registry/src/lib.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use once_cell::sync::Lazy;
2+
use simd_json::OwnedValue;
3+
use simd_json::derived::ValueObjectAccess;
4+
use simd_json::prelude::ValueArrayAccess;
5+
6+
// Parse once at startup
7+
static REGISTRY_BYTES: &[u8] = include_bytes!("../../../../assets/data/registries.json");
8+
9+
static LOADED_REGISTRY: Lazy<OwnedValue> = Lazy::new(|| {
10+
// simd-json mutates the buffer during parsing → needs a Vec<u8>
11+
let mut buf = REGISTRY_BYTES.to_vec();
12+
simd_json::to_owned_value(&mut buf).expect("parse registries.json")
13+
});
14+
15+
/// Looks up a value in the loaded JSON registry by a given path.
16+
///
17+
/// # Arguments
18+
/// * `path` - A string slice representing the path to the desired value in the JSON structure.
19+
/// Path segments are separated by `/`. Numeric segments are treated as array indices.
20+
///
21+
/// # Returns
22+
/// * `Option<&OwnedValue>` - Returns a reference to the value if found, or `None` if the path does not exist.
23+
///
24+
/// # Examples
25+
/// ```
26+
/// # use simd_json::prelude::{TypedScalarValue, ValueAsScalar};
27+
/// # use ferrumc_registry::lookup;
28+
/// let value = lookup("minecraft:item/entries/minecraft:apple/protocol_id");
29+
/// assert!(value.is_some());
30+
/// assert!(value.unwrap().is_u64()); // check if the value is a number
31+
/// assert_eq!(value.unwrap().as_u64().unwrap(), 840);
32+
/// ```
33+
pub fn lookup(path: &str) -> Option<&OwnedValue> {
34+
let mut cur: &OwnedValue = &LOADED_REGISTRY;
35+
36+
if path.is_empty() {
37+
return Some(cur); // return the root if path is empty
38+
}
39+
40+
for seg in path.split('/') {
41+
// allow numeric segments as array indices
42+
if let Ok(idx) = seg.parse::<usize>() {
43+
cur = cur.get_idx(idx)?; // works if current is an array
44+
} else {
45+
cur = cur.get(seg)?; // works if current is an object
46+
}
47+
}
48+
Some(cur)
49+
}
50+
51+
/// Looks up a value in the loaded JSON registry by a given path and returns an owned clone.
52+
///
53+
/// # Arguments
54+
/// * `path` - A string slice representing the path to the desired value in the JSON structure.
55+
///
56+
/// # Returns
57+
/// * `Option<OwnedValue>` - Returns an owned clone of the value if found, or `None` if the path does not exist.
58+
///
59+
/// # Examples
60+
/// ```
61+
/// # use simd_json::prelude::{TypedScalarValue, ValueAsScalar};
62+
/// # use ferrumc_registry::lookup_owned;
63+
/// let value = lookup_owned("minecraft:item/entries/minecraft:apple/protocol_id");
64+
/// assert_eq!(value.unwrap().as_u64().unwrap(), 840);
65+
/// ```
66+
pub fn lookup_owned(path: &str) -> Option<OwnedValue> {
67+
lookup(path).cloned()
68+
}
69+
70+
#[cfg(test)]
71+
mod tests {
72+
use super::*;
73+
use simd_json::prelude::ValueAsScalar;
74+
#[test]
75+
fn test_lookup() {
76+
let value = lookup("minecraft:item/entries/minecraft:apple/protocol_id");
77+
assert!(value.is_some());
78+
let value = value.unwrap();
79+
let numeric_value = value.as_u64();
80+
assert!(numeric_value.is_some());
81+
assert_eq!(numeric_value.unwrap(), 840);
82+
}
83+
84+
#[test]
85+
fn test_lookup_owned() {
86+
let value = lookup_owned("minecraft:item/entries/minecraft:apple/protocol_id");
87+
assert!(value.is_some());
88+
let value = value.unwrap();
89+
let numeric_value = value.as_u64();
90+
assert!(numeric_value.is_some());
91+
assert_eq!(numeric_value.unwrap(), 840);
92+
}
93+
94+
#[test]
95+
fn test_lookup_non_existent() {
96+
let value = lookup("minecraft:item/entries/minecraft:non_existent/protocol_id");
97+
assert!(value.is_none());
98+
}
99+
100+
#[test]
101+
fn test_lookup_empty_path() {
102+
// Edge case: empty path should return the root
103+
let value = lookup("");
104+
assert!(value.is_some());
105+
assert_eq!(value.unwrap(), &*LOADED_REGISTRY);
106+
}
107+
}

0 commit comments

Comments
 (0)