/*
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 .
*/
#![feature(iter_array_chunks)]
pub mod mesh;
use anyhow::{Result, bail};
use clap::Parser;
use gltf::image::Source;
use log::info;
use mesh::import_mesh;
use rand::random;
use std::{
fs::File,
io::{Read, Write},
net::{SocketAddr, TcpStream},
path::{Path, PathBuf},
thread::{self, sleep},
time::Duration,
};
use weareshared::{
Vec3A,
packets::{Data, Object, Packet, ReadWrite, Resource},
resources::{Image, LightPart, Prefab},
store::ResourceStore,
vec3a,
};
#[derive(Parser)]
struct Args {
address: SocketAddr,
scene: PathBuf,
#[arg(short, long)]
push: bool,
#[arg(short, long)]
spin: bool,
#[arg(short, long)]
clear: bool,
}
fn main() -> Result<()> {
env_logger::init_from_env("LOG");
let args = Args::parse();
let mut sock = TcpStream::connect(args.address)?;
let store = ResourceStore::new_memory();
Packet::Connect(random()).write(&mut sock)?;
let (gltf, buffers, _) = gltf::import(&args.scene)?;
let path_base = args.scene.parent().unwrap();
let mut prefab = Prefab::default();
for node in gltf.nodes() {
if let Some(mesh) = node.mesh() {
import_mesh(mesh, &buffers, &store, path_base, &node, &mut prefab)?;
}
}
prefab.light.push((
vec3a(5., 5., 5.),
store.set(&LightPart {
emission: Some(vec3a(0.5, 0.1, 1.0)),
radius: Some(0.2),
})?,
));
let ob = Object::new();
Packet::Add(ob, store.set(&prefab)?).write(&mut sock)?;
if args.spin {
let mut sock2 = sock.try_clone().unwrap();
thread::spawn(move || {
let mut x = 0.;
loop {
Packet::Position(ob, Vec3A::ZERO, vec3a(x, x * 0.3, x * 0.1))
.write(&mut sock2)
.unwrap();
sock2.flush().unwrap();
x += 0.1;
sleep(Duration::from_millis(50));
}
});
}
if args.push {
store.iter(|d| {
Packet::RespondResource(Data(d.to_vec()))
.write(&mut sock)
.unwrap();
})?;
sock.flush()?;
} else {
loop {
let packet = Packet::read(&mut sock)?;
match packet {
Packet::RequestResource(hash) => {
if let Some(d) = store.get_raw(hash)? {
Packet::RespondResource(Data(d)).write(&mut sock)?;
sock.flush()?;
}
}
Packet::Add(ob_a, _) => {
if ob_a != ob {
info!("removing object {ob_a}");
Packet::Remove(ob_a).write(&mut sock)?;
sock.flush()?;
}
}
_ => (),
}
}
}
Ok(())
}
fn load_texture(
name: &str,
store: &ResourceStore,
path: &Path,
buffers: &[gltf::buffer::Data],
source: &Source,
) -> Result> {
match source {
gltf::image::Source::View { view, mime_type } => {
info!("{name} texture is embedded and of type {mime_type:?}");
let buf =
&buffers[view.buffer().index()].0[view.offset()..view.offset() + view.length()];
return Ok(store.set(&Image(buf.to_vec()))?);
}
gltf::image::Source::Uri {
uri,
mime_type: Some(mime_type),
} => {
info!("{name} texture is at {uri:?} and of type {mime_type:?}");
let path = path.join(uri);
let mut buf = Vec::new();
File::open(path)?.read_to_end(&mut buf)?;
return Ok(store.set(&Image(buf))?);
}
_ => {
bail!("texture is external and has no mime type")
}
}
}