From 02d65e6b7ce7a0e6bae054bd321c68dda1cb0de3 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 7 Jan 2025 22:56:34 +0100 Subject: static typing for resources --- shared/src/packets.rs | 99 ++++++++++++++++++++++++++++++++++++++++--------- shared/src/resources.rs | 73 +++++++++++++++++++++--------------- shared/src/store.rs | 18 ++++++--- shared/src/tree.rs | 11 ++++-- 4 files changed, 144 insertions(+), 57 deletions(-) (limited to 'shared/src') diff --git a/shared/src/packets.rs b/shared/src/packets.rs index 9cecbe2..1d3155f 100644 --- a/shared/src/packets.rs +++ b/shared/src/packets.rs @@ -14,15 +14,31 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +use crate::resources::{Prefab, PrefabIndex}; use anyhow::{Result, bail}; use glam::Vec3A; use std::{ fmt::{Debug, Display}, + hash::Hash, io::{Read, Write}, + marker::PhantomData, }; -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct Resource(pub [u8; 32]); +#[derive(Clone, Copy)] +pub struct Resource(pub [u8; 32], pub PhantomData); + +impl PartialEq for Resource { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} +impl Eq for Resource {} +impl Hash for Resource { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Object(pub u128); @@ -35,12 +51,14 @@ pub enum Packet { Disconnect, RequestResource(Resource), RespondResource(Data), - Add(Object, Resource), + Add(Object, Resource), Remove(Object), Position(Object, Vec3A, Vec3A), Pose(Object, Vec), Parent(Object, Object), Sound(Object, Data), + PrefabIndex(Resource), + PrefabName(Resource, String), } pub trait ReadWrite: Sized { @@ -84,17 +102,13 @@ impl Packet { } Packet::Remove(object) => { w.write_all(&[0x04])?; - w.write_all(&object.0.to_be_bytes())?; + object.write(w)?; } Packet::Position(object, pos, rot) => { w.write_all(&[0x05])?; w.write_all(&object.0.to_be_bytes())?; - w.write_all(&pos.x.to_be_bytes())?; - w.write_all(&pos.y.to_be_bytes())?; - w.write_all(&pos.z.to_be_bytes())?; - w.write_all(&rot.x.to_be_bytes())?; - w.write_all(&rot.y.to_be_bytes())?; - w.write_all(&rot.z.to_be_bytes())?; + pos.write(w)?; + rot.write(w)?; } Packet::Pose(object, vec) => { w.write_all(&[0x06])?; @@ -111,6 +125,15 @@ impl Packet { w.write_all(&object.0.to_be_bytes())?; data.write(w)?; } + Packet::PrefabIndex(resource) => { + w.write_all(&[0x09])?; + resource.write(w)?; + } + Packet::PrefabName(resource, name) => { + w.write_all(&[0x0a])?; + resource.write(w)?; + name.write(w)?; + } } Ok(()) } @@ -154,21 +177,42 @@ fn read_u128(r: &mut dyn Read) -> Result { r.read_exact(&mut buf)?; Ok(u128::from_be_bytes(buf)) } -impl ReadWrite for Resource { +impl ReadWrite for Resource { fn write(&self, w: &mut dyn Write) -> Result<()> { w.write_all(&self.0)?; Ok(()) } fn read(r: &mut dyn Read) -> Result { - let mut s = Self([0; 32]); + let mut s = Self([0; 32], PhantomData); r.read_exact(&mut s.0)?; Ok(s) } } +impl ReadWrite for Object { + fn write(&self, w: &mut dyn Write) -> Result<()> { + w.write_all(&self.0.to_be_bytes())?; + Ok(()) + } + + fn read(r: &mut dyn Read) -> Result { + let mut s = [0; 16]; + r.read_exact(&mut s)?; + Ok(Object(u128::from_be_bytes(s))) + } +} + impl ReadWrite for Data { fn write(&self, w: &mut dyn Write) -> Result<()> { - w.write_all(&(self.0.len() as u32).to_be_bytes())?; - w.write_all(&self.0)?; + self.0.write(w) + } + fn read(r: &mut dyn Read) -> Result { + Ok(Self(Vec::read(r)?)) + } +} +impl ReadWrite for Vec { + fn write(&self, w: &mut dyn Write) -> Result<()> { + w.write_all(&(self.len() as u32).to_be_bytes())?; + w.write_all(&self)?; Ok(()) } fn read(r: &mut dyn Read) -> Result { @@ -177,7 +221,22 @@ impl ReadWrite for Data { let size = u32::from_be_bytes(size); let mut buf = vec![0; size as usize]; r.read_exact(&mut buf)?; - Ok(Data(buf)) + Ok(buf) + } +} +impl ReadWrite for String { + fn write(&self, w: &mut dyn Write) -> Result<()> { + w.write_all(&(self.len() as u32).to_be_bytes())?; + w.write_all(self.as_bytes())?; + Ok(()) + } + fn read(r: &mut dyn Read) -> Result { + let mut size = [0; 4]; + r.read_exact(&mut size)?; + let size = u32::from_be_bytes(size); + let mut buf = vec![0; size as usize]; + r.read_exact(&mut buf)?; + Ok(String::from_utf8(buf)?) } } fn read_params(r: &mut dyn Read) -> Result> { @@ -214,7 +273,7 @@ impl ReadWrite for u32 { } } -impl Display for Resource { +impl Display for Resource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -226,8 +285,7 @@ impl Display for Resource { ) } } - -impl Debug for Resource { +impl Debug for Resource { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self) } @@ -250,3 +308,8 @@ impl Debug for Data { f.debug_tuple("Data").finish_non_exhaustive() } } +impl Display for Object { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Object").field(&self.0).finish() + } +} diff --git a/shared/src/resources.rs b/shared/src/resources.rs index be34b21..d22e3ea 100644 --- a/shared/src/resources.rs +++ b/shared/src/resources.rs @@ -19,60 +19,64 @@ use anyhow::Result; use glam::{Affine3A, Vec3A}; use log::warn; use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, io::{Read, Write}, }; #[derive(Debug, Default, Clone)] pub struct Prefab { - pub mesh: Vec<(Affine3A, Resource)>, - pub light: Vec<(Vec3A, Resource)>, - pub environment: Option, + pub mesh: Vec<(Affine3A, Resource)>, + pub light: Vec<(Vec3A, Resource)>, + pub environment: Option>, } #[derive(Debug, Default, Clone)] pub struct LightPart { - emission: Option, - radius: Option, + pub emission: Option, + pub radius: Option, } #[derive(Debug, Default, Clone)] pub struct EnvironmentPart { - skybox: Option, - sun: Option<(Vec3A, Vec3A)>, + pub skybox: Option, + pub sun: Option<(Vec3A, Vec3A)>, } #[derive(Debug, Default, Clone)] pub struct MeshPart { - pub index: Option, + pub index: Option>, pub g_metallic: Option, pub g_roughness: Option, pub g_albedo: Option, pub g_transmission: Option, pub g_emission: Option, - pub va_position: Option<[Resource; 3]>, - pub va_normal: Option<[Resource; 3]>, - pub va_texcoord: Option<[Resource; 2]>, - pub va_roughness: Option, - pub va_metallic: Option, - pub va_albedo: Option<[Resource; 3]>, - pub va_transmission: Option, - pub va_emission: Option<[Resource; 3]>, - pub tex_normal: Option, - pub tex_roughness: Option, - pub tex_metallic: Option, - pub tex_albedo: Option, - pub tex_transmission: Option, - pub tex_emission: Option, + pub va_position: Option<[Resource; 3]>, + pub va_normal: Option<[Resource; 3]>, + pub va_texcoord: Option<[Resource; 2]>, + pub va_roughness: Option>, + pub va_metallic: Option>, + pub va_albedo: Option<[Resource; 3]>, + pub va_transmission: Option>, + pub va_emission: Option<[Resource; 3]>, + pub tex_normal: Option>, + pub tex_roughness: Option>, + pub tex_metallic: Option>, + pub tex_albedo: Option>, + pub tex_transmission: Option>, + pub tex_emission: Option>, } -pub struct PrefabIndex(pub HashMap); +#[derive(Debug, Default, Clone)] +pub struct PrefabIndex(pub BTreeMap); #[derive(Debug, Default, Clone)] pub struct AttributeArray(pub Vec); #[derive(Debug, Default, Clone)] pub struct IndexArray(pub Vec<[u16; 3]>); +#[derive(Debug, Clone)] +pub struct Image(pub Vec); + impl ReadWrite for PrefabIndex { fn write(&self, w: &mut dyn Write) -> Result<()> { for (k, v) in &self.0 { @@ -81,7 +85,7 @@ impl ReadWrite for PrefabIndex { Ok(()) } fn read(r: &mut dyn Read) -> Result { - let mut s = Self(HashMap::new()); + let mut s = Self(BTreeMap::new()); let mut g = Vec::new(); r.read_to_end(&mut g)?; let mut g = g.as_slice(); @@ -96,10 +100,10 @@ impl ReadWrite for PrefabIndex { impl ReadWrite for Prefab { fn write(&self, w: &mut dyn Write) -> Result<()> { for x in &self.mesh { - write_kv_opt(w, b"mesh", &Some(*x))?; + write_kv_opt(w, b"mesh", &Some(x.clone()))?; } for x in &self.light { - write_kv_opt(w, b"light", &Some(*x))?; + write_kv_opt(w, b"light", &Some(x.clone()))?; } write_kv_opt(w, b"environment", &self.environment)?; Ok(()) @@ -269,9 +273,9 @@ impl ReadWrite for u8 { impl ReadWrite for Vec3A { fn write(&self, w: &mut dyn Write) -> Result<()> { - w.write_all(&self.x.to_be_bytes())?; - w.write_all(&self.y.to_be_bytes())?; - w.write_all(&self.z.to_be_bytes())?; + self.x.write(w)?; + self.y.write(w)?; + self.z.write(w)?; Ok(()) } fn read(r: &mut dyn Read) -> Result { @@ -333,3 +337,12 @@ impl ReadWrite for (A, B) { Ok((A::read(r)?, B::read(r)?)) } } +impl ReadWrite for Image { + fn write(&self, w: &mut dyn Write) -> Result<()> { + self.0.write(w) + } + + fn read(r: &mut dyn Read) -> Result { + Ok(Self( as ReadWrite>::read(r)?)) + } +} diff --git a/shared/src/store.rs b/shared/src/store.rs index 38402fc..f63ed6c 100644 --- a/shared/src/store.rs +++ b/shared/src/store.rs @@ -14,11 +14,11 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -use crate::packets::Resource; +use crate::packets::{ReadWrite, Resource}; use anyhow::Result; use redb::{Database, TableDefinition}; use sha2::{Digest, Sha256}; -use std::{collections::HashMap, path::Path, sync::Mutex}; +use std::{collections::HashMap, marker::PhantomData, path::Path, sync::Mutex}; const T_ENTRIES: TableDefinition<[u8; 32], &[u8]> = TableDefinition::new("e"); @@ -33,7 +33,15 @@ impl ResourceStore { pub fn new_memory() -> Self { Self::Memory(HashMap::new().into()) } - pub fn get(&self, key: Resource) -> Result>> { + pub fn get(&self, key: Resource) -> Result> { + self.get_raw(Resource(key.0, PhantomData))? + .map(|b| T::read(&mut b.as_slice())) + .transpose() + } + pub fn set(&self, value: &T) -> Result> { + Ok(Resource(self.set_raw(&value.write_alloc())?.0, PhantomData)) + } + pub fn get_raw(&self, key: Resource) -> Result>> { match self { ResourceStore::Redb(database) => { let txn = database.begin_read()?; @@ -46,8 +54,8 @@ impl ResourceStore { ResourceStore::Memory(map) => Ok(map.lock().unwrap().get(&key).map(|x| x.to_vec())), } } - pub fn set(&self, value: &[u8]) -> Result { - let key = Resource(sha256(value)); + pub fn set_raw(&self, value: &[u8]) -> Result { + let key = Resource(sha256(value), PhantomData); match self { ResourceStore::Redb(database) => { let txn = database.begin_write()?; diff --git a/shared/src/tree.rs b/shared/src/tree.rs index e2c8250..9337cfb 100644 --- a/shared/src/tree.rs +++ b/shared/src/tree.rs @@ -14,7 +14,10 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -use crate::packets::{Object, Packet, Resource}; +use crate::{ + packets::{Object, Packet, Resource}, + resources::Prefab, +}; use glam::Vec3A; use std::collections::HashMap; @@ -26,7 +29,7 @@ pub struct ObjectData { pub rot: Vec3A, pub parent: Object, pub pose: Vec, - pub res: Resource, + pub res: Resource, } impl Default for SceneTree { fn default() -> Self { @@ -44,7 +47,7 @@ impl SceneTree { pos: Vec3A::ZERO, rot: Vec3A::ZERO, pose: Vec::new(), - res: *res, + res: res.clone(), }); } Packet::Remove(object) => { @@ -75,7 +78,7 @@ impl SceneTree { .iter() .map(|(object, data)| { [ - Packet::Add(*object, data.res), + Packet::Add(*object, data.res.clone()), Packet::Parent(*object, data.parent), Packet::Position(*object, data.pos, data.rot), Packet::Pose(*object, data.pose.clone()), -- cgit v1.2.3-70-g09d2