#![forbid(unsafe_code)]
#![warn(missing_docs)]
extern crate self as arkhe_forge_core;
pub mod action;
pub mod activity;
pub mod actor;
pub mod brand;
pub mod bridge;
pub mod component;
pub mod context;
pub mod entry;
pub mod event;
pub mod pii;
pub mod pipeline;
#[doc(hidden)]
#[path = "sealed.rs"]
pub mod __sealed;
pub mod space;
pub mod typecode;
pub mod user;
pub use arkhe_forge_macros::{arkhe_pure, ArkheAction, ArkheComponent, ArkheEvent};
use arkhe_kernel::abi::{EntityId, InstanceId, Tick, TypeCode};
pub const RUNTIME_SEMVER: (u16, u16, u16) = (0, 13, 0);
pub const MAX_ID_DERIVE_RETRIES: u32 = 16;
#[must_use]
pub fn derive_entity_id(
world_seed: &[u8; 32],
instance_id: InstanceId,
type_code: TypeCode,
tick: Tick,
seq: u32,
) -> Option<EntityId> {
let domain_key = blake3::derive_key("arkhe-forge-entity-id", world_seed);
for bump in 0..MAX_ID_DERIVE_RETRIES {
let candidate_seq = seq.wrapping_add(bump);
let mut h = blake3::Hasher::new_keyed(&domain_key);
h.update(&instance_id.get().to_be_bytes());
h.update(&type_code.0.to_be_bytes());
h.update(&tick.0.to_be_bytes());
h.update(&candidate_seq.to_be_bytes());
let out = h.finalize();
let digest = out.as_bytes();
let mut first_eight = [0u8; 8];
first_eight.copy_from_slice(&digest[..8]);
let raw = u64::from_be_bytes(first_eight);
if let Some(id) = EntityId::new(raw) {
return Some(id);
}
}
None
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod lib_tests {
use super::*;
fn instance(v: u64) -> InstanceId {
InstanceId::new(v).unwrap()
}
#[test]
fn derive_entity_id_is_deterministic() {
let seed = [0x11u8; 32];
let iid = instance(1);
let a = derive_entity_id(&seed, iid, TypeCode(0x0001_0001), Tick(42), 0);
let b = derive_entity_id(&seed, iid, TypeCode(0x0001_0001), Tick(42), 0);
assert_eq!(a, b);
assert!(a.is_some());
}
#[test]
fn derive_entity_id_differs_across_type_code() {
let seed = [0x11u8; 32];
let iid = instance(1);
let a = derive_entity_id(&seed, iid, TypeCode(0x0001_0001), Tick(1), 0);
let b = derive_entity_id(&seed, iid, TypeCode(0x0001_0002), Tick(1), 0);
assert_ne!(a, b);
}
#[test]
fn derive_entity_id_differs_across_instance() {
let seed = [0x11u8; 32];
let a = derive_entity_id(&seed, instance(1), TypeCode(0x0001_0001), Tick(1), 0);
let b = derive_entity_id(&seed, instance(2), TypeCode(0x0001_0001), Tick(1), 0);
assert_ne!(a, b);
}
#[test]
fn derive_entity_id_differs_across_world_seed() {
let s1 = [0x01u8; 32];
let s2 = [0x02u8; 32];
let iid = instance(1);
let a = derive_entity_id(&s1, iid, TypeCode(0x0001_0001), Tick(1), 0);
let b = derive_entity_id(&s2, iid, TypeCode(0x0001_0001), Tick(1), 0);
assert_ne!(a, b);
}
#[test]
fn derive_entity_id_seq_changes_output() {
let seed = [0x11u8; 32];
let iid = instance(1);
let a = derive_entity_id(&seed, iid, TypeCode(0x0001_0001), Tick(1), 0);
let b = derive_entity_id(&seed, iid, TypeCode(0x0001_0001), Tick(1), 1);
assert_ne!(a, b);
}
}