diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 46ca1a9fe4..77b6fb6175 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1,7 +1,7 @@ use crate::{ assets::{self, Asset}, comp::{ - item::{armor::Protection, Item, ItemKind}, + item::{armor::Protection, tool::AbilityMap, Item, ItemKind}, projectile::ProjectileConstructor, Body, CharacterState, EnergySource, Gravity, LightEmitter, StateUpdate, }, @@ -325,10 +325,10 @@ pub struct ItemConfig { pub dodge_ability: Option, } -impl From for ItemConfig { - fn from(item: Item) -> Self { +impl From<(Item, &AbilityMap)> for ItemConfig { + fn from((item, map): (Item, &AbilityMap)) -> Self { if let ItemKind::Tool(tool) = &item.kind() { - let abilities = tool.get_abilities().clone(); + let abilities = tool.get_abilities(map).clone(); return ItemConfig { item, diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 6aa649971b..7933596660 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -2,7 +2,7 @@ pub mod armor; pub mod tool; // Reexports -pub use tool::{Hands, Tool, ToolKind, UniqueKind}; +pub use tool::{AbilitySet, Hands, Tool, ToolKind, UniqueKind}; use crate::{ assets::{self, Asset, Error}, diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 351802fbe9..bc19b56ea7 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -95,7 +95,7 @@ impl Tool { Duration::from_millis(millis).div_f32(self.base_speed()) } - pub fn get_abilities(&self) -> AbilitySet { + pub fn get_abilities(&self, map: &AbilityMap) -> AbilitySet { let base_abilities = match AbilityMap::load("common.abilities.weapon_ability_manifest") { Ok(map) => map.0.get(&self.kind).map(|a| a.clone()).unwrap_or_default(), Err(err) => { diff --git a/common/src/comp/inventory/slot.rs b/common/src/comp/inventory/slot.rs index b199c7417e..2298623184 100644 --- a/common/src/comp/inventory/slot.rs +++ b/common/src/comp/inventory/slot.rs @@ -1,6 +1,6 @@ use crate::{ comp, - comp::{item, item::armor, ItemConfig}, + comp::{item::{self, armor, tool::AbilityMap}, ItemConfig}, }; use comp::{Inventory, Loadout}; use serde::{Deserialize, Serialize}; @@ -90,6 +90,7 @@ fn loadout_replace( equip_slot: EquipSlot, item: Option, loadout: &mut Loadout, + map: &AbilityMap, ) -> Option { use std::mem::replace; match equip_slot { @@ -107,10 +108,10 @@ fn loadout_replace( EquipSlot::Lantern => replace(&mut loadout.lantern, item), EquipSlot::Glider => replace(&mut loadout.glider, item), EquipSlot::Mainhand => { - replace(&mut loadout.active_item, item.map(ItemConfig::from)).map(|i| i.item) + replace(&mut loadout.active_item, item.map(|item| ItemConfig::from((item, map)))).map(|i| i.item) }, EquipSlot::Offhand => { - replace(&mut loadout.second_item, item.map(ItemConfig::from)).map(|i| i.item) + replace(&mut loadout.second_item, item.map(|item| ItemConfig::from((item, map)))).map(|i| i.item) }, } } @@ -122,8 +123,9 @@ fn loadout_insert( equip_slot: EquipSlot, item: item::Item, loadout: &mut Loadout, + map: &AbilityMap, ) -> Option { - loadout_replace(equip_slot, Some(item), loadout) + loadout_replace(equip_slot, Some(item), loadout, map) } /// Remove an item from a loadout. @@ -151,8 +153,8 @@ fn loadout_insert( /// loadout_remove(slot, &mut loadout); /// assert_eq!(None, loadout.active_item); /// ``` -pub fn loadout_remove(equip_slot: EquipSlot, loadout: &mut Loadout) -> Option { - loadout_replace(equip_slot, None, loadout) +pub fn loadout_remove(equip_slot: EquipSlot, loadout: &mut Loadout, map: &AbilityMap) -> Option { + loadout_replace(equip_slot, None, loadout, map) } /// Swap item in an inventory slot with one in a loadout slot. @@ -161,6 +163,7 @@ fn swap_inventory_loadout( equip_slot: EquipSlot, inventory: &mut Inventory, loadout: &mut Loadout, + map: &AbilityMap, ) { // Check if loadout slot can hold item if inventory @@ -168,7 +171,7 @@ fn swap_inventory_loadout( .map_or(true, |item| equip_slot.can_hold(&item.kind())) { // Take item from loadout - let from_equip = loadout_remove(equip_slot, loadout); + let from_equip = loadout_remove(equip_slot, loadout, map); // Swap with item in the inventory let from_inv = if let Some(item) = from_equip { // If this fails and we get item back as an err it will just be put back in the @@ -179,14 +182,14 @@ fn swap_inventory_loadout( }; // Put item from the inventory in loadout if let Some(item) = from_inv { - loadout_insert(equip_slot, item, loadout).unwrap_none(); // Can never fail + loadout_insert(equip_slot, item, loadout, map).unwrap_none(); // Can never fail } } } /// Swap items in loadout. Does nothing if items are not compatible with their /// new slots. -fn swap_loadout(slot_a: EquipSlot, slot_b: EquipSlot, loadout: &mut Loadout) { +fn swap_loadout(slot_a: EquipSlot, slot_b: EquipSlot, loadout: &mut Loadout, map: &AbilityMap) { // Ensure that the slots are not the same if slot_a == slot_b { warn!("Tried to swap equip slot with itself"); @@ -194,19 +197,19 @@ fn swap_loadout(slot_a: EquipSlot, slot_b: EquipSlot, loadout: &mut Loadout) { } // Get items from the slots - let item_a = loadout_remove(slot_a, loadout); - let item_b = loadout_remove(slot_b, loadout); + let item_a = loadout_remove(slot_a, loadout, map); + let item_b = loadout_remove(slot_b, loadout, map); // Check if items can go in the other slots if item_a.as_ref().map_or(true, |i| slot_b.can_hold(&i.kind())) && item_b.as_ref().map_or(true, |i| slot_a.can_hold(&i.kind())) { // Swap - loadout_replace(slot_b, item_a, loadout).unwrap_none(); - loadout_replace(slot_a, item_b, loadout).unwrap_none(); + loadout_replace(slot_b, item_a, loadout, map).unwrap_none(); + loadout_replace(slot_a, item_b, loadout, map).unwrap_none(); } else { // Otherwise put the items back - loadout_replace(slot_a, item_a, loadout).unwrap_none(); - loadout_replace(slot_b, item_b, loadout).unwrap_none(); + loadout_replace(slot_a, item_a, loadout, map).unwrap_none(); + loadout_replace(slot_b, item_b, loadout, map).unwrap_none(); } } @@ -219,6 +222,7 @@ pub fn swap( slot_b: Slot, inventory: Option<&mut Inventory>, loadout: Option<&mut Loadout>, + map: &AbilityMap, ) { match (slot_a, slot_b) { (Slot::Inventory(slot_a), Slot::Inventory(slot_b)) => { @@ -227,12 +231,12 @@ pub fn swap( (Slot::Inventory(inv_slot), Slot::Equip(equip_slot)) | (Slot::Equip(equip_slot), Slot::Inventory(inv_slot)) => { if let Some((inventory, loadout)) = loadout.and_then(|l| inventory.map(|i| (i, l))) { - swap_inventory_loadout(inv_slot, equip_slot, inventory, loadout); + swap_inventory_loadout(inv_slot, equip_slot, inventory, loadout, map); } }, (Slot::Equip(slot_a), Slot::Equip(slot_b)) => { - loadout.map(|l| swap_loadout(slot_a, slot_b, l)); + loadout.map(|l| swap_loadout(slot_a, slot_b, l, map)); }, } } @@ -261,7 +265,7 @@ pub fn swap( /// equip(0, &mut inv, &mut loadout); /// assert_eq!(Some(boots), loadout.foot); /// ``` -pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout) { +pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout, map: &AbilityMap) { use armor::Armor; use item::{armor::ArmorKind, ItemKind}; @@ -289,10 +293,10 @@ pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout) { // If item is going to mainhand, put mainhand in offhand and place offhand in // inventory if let EquipSlot::Mainhand = equip_slot { - swap_loadout(EquipSlot::Mainhand, EquipSlot::Offhand, loadout); + swap_loadout(EquipSlot::Mainhand, EquipSlot::Offhand, loadout, map); } - swap_inventory_loadout(slot, equip_slot, inventory, loadout); + swap_inventory_loadout(slot, equip_slot, inventory, loadout, map); } } @@ -322,10 +326,10 @@ pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout) { /// unequip(slot, &mut inv, &mut loadout); /// assert_eq!(None, loadout.active_item); /// ``` -pub fn unequip(slot: EquipSlot, inventory: &mut Inventory, loadout: &mut Loadout) { - loadout_remove(slot, loadout) // Remove item from loadout +pub fn unequip(slot: EquipSlot, inventory: &mut Inventory, loadout: &mut Loadout, map: &AbilityMap) { + loadout_remove(slot, loadout, map) // Remove item from loadout .and_then(|i| inventory.push(i)) // Insert into inventory - .and_then(|i| loadout_insert(slot, i, loadout)) // If that fails put back in loadout + .and_then(|i| loadout_insert(slot, i, loadout, map)) // If that fails put back in loadout .unwrap_none(); // Never fails } diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index 2811771d2e..b224dbb00e 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -1,6 +1,6 @@ use crate::comp::{ biped_large, golem, - item::{Item, ItemKind}, + item::{tool::AbilityMap, Item, ItemKind}, Alignment, Body, CharacterAbility, ItemConfig, Loadout, }; use rand::Rng; @@ -72,6 +72,7 @@ impl LoadoutBuilder { alignment: Alignment, mut main_tool: Option, is_giant: bool, + map: &AbilityMap, ) -> Self { match body { Body::Golem(golem) => match golem.species { @@ -145,21 +146,12 @@ impl LoadoutBuilder { }; let active_item = if let Some(ItemKind::Tool(_)) = main_tool.as_ref().map(|i| i.kind()) { - main_tool.map(ItemConfig::from) + main_tool.map(|item| ItemConfig::from((item, map))) } else { Some(ItemConfig { // We need the empty item so npcs can attack item: Item::new_from_asset_expect("common.items.weapons.empty.empty"), - ability1: Some(CharacterAbility::BasicMelee { - energy_cost: 0, - buildup_duration: 0, - swing_duration: 100, - recover_duration: 300, - base_damage: 40, - knockback: 0.0, - range: 3.5, - max_angle: 15.0, - }), + ability1: Some(CharacterAbility::default()), ability2: None, ability3: None, block_ability: None, @@ -370,7 +362,7 @@ impl LoadoutBuilder { /// abilities or their timings is desired, you should create and provide /// the item config directly to the [active_item](#method.active_item) /// method - pub fn default_item_config_from_item(item: Item) -> ItemConfig { ItemConfig::from(item) } + pub fn default_item_config_from_item(item: Item, map: &AbilityMap) -> ItemConfig { ItemConfig::from((item, map)) } /// Get an item's (weapon's) default /// [ItemConfig](../comp/struct.ItemConfig.html) @@ -378,8 +370,8 @@ impl LoadoutBuilder { /// the default abilities for that item via the /// [default_item_config_from_item](#method.default_item_config_from_item) /// function - pub fn default_item_config_from_str(item_ref: &str) -> ItemConfig { - Self::default_item_config_from_item(Item::new_from_asset_expect(item_ref)) + pub fn default_item_config_from_str(item_ref: &str, map: &AbilityMap) -> ItemConfig { + Self::default_item_config_from_item(Item::new_from_asset_expect(item_ref), map) } pub fn active_item(mut self, item: Option) -> Self { diff --git a/common/src/state.rs b/common/src/state.rs index 621ae69eb6..23789c14e7 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -1,4 +1,5 @@ use crate::{ + assets::Asset, comp, event::{EventBus, LocalEvent, ServerEvent}, metrics::{PhysicsMetrics, SysMetrics}, @@ -180,6 +181,7 @@ impl State { ecs.insert(BlockChange::default()); ecs.insert(TerrainChanges::default()); ecs.insert(EventBus::::default()); + ecs.insert(comp::item::tool::AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest")); // TODO: only register on the server ecs.insert(EventBus::::default()); ecs.insert(comp::group::GroupManager::default()); diff --git a/server/src/character_creator.rs b/server/src/character_creator.rs index ed285d047a..815bc2e852 100644 --- a/server/src/character_creator.rs +++ b/server/src/character_creator.rs @@ -1,6 +1,6 @@ use crate::persistence::character_loader::CharacterLoader; use common::{ - comp::{Body, Inventory, Stats}, + comp::{item::tool::AbilityMap, Body, Inventory, Stats}, loadout_builder::LoadoutBuilder, }; use specs::{Entity, ReadExpect}; @@ -12,13 +12,14 @@ pub fn create_character( character_tool: Option, body: Body, character_loader: &ReadExpect<'_, CharacterLoader>, + map: &AbilityMap, ) { let stats = Stats::new(character_alias.to_string(), body); let loadout = LoadoutBuilder::new() .defaults() .active_item(Some(LoadoutBuilder::default_item_config_from_str( - character_tool.as_deref().unwrap(), + character_tool.as_deref().unwrap(), map ))) .build(); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7ea9da3e64..bb72face28 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -9,7 +9,7 @@ use crate::{ use chrono::{NaiveTime, Timelike}; use common::{ cmd::{ChatCommand, CHAT_COMMANDS, CHAT_SHORTCUTS}, - comp::{self, ChatType, Item, LightEmitter, WaypointArea}, + comp::{self, item::tool::AbilityMap, ChatType, Item, LightEmitter, WaypointArea}, effect::Effect, event::{EventBus, ServerEvent}, msg::{DisconnectReason, Notification, PlayerListUpdate, ServerGeneral}, @@ -655,14 +655,17 @@ fn handle_spawn( let body = body(); + let map = server.state().ecs().fetch::(); + let loadout = LoadoutBuilder::build_loadout(body, alignment, None, false, &map).build(); + drop(map); + let mut entity_base = server .state .create_npc( pos, comp::Stats::new(get_npc_name(id).into(), body), comp::Health::new(body, 1), - LoadoutBuilder::build_loadout(body, alignment, None, false) - .build(), + loadout, body, ) .with(comp::Vel(vel)) diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 1789f94b22..f7e6c1228a 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -4,7 +4,7 @@ use crate::{ Server, }; use common::{ - comp::{self, item, Pos}, + comp::{self, item::{self, tool::AbilityMap}, Pos}, consts::MAX_MOUNT_RANGE, msg::ServerGeneral, sync::{Uid, WorldSyncExt}, @@ -103,8 +103,8 @@ pub fn handle_unmount(server: &mut Server, mounter: EcsEntity) { #[allow(clippy::nonminimal_bool)] // TODO: Pending review in #587 pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { - let state = &server.state; - let ecs = state.ecs(); + let ecs = &server.state.ecs(); + let map = ecs.fetch::(); if let (Some(possessor), Some(possesse)) = ( ecs.entity_from_uid(possessor_uid.into()), ecs.entity_from_uid(possesse_uid.into()), @@ -177,7 +177,7 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { let item = comp::Item::new_from_asset_expect("common.items.debug.possess"); if let item::ItemKind::Tool(_) = item.kind() { - let debug_item = comp::ItemConfig::from(item); + let debug_item = comp::ItemConfig::from((item, &*map)); std::mem::swap(&mut loadout.active_item, &mut loadout.second_item); loadout.active_item = Some(debug_item); } diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 2bfa117a08..1b22247ba9 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -1,7 +1,7 @@ use crate::{client::Client, Server, StateExt}; use common::{ comp::{ - self, item, + self, item::{self, tool::AbilityMap}, slot::{self, Slot}, Pos, }, @@ -203,7 +203,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv if let Some(lantern) = lantern_opt { swap_lantern(&mut state.ecs().write_storage(), entity, &lantern); } - slot::equip(slot, inventory, loadout); + let map = state.ecs().fetch::(); + slot::equip(slot, inventory, loadout, &map); Some(comp::InventoryUpdateEvent::Used) } else { None @@ -338,7 +339,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv if slot == slot::EquipSlot::Lantern { snuff_lantern(&mut state.ecs().write_storage(), entity); } - slot::unequip(slot, inventory, loadout); + let map = state.ecs().fetch::(); + slot::unequip(slot, inventory, loadout, &map); Some(comp::InventoryUpdateEvent::Used) } else { error!(?entity, "Entity doesn't have a loadout, can't unequip..."); @@ -364,12 +366,14 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv let mut loadouts = ecs.write_storage(); let inventory = inventories.get_mut(entity); let loadout = loadouts.get_mut(entity); + let map = state.ecs().fetch::(); - slot::swap(a, b, inventory, loadout); + slot::swap(a, b, inventory, loadout, &map); // :/ drop(loadouts); drop(inventories); + drop(map); state.write_component( entity, @@ -378,6 +382,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv }, comp::InventoryManip::Drop(slot) => { + let map = state.ecs().fetch::(); let item = match slot { Slot::Inventory(slot) => state .ecs() @@ -388,8 +393,9 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv .ecs() .write_storage() .get_mut(entity) - .and_then(|ldt| slot::loadout_remove(slot, ldt)), + .and_then(|ldt| slot::loadout_remove(slot, ldt, &map)), }; + drop(map); // FIXME: We should really require the drop and write to be atomic! if let (Some(mut item), Some(pos)) = diff --git a/server/src/lib.rs b/server/src/lib.rs index 79980fbc18..abc5de91ee 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -46,7 +46,7 @@ use crate::{ }; use common::{ cmd::ChatCommand, - comp::{self, ChatType}, + comp::{self, item::tool::AbilityMap, ChatType}, event::{EventBus, ServerEvent}, msg::{ ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerInit, ServerMsg, WorldMapMsg, @@ -159,9 +159,9 @@ impl Server { state .ecs_mut() .insert(CharacterUpdater::new(&persistence_db_dir)?); - state - .ecs_mut() - .insert(CharacterLoader::new(&persistence_db_dir)?); + + let character_loader = CharacterLoader::new(&persistence_db_dir, &*state.ecs().fetch::()); + state.ecs_mut().insert(character_loader); state.ecs_mut().insert(Vec::::new()); // System timers for performance monitoring diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index 66f29d4a03..b54dbd94c1 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -25,6 +25,7 @@ use crate::{ }; use common::{ character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER}, + comp::item::tool::AbilityMap, state::Time, }; use core::ops::Range; @@ -60,6 +61,7 @@ pub fn load_character_data( requesting_player_uuid: String, char_id: CharacterId, connection: VelorenTransaction, + map: &AbilityMap, ) -> CharacterDataResult { use schema::{body::dsl::*, character::dsl::*, item::dsl::*, stats::dsl::*}; @@ -102,7 +104,7 @@ pub fn load_character_data( convert_body_from_database(&char_body)?, convert_stats_from_database(&stats_data, character_data.alias), convert_inventory_from_database_items(&inventory_items)?, - convert_loadout_from_database_items(&loadout_items)?, + convert_loadout_from_database_items(&loadout_items, map)?, waypoint, )) } @@ -117,6 +119,7 @@ pub fn load_character_data( pub fn load_character_list( player_uuid_: &str, connection: VelorenTransaction, + map: &AbilityMap, ) -> CharacterListResult { use schema::{body::dsl::*, character::dsl::*, item::dsl::*, stats::dsl::*}; @@ -149,7 +152,7 @@ pub fn load_character_list( .filter(parent_container_item_id.eq(loadout_container_id)) .load::(&*connection)?; - let loadout = convert_loadout_from_database_items(&loadout_items)?; + let loadout = convert_loadout_from_database_items(&loadout_items, map)?; Ok(CharacterItem { character: char, @@ -166,6 +169,7 @@ pub fn create_character( character_alias: &str, persisted_components: PersistedComponents, connection: VelorenTransaction, + map: &AbilityMap, ) -> CharacterListResult { use schema::item::dsl::*; @@ -299,7 +303,7 @@ pub fn create_character( ))); } - load_character_list(uuid, connection) + load_character_list(uuid, connection, map) } /// Delete a character. Returns the updated character list. @@ -307,6 +311,7 @@ pub fn delete_character( requesting_player_uuid: &str, char_id: CharacterId, connection: VelorenTransaction, + map: &AbilityMap, ) -> CharacterListResult { use schema::{body::dsl::*, character::dsl::*, stats::dsl::*}; @@ -387,7 +392,7 @@ pub fn delete_character( ))); } - load_character_list(requesting_player_uuid, connection) + load_character_list(requesting_player_uuid, connection, map) } /// Before creating a character, we ensure that the limit on the number of diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs index 503b80be8b..0c4c0935ea 100644 --- a/server/src/persistence/character/conversions.rs +++ b/server/src/persistence/character/conversions.rs @@ -9,7 +9,7 @@ use crate::persistence::{ }; use common::{ character::CharacterId, - comp::{Body as CompBody, *}, + comp::{item::tool::AbilityMap, Body as CompBody, *}, loadout_builder, }; use core::{convert::TryFrom, num::NonZeroU64}; @@ -240,7 +240,7 @@ pub fn convert_inventory_from_database_items(database_items: &[Item]) -> Result< Ok(inventory) } -pub fn convert_loadout_from_database_items(database_items: &[Item]) -> Result { +pub fn convert_loadout_from_database_items(database_items: &[Item], map: &AbilityMap) -> Result { let mut loadout = loadout_builder::LoadoutBuilder::new(); for db_item in database_items.iter() { let item = common::comp::Item::new_from_asset(db_item.item_definition_id.as_str())?; @@ -251,8 +251,8 @@ pub fn convert_loadout_from_database_items(database_items: &[Item]) -> Result loadout = loadout.active_item(Some(ItemConfig::from(item))), - "second_item" => loadout = loadout.second_item(Some(ItemConfig::from(item))), + "active_item" => loadout = loadout.active_item(Some(ItemConfig::from((item, map)))), + "second_item" => loadout = loadout.second_item(Some(ItemConfig::from((item, map)))), "lantern" => loadout = loadout.lantern(Some(item)), "shoulder" => loadout = loadout.shoulder(Some(item)), "chest" => loadout = loadout.chest(Some(item)), diff --git a/server/src/persistence/character_loader.rs b/server/src/persistence/character_loader.rs index 049a0c47c1..2dd27f0d5f 100644 --- a/server/src/persistence/character_loader.rs +++ b/server/src/persistence/character_loader.rs @@ -3,7 +3,10 @@ use crate::persistence::{ error::Error, establish_connection, PersistedComponents, }; -use common::character::{CharacterId, CharacterItem}; +use common::{ + character::{CharacterId, CharacterItem}, + comp::item::tool::AbilityMap, +}; use crossbeam::{channel, channel::TryIter}; use std::path::Path; use tracing::error; @@ -65,12 +68,14 @@ pub struct CharacterLoader { } impl CharacterLoader { - pub fn new(db_dir: &Path) -> diesel::QueryResult { + pub fn new(db_dir: &Path, map: &AbilityMap) -> diesel::QueryResult { let (update_tx, internal_rx) = channel::unbounded::(); let (internal_tx, update_rx) = channel::unbounded::(); let mut conn = establish_connection(db_dir)?; + let map = map.clone(); + std::thread::spawn(move || { for request in internal_rx { let (entity, kind) = request; @@ -88,6 +93,7 @@ impl CharacterLoader { &character_alias, persisted_components, txn, + &map, ) })), CharacterLoaderRequestKind::DeleteCharacter { @@ -95,12 +101,12 @@ impl CharacterLoader { character_id, } => { CharacterLoaderResponseType::CharacterList(conn.transaction(|txn| { - delete_character(&player_uuid, character_id, txn) + delete_character(&player_uuid, character_id, txn, &map) })) }, CharacterLoaderRequestKind::LoadCharacterList { player_uuid } => { CharacterLoaderResponseType::CharacterList( - conn.transaction(|txn| load_character_list(&player_uuid, txn)), + conn.transaction(|txn| load_character_list(&player_uuid, txn, &map)), ) }, CharacterLoaderRequestKind::LoadCharacterData { @@ -108,7 +114,7 @@ impl CharacterLoader { character_id, } => { CharacterLoaderResponseType::CharacterData(Box::new(conn.transaction( - |txn| load_character_data(player_uuid, character_id, txn), + |txn| load_character_data(player_uuid, character_id, txn, &map), ))) }, }, diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs index 6dae2482fb..d9667b8f1f 100644 --- a/server/src/sys/msg/character_screen.rs +++ b/server/src/sys/msg/character_screen.rs @@ -4,7 +4,7 @@ use crate::{ persistence::character_loader::CharacterLoader, presence::Presence, EditableSettings, }; use common::{ - comp::{ChatType, Player, UnresolvedChatMsg}, + comp::{item::tool::AbilityMap, ChatType, Player, UnresolvedChatMsg}, event::{EventBus, ServerEvent}, msg::{ClientGeneral, ServerGeneral}, span, @@ -28,6 +28,7 @@ impl Sys { editable_settings: &ReadExpect<'_, EditableSettings>, alias_validator: &ReadExpect<'_, AliasValidator>, msg: ClientGeneral, + map: &AbilityMap, ) -> Result<(), crate::error::Error> { match msg { // Request spectator state @@ -101,6 +102,7 @@ impl Sys { tool, body, character_loader, + map, ); } }, @@ -134,6 +136,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Presence>, ReadExpect<'a, EditableSettings>, ReadExpect<'a, AliasValidator>, + ReadExpect<'a, AbilityMap>, ); fn run( @@ -149,6 +152,7 @@ impl<'a> System<'a> for Sys { presences, editable_settings, alias_validator, + map, ): Self::SystemData, ) { span!(_guard, "run", "msg::character_screen::Sys::run"); @@ -171,6 +175,7 @@ impl<'a> System<'a> for Sys { &editable_settings, &alias_validator, msg, + &map, ) }); } diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 39fdcb37c2..d0f4884dfe 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -1,7 +1,7 @@ use super::SysTimer; use crate::{chunk_generator::ChunkGenerator, client::Client, presence::Presence, Tick}; use common::{ - comp::{self, bird_medium, Alignment, Pos}, + comp::{self, bird_medium, item::tool::AbilityMap, Alignment, Pos}, event::{EventBus, ServerEvent}, generation::get_npc_name, msg::ServerGeneral, @@ -12,7 +12,7 @@ use common::{ LoadoutBuilder, }; use rand::Rng; -use specs::{Join, Read, ReadStorage, System, Write, WriteExpect}; +use specs::{Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect}; use std::sync::Arc; use vek::*; @@ -35,6 +35,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Pos>, ReadStorage<'a, Presence>, ReadStorage<'a, Client>, + ReadExpect<'a, AbilityMap>, ); fn run( @@ -49,6 +50,7 @@ impl<'a> System<'a> for Sys { positions, presences, clients, + map, ): Self::SystemData, ) { span!(_guard, "run", "terrain::Sys::run"); @@ -143,7 +145,7 @@ impl<'a> System<'a> for Sys { } let loadout = - LoadoutBuilder::build_loadout(body, alignment, main_tool, entity.is_giant) + LoadoutBuilder::build_loadout(body, alignment, main_tool, entity.is_giant, &map) .build(); let health = comp::Health::new(stats.body_type, stats.level.level()); diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 12a585f647..09fe7cd42d 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -39,7 +39,7 @@ use crate::{ settings::Settings, window::{Event, Window}, }; -use common::{assets::watch, clock::Clock}; +use common::{assets::watch, clock::Clock, comp::item::tool::AbilityMap}; /// A type used to store state that is shared between all play states. pub struct GlobalState { @@ -110,7 +110,7 @@ pub trait PlayState { fn enter(&mut self, global_state: &mut GlobalState, direction: Direction); /// Tick the play state - fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult; + fn tick(&mut self, global_state: &mut GlobalState, events: Vec, map: &AbilityMap) -> PlayStateResult; /// Get a descriptive name for this state type. fn name(&self) -> &'static str; diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index f87b32d35b..7afd1bdc81 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -192,5 +192,5 @@ fn main() { localization_watcher, }; - run::run(global_state, event_loop); + run::run(global_state, event_loop, map); } diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 7fc03783de..073d93daa0 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -10,7 +10,7 @@ use crate::{ Direction, GlobalState, PlayState, PlayStateResult, }; use client::{self, Client}; -use common::{assets::Asset, comp, span, state::DeltaTime}; +use common::{assets::Asset, comp::{self, item::tool::AbilityMap}, span, state::DeltaTime}; use specs::WorldExt; use std::{cell::RefCell, rc::Rc}; use tracing::error; @@ -62,7 +62,7 @@ impl PlayState for CharSelectionState { self.client.borrow_mut().load_character_list(); } - fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { + fn tick(&mut self, global_state: &mut GlobalState, events: Vec, map: &AbilityMap) -> PlayStateResult { span!(_guard, "tick", "::tick"); let (client_presence, client_registered) = { let client = self.client.borrow(); @@ -88,7 +88,7 @@ impl PlayState for CharSelectionState { // Maintain the UI. let events = self .char_selection_ui - .maintain(global_state, &mut self.client.borrow_mut()); + .maintain(global_state, &mut self.client.borrow_mut(), map); for event in events { match event { diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index 8fa52d72a0..682dde3055 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -24,7 +24,7 @@ use client::Client; use common::{ assets::Asset, character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER}, - comp::{self, humanoid}, + comp::{self, humanoid, item::tool::AbilityMap}, LoadoutBuilder, }; //ImageFrame, Tooltip, @@ -179,12 +179,12 @@ impl Mode { } } - pub fn create(name: String) -> Self { + pub fn create(name: String, map: &AbilityMap) -> Self { let tool = STARTER_SWORD; let loadout = LoadoutBuilder::new() .defaults() - .active_item(Some(LoadoutBuilder::default_item_config_from_str(tool))) + .active_item(Some(LoadoutBuilder::default_item_config_from_str(tool, map))) .build(); let loadout = Box::new(loadout); @@ -1172,7 +1172,7 @@ impl Controls { .into() } - fn update(&mut self, message: Message, events: &mut Vec, characters: &[CharacterItem]) { + fn update(&mut self, message: Message, events: &mut Vec, characters: &[CharacterItem], map: &AbilityMap) { match message { Message::Back => { if matches!(&self.mode, Mode::Create { .. }) { @@ -1206,7 +1206,7 @@ impl Controls { }, Message::NewCharacter => { if matches!(&self.mode, Mode::Select { .. }) { - self.mode = Mode::create(String::new()); + self.mode = Mode::create(String::new(), map); } }, Message::CreateCharacter => { @@ -1242,7 +1242,7 @@ impl Controls { Message::Tool(value) => { if let Mode::Create { tool, loadout, .. } = &mut self.mode { *tool = value; - loadout.active_item = Some(LoadoutBuilder::default_item_config_from_str(*tool)); + loadout.active_item = Some(LoadoutBuilder::default_item_config_from_str(*tool, map)); } }, Message::RandomizeCharacter => { @@ -1411,7 +1411,7 @@ impl CharSelectionUi { } // TODO: do we need whole client here or just character list? - pub fn maintain(&mut self, global_state: &mut GlobalState, client: &mut Client) -> Vec { + pub fn maintain(&mut self, global_state: &mut GlobalState, client: &mut Client, map: &AbilityMap) -> Vec { let mut events = Vec::new(); let (mut messages, _) = self.ui.maintain( @@ -1426,7 +1426,7 @@ impl CharSelectionUi { messages.into_iter().for_each(|message| { self.controls - .update(message, &mut events, &client.character_list.characters) + .update(message, &mut events, &client.character_list.characters, map) }); events diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index f3b57d1acc..64adf12985 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -12,7 +12,7 @@ use crate::{ Direction, GlobalState, PlayState, PlayStateResult, }; use client_init::{ClientInit, Error as InitError, Msg as InitMsg}; -use common::{assets::Asset, comp, span}; +use common::{assets::Asset, comp::{self, item::tool::AbilityMap}, span}; use tracing::error; use ui::{Event as MainMenuEvent, MainMenuUi}; @@ -48,7 +48,7 @@ impl PlayState for MainMenuState { } } - fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { + fn tick(&mut self, global_state: &mut GlobalState, events: Vec, map: &AbilityMap) -> PlayStateResult { span!(_guard, "tick", "::tick"); let mut localized_strings = crate::i18n::Localization::load_expect( &crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language), @@ -263,7 +263,7 @@ impl PlayState for MainMenuState { }, #[cfg(feature = "singleplayer")] MainMenuEvent::StartSingleplayer => { - let singleplayer = Singleplayer::new(None); // TODO: Make client and server use the same thread pool + let singleplayer = Singleplayer::new(None, map); // TODO: Make client and server use the same thread pool global_state.singleplayer = Some(singleplayer); }, diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index 1c75aced18..feedee7e52 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -4,11 +4,11 @@ use crate::{ window::{Event, EventLoop}, Direction, GlobalState, PlayState, PlayStateResult, }; -use common::{no_guard_span, span, util::GuardlessSpan}; +use common::{comp::item::tool::AbilityMap, no_guard_span, span, util::GuardlessSpan}; use std::{mem, time::Duration}; use tracing::debug; -pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { +pub fn run(mut global_state: GlobalState, event_loop: EventLoop, map: &AbilityMap) { // Set up the initial play state. let mut states: Vec> = vec![Box::new(MainMenuState::new(&mut global_state))]; states.last_mut().map(|current_state| { @@ -26,6 +26,8 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { let mut poll_span = None; let mut event_span = None; + let map = map.clone(); + event_loop.run(move |event, _, control_flow| { // Continuously run loop since we handle sleeping *control_flow = winit::event_loop::ControlFlow::Poll; @@ -53,7 +55,7 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { event_span.take(); poll_span.take(); if polled_twice { - handle_main_events_cleared(&mut states, control_flow, &mut global_state); + handle_main_events_cleared(&mut states, control_flow, &mut global_state, &map); } poll_span = Some(no_guard_span!("Poll Winit")); polled_twice = !polled_twice; @@ -82,6 +84,7 @@ fn handle_main_events_cleared( states: &mut Vec>, control_flow: &mut winit::event_loop::ControlFlow, global_state: &mut GlobalState, + map: &AbilityMap, ) { span!(guard, "Handle MainEventsCleared"); // Screenshot / Fullscreen toggle @@ -102,7 +105,7 @@ fn handle_main_events_cleared( let mut exit = true; while let Some(state_result) = states.last_mut().map(|last| { let events = global_state.window.fetch_events(); - last.tick(global_state, events) + last.tick(global_state, events, map) }) { // Implement state transfer logic. match state_result { diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 0a5f8e5503..a49f686d9c 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -15,7 +15,7 @@ use client::{self, Client}; use common::{ assets::Asset, comp, - comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel}, + comp::{item::tool::AbilityMap, ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel}, consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE}, event::EventBus, msg::PresenceKind, @@ -203,7 +203,7 @@ impl PlayState for SessionState { } } - fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { + fn tick(&mut self, global_state: &mut GlobalState, events: Vec, map: &AbilityMap) -> PlayStateResult { span!(_guard, "tick", "::tick"); // TODO: let mut client = self.client.borrow_mut(); // NOTE: Not strictly necessary, but useful for hotloading translation changes. diff --git a/voxygen/src/singleplayer.rs b/voxygen/src/singleplayer.rs index 0bc68f50d9..9b0704afa0 100644 --- a/voxygen/src/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -1,5 +1,5 @@ use client::Client; -use common::clock::Clock; +use common::{clock::Clock, comp::item::tool::AbilityMap}; use crossbeam::channel::{bounded, unbounded, Receiver, Sender, TryRecvError}; use server::{Error as ServerError, Event, Input, Server}; use std::{ @@ -31,7 +31,7 @@ pub struct Singleplayer { } impl Singleplayer { - pub fn new(client: Option<&Client>) -> Self { + pub fn new(client: Option<&Client>,map: &AbilityMap) -> Self { let (sender, receiver) = unbounded(); // Determine folder to save server data in