✨➕ 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",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.77"
|
version = "0.3.77"
|
||||||
|
@ -607,6 +613,12 @@ version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -633,6 +645,18 @@ dependencies = [
|
||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.10"
|
version = "0.4.10"
|
||||||
|
@ -762,6 +786,8 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/*"]
|
members = ["crates/*"]
|
||||||
|
resolver = "3"
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
bevy_app = "0.16.1"
|
bevy_app = "0.16.1"
|
||||||
bevy_ecs = "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]
|
[package]
|
||||||
name = "vanth"
|
name = "vanth"
|
||||||
version = "0.1.0"
|
version.workspace = true
|
||||||
edition = "2024"
|
edition.workspace = true
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
@ -10,3 +9,5 @@ path = "src/lib.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy_app.workspace = true
|
bevy_app.workspace = true
|
||||||
bevy_ecs.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.
|
/// Library crate for the `vanth` ECS-based database node.
|
||||||
use bevy_app::App;
|
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.
|
/// A server node wrapping a Bevy App without running it.
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
|
@ -20,4 +26,37 @@ impl Node {
|
||||||
// Query for no components returns one item per entity.
|
// Query for no components returns one item per entity.
|
||||||
// self.app.world().entities().len()
|
// 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