From d8d20f5112b7e9524a79edb6b4a351f7867a0a02 Mon Sep 17 00:00:00 2001 From: Nils Hasenbanck Date: Sat, 28 Dec 2024 21:35:46 +0100 Subject: [PATCH] Improve first map server connect On first login the map server will send a command to change the map regardless, so we don't need to handle the map loading in the network event when we disconnect from the character server. We only initialize the player entity and interface in this case. We also don't use the position that the map server sends us in the MapServerLoginSuccessPacket. The ChangeMapPacket is our only signal that we use to first load the new map and then set the correct position. The three positions that we used before this PR lead to some desync in the first few seconds, since it seems, only the final position of the map change network event is the correct one. We also now properly turn the screen black until we fully finished loading the map. Later we should also do the loading asynchronously on map change and turn the screen black while loading. --- korangar/src/graphics/engine.rs | 9 +- korangar/src/graphics/instruction.rs | 68 ++++++++- .../graphics/passes/interface/rectangle.rs | 12 +- .../graphics/passes/postprocessing/buffer.rs | 4 +- .../passes/postprocessing/rectangle.rs | 9 +- korangar/src/graphics/settings.rs | 2 +- korangar/src/main.rs | 139 ++++++++---------- korangar/src/world/action/mod.rs | 4 +- korangar_networking/src/event.rs | 8 - korangar_networking/src/lib.rs | 10 +- ragnarok_packets/src/handler.rs | 2 +- 11 files changed, 156 insertions(+), 111 deletions(-) diff --git a/korangar/src/graphics/engine.rs b/korangar/src/graphics/engine.rs index 2fa80173..5533d1ca 100644 --- a/korangar/src/graphics/engine.rs +++ b/korangar/src/graphics/engine.rs @@ -861,9 +861,12 @@ impl GraphicsEngine { .picker_render_pass_context .create_pass(&mut picker_encoder, &engine_context.global_context, None); - engine_context - .picker_tile_drawer - .draw(&mut render_pass, instruction.map_picker_tile_vertex_buffer); + if let Some(map_picker_tile_vertex_buffer) = instruction.map_picker_tile_vertex_buffer.as_ref() { + engine_context + .picker_tile_drawer + .draw(&mut render_pass, map_picker_tile_vertex_buffer); + } + engine_context.picker_entity_drawer.draw(&mut render_pass, instruction.entities); #[cfg(feature = "debug")] { diff --git a/korangar/src/graphics/instruction.rs b/korangar/src/graphics/instruction.rs index 744795cc..1584220b 100644 --- a/korangar/src/graphics/instruction.rs +++ b/korangar/src/graphics/instruction.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use cgmath::{Matrix4, Point3, Vector2, Vector3, Vector4}; +use cgmath::{Matrix4, Point3, SquareMatrix, Vector2, Vector3, Vector4, Zero}; use korangar_util::Rectangle; use ragnarok_packets::EntityId; use wgpu::BlendFactor; @@ -40,8 +40,8 @@ pub struct RenderInstruction<'a> { pub point_shadow_entities: &'a [EntityInstruction], pub effects: &'a [EffectInstruction], pub water: Option>, - pub map_picker_tile_vertex_buffer: &'a Buffer, - pub font_map_texture: &'a Texture, + pub map_picker_tile_vertex_buffer: Option<&'a Buffer>, + pub font_map_texture: Option<&'a Texture>, #[cfg(feature = "debug")] pub render_settings: RenderSettings, #[cfg(feature = "debug")] @@ -54,6 +54,42 @@ pub struct RenderInstruction<'a> { pub marker: &'a [MarkerInstruction], } +impl Default for RenderInstruction<'static> { + fn default() -> Self { + Self { + clear_interface: true, + show_interface: false, + picker_position: ScreenPosition::default(), + uniforms: Uniforms::default(), + indicator: None, + interface: &[], + bottom_layer_rectangles: &[], + middle_layer_rectangles: &[], + top_layer_rectangles: &[], + directional_light_with_shadow: DirectionalShadowCasterInstruction::default(), + point_light_shadow_caster: &[], + point_light: &[], + model_batches: &[], + models: &mut [], + entities: &mut [], + directional_model_batches: &[], + directional_shadow_models: &[], + directional_shadow_entities: &[], + point_shadow_models: &[], + point_shadow_entities: &[], + effects: &[], + water: None, + map_picker_tile_vertex_buffer: None, + font_map_texture: None, + render_settings: RenderSettings::default(), + aabb: &[], + circles: &[], + rectangles: &[], + marker: &[], + } + } +} + #[derive(Clone, Debug)] pub struct Uniforms { pub view_matrix: Matrix4, @@ -66,6 +102,21 @@ pub struct Uniforms { pub shadow_quality: ShadowQuality, } +impl Default for Uniforms { + fn default() -> Self { + Self { + view_matrix: Matrix4::identity(), + projection_matrix: Matrix4::identity(), + camera_position: Vector4::zero(), + animation_timer: 0.0, + day_timer: 0.0, + ambient_light_color: Color::default(), + enhanced_lighting: false, + shadow_quality: ShadowQuality::Soft, + } + } +} + #[derive(Clone, Debug)] pub struct WaterInstruction<'a> { pub water_texture: &'a Texture, @@ -86,6 +137,17 @@ pub struct DirectionalShadowCasterInstruction { pub color: Color, } +impl Default for DirectionalShadowCasterInstruction { + fn default() -> Self { + Self { + view_projection_matrix: Matrix4::identity(), + view_matrix: Matrix4::identity(), + direction: Vector3::zero(), + color: Color::default(), + } + } +} + /// Right now point shadows can't cast shadows of models that are not part of /// the map. #[derive(Clone, Debug)] diff --git a/korangar/src/graphics/passes/interface/rectangle.rs b/korangar/src/graphics/passes/interface/rectangle.rs index 0aba9eb3..86aa7fb7 100644 --- a/korangar/src/graphics/passes/interface/rectangle.rs +++ b/korangar/src/graphics/passes/interface/rectangle.rs @@ -250,6 +250,10 @@ impl Prepare for InterfaceRectangleDrawer { return; } + let Some(font_map_texture) = instructions.font_map_texture else { + return; + }; + self.instance_data.clear(); if self.bindless_support { @@ -381,8 +385,8 @@ impl Prepare for InterfaceRectangleDrawer { &self.bind_group_layout, &self.instance_data_buffer, &texture_views, - instructions.font_map_texture.get_texture_view(), - ) + font_map_texture.get_texture_view(), + ); } else { for instruction in instructions.interface.iter() { match instruction { @@ -481,8 +485,8 @@ impl Prepare for InterfaceRectangleDrawer { device, &self.bind_group_layout, &self.instance_data_buffer, - instructions.font_map_texture.get_texture_view(), - ) + font_map_texture.get_texture_view(), + ); } } diff --git a/korangar/src/graphics/passes/postprocessing/buffer.rs b/korangar/src/graphics/passes/postprocessing/buffer.rs index a5a96a59..da7ec216 100644 --- a/korangar/src/graphics/passes/postprocessing/buffer.rs +++ b/korangar/src/graphics/passes/postprocessing/buffer.rs @@ -114,7 +114,9 @@ impl Drawer<{ BindGroupCount::One }, { ColorAttachmentCount::One }, { DepthAttac impl Prepare for PostProcessingBufferDrawer { fn prepare(&mut self, device: &Device, instructions: &RenderInstruction) { - self.bind_group = Self::create_bind_group(device, &self.bind_group_layout, instructions.font_map_texture); + if let Some(font_map_texture) = instructions.font_map_texture { + self.bind_group = Self::create_bind_group(device, &self.bind_group_layout, font_map_texture); + } } fn upload(&mut self, _device: &Device, _staging_belt: &mut StagingBelt, _command_encoder: &mut CommandEncoder) { diff --git a/korangar/src/graphics/passes/postprocessing/rectangle.rs b/korangar/src/graphics/passes/postprocessing/rectangle.rs index f240f428..f78a7c92 100644 --- a/korangar/src/graphics/passes/postprocessing/rectangle.rs +++ b/korangar/src/graphics/passes/postprocessing/rectangle.rs @@ -274,6 +274,10 @@ impl Prepare for PostProcessingRectangleDrawer { return; } + let Some(font_map_texture) = instructions.font_map_texture else { + return; + }; + self.instance_data.clear(); if self.bindless_support { @@ -409,7 +413,7 @@ impl Prepare for PostProcessingRectangleDrawer { &self.bind_group_layout, &self.instance_data_buffer, &texture_views, - instructions.font_map_texture.get_texture_view(), + font_map_texture.get_texture_view(), ); } else { let mut offset = 0; @@ -507,12 +511,11 @@ impl Prepare for PostProcessingRectangleDrawer { } } - self.instance_data_buffer.reserve(device, self.instance_data.len()); self.bind_group = Self::create_bind_group( device, &self.bind_group_layout, &self.instance_data_buffer, - instructions.font_map_texture.get_texture_view(), + font_map_texture.get_texture_view(), ); } } diff --git a/korangar/src/graphics/settings.rs b/korangar/src/graphics/settings.rs index 696ad5ef..46e18f68 100644 --- a/korangar/src/graphics/settings.rs +++ b/korangar/src/graphics/settings.rs @@ -161,7 +161,7 @@ impl Display for ScreenSpaceAntiAliasing { } #[cfg(feature = "debug")] -#[derive(Copy, Clone, new)] +#[derive(Copy, Clone, Default, new)] pub struct RenderSettings { #[new(value = "true")] pub show_frames_per_second: bool, diff --git a/korangar/src/main.rs b/korangar/src/main.rs index 6c3ec9cf..a170ec09 100644 --- a/korangar/src/main.rs +++ b/korangar/src/main.rs @@ -221,6 +221,7 @@ struct Client { mouse_cursor: MouseCursor, dialog_system: DialogSystem, show_interface: bool, + show_game: bool, game_timer: GameTimer, #[cfg(feature = "debug")] @@ -460,6 +461,7 @@ impl Client { let mouse_cursor = MouseCursor::new(&mut sprite_loader, &mut action_loader); let dialog_system = DialogSystem::default(); let show_interface = true; + let show_game = true; }); time_phase!("initialize timer", { @@ -633,6 +635,7 @@ impl Client { mouse_cursor, dialog_system, show_interface, + show_game, game_timer, #[cfg(feature = "debug")] debug_camera, @@ -923,7 +926,8 @@ impl Client { self.interface .open_window(&self.application, &mut self.focus_state, &ErrorWindow::new(message.to_owned())) } - NetworkEvent::CharacterSelected { login_data, map_name } => { + NetworkEvent::CharacterSelected { login_data, .. } => { + self.show_game = false; self.audio_engine.play_sound_effect(self.main_menu_click_sound_effect); let saved_login_data = self.saved_login_data.as_ref().unwrap(); @@ -938,21 +942,6 @@ impl Client { .cloned() .unwrap(); - self.map = self - .map_loader - .load( - map_name, - &mut self.model_loader, - self.texture_loader.clone(), - #[cfg(feature = "debug")] - &self.tile_texture_mapping, - ) - .unwrap(); - - self.map.set_ambient_sound_sources(&self.audio_engine); - self.audio_engine - .play_background_music_track(self.map.background_music_track_name()); - self.saved_player_name = character_information.name.clone(); let player = Player::new( @@ -968,12 +957,10 @@ impl Client { client_tick, ); let player = Entity::Player(player); - - self.player_camera.set_focus_point(player.get_position()); self.entities.push(player); - // TODO: this will do one unnecessary restore_focus. check if - // that will be problematic + // TODO: This will do one unnecessary restore_focus. Check if + // that will be problematic. self.interface .close_window_with_class(&mut self.focus_state, CharacterSelectionWindow::WINDOW_CLASS); self.interface @@ -991,10 +978,6 @@ impl Client { // Put the dialog system in a well-defined state. self.dialog_system.close_dialog(); - - self.particle_holder.clear(); - let _ = self.networking_system.map_loaded(); - self.game_timer.set_client_tick(client_tick); } NetworkEvent::CharacterCreated { character_information } => { self.saved_characters.push(character_information); @@ -1078,6 +1061,7 @@ impl Client { self.entities[0].generate_pathing_mesh(&self.device, &self.queue, &self.map, &self.pathing_texture_mapping); } NetworkEvent::ChangeMap(map_name, player_position) => { + self.show_game = true; self.entities.truncate(1); self.map = self @@ -1104,11 +1088,6 @@ impl Client { self.point_light_manager.clear(); let _ = self.networking_system.map_loaded(); } - NetworkEvent::SetPlayerPosition(player_position) => { - let player_position = Vector2::new(player_position.x, player_position.y); - self.entities[0].set_position(&self.map, player_position, client_tick); - self.player_camera.set_focus_point(self.entities[0].get_position()); - } NetworkEvent::UpdateClientTick(client_tick) => { self.game_timer.set_client_tick(client_tick); } @@ -2267,55 +2246,59 @@ impl Client { let top_layer_instructions = self.top_interface_renderer.get_instructions(); let font_loader = self.font_loader.borrow(); - let render_instruction = RenderInstruction { - clear_interface, - show_interface: self.show_interface, - picker_position, - uniforms: Uniforms { - view_matrix, - projection_matrix, - camera_position, - animation_timer, - day_timer, - ambient_light_color, - enhanced_lighting: lighting_mode == LightingMode::Enhanced, - shadow_quality, - }, - indicator: indicator_instruction, - interface: interface_instructions.as_slice(), - bottom_layer_rectangles: bottom_layer_instructions.as_slice(), - middle_layer_rectangles: middle_layer_instructions.as_slice(), - top_layer_rectangles: top_layer_instructions.as_slice(), - directional_light_with_shadow: DirectionalShadowCasterInstruction { - view_projection_matrix: directional_light_view_projection_matrix, - view_matrix: directional_light_view_matrix, - direction: directional_light_direction, - color: directional_light_color, - }, - point_light_shadow_caster: &self.point_light_with_shadow_instructions, - point_light: &self.point_light_instructions, - model_batches: &self.model_batches, - models: &mut self.model_instructions, - entities: &mut self.entity_instructions, - directional_model_batches: &self.directional_shadow_model_batches, - directional_shadow_models: &self.directional_shadow_model_instructions, - directional_shadow_entities: &self.directional_shadow_entity_instructions, - point_shadow_models: &self.point_shadow_model_instructions, - point_shadow_entities: &self.point_shadow_entity_instructions, - effects: self.effect_renderer.get_instructions(), - water: water_instruction, - map_picker_tile_vertex_buffer: self.map.get_tile_picker_vertex_buffer(), - font_map_texture: font_loader.get_font_map(), - #[cfg(feature = "debug")] - render_settings: *self.render_settings.get(), - #[cfg(feature = "debug")] - aabb: &self.aabb_instructions, - #[cfg(feature = "debug")] - circles: &self.circle_instructions, - #[cfg(feature = "debug")] - rectangles: &self.rectangle_instructions, - #[cfg(feature = "debug")] - marker: self.debug_marker_renderer.get_instructions(), + let render_instruction = if self.show_game { + RenderInstruction { + clear_interface, + show_interface: self.show_interface, + picker_position, + uniforms: Uniforms { + view_matrix, + projection_matrix, + camera_position, + animation_timer, + day_timer, + ambient_light_color, + enhanced_lighting: lighting_mode == LightingMode::Enhanced, + shadow_quality, + }, + indicator: indicator_instruction, + interface: interface_instructions.as_slice(), + bottom_layer_rectangles: bottom_layer_instructions.as_slice(), + middle_layer_rectangles: middle_layer_instructions.as_slice(), + top_layer_rectangles: top_layer_instructions.as_slice(), + directional_light_with_shadow: DirectionalShadowCasterInstruction { + view_projection_matrix: directional_light_view_projection_matrix, + view_matrix: directional_light_view_matrix, + direction: directional_light_direction, + color: directional_light_color, + }, + point_light_shadow_caster: &self.point_light_with_shadow_instructions, + point_light: &self.point_light_instructions, + model_batches: &self.model_batches, + models: &mut self.model_instructions, + entities: &mut self.entity_instructions, + directional_model_batches: &self.directional_shadow_model_batches, + directional_shadow_models: &self.directional_shadow_model_instructions, + directional_shadow_entities: &self.directional_shadow_entity_instructions, + point_shadow_models: &self.point_shadow_model_instructions, + point_shadow_entities: &self.point_shadow_entity_instructions, + effects: self.effect_renderer.get_instructions(), + water: water_instruction, + map_picker_tile_vertex_buffer: Some(self.map.get_tile_picker_vertex_buffer()), + font_map_texture: Some(font_loader.get_font_map()), + #[cfg(feature = "debug")] + render_settings: *self.render_settings.get(), + #[cfg(feature = "debug")] + aabb: &self.aabb_instructions, + #[cfg(feature = "debug")] + circles: &self.circle_instructions, + #[cfg(feature = "debug")] + rectangles: &self.rectangle_instructions, + #[cfg(feature = "debug")] + marker: self.debug_marker_renderer.get_instructions(), + } + } else { + RenderInstruction::default() }; self.graphics_engine.render_next_frame(frame, render_instruction); diff --git a/korangar/src/world/action/mod.rs b/korangar/src/world/action/mod.rs index f9e08e57..ab2bbaca 100644 --- a/korangar/src/world/action/mod.rs +++ b/korangar/src/world/action/mod.rs @@ -5,7 +5,9 @@ use derive_new::new; use korangar_audio::SoundEffectKey; use korangar_interface::elements::{ElementCell, PrototypeElement}; use korangar_util::container::Cacheable; -use ragnarok_formats::action::{Action, ActionsData}; +use ragnarok_formats::action::Action; +#[cfg(feature = "debug")] +use ragnarok_formats::action::ActionsData; use ragnarok_packets::ClientTick; use crate::graphics::Color; diff --git a/korangar_networking/src/event.rs b/korangar_networking/src/event.rs index a472ea2b..c5e9a48d 100644 --- a/korangar_networking/src/event.rs +++ b/korangar_networking/src/event.rs @@ -37,7 +37,6 @@ pub enum NetworkEvent { }, CharacterSelected { login_data: CharacterServerLoginData, - map_name: String, }, CharacterSelectionFailed { reason: UnifiedCharacterSelectionFailedReason, @@ -126,7 +125,6 @@ pub enum NetworkEvent { account_id: AccountId, hair_id: u32, }, - SetPlayerPosition(WorldPosition), LoggedOut, FriendRequest { requestee: Friend, @@ -198,12 +196,6 @@ impl From> for NetworkEventList { } } -impl From<(NetworkEvent, NetworkEvent)> for NetworkEventList { - fn from(events: (NetworkEvent, NetworkEvent)) -> Self { - Self(vec![events.0, events.1]) - } -} - impl From for NetworkEventList { fn from(_: NoNetworkEvents) -> Self { Self(Vec::new()) diff --git a/korangar_networking/src/lib.rs b/korangar_networking/src/lib.rs index c8623e69..206c47b8 100644 --- a/korangar_networking/src/lib.rs +++ b/korangar_networking/src/lib.rs @@ -560,9 +560,8 @@ where server_port: packet.map_server_port, character_id: packet.character_id, }; - let map_name = packet.map_name.strip_suffix(".gat").unwrap().to_owned(); - NetworkEvent::CharacterSelected { login_data, map_name } + NetworkEvent::CharacterSelected { login_data } })?; packet_handler.register(|packet: CharacterSelectionFailedPacket| { let (reason, message) = match packet.reason { @@ -998,12 +997,7 @@ where })?; packet_handler.register_noop::()?; packet_handler.register_noop::()?; - packet_handler.register(|packet: MapServerLoginSuccessPacket| { - ( - NetworkEvent::UpdateClientTick(packet.client_tick), - NetworkEvent::SetPlayerPosition(packet.position), - ) - })?; + packet_handler.register(|packet: MapServerLoginSuccessPacket| NetworkEvent::UpdateClientTick(packet.client_tick))?; packet_handler.register(|packet: RestartResponsePacket| match packet.result { RestartResponseStatus::Ok => NetworkEvent::LoggedOut, RestartResponseStatus::Nothing => NetworkEvent::ChatMessage { diff --git a/ragnarok_packets/src/handler.rs b/ragnarok_packets/src/handler.rs index f812e270..7ae4bccf 100644 --- a/ragnarok_packets/src/handler.rs +++ b/ragnarok_packets/src/handler.rs @@ -60,7 +60,7 @@ impl PacketCallback for NoPacketCallback {} pub type HandlerFunction = Box) -> ConversionResult>; -/// A struct to help with reading packets from from a [`ByteReader`] and +/// A struct to help with reading packets from a [`ByteReader`] and /// converting them to some common event type. /// /// It allows passing a packet callback to monitor incoming packets.