diff options
author | metamuffin <metamuffin@disroot.org> | 2025-01-04 22:52:42 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-01-04 22:52:42 +0100 |
commit | 2707f03617478e2a5e521961c46c9c6511d5088d (patch) | |
tree | a3516660f36614b006ea44eecf92bb4ff709fa3b | |
parent | 2c1d8fdfd65ceb9361114f0105c23ff6a94bac2e (diff) | |
download | weareserver-2707f03617478e2a5e521961c46c9c6511d5088d.tar weareserver-2707f03617478e2a5e521961c46c9c6511d5088d.tar.bz2 weareserver-2707f03617478e2a5e521961c46c9c6511d5088d.tar.zst |
a
-rw-r--r-- | Cargo.lock | 187 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | a.md | 4 | ||||
-rw-r--r-- | network/src/lib.rs | 2 | ||||
-rw-r--r-- | server/Cargo.toml | 5 | ||||
-rw-r--r-- | server/src/main.rs | 129 | ||||
-rw-r--r-- | shared/Cargo.lock (renamed from network/Cargo.lock) | 0 | ||||
-rw-r--r-- | shared/Cargo.toml (renamed from network/Cargo.toml) | 3 | ||||
-rw-r--r-- | shared/src/lib.rs | 4 | ||||
-rw-r--r-- | shared/src/packets.rs (renamed from network/src/packets.rs) | 17 | ||||
-rw-r--r-- | shared/src/store.rs | 33 | ||||
-rw-r--r-- | shared/src/tree.rs | 70 |
12 files changed, 434 insertions, 22 deletions
@@ -34,6 +34,55 @@ dependencies = [ ] [[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] name = "anyhow" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -159,6 +208,52 @@ dependencies = [ ] [[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] name = "combine" version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -230,6 +325,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -257,6 +375,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] name = "indexmap" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -267,6 +397,12 @@ dependencies = [ ] [[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -502,6 +638,15 @@ dependencies = [ ] [[package]] +name = "redb" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0a72cd7140de9fc3e318823b883abf819c20d478ec89ce880466dc2ef263c6" +dependencies = [ + "libc", +] + +[[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -570,6 +715,11 @@ name = "server" version = "0.1.0" dependencies = [ "anyhow", + "clap", + "env_logger", + "log", + "weareshared", + "xdg", ] [[package]] @@ -579,6 +729,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] name = "syn" version = "2.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -633,15 +789,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] -name = "vrding" -version = "0.1.0" -dependencies = [ - "anyhow", - "audiopus", - "bincode", - "cpal", - "glam", -] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "walkdir" @@ -721,6 +872,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] +name = "weareshared" +version = "0.1.0" +dependencies = [ + "anyhow", + "audiopus", + "bincode", + "cpal", + "glam", + "redb", +] + +[[package]] name = "web-sys" version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -915,3 +1078,9 @@ checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" dependencies = [ "memchr", ] + +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" @@ -1,3 +1,3 @@ [workspace] -members = ["network", "server"] +members = ["shared", "server"] resolver = "3" @@ -7,7 +7,7 @@ type Res = u128 02 respond_resource(name: Res, data: Vec<u8>) 03 add(id: Obj, prefab: Res) 04 remove(id: Obj) -05 position(id: Obj, pos: Vec3) +05 position(id: Obj, pos: Vec3, rot: Vec3) 06 pose(id: Obj, params: Vec<f32>) 07 parent(parent: Obj, child: Obj) 08 sound(id: Obj, data: Vec<u8>) @@ -38,7 +38,7 @@ type Part = { ### Mesh ``` -[vertex count:u32] [index count:u32] ([x:f32] [y:f32] [z:f32] [u:f32] [v:f32])* ([n:u32])* +[vertex count:u16] [index count:u16] ([x:f32] [y:f32] [z:f32] [u:f32] [v:f32])* ([n:u16])* ``` ### Texture diff --git a/network/src/lib.rs b/network/src/lib.rs deleted file mode 100644 index d13c81d..0000000 --- a/network/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ - -pub mod packets; diff --git a/server/Cargo.toml b/server/Cargo.toml index a504e65..3a091bd 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -5,3 +5,8 @@ edition = "2024" [dependencies] anyhow = "1.0.95" +clap = { version = "4.5.23", features = ["derive"] } +env_logger = "0.11.6" +log = "0.4.22" +weareshared = { path = "../shared" } +xdg = "2.5.2" diff --git a/server/src/main.rs b/server/src/main.rs index e7a11a9..02ea03f 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,3 +1,128 @@ -fn main() { - println!("Hello, world!"); +use anyhow::Result; +use clap::Parser; +use log::{debug, info}; +use std::{ + collections::HashMap, + io::{BufReader, BufWriter}, + net::{IpAddr, TcpListener, TcpStream}, + sync::{ + Arc, Mutex, + mpsc::{Receiver, Sender, channel}, + }, + thread::spawn, +}; +use weareshared::{packets::Packet, store::ResourceStore, tree::SceneTree}; + +#[derive(Parser, Debug)] +struct Args { + #[arg(short, long, default_value = "::0")] + bind_addr: IpAddr, + #[arg(short, long, default_value = "28555")] + port: u16, +} + +struct State { + tx: HashMap<usize, Sender<Packet>>, + store: ResourceStore, + tree: SceneTree, +} + +fn main() -> Result<()> { + env_logger::init_from_env("LOG"); + let args = Args::parse(); + + let listener = TcpListener::bind((args.bind_addr, args.port))?; + info!("bound to {}", listener.local_addr()?); + let state = Arc::new(Mutex::new(State::new()?)); + for conn in 0.. { + let (sock, addr) = listener.accept()?; + info!("{addr} connected"); + let (tx, rx) = channel(); + state.lock().unwrap().tx.insert(conn, tx); + let state2 = state.clone(); + let sock2 = sock.try_clone().unwrap(); + spawn(move || { + let _ = handle_conn_read(conn, sock, state2.clone()); + info!("{addr} disconnected"); + state2.lock().unwrap().tx.remove(&conn); + }); + spawn(move || { + let _ = handle_conn_write(conn, sock2, rx); + }); + } + Ok(()) +} + +fn handle_conn_read(conn: usize, sock: TcpStream, state: Arc<Mutex<State>>) -> Result<()> { + let mut sock = BufReader::new(sock); + loop { + let packet = Packet::deserialize(&mut sock)?; + debug!("{conn} <- {packet:?}"); + state.lock().unwrap().handle_packet(conn, packet)?; + } +} +fn handle_conn_write(conn: usize, sock: TcpStream, rx: Receiver<Packet>) -> Result<()> { + let mut sock = BufWriter::new(sock); + for packet in rx { + debug!("{conn} -> {packet:?}"); + packet.serialize(&mut sock)?; + } + Ok(()) +} +impl State { + pub fn new() -> Result<Self> { + Ok(Self { + tx: HashMap::new(), + store: ResourceStore::new( + &xdg::BaseDirectories::with_prefix("weareserver")?.place_cache_file("resources")?, + )?, + tree: SceneTree::default(), + }) + } + pub fn broadcast(&self, packet: Packet) -> Result<()> { + for tx in self.tx.values() { + tx.send(packet.clone())?; + } + Ok(()) + } + pub fn send(&self, conn: usize, packet: Packet) -> Result<()> { + if let Some(tx) = self.tx.get(&conn) { + tx.send(packet)?; + } + Ok(()) + } + pub fn handle_packet(&mut self, conn: usize, packet: Packet) -> Result<()> { + self.tree.update(&packet); + match packet { + Packet::RequestResource(resource) => { + if let Some(r) = self.store.get(resource)? { + self.send(conn, Packet::RespondResource(resource, r))?; + } else { + self.broadcast(Packet::RequestResource(resource))?; + } + } + Packet::RespondResource(resource, vec) => { + self.broadcast(Packet::RespondResource(resource, vec))?; + } + Packet::Add(object, resource) => { + self.broadcast(Packet::Add(object, resource))?; + } + Packet::Remove(object) => { + self.broadcast(Packet::Remove(object))?; + } + Packet::Position(object, pos, rot) => { + self.broadcast(Packet::Position(object, pos, rot))?; + } + Packet::Pose(object, vec) => { + self.broadcast(Packet::Pose(object, vec))?; + } + Packet::Parent(parent, child) => { + self.broadcast(Packet::Parent(parent, child))?; + } + Packet::Sound(object, vec) => { + self.broadcast(Packet::Sound(object, vec))?; + } + } + Ok(()) + } } diff --git a/network/Cargo.lock b/shared/Cargo.lock index 8e3c9b7..8e3c9b7 100644 --- a/network/Cargo.lock +++ b/shared/Cargo.lock diff --git a/network/Cargo.toml b/shared/Cargo.toml index 2f6c8fa..9236576 100644 --- a/network/Cargo.toml +++ b/shared/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "vrding" +name = "weareshared" version = "0.1.0" edition = "2024" @@ -9,3 +9,4 @@ audiopus = "0.2.0" bincode = "1.3.3" cpal = "0.15.3" glam = { version = "0.29.2", features = ["serde"] } +redb = "2.4.0" diff --git a/shared/src/lib.rs b/shared/src/lib.rs new file mode 100644 index 0000000..a692a82 --- /dev/null +++ b/shared/src/lib.rs @@ -0,0 +1,4 @@ + +pub mod packets; +pub mod store; +pub mod tree; diff --git a/network/src/packets.rs b/shared/src/packets.rs index 03c7270..79dd1ea 100644 --- a/network/src/packets.rs +++ b/shared/src/packets.rs @@ -2,15 +2,18 @@ use anyhow::{Result, bail}; use glam::Vec3; use std::io::{Read, Write}; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Resource(pub u128); +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Object(pub u128); +#[derive(Debug, Clone)] pub enum Packet { RequestResource(Resource), RespondResource(Resource, Vec<u8>), Add(Object, Resource), Remove(Object), - Position(Object, Vec3), + Position(Object, Vec3, Vec3), Pose(Object, Vec<f32>), Parent(Object, Object), Sound(Object, Vec<u8>), @@ -38,12 +41,15 @@ impl Packet { w.write_all(&[0x04])?; w.write_all(&object.0.to_be_bytes())?; } - Packet::Position(object, vec3) => { + Packet::Position(object, pos, rot) => { w.write_all(&[0x05])?; w.write_all(&object.0.to_be_bytes())?; - w.write_all(&vec3.x.to_be_bytes())?; - w.write_all(&vec3.y.to_be_bytes())?; - w.write_all(&vec3.z.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())?; } Packet::Pose(object, vec) => { w.write_all(&[0x06])?; @@ -74,6 +80,7 @@ impl Packet { 0x05 => Packet::Position( Object(read_u128(r)?), Vec3::new(read_float(r)?, read_float(r)?, read_float(r)?), + Vec3::new(read_float(r)?, read_float(r)?, read_float(r)?), ), 0x06 => Packet::Pose(Object(read_u128(r)?), read_params(r)?), 0x07 => Packet::Parent(Object(read_u128(r)?), Object(read_u128(r)?)), diff --git a/shared/src/store.rs b/shared/src/store.rs new file mode 100644 index 0000000..83f1a25 --- /dev/null +++ b/shared/src/store.rs @@ -0,0 +1,33 @@ +use crate::packets::Resource; +use anyhow::Result; +use redb::{Database, TableDefinition}; +use std::path::Path; + +const T_ENTRIES: TableDefinition<u128, &[u8]> = TableDefinition::new("e"); + +pub struct ResourceStore { + db: Database, +} +impl ResourceStore { + pub fn new(path: &Path) -> Result<Self> { + Ok(Self { + db: Database::create(path)?, + }) + } + pub fn get(&self, key: Resource) -> Result<Option<Vec<u8>>> { + let txn = self.db.begin_read()?; + let ent = txn.open_table(T_ENTRIES)?; + match ent.get(key.0)? { + Some(x) => Ok(Some(x.value().to_vec())), + None => Ok(None), + } + } + pub fn set(&self, key: Resource, value: &[u8]) -> Result<()> { + let txn = self.db.begin_write()?; + let mut ent = txn.open_table(T_ENTRIES)?; + ent.insert(key.0, value)?; + drop(ent); + txn.commit()?; + Ok(()) + } +} diff --git a/shared/src/tree.rs b/shared/src/tree.rs new file mode 100644 index 0000000..fce6f8a --- /dev/null +++ b/shared/src/tree.rs @@ -0,0 +1,70 @@ +use crate::packets::{Object, Packet, Resource}; +use glam::Vec3; +use std::collections::HashMap; + +pub struct SceneTree { + objects: HashMap<Object, ObjectData>, +} +struct ObjectData { + pos: Vec3, + rot: Vec3, + parent: Object, + pose: Vec<f32>, + res: Resource, +} +impl Default for SceneTree { + fn default() -> Self { + Self { + objects: Default::default(), + } + } +} +impl SceneTree { + pub fn update(&mut self, p: &Packet) { + match p { + Packet::Add(object, res) => { + self.objects.insert(*object, ObjectData { + parent: Object(0), + pos: Vec3::ZERO, + rot: Vec3::ZERO, + pose: Vec::new(), + res: *res, + }); + } + Packet::Remove(object) => { + self.objects.remove(&object); + } + Packet::Position(object, pos, rot) => { + if let Some(o) = self.objects.get_mut(&object) { + o.pos = *pos; + o.rot = *rot; + } + } + Packet::Pose(object, pose) => { + if let Some(o) = self.objects.get_mut(&object) { + o.pose = pose.to_vec(); + } + } + Packet::Parent(parent, child) => { + if let Some(o) = self.objects.get_mut(&parent) { + o.parent = *child + } + } + _ => (), + } + } + + pub fn prime_client(&self) -> impl Iterator<Item = Packet> { + self.objects + .iter() + .map(|(object, data)| { + [ + Packet::Add(*object, data.res), + Packet::Parent(*object, data.parent), + Packet::Position(*object, data.pos, data.rot), + Packet::Pose(*object, data.pose.clone()), + ] + }) + .flatten() + } +} |