diff --git a/korangar/Cargo.toml b/korangar/Cargo.toml index e23101c1..b7a18666 100644 --- a/korangar/Cargo.toml +++ b/korangar/Cargo.toml @@ -21,7 +21,7 @@ korangar_interface = { workspace = true, features = ["serde", "cgmath"] } korangar_networking = { workspace = true, features = ["debug"] } korangar_util = { workspace = true, features = ["interface"] } lunify = { workspace = true } -mlua = { workspace = true, features = ["lua51", "vendored"] } +mlua = { workspace = true, features = ["lua51", "send", "vendored"] } num = { workspace = true } option-ext = { workspace = true } pollster = { workspace = true } diff --git a/korangar/src/interface/cursor/mod.rs b/korangar/src/interface/cursor/mod.rs index b0de3519..0e2bb15a 100644 --- a/korangar/src/interface/cursor/mod.rs +++ b/korangar/src/interface/cursor/mod.rs @@ -45,7 +45,7 @@ pub struct MouseCursor { } impl MouseCursor { - pub fn new(sprite_loader: &mut SpriteLoader, action_loader: &mut ActionLoader) -> Self { + pub fn new(sprite_loader: &SpriteLoader, action_loader: &ActionLoader) -> Self { let sprite = sprite_loader.get("cursors.spr").unwrap(); let actions = action_loader.get("cursors.act").unwrap(); let animation_state = SpriteAnimationState::new(ClientTick(0)); diff --git a/korangar/src/inventory/skills.rs b/korangar/src/inventory/skills.rs index a299e7dd..5f60d0b6 100644 --- a/korangar/src/inventory/skills.rs +++ b/korangar/src/inventory/skills.rs @@ -25,8 +25,8 @@ pub struct SkillTree { impl SkillTree { pub fn fill( &mut self, - sprite_loader: &mut SpriteLoader, - action_loader: &mut ActionLoader, + sprite_loader: &SpriteLoader, + action_loader: &ActionLoader, skill_data: Vec, client_tick: ClientTick, ) { diff --git a/korangar/src/loaders/action/mod.rs b/korangar/src/loaders/action/mod.rs index 788e06b3..07247c1b 100644 --- a/korangar/src/loaders/action/mod.rs +++ b/korangar/src/loaders/action/mod.rs @@ -1,5 +1,5 @@ use std::num::{NonZeroU32, NonZeroUsize}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use korangar_audio::AudioEngine; #[cfg(feature = "debug")] @@ -20,7 +20,7 @@ const MAX_CACHE_SIZE: usize = 64 * 1024 * 1024; pub struct ActionLoader { game_file_loader: Arc, audio_engine: Arc>, - cache: SimpleCache>, + cache: Mutex>>, } impl ActionLoader { @@ -28,14 +28,14 @@ impl ActionLoader { Self { game_file_loader, audio_engine, - cache: SimpleCache::new( + cache: Mutex::new(SimpleCache::new( NonZeroU32::new(MAX_CACHE_COUNT).unwrap(), NonZeroUsize::new(MAX_CACHE_SIZE).unwrap(), - ), + )), } } - fn load(&mut self, path: &str) -> Result, LoadError> { + fn load(&self, path: &str) -> Result, LoadError> { #[cfg(feature = "debug")] let timer = Timer::new_dynamic(format!("load actions from {}", path.magenta())); @@ -99,7 +99,7 @@ impl ActionLoader { actions_data: saved_actions_data, }); - self.cache.insert(path.to_string(), sprite.clone()).unwrap(); + self.cache.lock().unwrap().insert(path.to_string(), sprite.clone()).unwrap(); #[cfg(feature = "debug")] timer.stop(); @@ -107,10 +107,15 @@ impl ActionLoader { Ok(sprite) } - pub fn get(&mut self, path: &str) -> Result, LoadError> { - match self.cache.get(path) { + pub fn get(&self, path: &str) -> Result, LoadError> { + let mut lock = self.cache.lock().unwrap(); + match lock.get(path) { Some(sprite) => Ok(sprite.clone()), - None => self.load(path), + None => { + // We need to drop to avoid a deadlock here. + drop(lock); + self.load(path) + } } } } diff --git a/korangar/src/loaders/animation/mod.rs b/korangar/src/loaders/animation/mod.rs index 533ce41a..9de2a538 100644 --- a/korangar/src/loaders/animation/mod.rs +++ b/korangar/src/loaders/animation/mod.rs @@ -1,6 +1,6 @@ use std::cmp::{max, min}; use std::num::{NonZeroU32, NonZeroUsize}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; #[cfg(feature = "debug")] use cgmath::SquareMatrix; @@ -17,23 +17,23 @@ const MAX_CACHE_COUNT: u32 = 256; const MAX_CACHE_SIZE: usize = 64 * 1024 * 1024; pub struct AnimationLoader { - cache: SimpleCache, Arc>, + cache: Mutex, Arc>>, } impl AnimationLoader { pub fn new() -> Self { Self { - cache: SimpleCache::new( + cache: Mutex::new(SimpleCache::new( NonZeroU32::new(MAX_CACHE_COUNT).unwrap(), NonZeroUsize::new(MAX_CACHE_SIZE).unwrap(), - ), + )), } } pub fn load( - &mut self, - sprite_loader: &mut SpriteLoader, - action_loader: &mut ActionLoader, + &self, + sprite_loader: &SpriteLoader, + action_loader: &ActionLoader, entity_type: EntityType, entity_part_files: &[String], ) -> Result, LoadError> { @@ -301,21 +301,30 @@ impl AnimationLoader { entity_type, }); - self.cache.insert(entity_part_files.to_vec(), animation_data.clone()).unwrap(); + self.cache + .lock() + .unwrap() + .insert(entity_part_files.to_vec(), animation_data.clone()) + .unwrap(); Ok(animation_data) } pub fn get( - &mut self, - sprite_loader: &mut SpriteLoader, - action_loader: &mut ActionLoader, + &self, + sprite_loader: &SpriteLoader, + action_loader: &ActionLoader, entity_type: EntityType, entity_part_files: &[String], ) -> Result, LoadError> { - match self.cache.get(entity_part_files) { + let mut lock = self.cache.lock().unwrap(); + match lock.get(entity_part_files) { Some(animation_data) => Ok(animation_data.clone()), - None => self.load(sprite_loader, action_loader, entity_type, entity_part_files), + None => { + // We need to drop to avoid a deadlock here. + drop(lock); + self.load(sprite_loader, action_loader, entity_type, entity_part_files) + } } } } diff --git a/korangar/src/loaders/effect/mod.rs b/korangar/src/loaders/effect/mod.rs index ff11f313..6e3ed0f9 100644 --- a/korangar/src/loaders/effect/mod.rs +++ b/korangar/src/loaders/effect/mod.rs @@ -1,5 +1,5 @@ use std::num::{NonZeroU32, NonZeroUsize}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use cgmath::Deg; #[cfg(feature = "debug")] @@ -22,21 +22,21 @@ const MAX_CACHE_SIZE: usize = 64 * 1024 * 1024; pub struct EffectLoader { game_file_loader: Arc, - cache: SimpleCache>, + cache: Mutex>>, } impl EffectLoader { pub fn new(game_file_loader: Arc) -> Self { Self { game_file_loader, - cache: SimpleCache::new( + cache: Mutex::new(SimpleCache::new( NonZeroU32::new(MAX_CACHE_COUNT).unwrap(), NonZeroUsize::new(MAX_CACHE_SIZE).unwrap(), - ), + )), } } - fn load(&mut self, path: &str, texture_loader: &TextureLoader) -> Result, LoadError> { + fn load(&self, path: &str, texture_loader: &TextureLoader) -> Result, LoadError> { #[cfg(feature = "debug")] let timer = Timer::new_dynamic(format!("load effect from {}", path.magenta())); @@ -148,7 +148,7 @@ impl EffectLoader { .collect(), )); - self.cache.insert(path.to_string(), effect.clone()).unwrap(); + self.cache.lock().unwrap().insert(path.to_string(), effect.clone()).unwrap(); #[cfg(feature = "debug")] timer.stop(); @@ -156,10 +156,15 @@ impl EffectLoader { Ok(effect) } - pub fn get(&mut self, path: &str, texture_loader: &TextureLoader) -> Result, LoadError> { - match self.cache.get(path) { + pub fn get(&self, path: &str, texture_loader: &TextureLoader) -> Result, LoadError> { + let mut lock = self.cache.lock().unwrap(); + match lock.get(path) { Some(effect) => Ok(effect.clone()), - None => self.load(path, texture_loader), + None => { + // We need to drop to avoid a deadlock here. + drop(lock); + self.load(path, texture_loader) + } } } } diff --git a/korangar/src/loaders/map/mod.rs b/korangar/src/loaders/map/mod.rs index 547512ac..761f1c62 100644 --- a/korangar/src/loaders/map/mod.rs +++ b/korangar/src/loaders/map/mod.rs @@ -56,9 +56,9 @@ pub struct MapLoader { impl MapLoader { pub fn load( - &mut self, + &self, resource_file: String, - model_loader: &mut ModelLoader, + model_loader: &ModelLoader, texture_loader: Arc, #[cfg(feature = "debug")] tile_texture_mapping: &[AtlasAllocation], ) -> Result { @@ -236,7 +236,7 @@ impl MapLoader { } fn generate_vertex_buffer_and_atlas_texture( - &mut self, + &self, resource_file: &str, mut texture_atlas_factory: TextureAtlasFactory, mut deferred_vertex_generation: Vec, diff --git a/korangar/src/loaders/model/mod.rs b/korangar/src/loaders/model/mod.rs index 61054984..89ffbdee 100644 --- a/korangar/src/loaders/model/mod.rs +++ b/korangar/src/loaders/model/mod.rs @@ -350,7 +350,7 @@ impl ModelLoader { } pub fn load( - &mut self, + &self, texture_atlas_factory: &mut TextureAtlasFactory, vertex_offset: &mut usize, model_file: &str, diff --git a/korangar/src/loaders/sprite/mod.rs b/korangar/src/loaders/sprite/mod.rs index e51ad0bc..0393830b 100644 --- a/korangar/src/loaders/sprite/mod.rs +++ b/korangar/src/loaders/sprite/mod.rs @@ -1,5 +1,5 @@ use std::num::{NonZeroU32, NonZeroUsize}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use image::RgbaImage; #[cfg(feature = "debug")] @@ -40,7 +40,7 @@ pub struct SpriteLoader { device: Arc, queue: Arc, game_file_loader: Arc, - cache: SimpleCache>, + cache: Mutex>>, } impl SpriteLoader { @@ -49,14 +49,14 @@ impl SpriteLoader { device, queue, game_file_loader, - cache: SimpleCache::new( + cache: Mutex::new(SimpleCache::new( NonZeroU32::new(MAX_CACHE_COUNT).unwrap(), NonZeroUsize::new(MAX_CACHE_SIZE).unwrap(), - ), + )), } } - fn load(&mut self, path: &str) -> Result, LoadError> { + fn load(&self, path: &str) -> Result, LoadError> { #[cfg(feature = "debug")] let timer = Timer::new_dynamic(format!("load sprite from {}", path.magenta())); @@ -183,7 +183,7 @@ impl SpriteLoader { #[cfg(feature = "debug")] sprite_data: cloned_sprite_data, }); - let _ = self.cache.insert(path.to_string(), sprite.clone()); + let _ = self.cache.lock().unwrap().insert(path.to_string(), sprite.clone()); #[cfg(feature = "debug")] timer.stop(); @@ -191,10 +191,15 @@ impl SpriteLoader { Ok(sprite) } - pub fn get(&mut self, path: &str) -> Result, LoadError> { - match self.cache.get(path) { + pub fn get(&self, path: &str) -> Result, LoadError> { + let mut lock = self.cache.lock().unwrap(); + match lock.get(path) { Some(sprite) => Ok(sprite.clone()), - None => self.load(path), + None => { + // We need to drop to avoid a deadlock here. + drop(lock); + self.load(path) + } } } } diff --git a/korangar/src/loaders/texture/mod.rs b/korangar/src/loaders/texture/mod.rs index 86b9d95e..6dc38412 100644 --- a/korangar/src/loaders/texture/mod.rs +++ b/korangar/src/loaders/texture/mod.rs @@ -346,8 +346,8 @@ impl TextureLoader { } pub fn get(&self, path: &str, image_type: ImageType) -> Result, LoadError> { - let mut lock = self.cache.lock(); - match lock.as_mut().unwrap().get(&(path.into(), image_type)) { + let mut lock = self.cache.lock().unwrap(); + match lock.get(&(path.into(), image_type)) { Some(texture) => Ok(texture.clone()), None => { // We need to drop to avoid a deadlock here. diff --git a/korangar/src/main.rs b/korangar/src/main.rs index 5766dde7..4c277236 100644 --- a/korangar/src/main.rs +++ b/korangar/src/main.rs @@ -163,15 +163,15 @@ struct Client { #[cfg(not(feature = "debug"))] networking_system: NetworkingSystem, - model_loader: ModelLoader, + model_loader: Arc, texture_loader: Arc, font_loader: Rc>, - map_loader: MapLoader, - sprite_loader: SpriteLoader, - script_loader: ScriptLoader, - action_loader: ActionLoader, - effect_loader: EffectLoader, - animation_loader: AnimationLoader, + map_loader: Arc, + sprite_loader: Arc, + script_loader: Arc, + action_loader: Arc, + effect_loader: Arc, + animation_loader: Arc, interface_renderer: InterfaceRenderer, bottom_interface_renderer: GameInterfaceRenderer, @@ -376,16 +376,21 @@ impl Client { time_phase!("create resource managers", { std::fs::create_dir_all("client/themes").unwrap(); - let mut model_loader = ModelLoader::new(game_file_loader.clone()); + let model_loader = Arc::new(ModelLoader::new(game_file_loader.clone())); let texture_loader = Arc::new(TextureLoader::new(device.clone(), queue.clone(), game_file_loader.clone())); let font_loader = Rc::new(RefCell::new(FontLoader::new(&game_file_loader, &texture_loader))); - let mut map_loader = MapLoader::new(device.clone(), queue.clone(), game_file_loader.clone(), audio_engine.clone()); - let mut sprite_loader = SpriteLoader::new(device.clone(), queue.clone(), game_file_loader.clone()); - let mut action_loader = ActionLoader::new(game_file_loader.clone(), audio_engine.clone()); - let effect_loader = EffectLoader::new(game_file_loader.clone()); - let animation_loader = AnimationLoader::new(); - - let script_loader = ScriptLoader::new(&game_file_loader).unwrap_or_else(|_| { + let map_loader = Arc::new(MapLoader::new( + device.clone(), + queue.clone(), + game_file_loader.clone(), + audio_engine.clone(), + )); + let sprite_loader = Arc::new(SpriteLoader::new(device.clone(), queue.clone(), game_file_loader.clone())); + let action_loader = Arc::new(ActionLoader::new(game_file_loader.clone(), audio_engine.clone())); + let effect_loader = Arc::new(EffectLoader::new(game_file_loader.clone())); + let animation_loader = Arc::new(AnimationLoader::new()); + + let script_loader = Arc::new(ScriptLoader::new(&game_file_loader).unwrap_or_else(|_| { // The scrip loader not being created correctly means that the lua files were // not valid. It's possible that the archive was copied from a // different machine with a different architecture, so the one thing @@ -401,7 +406,7 @@ impl Client { game_file_loader.load_patched_lua_files(); ScriptLoader::new(&game_file_loader).unwrap() - }); + })); let interface_renderer = InterfaceRenderer::new( INITIAL_SCREEN_SIZE, @@ -457,7 +462,7 @@ impl Client { let application = InterfaceSettings::load_or_default(); let mut interface = Interface::new(INITIAL_SCREEN_SIZE); let mut focus_state = FocusState::default(); - let mouse_cursor = MouseCursor::new(&mut sprite_loader, &mut action_loader); + let mouse_cursor = MouseCursor::new(&sprite_loader, &action_loader); let dialog_system = DialogSystem::default(); let show_interface = true; }); @@ -557,7 +562,7 @@ impl Client { let map = map_loader .load( DEFAULT_MAP.to_string(), - &mut model_loader, + &model_loader, texture_loader.clone(), #[cfg(feature = "debug")] &tile_texture_mapping, @@ -857,7 +862,7 @@ impl Client { .map_loader .load( DEFAULT_MAP.to_string(), - &mut self.model_loader, + &self.model_loader, self.texture_loader.clone(), #[cfg(feature = "debug")] &self.tile_texture_mapping, @@ -946,9 +951,9 @@ impl Client { self.saved_player_name = character_information.name.clone(); let player = Player::new( - &mut self.sprite_loader, - &mut self.action_loader, - &mut self.animation_loader, + &self.sprite_loader, + &self.action_loader, + &self.animation_loader, &self.script_loader, saved_login_data.account_id, character_information, @@ -1005,12 +1010,11 @@ impl Client { .retain(|entity| entity.get_entity_id() != entity_appeared_data.entity_id); let npc = Npc::new( - &mut self.sprite_loader, - &mut self.action_loader, - &mut self.animation_loader, + &self.sprite_loader, + &self.action_loader, + &self.animation_loader, &self.script_loader, map, - &mut self.path_finder, entity_appeared_data, client_tick, ); @@ -1076,7 +1080,7 @@ impl Client { .map_loader .load( map_name, - &mut self.model_loader, + &self.model_loader, self.texture_loader.clone(), #[cfg(feature = "debug")] &self.tile_texture_mapping, @@ -1178,7 +1182,7 @@ impl Client { } NetworkEvent::SkillTree(skill_information) => { self.player_skill_tree - .fill(&mut self.sprite_loader, &mut self.action_loader, skill_information, client_tick); + .fill(&self.sprite_loader, &self.action_loader, skill_information, client_tick); } NetworkEvent::UpdateEquippedPosition { index, equipped_position } => { self.player_inventory.update_equipped_position(index, equipped_position); @@ -1196,9 +1200,9 @@ impl Client { entity.set_job(job_id as usize); entity.reload_sprite( - &mut self.sprite_loader, - &mut self.action_loader, - &mut self.animation_loader, + &self.sprite_loader, + &self.action_loader, + &self.animation_loader, &self.script_loader, ); } @@ -1211,9 +1215,9 @@ impl Client { entity.set_hair(hair_id as usize); entity.reload_sprite( - &mut self.sprite_loader, - &mut self.action_loader, - &mut self.animation_loader, + &self.sprite_loader, + &self.action_loader, + &self.animation_loader, &self.script_loader, ); } diff --git a/korangar/src/world/entity/mod.rs b/korangar/src/world/entity/mod.rs index 486ef14e..ebf73f9b 100644 --- a/korangar/src/world/entity/mod.rs +++ b/korangar/src/world/entity/mod.rs @@ -343,9 +343,9 @@ fn get_entity_part_files( impl Common { pub fn new( - sprite_loader: &mut SpriteLoader, - action_loader: &mut ActionLoader, - animation_loader: &mut AnimationLoader, + sprite_loader: &SpriteLoader, + action_loader: &ActionLoader, + animation_loader: &AnimationLoader, script_loader: &ScriptLoader, entity_data: &EntityData, grid_position: Vector2, @@ -395,10 +395,10 @@ impl Common { pub fn reload_sprite( &mut self, - sprite_loader: &mut SpriteLoader, - action_loader: &mut ActionLoader, + sprite_loader: &SpriteLoader, + action_loader: &ActionLoader, script_loader: &ScriptLoader, - animation_loader: &mut AnimationLoader, + animation_loader: &AnimationLoader, ) { let entity_part_files = get_entity_part_files(script_loader, self.entity_type, self.job_id, self.sex, None); self.animation_data = animation_loader @@ -792,9 +792,9 @@ impl Player { /// "void". When a new map is loaded on map change, the server sends /// the correct position we need to position the player to. pub fn new( - sprite_loader: &mut SpriteLoader, - action_loader: &mut ActionLoader, - animation_loader: &mut AnimationLoader, + sprite_loader: &SpriteLoader, + action_loader: &ActionLoader, + animation_loader: &AnimationLoader, script_loader: &ScriptLoader, account_id: AccountId, character_information: CharacterInformation, @@ -917,10 +917,10 @@ impl Player { pub fn reload_sprite( &mut self, - sprite_loader: &mut SpriteLoader, - action_loader: &mut ActionLoader, + sprite_loader: &SpriteLoader, + action_loader: &ActionLoader, script_loader: &ScriptLoader, - animation_loader: &mut AnimationLoader, + animation_loader: &AnimationLoader, ) { let entity_part_files = get_entity_part_files( script_loader, @@ -947,12 +947,11 @@ pub struct Npc { impl Npc { pub fn new( - sprite_loader: &mut SpriteLoader, - action_loader: &mut ActionLoader, - animation_loader: &mut AnimationLoader, + sprite_loader: &SpriteLoader, + action_loader: &ActionLoader, + animation_loader: &AnimationLoader, script_loader: &ScriptLoader, map: &Map, - path_finder: &mut PathFinder, entity_data: EntityData, client_tick: ClientTick, ) -> Self { @@ -971,9 +970,10 @@ impl Npc { ); if let Some(destination) = entity_data.destination { + let mut path_finder = PathFinder::default(); let position_from = Vector2::new(entity_data.position.x, entity_data.position.y); let position_to = Vector2::new(destination.x, destination.y); - common.move_from_to(map, path_finder, position_from, position_to, client_tick); + common.move_from_to(map, &mut path_finder, position_from, position_to, client_tick); } Self { common } @@ -1072,9 +1072,9 @@ impl Entity { pub fn reload_sprite( &mut self, - sprite_loader: &mut SpriteLoader, - action_loader: &mut ActionLoader, - animation_loader: &mut AnimationLoader, + sprite_loader: &SpriteLoader, + action_loader: &ActionLoader, + animation_loader: &AnimationLoader, script_loader: &ScriptLoader, ) { match self {