/* 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::{ audio::Audio, camera::Camera, download::Downloader, interfaces::{ui_selector, InterfaceData}, network::Network, render::Renderer, ui::UiEvent }; use anyhow::{Context, Result}; use glam::{Vec2, Vec3}; use log::{info, warn}; use std::{net::TcpStream, sync::Arc, time::Instant}; use weareshared::{ packets::{Data, Object, Packet, Resource}, resources::PrefabIndex, store::ResourceStore, tree::SceneTree, }; use winit::event::MouseButton; pub struct State<'a> { pub network: Arc, pub downloader: Arc, pub renderer: Renderer<'a>, pub tree: SceneTree, pub camera: Camera, pub input_state: InputState, pub audio: Audio, pub prefab_index: Arc, pub prefab_index_res_loaded: Option>, pub prefab_index_res: Option>, pub avatar_ob: Option, pub last_position_tx: Instant, } pub struct InputState { time: Instant, pub move_dir: Vec3, pub mouse_acc: Vec2, pub cursor_pos: Vec2, pub ui_events: Vec, } impl<'a> State<'a> { pub fn new(conn: TcpStream, window: &'a winit::window::Window) -> Result> { info!("new state"); let downloader = Arc::new(Downloader::new(ResourceStore::new_env()?)); Ok(Self { audio: Audio::new()?, camera: Camera::new(), network: Network::new(conn).into(), tree: SceneTree::default(), renderer: Renderer::new(window, downloader.clone())?, downloader, input_state: InputState { time: Instant::now(), move_dir: Vec3::ZERO, mouse_acc: Vec2::ZERO, cursor_pos: Vec2::ZERO, ui_events: Vec::new(), }, prefab_index_res: None, prefab_index_res_loaded: None, avatar_ob: None, prefab_index: PrefabIndex::default().into(), last_position_tx: Instant::now(), }) } pub fn draw(&mut self) { if let Err(e) = self .renderer .draw(&self.tree, &self.camera, &mut self.input_state) { warn!("draw failed: {e:?}"); } } pub fn resize(&mut self, width: u32, height: u32) { self.renderer.resize(width, height); self.camera.aspect = width as f32 / height as f32; } pub fn click(&mut self, button: MouseButton, down: bool) { self.input_state .ui_events .push(UiEvent::Click(self.input_state.cursor_pos, button, down)); if !down || button != MouseButton::Right { return; } let idata = Arc::new(InterfaceData { scene_prepare: self.renderer.scene_prepare.clone(), downloader: self.downloader.clone(), network: self.network.clone(), prefab_index: self.prefab_index.clone(), render_timing: self.renderer.timing_submit.clone(), adapter_info: self.renderer.adapter_info.clone(), }); self.renderer .ui_renderer .add_surface(self.camera.new_ui_affine(), ui_selector(idata)); } pub fn update(&mut self) -> Result<()> { let now = Instant::now(); let dt = (now - self.input_state.time).as_secs_f32(); self.input_state.time = now; self.camera .update(self.input_state.move_dir, self.input_state.mouse_acc, dt); self.input_state.mouse_acc = Vec2::ZERO; for p in self.network.packet_recv.try_iter() { self.downloader.packet(&p)?; self.tree.packet(&p); match &p { Packet::PrefabIndex(res) => self.prefab_index_res = Some(res.to_owned()), Packet::Sound(ob, data) => { if Some(ob) != self.avatar_ob.as_ref() { if let Some(obdata) = self.tree.objects.get(ob) { self.audio .incoming_packet(ob.0, obdata.pos.into(), data.0.clone()) } } } _ => (), } } self.downloader .update(&self.network) .context("downloader state")?; if self.prefab_index_res != self.prefab_index_res_loaded { if let Some(res) = &self.prefab_index_res { if let Some(index) = self.downloader.try_get(res.to_owned())? { info!("prefab index loaded"); self.prefab_index = index.into(); self.prefab_index_res_loaded = Some(res.to_owned()); } } } if let Some(ob) = self.avatar_ob { if self.last_position_tx.elapsed().as_secs_f32() > 0.1 { self.network.packet_send.send(Packet::Position( ob, self.camera.position().into(), self.camera.rotation().into(), ))?; self.last_position_tx = Instant::now(); } while let Some(p) = self.audio.pop_output() { self.network.packet_send.send(Packet::Sound(ob, Data(p)))?; } } else { if let Some(res) = self.prefab_index.0.get("avatar_test") { info!("found avatar resource {res}"); let ob = Object::new(); self.network .packet_send .send(Packet::Add(ob, res.to_owned())) .unwrap(); self.avatar_ob = Some(ob) } } Ok(()) } }