/* 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, network::Network, renderer::Renderer, }; use anyhow::{Context, Result}; use glam::{Vec2, Vec3}; use log::{info, warn}; use std::{net::TcpStream, sync::Arc, time::Instant}; use weareshared::{ packets::{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 struct InputState { time: Instant, pub move_dir: Vec3, pub mouse_acc: Vec2, pub cursor_pos: Vec2, pub egui_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, egui_events: Vec::new(), }, prefab_index_res: None, prefab_index_res_loaded: None, avatar_ob: None, prefab_index: PrefabIndex::default().into(), }) } pub fn draw(&mut self) { if let Err(e) = self .renderer .draw(&self.tree, &self.camera, &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 .egui_events .push(egui::Event::PointerButton { pos: egui::Pos2::new(self.input_state.cursor_pos.x, self.input_state.cursor_pos.y), button: match button { MouseButton::Left => egui::PointerButton::Primary, MouseButton::Right => egui::PointerButton::Secondary, MouseButton::Middle => egui::PointerButton::Middle, MouseButton::Back => egui::PointerButton::Extra1, MouseButton::Forward => egui::PointerButton::Extra2, MouseButton::Other(_) => egui::PointerButton::Extra1, }, pressed: down, modifiers: egui::Modifiers::default(), }); if !down || button != MouseButton::Right { return; } let pi = self.prefab_index.clone(); let net = self.network.clone(); self.renderer .ui_renderer .add_surface(self.camera.new_ui_affine(), move |ctx| { let mut open = true; egui::Window::new("Prefab Index") .open(&mut open) .show(ctx, |ui| { for (key, res) in &pi.0 { ui.horizontal(|ui| { ui.label(key); if ui.button("Add").clicked() { info!("spawn {res}"); net.packet_send .send(Packet::Add(Object::new(), res.clone())) .unwrap(); } }); } }); open }); } 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.audio.update(); 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); if let Packet::PrefabIndex(res) = &p { self.prefab_index_res = Some(res.to_owned()); } } 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 { self.network .packet_send .send(Packet::Position( ob, self.camera.position().into(), self.camera.rotation().into(), )) .unwrap(); } 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) } } self.input_state.egui_events.clear(); Ok(()) } }