aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock191
-rw-r--r--client/Cargo.toml10
-rw-r--r--client/src/main.rs3
-rw-r--r--client/src/renderer.rs184
-rw-r--r--client/src/skin_manager.rs146
-rw-r--r--client/src/window.rs20
6 files changed, 363 insertions, 191 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 469d95b..34c16da 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -243,15 +243,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
[[package]]
-name = "bincode"
-version = "1.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
-dependencies = [
- "serde",
-]
-
-[[package]]
name = "binrw"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -493,6 +484,7 @@ dependencies = [
"anyhow",
"arrayvec 0.5.2",
"env_logger 0.11.6",
+ "image",
"libtw2-demo",
"libtw2-event-loop",
"libtw2-gamenet-common",
@@ -501,9 +493,12 @@ dependencies = [
"libtw2-snapshot",
"log 0.4.22",
"pollster",
- "twgame 0.9.3",
+ "twgame",
"twgpu",
- "twsnap 0.7.3",
+ "twmap",
+ "twsnap",
+ "twstorage",
+ "vek",
"warn",
"wgpu",
"winit 0.30.8",
@@ -636,9 +631,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
-version = "0.2.2"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
[[package]]
name = "cursor-icon"
@@ -1478,9 +1473,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
+checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
dependencies = [
"adler2",
"simd-adler32",
@@ -2065,25 +2060,6 @@ dependencies = [
]
[[package]]
-name = "pre-rfc3243-libtw2-demo"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8d82220e0a2df5677108f03f4f83e713cb6cd60eb53ed8e5aaf57d219735f86"
-dependencies = [
- "arrayvec 0.5.2",
- "binrw",
- "buffer",
- "pre-rfc3243-libtw2-common",
- "pre-rfc3243-libtw2-gamenet-common",
- "pre-rfc3243-libtw2-huffman",
- "pre-rfc3243-libtw2-packer",
- "pre-rfc3243-libtw2-snapshot",
- "thiserror",
- "uuid 0.8.2",
- "warn",
-]
-
-[[package]]
name = "pre-rfc3243-libtw2-gamenet-common"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2146,18 +2122,6 @@ dependencies = [
]
[[package]]
-name = "pre-rfc3243-libtw2-huffman"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f42f3d7315871c61fb7bf12c317e3b2eb92870e46bc919eacabfc14420884fa3"
-dependencies = [
- "arrayvec 0.5.2",
- "buffer",
- "itertools",
- "pre-rfc3243-libtw2-common",
-]
-
-[[package]]
name = "pre-rfc3243-libtw2-packer"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2171,20 +2135,6 @@ dependencies = [
]
[[package]]
-name = "pre-rfc3243-libtw2-snapshot"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b40486fb774b475e16d86a0ee769695b81b151759aa2fb66ad456d1a4216421"
-dependencies = [
- "buffer",
- "pre-rfc3243-libtw2-common",
- "pre-rfc3243-libtw2-gamenet-snap",
- "pre-rfc3243-libtw2-packer",
- "vec_map",
- "warn",
-]
-
-[[package]]
name = "presser"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2492,9 +2442,9 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.24"
+version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
+checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
[[package]]
name = "serde"
@@ -2518,9 +2468,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.135"
+version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
+checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
dependencies = [
"itoa",
"memchr 2.7.4",
@@ -2689,24 +2639,12 @@ dependencies = [
[[package]]
name = "teehistorian"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b39af9384bec948ccd9d6d5777bbef8532ff5cabda9972b0cccff84a06139885"
-dependencies = [
- "arrayvec 0.7.6",
- "nom",
- "serde",
- "uuid 1.11.1",
-]
-
-[[package]]
-name = "teehistorian"
version = "0.12.0"
dependencies = [
"arrayvec 0.7.6",
"nom",
"serde",
- "uuid 1.11.1",
+ "uuid 1.12.1",
]
[[package]]
@@ -2834,28 +2772,6 @@ checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "twgame"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c59204ffd091cdd6d2cff73851671842c291b9795d9cfb34ff5a802ec1d5a658"
-dependencies = [
- "arrayvec 0.7.6",
- "bitflags 2.7.0",
- "hashbrown 0.14.5",
- "indexmap",
- "ndarray",
- "rand",
- "rand_pcg",
- "serde",
- "serde_json",
- "twgame-core 0.3.0",
- "twmap",
- "twsnap 0.2.1",
- "uuid 1.11.1",
- "vek",
-]
-
-[[package]]
-name = "twgame"
version = "0.9.3"
dependencies = [
"arrayvec 0.7.6",
@@ -2869,35 +2785,14 @@ dependencies = [
"serde",
"serde_json",
"slotmap",
- "twgame-core 0.7.1",
+ "twgame-core",
"twmap",
- "uuid 1.11.1",
+ "uuid 1.12.1",
"vek",
]
[[package]]
name = "twgame-core"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd991e8dbf1c167325bfd7635801a81a01894adfc6cf64f3c4961af2747ff33a"
-dependencies = [
- "bincode",
- "bitflags 2.7.0",
- "buffer",
- "hex",
- "pre-rfc3243-libtw2-gamenet-ddnet",
- "pre-rfc3243-libtw2-gamenet-teeworlds-0-7",
- "pre-rfc3243-libtw2-packer",
- "serde",
- "teehistorian 0.11.1",
- "twsnap 0.2.1",
- "uuid 1.11.1",
- "vek",
- "warn",
-]
-
-[[package]]
-name = "twgame-core"
version = "0.7.1"
dependencies = [
"hex",
@@ -2906,8 +2801,8 @@ dependencies = [
"pre-rfc3243-libtw2-gamenet-teeworlds-0-7",
"pre-rfc3243-libtw2-packer",
"serde",
- "teehistorian 0.12.0",
- "twsnap 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "teehistorian",
+ "twsnap",
"vek",
"warn",
]
@@ -2922,9 +2817,9 @@ dependencies = [
"ndarray",
"rand_core",
"rand_pcg",
- "twgame 0.4.0",
+ "twgame",
"twmap",
- "twsnap 0.2.1",
+ "twsnap",
"vek",
"wgpu",
"winit 0.29.15",
@@ -2933,8 +2828,6 @@ dependencies = [
[[package]]
name = "twmap"
version = "0.12.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "420a7fab81c01d4a646f24834492d9ee8f606e491fa81610a192b5be0bbd7688"
dependencies = [
"az",
"bitflags 2.7.0",
@@ -2957,25 +2850,6 @@ dependencies = [
[[package]]
name = "twsnap"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07891b4605bd1ca12054f2c601b24c3160e486b0a0e24447a432aac210cf54a2"
-dependencies = [
- "arrayvec 0.7.6",
- "bitflags 2.7.0",
- "fixed",
- "hashbrown 0.14.5",
- "num_enum",
- "pre-rfc3243-libtw2-common",
- "pre-rfc3243-libtw2-demo",
- "pre-rfc3243-libtw2-gamenet-ddnet",
- "serde",
- "vek",
- "warn",
-]
-
-[[package]]
-name = "twsnap"
version = "0.7.3"
dependencies = [
"arrayvec 0.7.6",
@@ -2992,25 +2866,6 @@ dependencies = [
]
[[package]]
-name = "twsnap"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ffb55636a9828dda08ad4f9d7e2a232007dc339b949d412709941646665cbbb"
-dependencies = [
- "arrayvec 0.7.6",
- "bitflags 2.7.0",
- "fixed",
- "hashbrown 0.14.5",
- "num_enum",
- "pre-rfc3243-libtw2-common",
- "pre-rfc3243-libtw2-demo",
- "pre-rfc3243-libtw2-gamenet-ddnet",
- "serde",
- "vek",
- "warn",
-]
-
-[[package]]
name = "twstorage"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3072,9 +2927,9 @@ dependencies = [
[[package]]
name = "uuid"
-version = "1.11.1"
+version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4"
+checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
dependencies = [
"getrandom",
"serde",
diff --git a/client/Cargo.toml b/client/Cargo.toml
index 8101167..93c1431 100644
--- a/client/Cargo.toml
+++ b/client/Cargo.toml
@@ -13,16 +13,26 @@ xdg = "2.5.2"
wgpu = "23.0.1"
pollster = "0.4.0"
winit = "0.30.8"
+vek = { version = "0.16.1", default-features = false, features = [
+ "bytemuck",
+ "az",
+ "rgba",
+ "std",
+ "uv",
+] }
+image = "0.24.9"
twgame = { path = "../../twgame/twgame" }
twgpu = { path = "../../twgpu/twgpu" }
twsnap = { path = "../../twsnap" }
+twmap = { path = "../../twmap/twmap" }
libtw2-gamenet-ddnet = { path = "../../libtw2/gamenet/ddnet" }
libtw2-gamenet-common = { path = "../../libtw2/gamenet/common" }
libtw2-event-loop = { path = "../../libtw2/event-loop" }
libtw2-packer = { path = "../../libtw2/packer" }
libtw2-demo = { path = "../../libtw2/demo" }
libtw2-snapshot = { path = "../../libtw2/snapshot" }
+twstorage = "0.1.1"
# pre-rfc3243-libtw2-gamenet-ddnet = "*"
# pre-rfc3243-libtw2-gamenet-common = "*"
diff --git a/client/src/main.rs b/client/src/main.rs
index 8373db9..5a52620 100644
--- a/client/src/main.rs
+++ b/client/src/main.rs
@@ -6,11 +6,12 @@ use winit::event_loop::EventLoop;
pub mod client;
pub mod renderer;
pub mod window;
+pub mod skin_manager;
fn main() -> Result<()> {
env_logger::init_from_env("LOG");
- Client::new()?;
+ // Client::new()?;
let evloop = EventLoop::new()?;
evloop.run_app(&mut WindowState::new())?;
diff --git a/client/src/renderer.rs b/client/src/renderer.rs
index c8f7f0b..b213307 100644
--- a/client/src/renderer.rs
+++ b/client/src/renderer.rs
@@ -1,22 +1,44 @@
-use std::sync::Arc;
-
+use crate::skin_manager::{SkinManager, init_sprite_textures};
use anyhow::{Result, anyhow};
+use log::warn;
use pollster::FutureExt;
+use std::{sync::Arc, time::Instant};
use twgpu::{
- Camera, GpuCamera,
- map::{GpuMapData, GpuMapStatic},
- shared::Rng,
- sprites::{ParticleData, SpriteTextures, SpritesData, SpritesStatic},
+ Camera, GpuCamera, TwRenderPass,
+ blit::Blit,
+ buffer::GpuBuffer,
+ map::{GpuMapData, GpuMapRender, GpuMapStatic},
+ sprites::{ParticleData, ParticleGroup, SpriteTextures, SpritesData, SpritesStatic},
textures::Samplers,
};
+use twmap::TwMap;
+use vek::Vec2;
use wgpu::{
- Backends, DeviceDescriptor, Features, Instance, InstanceDescriptor, Limits, MemoryHints,
- PowerPreference, RequestAdapterOptions, TextureFormat,
+ Backends, Color, CommandEncoderDescriptor, Device, DeviceDescriptor, Features, Instance,
+ InstanceDescriptor, Limits, LoadOp, MaintainBase, MemoryHints, Operations, PowerPreference,
+ Queue, RenderPassColorAttachment, RenderPassDescriptor, RequestAdapterOptions, StoreOp,
+ Surface, SurfaceConfiguration, TextureFormat, TextureViewDescriptor,
};
-use winit::window::Window;
+use winit::{dpi::PhysicalSize, window::Window};
pub struct Renderer<'a> {
+ device: Arc<Device>,
+ queue: Arc<Queue>,
window: &'a Window,
+ surface: Surface<'a>,
+ surface_configuration: SurfaceConfiguration,
+ twmap: TwMap,
+ map_render: GpuMapRender,
+ map_data: GpuMapData,
+ camera: Camera,
+ sprites_data: SpritesData,
+ particle_data: ParticleData,
+ skin_manager: SkinManager,
+ sprites_static: SpritesStatic,
+ sprite_textures: SpriteTextures,
+ gpu_camera: Arc<GpuBuffer<Camera>>,
+ start: Instant,
+ need_reconfigure: bool,
}
impl<'a> Renderer<'a> {
@@ -51,9 +73,17 @@ impl<'a> Renderer<'a> {
None,
)
.block_on()?;
+ let device = Arc::new(device);
+ let queue = Arc::new(queue);
+
+ let mut twmap = TwMap::parse(include_bytes!(
+ "/home/muffin/etc/ddnet-maps/types/novice/maps/Mint.map"
+ ))?;
+
+ twmap.load()?;
let samplers = Arc::new(Samplers::new(&device));
- let mut camera = Camera::new(1.);
+ let camera = Camera::new(1.);
let gpu_camera = Arc::new(GpuCamera::upload(&camera, &device));
let map_static = GpuMapStatic::new(texture_format, &device);
@@ -62,15 +92,133 @@ impl<'a> Renderer<'a> {
map_static.prepare_render(&twmap, &map_data, &gpu_camera, &samplers, &device);
let sprites_static = SpritesStatic::new(texture_format, &device);
- let mut sprites_data = SpritesData::new(&device);
- let mut particle_data = ParticleData::new(demo.current_time(), &device);
- let mut textures = SpriteTextures::new(&device, &queue, &gpu_camera, &samplers);
- let mut rng = Rng::new(0);
- init_sprite_textures(&mut textures, twmap.version, &device, &queue)?;
+ let sprites_data = SpritesData::new(&device);
+ let particle_data = ParticleData::new(0.0, &device);
+ let mut sprite_textures = SpriteTextures::new(&device, &queue, &gpu_camera, &samplers);
+ init_sprite_textures(&mut sprite_textures, twmap.version, &device, &queue)?;
let blit = Arc::new(Blit::new(&device));
- let mut skin_manager = SkinManager::new(blit, &mut textures, device.clone(), queue.clone());
+ let skin_manager =
+ SkinManager::new(blit, &mut sprite_textures, device.clone(), queue.clone());
+
+ let surface_configuration = surface
+ .get_default_config(&adapter, 256, 256)
+ .ok_or(anyhow!("no surface config"))?;
+
+ surface.configure(&device, &surface_configuration);
+
+ Ok(Self {
+ start: Instant::now(),
+ map_data,
+ sprites_static,
+ window,
+ device,
+ queue,
+ twmap,
+ surface,
+ surface_configuration,
+ map_render,
+ camera,
+ gpu_camera,
+ particle_data,
+ skin_manager,
+ sprites_data,
+ sprite_textures,
+ need_reconfigure: false,
+ })
+ }
+ pub fn resize(&mut self, size: PhysicalSize<u32>) {
+ self.surface_configuration.width = size.width;
+ self.surface_configuration.height = size.height;
+ self.camera
+ .switch_aspect_ratio(size.width as f32 / size.height as f32);
+ self.reconfigure();
+ }
+ pub fn reconfigure(&mut self) {
+ self.surface
+ .configure(&self.device, &self.surface_configuration);
+ self.need_reconfigure = false;
+ }
+
+ pub fn redraw(&mut self) -> Result<()> {
+ if self.need_reconfigure {
+ self.reconfigure();
+ }
+ let target = self.surface.get_current_texture()?;
+ if target.suboptimal {
+ warn!("suboptimal surface, need reconfigure");
+ self.need_reconfigure = true;
+ }
+
+ let time = self.start.elapsed().as_micros() as i64;
+ let size = Vec2::new(
+ self.surface_configuration.width,
+ self.surface_configuration.height,
+ );
+ self.map_data
+ .update(&self.twmap, &self.camera, size, time, time, &self.queue);
+ self.gpu_camera.update(&self.camera, &self.queue);
+
+ let target_view = target
+ .texture
+ .create_view(&TextureViewDescriptor::default());
+
+ let mut commands = self
+ .device
+ .create_command_encoder(&CommandEncoderDescriptor { label: None });
+
+ {
+ let rpass = commands.begin_render_pass(&RenderPassDescriptor {
+ label: None,
+ color_attachments: &[Some(RenderPassColorAttachment {
+ view: &target_view,
+ resolve_target: None,
+ ops: Operations {
+ load: LoadOp::Clear(Color::BLACK),
+ store: StoreOp::Store,
+ },
+ })],
+ ..Default::default()
+ });
+ let mut rpass = TwRenderPass::new(rpass, size, &self.camera);
+ self.map_render.render_background(&mut rpass);
+ self.sprites_static.render_particles(
+ &self.particle_data,
+ ParticleGroup::Trails,
+ &self.sprite_textures,
+ &mut rpass.render_pass,
+ );
+ self.sprites_static.render(
+ &self.sprites_data,
+ &self.sprite_textures,
+ &mut rpass.render_pass,
+ );
+ self.map_render.render_foreground(&mut rpass);
+ self.sprites_static.render_particles(
+ &self.particle_data,
+ ParticleGroup::Explosions,
+ &self.sprite_textures,
+ &mut rpass.render_pass,
+ );
+ self.sprites_static.render_particles(
+ &self.particle_data,
+ ParticleGroup::Extra,
+ &self.sprite_textures,
+ &mut rpass.render_pass,
+ );
+ self.sprites_static.render_particles(
+ &self.particle_data,
+ ParticleGroup::General,
+ &self.sprite_textures,
+ &mut rpass.render_pass,
+ );
+ }
+
+ let submission = self.queue.submit(Some(commands.finish()));
+ self.device
+ .poll(MaintainBase::WaitForSubmissionIndex(submission));
+
+ target.present();
- Self { window }
+ Ok(())
}
- pub fn redraw() {}
}
diff --git a/client/src/skin_manager.rs b/client/src/skin_manager.rs
new file mode 100644
index 0000000..1f6624d
--- /dev/null
+++ b/client/src/skin_manager.rs
@@ -0,0 +1,146 @@
+// Adapted from twgpu-tools
+// AGPL-3.0-only, Copyright 2025 Patiga
+
+use anyhow::Result;
+use image::{ImageFormat, RgbaImage};
+use std::{collections::HashSet, io::BufReader, sync::Arc};
+use twgpu::{
+ blit::Blit,
+ sprites::{AtlasToken, SpriteTextures, TeeSprite},
+};
+use wgpu::{Device, Queue, Texture};
+
+pub fn load_png(path: &str, version: twmap::Version) -> Result<RgbaImage> {
+ let file = twstorage::read_file(path, version.into())?;
+ let reader = image::io::Reader::with_format(BufReader::new(file), ImageFormat::Png);
+ Ok(reader.decode()?.into_rgba8())
+}
+
+pub fn init_sprite_textures(
+ textures: &mut SpriteTextures,
+ version: twmap::Version,
+ device: &Device,
+ queue: &Queue,
+) -> Result<()> {
+ println!("Loading sprite textures from filesystem");
+ let game = load_png("game.png", version)?;
+ let particles = load_png("particles.png", version)?;
+ let emoticons = load_png("emoticons.png", version)?;
+ let extras = load_png("extras.png", version)?;
+ textures.game_skin = textures.register_atlas_image(&game, device, queue);
+ textures.particles = textures.register_atlas_image(&particles, device, queue);
+ textures.emoticons = textures.register_atlas_image(&emoticons, device, queue);
+ textures.extras = textures.register_atlas_image(&extras, device, queue);
+ Ok(())
+}
+
+type SkinResult = Result<(String, Vec<Texture>), (String, String)>;
+
+pub struct SkinManager {
+ known: HashSet<String>,
+ handles: Vec<std::thread::JoinHandle<SkinResult>>,
+ blit: Arc<Blit>,
+ device: Arc<Device>,
+ queue: Arc<Queue>,
+}
+
+fn load_skin(name: &str, blit: &Blit, device: &Device, queue: &Queue) -> Result<Vec<Texture>> {
+ // TODO check filename
+ let path = format!("skins/{name}.png");
+ let path2 = format!("downloadedskins/{name}.png");
+ let image = match load_png(&path, twmap::Version::DDNet06) {
+ Err(_) => load_png(&path2, twmap::Version::DDNet06)?,
+ Ok(img) => img,
+ };
+
+ let texture = blit.upload_mipmapped_atlas::<TeeSprite>(&image, device, queue);
+ Ok(texture)
+}
+
+impl SkinManager {
+ pub fn new(
+ blit: Arc<Blit>,
+ textures: &mut SpriteTextures,
+ device: Arc<Device>,
+ queue: Arc<Queue>,
+ ) -> Self {
+ let mut manager = Self {
+ known: HashSet::new(),
+ handles: Vec::new(),
+ blit,
+ device,
+ queue,
+ };
+ if let Some(default) = manager.load_skin("default", textures) {
+ textures.default_skin = default;
+ }
+ if let Some(ninja) = manager.load_skin("x_ninja", textures) {
+ textures.ninja_skin = ninja;
+ }
+ manager
+ }
+
+ pub fn load_skin(
+ &mut self,
+ name: &str,
+ textures: &mut SpriteTextures,
+ ) -> Option<AtlasToken<TeeSprite>> {
+ let texture = match load_skin(name, &self.blit, &self.device, &self.queue) {
+ Ok(texture) => texture,
+ Err(err) => {
+ println!("Error loading skin '{name}': {err}");
+ return None;
+ }
+ };
+ self.known.insert(name.to_string());
+ let token = textures.register_skin_texture(texture, name.to_string(), &self.device);
+ Some(token)
+ }
+
+ pub fn queue_load_skin(&mut self, name: String) {
+ let device = self.device.clone();
+ let queue = self.queue.clone();
+ let blit = self.blit.clone();
+ let handle = std::thread::spawn(move || match load_skin(&name, &blit, &device, &queue) {
+ Err(err) => Err((name, err.to_string())),
+ Ok(texture) => Ok((name, texture)),
+ });
+ self.handles.push(handle);
+ }
+
+ pub fn wait_for_queued(&mut self, textures: &mut SpriteTextures) {
+ while let Some(handle) = self.handles.pop() {
+ match handle.join().unwrap() {
+ Ok((name, texture)) => {
+ textures.register_skin_texture(texture, name, &self.device);
+ }
+ Err((name, err)) => println!("Error with skin '{name}': {err}"),
+ }
+ }
+ }
+
+ pub fn poll_queued(&mut self, textures: &mut SpriteTextures) {
+ while !self.handles.is_empty() {
+ if self.handles[0].is_finished() {
+ match self.handles.remove(0).join().unwrap() {
+ Ok((name, texture)) => {
+ textures.register_skin_texture(texture, name, &self.device);
+ }
+ Err((name, err)) => println!("Error with skin '{name}': {err}"),
+ }
+ } else {
+ return;
+ }
+ }
+ }
+
+ pub fn queue_snap_skins(&mut self, snap: &twsnap::Snap) {
+ for player in snap.players.values() {
+ if !self.known.contains(player.skin.as_str()) {
+ println!("New skin: '{}'", player.skin);
+ self.known.insert(player.skin.to_string());
+ self.queue_load_skin(player.skin.to_string());
+ }
+ }
+ }
+}
diff --git a/client/src/window.rs b/client/src/window.rs
index d0296e7..0bcac6a 100644
--- a/client/src/window.rs
+++ b/client/src/window.rs
@@ -1,4 +1,5 @@
use crate::renderer::Renderer;
+use log::warn;
use winit::{
application::ApplicationHandler,
event::WindowEvent,
@@ -20,19 +21,30 @@ impl ApplicationHandler for WindowState {
let window = event_loop
.create_window(WindowAttributes::default().with_maximized(true))
.unwrap();
- let renderer = Renderer::new(unsafe { std::mem::transmute(&window) });
+ let renderer = Renderer::new(unsafe { std::mem::transmute(&window) }).unwrap();
self.window = Some((window, renderer))
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
- window_id: WindowId,
+ _window_id: WindowId,
event: WindowEvent,
) {
- if let Some((win, ren)) = &self.window {
+ if let Some((win, ren)) = &mut self.window {
match event {
- WindowEvent::RedrawRequested => {}
+ WindowEvent::CloseRequested => {
+ event_loop.exit();
+ }
+ WindowEvent::Resized(size) => {
+ ren.resize(size);
+ }
+ WindowEvent::RedrawRequested => {
+ if let Err(e) = ren.redraw() {
+ warn!("{e:?}")
+ }
+ win.request_redraw();
+ }
_ => (),
}
}