/*
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 crate::{
helper::ReadWrite,
resources::{Prefab, PrefabIndex},
};
use anyhow::{Result, bail};
use glam::{Affine3A, Vec3A};
use std::{
fmt::{Debug, Display},
hash::Hash,
io::{Read, Write},
marker::PhantomData,
};
#[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);
}
}
impl PartialOrd for Resource {
fn partial_cmp(&self, other: &Self) -> Option {
Some(self.cmp(other))
}
}
impl Ord for Resource {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl Resource {
pub fn to_generic(&self) -> Resource {
Resource(self.0, PhantomData)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Object(pub u128);
#[derive(Clone)]
pub struct Data(pub Vec);
#[derive(Debug, Clone)]
pub struct Message(pub String);
#[derive(Debug, Clone)]
pub enum Packet {
Connect(u128),
Disconnect,
RequestResource(Resource),
RespondResource(Resource, Data),
Add(Object, Resource),
Remove(Object),
Position(Object, Vec3A, Vec3A),
Pose(Object, Vec<(u16, Affine3A)>),
Parent(Object, Object),
Sound(Object, Data),
PrefabIndex(Resource),
AnnouncePrefab(Resource),
Chat(Object, Message),
}
impl Object {
pub fn new() -> Self {
Self(rand::random())
}
}
impl Default for Object {
fn default() -> Self {
Self::new()
}
}
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_le_bytes())?;
}
Packet::Disconnect => {
w.write_all(&[0xff])?;
}
Packet::RequestResource(resource) => {
w.write_all(&[0x01])?;
w.write_all(&resource.0)?;
}
Packet::RespondResource(resource, data) => {
w.write_all(&[0x02])?;
resource.write(w)?;
data.write(w)?;
}
Packet::Add(object, resource) => {
w.write_all(&[0x03])?;
w.write_all(&object.0.to_le_bytes())?;
w.write_all(&resource.0)?;
}
Packet::Remove(object) => {
w.write_all(&[0x04])?;
object.write(w)?;
}
Packet::Position(object, pos, rot) => {
w.write_all(&[0x05])?;
w.write_all(&object.0.to_le_bytes())?;
pos.write(w)?;
rot.write(w)?;
}
Packet::Pose(object, vec) => {
w.write_all(&[0x06])?;
w.write_all(&object.0.to_le_bytes())?;
w.write_all(&(vec.len() as u32).to_le_bytes())?;
for (i, a) in vec {
i.write(w)?;
a.write(w)?;
}
}
Packet::Parent(parent, child) => {
w.write_all(&[0x07])?;
w.write_all(&parent.0.to_le_bytes())?;
w.write_all(&child.0.to_le_bytes())?;
}
Packet::Sound(object, data) => {
w.write_all(&[0x08])?;
w.write_all(&object.0.to_le_bytes())?;
data.write(w)?;
}
Packet::PrefabIndex(resource) => {
w.write_all(&[0x09])?;
resource.write(w)?;
}
Packet::AnnouncePrefab(resource) => {
w.write_all(&[0x0a])?;
resource.write(w)?;
}
Packet::Chat(object, message) => {
w.write_all(&[0x0b])?;
object.write(w)?;
message.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_le_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(Resource::read(r)?, Data::read(r)?),
0x03 => Packet::Add(Object::read(r)?, Resource::read(r)?),
0x04 => Packet::Remove(Object::read(r)?),
0x05 => Packet::Position(
Object::read(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(r)?, read_params(r)?),
0x07 => Packet::Parent(Object::read(r)?, Object::read(r)?),
0x08 => Packet::Sound(Object::read(r)?, Data::read(r)?),
0x09 => Packet::PrefabIndex(Resource::read(r)?),
0x0a => Packet::AnnouncePrefab(Resource::read(r)?),
0x0b => Packet::Chat(Object::read(r)?, Message::read(r)?),
tag => {
for _ in 0..packet_len.max(1) - 1 {
r.read_exact(&mut [0])?;
}
bail!("unknown packet tag {tag:x}");
}
})
}
}
fn read_u128(r: &mut dyn Read) -> Result {
let mut buf = [0; 16];
r.read_exact(&mut buf)?;
Ok(u128::from_le_bytes(buf))
}
fn read_params(r: &mut dyn Read) -> Result> {
let mut size = [0; 4];
r.read_exact(&mut size)?;
let size = u32::from_le_bytes(size);
let mut v = Vec::with_capacity(size as usize);
for _ in 0..size {
v.push((u16::read(r)?, Affine3A::read(r)?));
}
Ok(v)
}
impl Display for Resource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Res{{{:016x}{:016x}{:016x}{:016x}}}",
u64::from_le_bytes(self.0[0..8].try_into().unwrap()),
u64::from_le_bytes(self.0[8..16].try_into().unwrap()),
u64::from_le_bytes(self.0[16..24].try_into().unwrap()),
u64::from_le_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 Debug for Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
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_with(|f| write!(f, "{:016x}", self.0))
.finish()
}
}
impl Object {
pub const ROOT: Object = Object(0);
}