✨➕ vanth, workspace: Add entity ID system and serialization dependencies
- Added new modules `entity` and `store` to `vanth` crate for ECS foundations - Implemented generic `Id` type with serialization/deserialization support - Introduced `serde` and `serde_json` dependencies in workspace and vanth crate - Defined `Vanth` struct and `ContentHash` component in `lib.rs` - Added placeholder `Store` component in new module - Updated workspace configuration with `resolver = "3"` and dependency versions
This commit is contained in:
parent
93e114a23a
commit
be75844fdd
7 changed files with 243 additions and 4 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
@ -456,6 +456,12 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
|
@ -607,6 +613,12 @@ version = "1.0.21"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
|
@ -633,6 +645,18 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.141"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.10"
|
||||
|
@ -762,6 +786,8 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_ecs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
[workspace]
|
||||
members = ["crates/*"]
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[workspace.dependencies]
|
||||
bevy_app = "0.16.1"
|
||||
bevy_ecs = "0.16.1"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
[package]
|
||||
name = "vanth"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
@ -10,3 +9,5 @@ path = "src/lib.rs"
|
|||
[dependencies]
|
||||
bevy_app.workspace = true
|
||||
bevy_ecs.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
|
144
crates/vanth/src/entity.rs
Normal file
144
crates/vanth/src/entity.rs
Normal file
|
@ -0,0 +1,144 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A generic identifier type that can be used for different entity types
|
||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct Id<T: ?Sized> {
|
||||
/// The raw identifier value
|
||||
pub value: [u8; 32],
|
||||
/// Phantom data to associate the ID with a specific type
|
||||
#[serde(skip)]
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Id<T> {
|
||||
/// Create a new ID with the given value
|
||||
pub fn new(value: [u8; 32]) -> Self {
|
||||
Self {
|
||||
value,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a random ID
|
||||
pub fn random() -> Self {
|
||||
let mut value = [0u8; 32];
|
||||
// Simple random generation for demonstration
|
||||
for i in 0..32 {
|
||||
value[i] = (std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos() as u8)
|
||||
.wrapping_add(i as u8);
|
||||
}
|
||||
Self::new(value)
|
||||
}
|
||||
|
||||
/// Convert the ID to a u64 for easier handling (uses only first 8 bytes)
|
||||
pub fn to_u64(&self) -> u64 {
|
||||
let mut result = 0u64;
|
||||
for i in 0..8 {
|
||||
if i < self.value.len() {
|
||||
result |= (self.value[i] as u64) << (i * 8);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Create an ID from a u64 (fills only first 8 bytes)
|
||||
pub fn from_u64(value: u64) -> Self {
|
||||
let mut bytes = [0u8; 32];
|
||||
for i in 0..8 {
|
||||
bytes[i] = ((value >> (i * 8)) & 0xFF) as u8;
|
||||
}
|
||||
Self::new(bytes)
|
||||
}
|
||||
|
||||
/// Convert the ID to a pair of u128 values for handling larger values
|
||||
/// Returns (high_bits, low_bits)
|
||||
pub fn to_u128_pair(&self) -> (u128, u128) {
|
||||
let mut high = 0u128;
|
||||
let mut low = 0u128;
|
||||
|
||||
// First 16 bytes go to low
|
||||
for i in 0..16 {
|
||||
low |= (self.value[i] as u128) << (i * 8);
|
||||
}
|
||||
|
||||
// Next 16 bytes go to high
|
||||
for i in 0..16 {
|
||||
high |= (self.value[i + 16] as u128) << (i * 8);
|
||||
}
|
||||
|
||||
(high, low)
|
||||
}
|
||||
|
||||
/// Create an ID from a pair of u128 values
|
||||
pub fn from_u128_pair(high: u128, low: u128) -> Self {
|
||||
let mut bytes = [0u8; 32];
|
||||
|
||||
// Low bits fill first 16 bytes
|
||||
for i in 0..16 {
|
||||
bytes[i] = ((low >> (i * 8)) & 0xFF) as u8;
|
||||
}
|
||||
|
||||
// High bits fill next 16 bytes
|
||||
for i in 0..16 {
|
||||
bytes[i + 16] = ((high >> (i * 8)) & 0xFF) as u8;
|
||||
}
|
||||
|
||||
Self::new(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> PartialEq for Id<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.value == other.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Eq for Id<T> {}
|
||||
|
||||
impl<T: ?Sized> Hash for Id<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.value.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Debug for Id<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let (high, low) = self.to_u128_pair();
|
||||
write!(
|
||||
f,
|
||||
"Id<{}>({:016x}{:016x})",
|
||||
std::any::type_name::<T>(),
|
||||
high,
|
||||
low
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Display for Id<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let (high, low) = self.to_u128_pair();
|
||||
write!(f, "{:016x}{:016x}", high, low)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContentHash {
|
||||
value: [u8; 32],
|
||||
}
|
||||
|
||||
impl ContentHash {}
|
||||
|
||||
pub trait Entity {
|
||||
fn entity_id() -> Id<dyn Entity> where Self: Sized;
|
||||
}
|
||||
|
||||
pub trait Component: Send + Sync + 'static {
|
||||
fn component_id() -> &'static str;
|
||||
}
|
||||
|
||||
// impl<'de, T> Component for T where T: Serialize + Deserialize<'de> {}
|
|
@ -1,6 +1,12 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
/// Library crate for the `vanth` ECS-based database node.
|
||||
use bevy_app::App;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_ecs::{prelude::*, query::QueryData};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod store;
|
||||
pub mod entity;
|
||||
|
||||
/// A server node wrapping a Bevy App without running it.
|
||||
pub struct Node {
|
||||
|
@ -20,4 +26,37 @@ impl Node {
|
|||
// Query for no components returns one item per entity.
|
||||
// self.app.world().entities().len()
|
||||
}
|
||||
|
||||
// TODO
|
||||
pub fn run() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Component, Serialize)]
|
||||
pub struct Vanth<T: VanthTuple + QueryData> {
|
||||
id: crate::entity::Id<Self>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl <T: VanthTuple + QueryData> Vanth<T> {
|
||||
fn save_system(query: Query<T>) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Impl for different tuple sizes
|
||||
pub trait VanthTuple {
|
||||
|
||||
}
|
||||
|
||||
// use a macro to implement VanthTuiple here.
|
||||
|
||||
#[derive(Debug, Deserialize, Component, Serialize)]
|
||||
pub struct ContentHash {
|
||||
pub hash: [u8; 32],
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// A trait is derivable for ECS components
|
||||
// The components must have a content hash, not the entity. For efficiency and ergonomics. This means that a hash of each relevant component must be stored in the Vanth component of the entity, in a `HashMap` or something. The ID of the component used by Vanth should be a method on the derived trait.
|
||||
|
|
13
crates/vanth/src/store.rs
Normal file
13
crates/vanth/src/store.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use bevy_ecs::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Component, Serialize)]
|
||||
pub struct Store {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
|
||||
}
|
9
crates/vanth/tests/derive.rs
Normal file
9
crates/vanth/tests/derive.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use bevy_ecs::component::Component;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vanth::Vanth;
|
||||
|
||||
// TODO: derive `Vanth`
|
||||
#[derive(Debug, Deserialize, Component, Serialize)]
|
||||
struct Foo {
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue