/*
wearechat - generic multiplayer game with voip
Copyright (C) 2025 metamuffin
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
use anyhow::{Result, bail};
use glam::Vec3A;
use std::{
fmt::{Debug, Display},
io::{Read, Write},
};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Resource(pub [u8; 32]);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Object(pub u128);
#[derive(Clone)]
pub struct Data(pub Vec);
#[derive(Debug, Clone)]
pub enum Packet {
Connect(u128),
Disconnect,
RequestResource(Resource),
RespondResource(Data),
Add(Object, Resource),
Remove(Object),
Position(Object, Vec3A, Vec3A),
Pose(Object, Vec),
Parent(Object, Object),
Sound(Object, Data),
}
pub trait ReadWrite: Sized {
fn write(&self, w: &mut dyn Write) -> Result<()>;
fn read(r: &mut dyn Read) -> Result;
fn write_alloc(&self) -> Vec {
let mut buf = Vec::new();
self.write(&mut buf).unwrap();
buf
}
}
impl Object {
pub fn new() -> Self {
Self(rand::random())
}
}
impl Packet {
fn serialize_inner(&self, w: &mut impl Write) -> Result<()> {
match self {
Packet::Connect(id) => {
w.write_all(&[0x00])?;
w.write_all(&id.to_be_bytes())?;
}
Packet::Disconnect => {
w.write_all(&[0xff])?;
}
Packet::RequestResource(resource) => {
w.write_all(&[0x01])?;
w.write_all(&resource.0)?;
}
Packet::RespondResource(data) => {
w.write_all(&[0x02])?;
data.write(w)?;
}
Packet::Add(object, resource) => {
w.write_all(&[0x03])?;
w.write_all(&object.0.to_be_bytes())?;
w.write_all(&resource.0)?;
}
Packet::Remove(object) => {
w.write_all(&[0x04])?;
w.write_all(&object.0.to_be_bytes())?;
}
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())?;
}
Packet::Pose(object, vec) => {
w.write_all(&[0x06])?;
w.write_all(&object.0.to_be_bytes())?;
w.write_all(&(vec.len() as u16).to_be_bytes())?;
}
Packet::Parent(parent, child) => {
w.write_all(&[0x07])?;
w.write_all(&parent.0.to_be_bytes())?;
w.write_all(&child.0.to_be_bytes())?;
}
Packet::Sound(object, data) => {
w.write_all(&[0x08])?;
w.write_all(&object.0.to_be_bytes())?;
data.write(w)?;
}
}
Ok(())
}
}
impl ReadWrite for Packet {
fn write(&self, w: &mut dyn Write) -> Result<()> {
let mut buf = Vec::new();
self.serialize_inner(&mut buf)?;
w.write_all(&(buf.len() as u32).to_be_bytes())?;
w.write_all(&buf)?;
Ok(())
}
fn read(r: &mut dyn Read) -> Result {
let packet_len = u32::read(r)?;
Ok(match u8::read(r)? {
0x00 => Packet::Connect(read_u128(r)?),
0x01 => Packet::RequestResource(Resource::read(r)?),
0x02 => Packet::RespondResource(Data::read(r)?),
0x03 => Packet::Add(Object(read_u128(r)?), Resource::read(r)?),
0x04 => Packet::Remove(Object(read_u128(r)?)),
0x05 => Packet::Position(
Object(read_u128(r)?),
Vec3A::new(f32::read(r)?, f32::read(r)?, f32::read(r)?),
Vec3A::new(f32::read(r)?, f32::read(r)?, f32::read(r)?),
),
0x06 => Packet::Pose(Object(read_u128(r)?), read_params(r)?),
0x07 => Packet::Parent(Object(read_u128(r)?), Object(read_u128(r)?)),
0x08 => Packet::Sound(Object(read_u128(r)?), Data::read(r)?),
_ => {
for _ in 0..packet_len.max(1) - 1 {
r.read_exact(&mut [0])?;
}
bail!("unknown packet tag");
}
})
}
}
fn read_u128(r: &mut dyn Read) -> Result {
let mut buf = [0; 16];
r.read_exact(&mut buf)?;
Ok(u128::from_be_bytes(buf))
}
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]);
r.read_exact(&mut s.0)?;
Ok(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)?;
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(Data(buf))
}
}
fn read_params(r: &mut dyn Read) -> Result> {
let mut size = [0; 2];
r.read_exact(&mut size)?;
let size = u16::from_be_bytes(size);
let mut v = Vec::with_capacity(size as usize);
for _ in 0..size {
v.push(f32::read(r)?);
}
Ok(v)
}
impl ReadWrite for f32 {
fn write(&self, w: &mut dyn Write) -> Result<()> {
w.write_all(&self.to_be_bytes())?;
Ok(())
}
fn read(r: &mut dyn Read) -> Result {
let mut buf = [0; 4];
r.read_exact(&mut buf)?;
Ok(f32::from_be_bytes(buf))
}
}
impl ReadWrite for u32 {
fn write(&self, w: &mut dyn Write) -> Result<()> {
w.write_all(&self.to_be_bytes())?;
Ok(())
}
fn read(r: &mut dyn Read) -> Result {
let mut buf = [0; 4];
r.read_exact(&mut buf)?;
Ok(u32::from_be_bytes(buf))
}
}
impl Display for Resource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Res{{{:08x}{:08x}{:08x}{:08x}}}",
u64::from_be_bytes(self.0[0..8].try_into().unwrap()),
u64::from_be_bytes(self.0[8..16].try_into().unwrap()),
u64::from_be_bytes(self.0[16..24].try_into().unwrap()),
u64::from_be_bytes(self.0[24..32].try_into().unwrap()),
)
}
}
impl Debug for Resource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl ReadWrite for [T; N] {
fn write(&self, w: &mut dyn Write) -> Result<()> {
for e in self {
e.write(w)?;
}
Ok(())
}
fn read(r: &mut dyn Read) -> Result {
[(); N].try_map(|()| T::read(r))
}
}
impl Debug for Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Data").finish_non_exhaustive()
}
}