From e283882dbcf61e4476dea37f4ae698f4b7d5c816 Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Sun, 28 Sep 2025 22:32:14 +0200 Subject: [PATCH 1/6] Fix vote % text --- game/client-ui/src/vote/main_frame.rs | 36 ++++++++++++++++++++------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/game/client-ui/src/vote/main_frame.rs b/game/client-ui/src/vote/main_frame.rs index 01723188..2664f8ad 100644 --- a/game/client-ui/src/vote/main_frame.rs +++ b/game/client-ui/src/vote/main_frame.rs @@ -116,14 +116,24 @@ pub fn render(ui: &mut egui::Ui, pipe: &mut UiRenderPipe, ui_state: &m }, Color32::RED, ); - at.x -= no_size / 2.0 - 5.0; - ui.painter().text( - at, - egui::Align2::LEFT_CENTER, + at = rect.left_center(); + let galley = ui.painter().layout_no_wrap( format!("{:.1}%", no_perc * 100.0), FontId::default(), Color32::WHITE, ); + let size = galley.size(); + ui.painter().galley( + if size.x + 10.0 < rect.width() { + at + egui::vec2(5.0, -size.y / 2.0) + } else { + rect.right_center() + - egui::vec2(size.x, size.y / 2.0) + - egui::vec2(5.0, 0.0) + }, + galley, + Color32::WHITE, + ); } if yes_perc > 0.0 { @@ -142,13 +152,21 @@ pub fn render(ui: &mut egui::Ui, pipe: &mut UiRenderPipe, ui_state: &m }, Color32::GREEN, ); - at.x += yes_size / 2.0 - 5.0; - ui.painter().text( - at, - egui::Align2::RIGHT_CENTER, + at = rect.right_center(); + let galley = ui.painter().layout_no_wrap( format!("{:.1}%", yes_perc * 100.0), FontId::default(), - Color32::BLACK, + Color32::DARK_GRAY, + ); + let size = galley.size(); + ui.painter().galley( + if size.x + 10.0 < rect.width() { + at - egui::vec2(size.x, size.y / 2.0) - egui::vec2(5.0, 0.0) + } else { + rect.left_center() + egui::vec2(5.0, -size.y / 2.0) + }, + galley, + Color32::DARK_GRAY, ); } From fb8d74afea18db73dd13c2ccf690eed92da2b75a Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Sat, 4 Oct 2025 19:15:33 +0200 Subject: [PATCH 2/6] Add glass shader support & add for emote wheel cursor --- data | 2 +- game/client-ui/src/emote_wheel/main_frame.rs | 8 +- lib/api-ui/src/ui_impl.rs | 9 +- lib/graphics-backend-traits/src/plugin.rs | 1 + .../src/backends/vulkan/render_cmds.rs | 139 ++++- .../src/backends/vulkan/sub_render_pass.rs | 549 ++++++++++-------- .../src/backends/vulkan/vulkan.rs | 10 + .../src/backends/vulkan/vulkan_uniform.rs | 19 + lib/graphics-backend/src/checker.rs | 3 + lib/graphics-types/src/commands.rs | 8 +- lib/graphics-types/src/rendering.rs | 19 + lib/graphics/src/handles/stream.rs | 1 + lib/graphics/src/utils.rs | 102 +++- lib/ui-base/src/types.rs | 43 +- lib/ui-generic/src/generic_ui_renderer.rs | 89 ++- lib/ui-wasm-manager/src/lib.rs | 8 + misc | 2 +- 17 files changed, 726 insertions(+), 286 deletions(-) diff --git a/data b/data index 58a84fc6..36a4ee78 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 58a84fc69d110f4e88f2d681c8e781b709248b52 +Subproject commit 36a4ee7807e73ac0195ec138d7c8f347fafa6896 diff --git a/game/client-ui/src/emote_wheel/main_frame.rs b/game/client-ui/src/emote_wheel/main_frame.rs index 4d586c6d..042da8ba 100644 --- a/game/client-ui/src/emote_wheel/main_frame.rs +++ b/game/client-ui/src/emote_wheel/main_frame.rs @@ -252,10 +252,10 @@ pub fn render(ui: &mut egui::Ui, pipe: &mut UiRenderPipe, ui_state: &m ); } - ui.painter().circle( + ui_state.add_glass_elipse( egui::pos2(mouse.x as f32, mouse.y as f32), - 10.0, - Color32::from_white_alpha(100), - Stroke::NONE, + egui::vec2(75.0, 75.0), + 2.2, + Color32::from_rgba_unmultiplied(200, 200, 255, 255), ); } diff --git a/lib/api-ui/src/ui_impl.rs b/lib/api-ui/src/ui_impl.rs index 23235607..261dde34 100644 --- a/lib/api-ui/src/ui_impl.rs +++ b/lib/api-ui/src/ui_impl.rs @@ -4,7 +4,7 @@ use api::{GRAPHICS, GRAPHICS_BACKEND, read_param_from_host, upload_return_val}; use graphics_types::types::WindowProps; use ui_base::{ - types::{BlurShape, RawInputWrapper, RawOutputWrapper, UiFonts, UiRenderPipe}, + types::{BlurShape, GlassShape, RawInputWrapper, RawOutputWrapper, UiFonts, UiRenderPipe}, ui::UiContainer, ui_render::render_ui, }; @@ -44,7 +44,7 @@ fn ui_run_impl( inp: RawInputWrapper, zoom_level: Option, mut user_data: U, -) -> (egui::PlatformOutput, Vec) { +) -> (egui::PlatformOutput, Vec, Vec) { API_UI.with(|g| g.borrow_mut().zoom_level.set(zoom_level)); GRAPHICS.with(|g| g.resized(window_props)); @@ -88,6 +88,7 @@ fn ui_run_impl( ( platform_output, API_UI.with(|ui| std::mem::take(&mut ui.borrow_mut().ui_state.blur_shapes)), + API_UI.with(|ui| std::mem::take(&mut ui.borrow_mut().ui_state.glass_shapes)), ) } @@ -108,10 +109,12 @@ pub fn ui_run() { let inp = read_param_from_host::(2); let zoom_level = read_param_from_host::>(3); - let (output, blur_shapes) = ui_run_impl(cur_time, window_props, inp, zoom_level, ()); + let (output, blur_shapes, glass_shapes) = + ui_run_impl(cur_time, window_props, inp, zoom_level, ()); upload_return_val(RawOutputWrapper { output, blur_shapes, + glass_shapes, zoom_level: API_UI.with(|g| g.borrow().zoom_level.get()), }); } diff --git a/lib/graphics-backend-traits/src/plugin.rs b/lib/graphics-backend-traits/src/plugin.rs index b1e67839..a7bd2161 100644 --- a/lib/graphics-backend-traits/src/plugin.rs +++ b/lib/graphics-backend-traits/src/plugin.rs @@ -159,6 +159,7 @@ pub enum SubRenderPassAttributes { StandardBlurPipeline, Standard3dPipeline, BlurPipeline, + GlassPipeline, PrimExPipeline, PrimExRotationlessPipeline, SpriteMultiPipeline, diff --git a/lib/graphics-backend/src/backends/vulkan/render_cmds.rs b/lib/graphics-backend/src/backends/vulkan/render_cmds.rs index 0724ff87..2eccc848 100644 --- a/lib/graphics-backend/src/backends/vulkan/render_cmds.rs +++ b/lib/graphics-backend/src/backends/vulkan/render_cmds.rs @@ -11,10 +11,12 @@ use graphics_types::{ CommandsRenderQuadContainer, CommandsRenderStream, GRAPHICS_DEFAULT_UNIFORM_SIZE, GRAPHICS_MAX_UNIFORM_RENDER_COUNT, PrimType, RenderSpriteInfo, }, - rendering::{ColorRgba, State, StateTexture, WrapType}, + rendering::{ColorRgba, RenderModeGlass, State, StateTexture, WrapType}, }; use math::math::vector::{vec2, vec4}; +use crate::backends::vulkan::vulkan_uniform::UniformGGlass; + use super::{ command_pool::AutoCommandBuffer, logical_device::LogicalDevice, @@ -38,6 +40,16 @@ pub fn get_address_mode_index(state: &State) -> usize { } } +enum RenderMode { + Normal, + Blur { + blur_radius: f32, + scale: vec2, + blur_color: vec4, + }, + Glass(RenderModeGlass), +} + fn render_blur( mut render_manager: RenderManager, state: &State, @@ -58,10 +70,33 @@ fn render_blur( prim_type, primitive_count, &m, - true, - blur_radius, - scale, - blur_color, + RenderMode::Blur { + blur_radius, + scale, + blur_color, + }, + ) +} + +fn render_glass( + mut render_manager: RenderManager, + state: &State, + texture_index: &StateTexture, + prim_type: PrimType, + primitive_count: usize, + glass: RenderModeGlass, +) { + let mut m: [f32; 4 * 2] = Default::default(); + render_manager.get_state_matrix(state, &mut m); + + render_manager.bind_pipeline(state, texture_index, SubRenderPassAttributes::GlassPipeline); + + render_standard_impl::( + render_manager, + prim_type, + primitive_count, + &m, + RenderMode::Glass(glass), ) } @@ -70,10 +105,7 @@ fn render_standard_impl( prim_type: PrimType, primitive_count: usize, m: &[f32], - as_blur: bool, - blur_radius: f32, - scale: vec2, - blur_color: vec4, + mode: RenderMode, ) { let mut vert_per_prim: usize = 2; let mut is_indexed: bool = false; @@ -99,24 +131,60 @@ fn render_standard_impl( std::mem::size_of_val(m), ) }); - if as_blur { - let viewport_size = render_manager.viewport_size(); - let blur_push = UniformGBlur { - texture_size: vec2::new(viewport_size.width as f32, viewport_size.height as f32), + match mode { + RenderMode::Normal => { + // nothing to do + } + RenderMode::Blur { blur_radius, scale, - color: blur_color, - }; - render_manager.push_constants( - BackendShaderStage::FRAGMENT, - std::mem::size_of::() as u32, - unsafe { - std::slice::from_raw_parts( - &blur_push as *const _ as *const u8, - std::mem::size_of::(), - ) - }, - ); + blur_color, + } => { + let viewport_size = render_manager.viewport_size(); + let blur_push = UniformGBlur { + texture_size: vec2::new(viewport_size.width as f32, viewport_size.height as f32), + blur_radius, + scale, + color: blur_color, + }; + render_manager.push_constants( + BackendShaderStage::FRAGMENT, + std::mem::size_of::() as u32, + unsafe { + std::slice::from_raw_parts( + &blur_push as *const _ as *const u8, + std::mem::size_of::(), + ) + }, + ); + } + RenderMode::Glass(glass) => { + let blur_push = UniformGGlass { + elipse_strength: glass.elipse_strength, + exponent_offset: glass.exponent_offset, + decay_scale: glass.decay_scale, + base_factor: glass.base_factor, + deca_rate: glass.deca_rate, + refraction_falloff: glass.refraction_falloff, + noise: glass.noise, + glow_weight: glass.glow_weight, + glow_bias: glass.glow_bias, + glow_edge0: glass.glow_edge0, + glow_edge1: glass.glow_edge1, + center: glass.center, + size: glass.size, + }; + render_manager.push_constants( + BackendShaderStage::FRAGMENT, + std::mem::size_of::() as u32, + unsafe { + std::slice::from_raw_parts( + &blur_push as *const _ as *const u8, + std::mem::size_of::(), + ) + }, + ); + } } if is_indexed { @@ -155,10 +223,7 @@ fn render_standard( prim_type, primitive_count, &m, - false, - 0.0, - vec2::default(), - vec4::default(), + RenderMode::Normal, ) } @@ -192,6 +257,18 @@ fn cmd_render_blurred( ) } +fn cmd_render_glass(render_manager: RenderManager, cmd: &CommandRender, glass: RenderModeGlass) { + // draw where the stencil buffer triggered + render_glass( + render_manager, + &cmd.state, + &cmd.texture_index, + cmd.prim_type, + cmd.prim_count, + glass, + ) +} + fn cmd_render_quad_container_ex( mut render_manager: RenderManager, cmd: &CommandRenderQuadContainer, @@ -399,6 +476,10 @@ pub(crate) fn command_cb_render( cmd_render_blurred(render_manager, cmd, *blur_radius, *scale, *blur_color); Ok(()) } + CommandsRenderStream::RenderGlass { cmd, glass } => { + cmd_render_glass(render_manager, cmd, *glass); + Ok(()) + } }, CommandsRender::QuadContainer(cmd) => match cmd { CommandsRenderQuadContainer::Render(cmd) => { diff --git a/lib/graphics-backend/src/backends/vulkan/sub_render_pass.rs b/lib/graphics-backend/src/backends/vulkan/sub_render_pass.rs index 459ffc01..a37d374f 100644 --- a/lib/graphics-backend/src/backends/vulkan/sub_render_pass.rs +++ b/lib/graphics-backend/src/backends/vulkan/sub_render_pass.rs @@ -11,7 +11,10 @@ use hiarc::Hiarc; use num_traits::FromPrimitive; use strum::EnumCount; -use crate::{backend::CustomPipelines, backends::vulkan::vulkan_types::SupportedBlendModes}; +use crate::{ + backend::CustomPipelines, + backends::vulkan::{vulkan_types::SupportedBlendModes, vulkan_uniform::UniformGGlass}, +}; use super::{ compiler::compiler::ShaderCompiler, @@ -40,6 +43,7 @@ pub struct SubRenderPass { pub standard_blur_pipeline: PipelineContainer, pub standard_3d_pipeline: PipelineContainer, pub blur_pipeline: PipelineContainer, + pub glass_pipeline: PipelineContainer, pub prim_ex_pipeline: PipelineContainer, pub prim_ex_rotationless_pipeline: PipelineContainer, pub sprite_multi_pipeline: PipelineContainer, @@ -54,6 +58,7 @@ impl SubRenderPass { SubRenderPassAttributes::StandardBlurPipeline => &self.standard_blur_pipeline, SubRenderPassAttributes::Standard3dPipeline => &self.standard_3d_pipeline, SubRenderPassAttributes::BlurPipeline => &self.blur_pipeline, + SubRenderPassAttributes::GlassPipeline => &self.glass_pipeline, SubRenderPassAttributes::PrimExPipeline => &self.prim_ex_pipeline, SubRenderPassAttributes::PrimExRotationlessPipeline => { &self.prim_ex_rotationless_pipeline @@ -564,235 +569,305 @@ impl SubRenderPass { pipeline_cache, ); - let (res1, res2, res3, res4, res5, res6, res7, res8) = runtime_threadpool.install(|| { - join_all!( - || -> anyhow::Result<()> { - (!stop_execution_flag - .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) - .then_some(Some(())) - .ok_or(anyhow!("Execution termination was requested."))?; - Self::create_graphics_pipeline_generic( - &mut self.standard_pipeline, - &pipeline_manager, - |is_textured| { - if is_textured { - Some(( - "shader/vulkan/prim.vert.spv".into(), - "shader/vulkan/prim_textured.frag.spv".into(), - )) - } else { - Some(( - "shader/vulkan/prim.vert.spv".into(), - "shader/vulkan/prim.frag.spv".into(), - )) - } - }, - |is_textured, address_mode| { - Self::standard_pipeline_layout( - layouts, - is_textured, - false, - address_mode, - ) - }, - ) - .map_err(|err| anyhow!("Create standard graphics pipeline: {err}"))?; - Ok(()) - }, - || -> anyhow::Result<()> { - (!stop_execution_flag - .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) - .then_some(Some(())) - .ok_or(anyhow!("Execution termination was requested."))?; - Self::create_graphics_pipeline_generic( - &mut self.standard_line_pipeline, - &pipeline_manager, - |is_textured| { - if is_textured { - None - } else { - Some(( - "shader/vulkan/prim.vert.spv".into(), - "shader/vulkan/prim.frag.spv".into(), - )) - } - }, - |is_textured, address_mode| { - Self::standard_pipeline_layout(layouts, is_textured, true, address_mode) - }, - ) - .map_err(|err| anyhow!("Create standard line graphics pipeline: {err}"))?; - Ok(()) - }, - || -> anyhow::Result<()> { - (!stop_execution_flag - .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) - .then_some(Some(())) - .ok_or(anyhow!("Execution termination was requested."))?; - Self::create_graphics_pipeline_generic( - &mut self.blur_pipeline, - &pipeline_manager, - |is_textured| { - if is_textured { - Some(( - "shader/vulkan/prim.vert.spv".into(), - "shader/vulkan/blur.frag.spv".into(), - )) - } else { - None - } - }, - |is_textured, address_mode| { - let ( - input_attributes, - set_layouts, - mut push_constants, - stride, - is_line, - ) = Self::standard_pipeline_layout( - layouts, - is_textured, - false, - address_mode, - ); - push_constants.push(vk::PushConstantRange { - stage_flags: vk::ShaderStageFlags::FRAGMENT, - offset: std::mem::size_of::() as u32, - size: std::mem::size_of::() as u32, - }); - ( - input_attributes, - set_layouts, - push_constants, - stride, - is_line, - ) - }, - ) - .map_err(|err| anyhow!("Create blur graphics pipeline: {err}"))?; - Ok(()) - }, - || -> anyhow::Result<()> { - (!stop_execution_flag - .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) - .then_some(Some(())) - .ok_or(anyhow!("Execution termination was requested."))?; - Self::create_graphics_pipeline_generic( - &mut self.standard_3d_pipeline, - &pipeline_manager, - |is_textured| { - if is_textured { - Some(( - "shader/vulkan/prim3d_textured.vert.spv".into(), - "shader/vulkan/prim3d_textured.frag.spv".into(), - )) - } else { - Some(( - "shader/vulkan/prim3d.vert.spv".into(), - "shader/vulkan/prim3d.frag.spv".into(), - )) - } - }, - |is_textured, address_mode| { - Self::standard_3d_pipeline_layout(layouts, is_textured, address_mode) - }, - ) - .map_err(|err| anyhow!("Create standard 3d graphics pipeline: {err}"))?; - - Ok(()) - }, - || -> anyhow::Result<()> { - (!stop_execution_flag - .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) - .then_some(Some(())) - .ok_or(anyhow!("Execution termination was requested."))?; - Self::create_graphics_pipeline_generic( - &mut self.prim_ex_rotationless_pipeline, - &pipeline_manager, - |is_textured| { - if is_textured { - Some(( - "shader/vulkan/primex_tex_rotationless.vert.spv".into(), - "shader/vulkan/primex_tex_rotationless.frag.spv".into(), - )) - } else { - Some(( - "shader/vulkan/primex_rotationless.vert.spv".into(), - "shader/vulkan/primex_rotationless.frag.spv".into(), - )) - } - }, - |is_textured, address_mode| { - Self::prim_ex_pipeline_layout(layouts, is_textured, true, address_mode) - }, - ) - .map_err(|err| { - anyhow!("Create prim ex graphics rotationless pipeline: {err}") - })?; - Ok(()) - }, - || -> anyhow::Result<()> { - (!stop_execution_flag - .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) - .then_some(Some(())) - .ok_or(anyhow!("Execution termination was requested."))?; - Self::create_graphics_pipeline_generic( - &mut self.prim_ex_pipeline, - &pipeline_manager, - |is_textured| { - if is_textured { - Some(( - "shader/vulkan/primex_tex.vert.spv".into(), - "shader/vulkan/primex_tex.frag.spv".into(), - )) - } else { - Some(( - "shader/vulkan/primex.vert.spv".into(), - "shader/vulkan/primex.frag.spv".into(), - )) - } - }, - |is_textured, address_mode| { - Self::prim_ex_pipeline_layout(layouts, is_textured, false, address_mode) - }, - ) - .map_err(|err| anyhow!("Create prim ex graphics pipeline: {err}"))?; - Ok(()) - }, - || -> anyhow::Result<()> { - (!stop_execution_flag - .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) - .then_some(Some(())) - .ok_or(anyhow!("Execution termination was requested."))?; - Self::create_graphics_pipeline_generic( - &mut self.sprite_multi_pipeline, - &pipeline_manager, - |is_textured| { - if is_textured { - Some(( - "shader/vulkan/spritemulti.vert.spv".into(), - "shader/vulkan/spritemulti.frag.spv".into(), - )) - } else { - None - } - }, - |_, address_mode| Self::sprite_multi_pipeline_layout(layouts, address_mode), - ) - .map_err(|err| anyhow!("Create sprite multi graphics pipeline: {err}"))?; - Ok(()) - }, - || -> anyhow::Result<()> { - let custom_pipes = custom_pipes.read(); - let mut name_offset = 0; - for custom_pipe in custom_pipes.iter() { - let count = custom_pipe.pipeline_count(); - for _ in 0..count { - (!stop_execution_flag - .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) - .then_some(Some(())) - .ok_or(anyhow!("Execution termination was requested."))?; - Self::create_graphics_pipeline_generic( + let (res1, res2, res3, res4, res5, res6, res7, res8, res9) = + runtime_threadpool.install(|| { + join_all!( + || -> anyhow::Result<()> { + (!stop_execution_flag + .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) + .then_some(Some(())) + .ok_or(anyhow!("Execution termination was requested."))?; + Self::create_graphics_pipeline_generic( + &mut self.standard_pipeline, + &pipeline_manager, + |is_textured| { + if is_textured { + Some(( + "shader/vulkan/prim.vert.spv".into(), + "shader/vulkan/prim_textured.frag.spv".into(), + )) + } else { + Some(( + "shader/vulkan/prim.vert.spv".into(), + "shader/vulkan/prim.frag.spv".into(), + )) + } + }, + |is_textured, address_mode| { + Self::standard_pipeline_layout( + layouts, + is_textured, + false, + address_mode, + ) + }, + ) + .map_err(|err| anyhow!("Create standard graphics pipeline: {err}"))?; + Ok(()) + }, + || -> anyhow::Result<()> { + (!stop_execution_flag + .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) + .then_some(Some(())) + .ok_or(anyhow!("Execution termination was requested."))?; + Self::create_graphics_pipeline_generic( + &mut self.standard_line_pipeline, + &pipeline_manager, + |is_textured| { + if is_textured { + None + } else { + Some(( + "shader/vulkan/prim.vert.spv".into(), + "shader/vulkan/prim.frag.spv".into(), + )) + } + }, + |is_textured, address_mode| { + Self::standard_pipeline_layout( + layouts, + is_textured, + true, + address_mode, + ) + }, + ) + .map_err(|err| anyhow!("Create standard line graphics pipeline: {err}"))?; + Ok(()) + }, + || -> anyhow::Result<()> { + (!stop_execution_flag + .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) + .then_some(Some(())) + .ok_or(anyhow!("Execution termination was requested."))?; + Self::create_graphics_pipeline_generic( + &mut self.blur_pipeline, + &pipeline_manager, + |is_textured| { + if is_textured { + Some(( + "shader/vulkan/prim.vert.spv".into(), + "shader/vulkan/blur.frag.spv".into(), + )) + } else { + None + } + }, + |is_textured, address_mode| { + let ( + input_attributes, + set_layouts, + mut push_constants, + stride, + is_line, + ) = Self::standard_pipeline_layout( + layouts, + is_textured, + false, + address_mode, + ); + push_constants.push(vk::PushConstantRange { + stage_flags: vk::ShaderStageFlags::FRAGMENT, + offset: std::mem::size_of::() as u32, + size: std::mem::size_of::() as u32, + }); + ( + input_attributes, + set_layouts, + push_constants, + stride, + is_line, + ) + }, + ) + .map_err(|err| anyhow!("Create blur graphics pipeline: {err}"))?; + Ok(()) + }, + || -> anyhow::Result<()> { + (!stop_execution_flag + .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) + .then_some(Some(())) + .ok_or(anyhow!("Execution termination was requested."))?; + Self::create_graphics_pipeline_generic( + &mut self.glass_pipeline, + &pipeline_manager, + |is_textured| { + if is_textured { + Some(( + "shader/vulkan/prim.vert.spv".into(), + "shader/vulkan/glass.frag.spv".into(), + )) + } else { + None + } + }, + |is_textured, address_mode| { + let ( + input_attributes, + set_layouts, + mut push_constants, + stride, + is_line, + ) = Self::standard_pipeline_layout( + layouts, + is_textured, + false, + address_mode, + ); + push_constants.push(vk::PushConstantRange { + stage_flags: vk::ShaderStageFlags::FRAGMENT, + offset: std::mem::size_of::() as u32, + size: std::mem::size_of::() as u32, + }); + ( + input_attributes, + set_layouts, + push_constants, + stride, + is_line, + ) + }, + ) + .map_err(|err| anyhow!("Create glass graphics pipeline: {err}"))?; + Ok(()) + }, + || -> anyhow::Result<()> { + (!stop_execution_flag + .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) + .then_some(Some(())) + .ok_or(anyhow!("Execution termination was requested."))?; + Self::create_graphics_pipeline_generic( + &mut self.standard_3d_pipeline, + &pipeline_manager, + |is_textured| { + if is_textured { + Some(( + "shader/vulkan/prim3d_textured.vert.spv".into(), + "shader/vulkan/prim3d_textured.frag.spv".into(), + )) + } else { + Some(( + "shader/vulkan/prim3d.vert.spv".into(), + "shader/vulkan/prim3d.frag.spv".into(), + )) + } + }, + |is_textured, address_mode| { + Self::standard_3d_pipeline_layout( + layouts, + is_textured, + address_mode, + ) + }, + ) + .map_err(|err| anyhow!("Create standard 3d graphics pipeline: {err}"))?; + + Ok(()) + }, + || -> anyhow::Result<()> { + (!stop_execution_flag + .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) + .then_some(Some(())) + .ok_or(anyhow!("Execution termination was requested."))?; + Self::create_graphics_pipeline_generic( + &mut self.prim_ex_rotationless_pipeline, + &pipeline_manager, + |is_textured| { + if is_textured { + Some(( + "shader/vulkan/primex_tex_rotationless.vert.spv".into(), + "shader/vulkan/primex_tex_rotationless.frag.spv".into(), + )) + } else { + Some(( + "shader/vulkan/primex_rotationless.vert.spv".into(), + "shader/vulkan/primex_rotationless.frag.spv".into(), + )) + } + }, + |is_textured, address_mode| { + Self::prim_ex_pipeline_layout( + layouts, + is_textured, + true, + address_mode, + ) + }, + ) + .map_err(|err| { + anyhow!("Create prim ex graphics rotationless pipeline: {err}") + })?; + Ok(()) + }, + || -> anyhow::Result<()> { + (!stop_execution_flag + .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) + .then_some(Some(())) + .ok_or(anyhow!("Execution termination was requested."))?; + Self::create_graphics_pipeline_generic( + &mut self.prim_ex_pipeline, + &pipeline_manager, + |is_textured| { + if is_textured { + Some(( + "shader/vulkan/primex_tex.vert.spv".into(), + "shader/vulkan/primex_tex.frag.spv".into(), + )) + } else { + Some(( + "shader/vulkan/primex.vert.spv".into(), + "shader/vulkan/primex.frag.spv".into(), + )) + } + }, + |is_textured, address_mode| { + Self::prim_ex_pipeline_layout( + layouts, + is_textured, + false, + address_mode, + ) + }, + ) + .map_err(|err| anyhow!("Create prim ex graphics pipeline: {err}"))?; + Ok(()) + }, + || -> anyhow::Result<()> { + (!stop_execution_flag + .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) + .then_some(Some(())) + .ok_or(anyhow!("Execution termination was requested."))?; + Self::create_graphics_pipeline_generic( + &mut self.sprite_multi_pipeline, + &pipeline_manager, + |is_textured| { + if is_textured { + Some(( + "shader/vulkan/spritemulti.vert.spv".into(), + "shader/vulkan/spritemulti.frag.spv".into(), + )) + } else { + None + } + }, + |_, address_mode| { + Self::sprite_multi_pipeline_layout(layouts, address_mode) + }, + ) + .map_err(|err| anyhow!("Create sprite multi graphics pipeline: {err}"))?; + Ok(()) + }, + || -> anyhow::Result<()> { + let custom_pipes = custom_pipes.read(); + let mut name_offset = 0; + for custom_pipe in custom_pipes.iter() { + let count = custom_pipe.pipeline_count(); + for _ in 0..count { + (!stop_execution_flag + .is_some_and(|v| v.load(std::sync::atomic::Ordering::Relaxed))) + .then_some(Some(())) + .ok_or(anyhow!("Execution termination was requested."))?; + Self::create_graphics_pipeline_generic( &mut self.additional_pipes[name_offset as usize], &pipeline_manager, |is_textured| { @@ -811,14 +886,14 @@ impl SubRenderPass { "Creating custom graphics pipeline (index: {name_offset}) failed: {err}" ) })?; - name_offset += 1; + name_offset += 1; + } } - } - Ok(()) - } - ) - }); + Ok(()) + } + ) + }); res1?; res2?; @@ -828,6 +903,7 @@ impl SubRenderPass { res6?; res7?; res8?; + res9?; Ok(()) } @@ -880,6 +956,7 @@ impl SubRenderPass { standard_blur_pipeline: PipelineContainer::new(mode.clone()), standard_3d_pipeline: PipelineContainer::new(mode.clone()), blur_pipeline: PipelineContainer::new(mode.clone()), + glass_pipeline: PipelineContainer::new(mode.clone()), prim_ex_pipeline: PipelineContainer::new(mode.clone()), prim_ex_rotationless_pipeline: PipelineContainer::new(mode.clone()), sprite_multi_pipeline: PipelineContainer::new(mode.clone()), diff --git a/lib/graphics-backend/src/backends/vulkan/vulkan.rs b/lib/graphics-backend/src/backends/vulkan/vulkan.rs index 2df722d2..d5e3e040 100644 --- a/lib/graphics-backend/src/backends/vulkan/vulkan.rs +++ b/lib/graphics-backend/src/backends/vulkan/vulkan.rs @@ -716,6 +716,9 @@ impl VulkanBackend { CommandsRenderStream::RenderBlurred { cmd, .. } => { Self::cmd_render_blurred_fill_execute_buffer(&mut render_execute_manager, cmd) } + CommandsRenderStream::RenderGlass { cmd, .. } => { + Self::cmd_render_glass_fill_execute_buffer(&mut render_execute_manager, cmd) + } }, CommandsRender::QuadContainer(cmd) => match cmd { CommandsRenderQuadContainer::Render(cmd) => { @@ -2294,6 +2297,13 @@ impl VulkanBackend { Self::cmd_render_fill_execute_buffer(render_execute_manager, cmd); } + fn cmd_render_glass_fill_execute_buffer( + render_execute_manager: &mut RenderCommandExecuteManager, + cmd: &CommandRender, + ) { + Self::cmd_render_fill_execute_buffer(render_execute_manager, cmd); + } + fn cmd_update_viewport(&mut self, cmd: &CommandUpdateViewport) -> anyhow::Result<()> { let viewport = self.render.get().native.swap_img_and_viewport_extent; if cmd.x != 0 || cmd.y != 0 || cmd.width != viewport.width || cmd.height != viewport.height diff --git a/lib/graphics-backend/src/backends/vulkan/vulkan_uniform.rs b/lib/graphics-backend/src/backends/vulkan/vulkan_uniform.rs index cbc615c5..b953472f 100644 --- a/lib/graphics-backend/src/backends/vulkan/vulkan_uniform.rs +++ b/lib/graphics-backend/src/backends/vulkan/vulkan_uniform.rs @@ -20,6 +20,25 @@ pub struct UniformGBlur { pub blur_radius: f32, } +#[derive(Default)] +#[repr(C)] +pub struct UniformGGlass { + pub center: vec2, + pub size: vec2, + + pub elipse_strength: f32, + pub exponent_offset: f32, + pub decay_scale: f32, + pub base_factor: f32, + pub deca_rate: f32, + pub refraction_falloff: f32, + pub noise: f32, + pub glow_weight: f32, + pub glow_bias: f32, + pub glow_edge0: f32, + pub glow_edge1: f32, +} + #[derive(Default)] #[repr(C)] pub struct UniformPrimExGPosRotationless { diff --git a/lib/graphics-backend/src/checker.rs b/lib/graphics-backend/src/checker.rs index ed2960b0..b39b1233 100644 --- a/lib/graphics-backend/src/checker.rs +++ b/lib/graphics-backend/src/checker.rs @@ -446,6 +446,9 @@ impl GraphicsContainersAPI { CommandsRenderStream::RenderBlurred { cmd, .. } => { self.check_render_cmd(stream_handle, cmd, vertices_offset_before_commands) } + CommandsRenderStream::RenderGlass { cmd, .. } => { + self.check_render_cmd(stream_handle, cmd, vertices_offset_before_commands) + } }, CommandsRender::Clear(_) => { todo!() diff --git a/lib/graphics-types/src/commands.rs b/lib/graphics-types/src/commands.rs index 215fae85..b24bac1c 100644 --- a/lib/graphics-types/src/commands.rs +++ b/lib/graphics-types/src/commands.rs @@ -4,7 +4,9 @@ use pool::mt_datatypes::{PoolString, PoolVec}; use serde::{Deserialize, Serialize}; use crate::{ - rendering::{ColorRgba, GlColor, GlColorf, GlPoint, SPoint, State, StateTexture}, + rendering::{ + ColorRgba, GlColor, GlColorf, GlPoint, RenderModeGlass, SPoint, State, StateTexture, + }, types::GraphicsBackendMemory, }; use math::math::vector::*; @@ -332,6 +334,10 @@ pub enum CommandsRenderStream { scale: vec2, blur_color: vec4, }, + RenderGlass { + cmd: CommandRender, + glass: RenderModeGlass, + }, } #[derive(Debug, Serialize, Deserialize, Hiarc)] diff --git a/lib/graphics-types/src/rendering.rs b/lib/graphics-types/src/rendering.rs index 9cd3a7a5..472798c1 100644 --- a/lib/graphics-types/src/rendering.rs +++ b/lib/graphics-types/src/rendering.rs @@ -205,6 +205,24 @@ impl Default for State { } } +#[derive(Debug, Hiarc, Default, Clone, Copy, Serialize, Deserialize)] +pub struct RenderModeGlass { + pub elipse_strength: f32, + pub exponent_offset: f32, + pub decay_scale: f32, + pub base_factor: f32, + pub deca_rate: f32, + pub refraction_falloff: f32, + pub noise: f32, + pub glow_weight: f32, + pub glow_bias: f32, + pub glow_edge0: f32, + pub glow_edge1: f32, + + pub center: vec2, + pub size: vec2, +} + #[derive(Debug, Hiarc, Default, Clone, Copy)] pub enum RenderMode { #[default] @@ -218,6 +236,7 @@ pub enum RenderMode { /// transparency blur_color: vec4, }, + Glass(RenderModeGlass), } #[derive(Debug, Hiarc, Default, Clone, Copy, Serialize, Deserialize)] diff --git a/lib/graphics/src/handles/stream.rs b/lib/graphics/src/handles/stream.rs index fc2c2207..f7538cdb 100644 --- a/lib/graphics/src/handles/stream.rs +++ b/lib/graphics/src/handles/stream.rs @@ -370,6 +370,7 @@ pub mod stream { scale: *scale, blur_color: *blur_color, }, + &RenderMode::Glass(glass) => CommandsRenderStream::RenderGlass { cmd, glass }, }; self.backend_handle diff --git a/lib/graphics/src/utils.rs b/lib/graphics/src/utils.rs index b3e2c543..3735e444 100644 --- a/lib/graphics/src/utils.rs +++ b/lib/graphics/src/utils.rs @@ -1,5 +1,5 @@ use graphics_types::rendering::{ - BlendType, ColorMaskMode, RenderMode, State, StencilMode, WrapType, + BlendType, ColorMaskMode, RenderMode, RenderModeGlass, State, StencilMode, WrapType, }; use hiarc::hi_closure; use math::math::vector::{vec2, vec4}; @@ -182,3 +182,103 @@ pub fn render_swapped_frame( ); } } + +pub fn render_glass( + stream_handle: &GraphicsStreamHandle, + screen_rect_pos: vec2, + screen_rect_size: vec2, + center: vec2, + size: vec2, + elipse_strength: f32, + color: vec4, +) { + let mut state = State::new(); + state.map_canvas( + screen_rect_pos.x, + screen_rect_pos.y, + screen_rect_size.x, + screen_rect_size.y, + ); + state.set_stencil_mode(StencilMode::StencilPassed); + state.wrap(WrapType::Clamp); + state.blend(BlendType::None); + + stream_handle.stream_quads( + hi_closure!([ + center: vec2, + size: vec2, + screen_rect_pos: vec2, + screen_rect_size: vec2, + elipse_strength: f32, + color: vec4, + ], + |mut stream_handle: QuadStreamHandle<'_>| -> () { + let pos = center - size / 2.0; + let u = screen_rect_pos.x + pos.x / screen_rect_size.x; + let v = screen_rect_pos.x + pos.y / screen_rect_size.y; + let uw = size.x / screen_rect_size.x; + let vh = size.y / screen_rect_size.y; + + + stream_handle.set_color_attachment_texture(); + stream_handle.set_render_mode(RenderMode::Glass(RenderModeGlass { + elipse_strength, + exponent_offset: 5.7, + decay_scale: 600.0, + base_factor: 2.0, + deca_rate: 0.8, + refraction_falloff: 10.0, + noise: 0.0, + glow_weight: 1.0, + glow_bias: 0.0, + glow_edge0: 1.0, + glow_edge1: -1.0, + + center: vec2::new(u + uw / 2.0, v + vh / 2.0), + size: vec2::new(uw, vh), + })); + stream_handle.add_vertices( + StreamedQuad::default() + .from_pos_and_size(pos, size) + .tex_free_form( + vec2::new(u, v), + vec2::new(u + uw, v), + vec2::new(u + uw, v + vh), + vec2::new(u, v + vh), + ) + .colorf(color) + .into(), + ); + }), + state, + ); +} + +pub fn render_glass_rest( + backend_handle: &GraphicsBackendHandle, + stream_handle: &GraphicsStreamHandle, +) { + let mut state = State::new(); + state.map_canvas(0.0, 0.0, 1.0, 1.0); + state.set_stencil_mode(StencilMode::StencilNotPassed { + clear_stencil: false, + }); + state.wrap(WrapType::Clamp); + state.blend(BlendType::None); + + stream_handle.render_quads( + &[StreamedQuad::default() + .from_pos_and_size(vec2::new(0.0, 0.0), vec2::new(1.0, 1.0)) + .tex_free_form( + vec2::new(0.0, 0.0), + vec2::new(1.0, 0.0), + vec2::new(1.0, 1.0), + vec2::new(0.0, 1.0), + ) + .colorf(vec4::new(1.0, 1.0, 1.0, 1.0))], + state, + TextureType::ColorAttachmentOfPreviousPass, + ); + + backend_handle.next_switch_pass(); +} diff --git a/lib/ui-base/src/types.rs b/lib/ui-base/src/types.rs index 544df228..98fc3263 100644 --- a/lib/ui-base/src/types.rs +++ b/lib/ui-base/src/types.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, rc::Rc, sync::Arc, time::Duration}; -use egui::{Color32, CornerRadius, FontDefinitions, Pos2, Rect}; +use egui::{Color32, CornerRadius, FontDefinitions, Pos2, Rect, Vec2}; use serde::{Deserialize, Serialize}; use crate::custom_callback::CustomCallbackTrait; @@ -19,7 +19,7 @@ impl<'a, U: 'a> UiRenderPipe<'a, U> { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] pub struct BlurRect { pub rect: Rect, pub rounding: CornerRadius, @@ -36,7 +36,7 @@ impl BlurRect { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] pub struct BlurCircle { pub center: Pos2, pub radius: f32, @@ -53,12 +53,36 @@ impl BlurCircle { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] pub enum BlurShape { Rect(BlurRect), Circle(BlurCircle), } +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] +pub struct GlassElipse { + pub center: Pos2, + pub size: Vec2, + pub power: f32, + pub color: Color32, +} + +impl GlassElipse { + pub fn new(center: Pos2, size: Vec2, power: f32, color: Color32) -> Self { + Self { + center, + size, + power, + color, + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] +pub enum GlassShape { + Elipse(GlassElipse), +} + #[derive(Debug)] pub struct UiState { pub is_ui_open: bool, @@ -70,6 +94,9 @@ pub struct UiState { /// blur shapes of this frame, if empty, then /// bluring is skipped. pub blur_shapes: Vec, + /// glass shapes of this frame, if empty, then + /// all glass rendering is skipped. + pub glass_shapes: Vec, } impl Default for UiState { @@ -82,6 +109,7 @@ impl Default for UiState { custom_paint_id: 0, blur_shapes: Default::default(), + glass_shapes: Default::default(), } } } @@ -111,6 +139,12 @@ impl UiState { self.blur_shapes .push(BlurShape::Circle(BlurCircle::new(center, radius))); } + + pub fn add_glass_elipse(&mut self, center: Pos2, size: Vec2, power: f32, color: Color32) { + self.glass_shapes.push(GlassShape::Elipse(GlassElipse::new( + center, size, power, color, + ))); + } } /// for encode and decode @@ -124,6 +158,7 @@ pub struct RawInputWrapper { pub struct RawOutputWrapper { pub output: egui::PlatformOutput, pub blur_shapes: Vec, + pub glass_shapes: Vec, pub zoom_level: Option, } diff --git a/lib/ui-generic/src/generic_ui_renderer.rs b/lib/ui-generic/src/generic_ui_renderer.rs index c623afa8..c64d899b 100644 --- a/lib/ui-generic/src/generic_ui_renderer.rs +++ b/lib/ui-generic/src/generic_ui_renderer.rs @@ -1,18 +1,21 @@ use std::time::Duration; use crate::traits::UiPageInterface; -use egui::Stroke; +use egui::{Color32, Rect, Stroke}; use graphics::{ handles::{ backend::backend::GraphicsBackendHandle, canvas::canvas::GraphicsCanvasHandle, stream::stream::GraphicsStreamHandle, texture::texture::GraphicsTextureHandle, }, - utils::{DEFAULT_BLUR_MIX_LENGTH, DEFAULT_BLUR_RADIUS, render_blur, render_swapped_frame}, + utils::{ + DEFAULT_BLUR_MIX_LENGTH, DEFAULT_BLUR_RADIUS, render_blur, render_glass, render_glass_rest, + render_swapped_frame, + }, }; -use math::math::vector::vec4; +use math::math::vector::{vec2, vec4}; use tracing::instrument; use ui_base::{ - types::{BlurShape, UiRenderPipe, UiState}, + types::{BlurShape, GlassShape, UiRenderPipe, UiState}, ui::UiContainer, ui_render::render_ui, }; @@ -111,6 +114,72 @@ pub fn render_blur_if_needed( } } +#[instrument(level = "trace", skip_all)] +pub fn render_glass_if_needed( + backend_handle: &GraphicsBackendHandle, + texture_handle: &GraphicsTextureHandle, + stream_handle: &GraphicsStreamHandle, + canvas_handle: &GraphicsCanvasHandle, + ui: &mut UiContainer, +) { + if !ui.ui_state.glass_shapes.is_empty() { + let glass_shapes = ui.ui_state.glass_shapes.clone(); + let (screen_rect, full_output, zoom_level) = render_impl( + canvas_handle, + ui, + |ui, _, ui_state| { + for glass_shape in ui_state.glass_shapes.drain(..) { + let GlassShape::Elipse(elipse) = glass_shape; + + ui.painter().rect( + Rect::from_center_size(elipse.center, elipse.size - egui::vec2(2.0, 2.0)), + 0.0, + Color32::WHITE, + Stroke::NONE, + egui::StrokeKind::Inside, + ); + } + }, + &mut UiRenderPipe { + cur_time: Duration::ZERO, + user_data: &mut (), + }, + egui::RawInput::default(), + true, + ); + backend_handle.next_switch_pass(); + let _ = render_ui( + ui, + full_output, + &screen_rect, + zoom_level, + backend_handle, + texture_handle, + stream_handle, + true, + ); + for glass_shape in glass_shapes { + let GlassShape::Elipse(elipse) = glass_shape; + render_glass( + stream_handle, + vec2::new(screen_rect.min.x, screen_rect.min.y), + vec2::new(screen_rect.width(), screen_rect.height()), + vec2::new(elipse.center.x, elipse.center.y), + vec2::new(elipse.size.x, elipse.size.y), + elipse.power, + vec4::new( + elipse.color.r() as f32 / 255.0, + elipse.color.g() as f32 / 255.0, + elipse.color.b() as f32 / 255.0, + elipse.color.a() as f32 / 255.0, + ), + ); + } + render_glass_rest(backend_handle, stream_handle); + render_swapped_frame(canvas_handle, stream_handle); + } +} + #[allow(clippy::too_many_arguments)] #[instrument(level = "trace", skip_all)] pub fn render_ex( @@ -146,7 +215,7 @@ pub fn render_ex( canvas_handle, ui, ); - render_ui( + let res = render_ui( ui, full_output, &screen_rect, @@ -155,7 +224,15 @@ pub fn render_ex( texture_handle, stream_handle, false, - ) + ); + render_glass_if_needed( + backend_handle, + texture_handle, + stream_handle, + canvas_handle, + ui, + ); + res } #[allow(clippy::too_many_arguments)] diff --git a/lib/ui-wasm-manager/src/lib.rs b/lib/ui-wasm-manager/src/lib.rs index 5ca15ace..ebc9d9bf 100644 --- a/lib/ui-wasm-manager/src/lib.rs +++ b/lib/ui-wasm-manager/src/lib.rs @@ -249,6 +249,14 @@ where Ok(output) => { self.ui.zoom_level.set(output.zoom_level); self.ui.ui_state.blur_shapes = output.blur_shapes; + self.ui.ui_state.glass_shapes = output.glass_shapes; + generic_ui_renderer::render_glass_if_needed( + &graphics.backend_handle, + &graphics.texture_handle, + &graphics.stream_handle, + &graphics.canvas_handle, + &mut self.ui, + ); UiPageRunReturn::Success(output.output) } Err(err) => UiPageRunReturn::RuntimeError(err), diff --git a/misc b/misc index 4e0f1a85..dcc8a136 160000 --- a/misc +++ b/misc @@ -1 +1 @@ -Subproject commit 4e0f1a85ca30c81612bd5b7a8d873b5b3b7d0577 +Subproject commit dcc8a136c84c5df9a7d9bc910d85be095ce335fe From 493586eaa5c3a51d8d7c120e8dd41cde70483b32 Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Sun, 5 Oct 2025 10:07:12 +0200 Subject: [PATCH 3/6] Legacy-proxy: Use whole startup phase as overhead time. --- game/legacy-proxy/src/client.rs | 7 +------ game/legacy-proxy/src/lib.rs | 36 ++++++++++----------------------- 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/game/legacy-proxy/src/client.rs b/game/legacy-proxy/src/client.rs index e0dbd1ee..a39ce591 100644 --- a/game/legacy-proxy/src/client.rs +++ b/game/legacy-proxy/src/client.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, net::SocketAddr, time::Duration}; +use std::{collections::BTreeMap, net::SocketAddr}; use anyhow::anyhow; use arrayvec::ArrayVec; @@ -208,8 +208,6 @@ pub struct ClientData { pub latest_input: snap_obj::PlayerInput, pub latest_char_input: CharacterInput, - pub connect_time: Duration, - pub player_info: NetworkCharacterInfo, pub latest_inputs: BTreeMap, @@ -226,7 +224,6 @@ impl ProxyClient { pub fn new( player_info: NetworkCharacterInfo, socket: SocketClient, - connect_time: Duration, id: u64, secondary_player: bool, ) -> Self { @@ -255,8 +252,6 @@ impl ProxyClient { inp }, - connect_time, - player_info, latest_inputs: Default::default(), diff --git a/game/legacy-proxy/src/lib.rs b/game/legacy-proxy/src/lib.rs index f95fba95..eb390161 100644 --- a/game/legacy-proxy/src/lib.rs +++ b/game/legacy-proxy/src/lib.rs @@ -347,6 +347,8 @@ struct Client { log: ConnectingLog, last_snapshot: Snapshot, + + start_time: Duration, } impl Client { @@ -356,6 +358,7 @@ impl Client { addr: SocketAddr, log: ConnectingLog, ) -> anyhow::Result { + let proxy_start = time.now(); let fs = io.fs.clone(); log.log("Preparing proxy socket"); @@ -675,6 +678,8 @@ impl Client { is_finished: is_finished_thread, log, + + start_time: proxy_start, }; app.run_loop().unwrap(); @@ -3528,13 +3533,7 @@ impl Client { self.players.insert( self.base.id_generator.next_id(), - ProxyClient::new( - Default::default(), - sock_loop, - self.time.now(), - 0, - false, - ), + ProxyClient::new(Default::default(), sock_loop, 0, false), ); } } @@ -3564,13 +3563,7 @@ impl Client { self.players.insert( self.base.id_generator.next_id(), - ProxyClient::new( - Default::default(), - sock_loop, - self.time.now(), - 0, - false, - ), + ProxyClient::new(Default::default(), sock_loop, 0, false), ); } ClientToServerMessage::Ready(msg) => { @@ -3619,13 +3612,7 @@ impl Client { let player_id = self.base.id_generator.next_id(); self.players.insert( player_id, - ProxyClient::new( - ev.player_info, - sock_loop, - self.time.now(), - ev.id, - true, - ), + ProxyClient::new(ev.player_info, sock_loop, ev.id, true), ); if let Some(con_id) = self.con_id { self.server_network.send_unordered_to( @@ -4531,10 +4518,9 @@ impl Client { self.server_network.send_unordered_to( &ServerToClientMessage::ServerInfo { info: server_info, - overhead: self - .time - .now() - .saturating_sub(player.data.connect_time), + // the proxy is not really good at estimating the first ping + // so give it the whole startup time as overhead instead + overhead: self.time.now().saturating_sub(self.start_time), }, &con_id, ); From 427be5bd27326965e339aafb4bd5ad47a03c9998 Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Sun, 5 Oct 2025 10:31:56 +0200 Subject: [PATCH 4/6] Legacy-proxy: Send a new request after first connection loop --- game/legacy-proxy/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/game/legacy-proxy/src/lib.rs b/game/legacy-proxy/src/lib.rs index eb390161..2f0bcd07 100644 --- a/game/legacy-proxy/src/lib.rs +++ b/game/legacy-proxy/src/lib.rs @@ -440,7 +440,7 @@ impl Client { Connless::RequestInfo(msg::connless::RequestInfo { token: tokens[0] }), ); let start_time = time.now(); - let mut last_req = start_time; + let mut last_req = None; let mut last_reconnect = start_time; while server_info.is_none() && !is_finished_thread.load(std::sync::atomic::Ordering::SeqCst) @@ -514,8 +514,12 @@ impl Client { let cur_time = time.now(); // send new request - if cur_time.saturating_sub(last_req) > Duration::from_secs(5) { - log.log("Sending new info request after 5s timeout"); + if last_req.is_none_or(|last_req| { + cur_time.saturating_sub(last_req) > Duration::from_secs(1) + }) { + if last_req.is_some() { + log.log("Sending new info request after 1s timeout"); + } let token = rand::rng().next_u32() as u8; conless.sendc( addr, @@ -524,7 +528,7 @@ impl Client { tokens.push(token); - last_req = cur_time; + last_req = Some(cur_time); } // try to reconnect From 55390ef084bbe35a84a518573b0ad375b2de7f2a Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Sun, 5 Oct 2025 10:43:00 +0200 Subject: [PATCH 5/6] Give tees their air jump in UI --- game/client-ui/src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/client-ui/src/utils.rs b/game/client-ui/src/utils.rs index dad983a4..d70143f7 100644 --- a/game/client-ui/src/utils.rs +++ b/game/client-ui/src/utils.rs @@ -102,7 +102,7 @@ pub fn render_tee_for_ui_with_skin( let tee_render_info = TeeRenderInfo { color_body, color_feet, - got_air_jump: false, + got_air_jump: true, feet_flipped: false, size: self.size, eye_left: self.eyes, From 8026e355840c19f0df5b73b450b37e1264f4345e Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Sun, 5 Oct 2025 12:56:51 +0200 Subject: [PATCH 6/6] Use more player info from local player for stuff like entities --- game/legacy-proxy/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/game/legacy-proxy/src/lib.rs b/game/legacy-proxy/src/lib.rs index 2f0bcd07..24661f66 100644 --- a/game/legacy-proxy/src/lib.rs +++ b/game/legacy-proxy/src/lib.rs @@ -1593,7 +1593,7 @@ impl Client { } } SnapObj::ClientInfo(client_info) => { - if let Some((character_id, info)) = Self::player_info_mut(id, base, snapshot) { + if let Some((_, info)) = Self::player_info_mut(id, base, snapshot) { fn ints_to_net_str( int_arr: &[i32], ) -> NetworkString { @@ -1605,14 +1605,14 @@ impl Client { .map(NetworkString::new_lossy) .unwrap_or_default() } - let mut player_info = (*info.player_info).clone(); // Apply as much info from known player info as possible - if character_id == player_id { - player_info = player.player_info.clone(); - } else if let Some(dummy) = base.local_players.get(&id) { - player_info = dummy.player_info.clone(); - } + let mut player_info = if let Some(dummy) = base.local_players.get(&id) { + dummy.player_info.clone() + } else { + // fall back to player info, since legacy servers don't send enough info + player.player_info.clone() + }; // Then overwrite the info the server knows about player_info.name = ints_to_net_str(client_info.name.as_slice());