/*
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::{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::{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 prefab_index: Arc,
pub prefab_index_res_loaded: Option>,
pub prefab_index_res: 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_memory()));
Ok(Self {
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,
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();
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() {}
});
}
});
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.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());
}
}
}
self.input_state.egui_events.clear();
Ok(())
}
}