Made swapping equipped weapons a server event instead of being called in common to prevent server-client desyncs.

This commit is contained in:
Sam 2021-07-25 18:30:17 -05:00
parent b4f70dcf19
commit f164d6036e
8 changed files with 56 additions and 29 deletions

View File

@ -51,6 +51,7 @@ pub enum InventoryManip {
recipe: String,
craft_sprite: Option<Vec3<i32>>,
},
SwapEquippedWeapons,
}
impl From<InventoryAction> for InventoryManip {

View File

@ -87,7 +87,7 @@ pub struct JoinData<'a> {
pub inputs: &'a ControllerInputs,
pub health: Option<&'a Health>,
pub energy: &'a Energy,
pub inventory: &'a Inventory,
pub inventory: Option<&'a Inventory>,
pub body: &'a Body,
pub physics: &'a PhysicsState,
pub melee_attack: Option<&'a Melee>,
@ -119,7 +119,7 @@ pub struct JoinStruct<'a> {
pub mass: &'a Mass,
pub density: &'a mut Density,
pub energy: RestrictedMut<'a, Energy>,
pub inventory: RestrictedMut<'a, Inventory>,
pub inventory: Option<&'a Inventory>,
pub controller: &'a mut Controller,
pub health: Option<&'a Health>,
pub body: &'a Body,
@ -150,7 +150,7 @@ impl<'a> JoinData<'a> {
mass: j.mass,
density: j.density,
energy: j.energy.get_unchecked(),
inventory: j.inventory.get_unchecked(),
inventory: j.inventory,
controller: j.controller,
inputs: &j.controller.inputs,
health: j.health,

View File

@ -80,7 +80,10 @@ impl CharacterBehavior for Data {
update.character = CharacterState::GlideWield;
update.ori = update.ori.to_horizontal();
} else if data.physics.in_liquid().is_some()
|| data.inventory.equipped(EquipSlot::Glider).is_none()
|| data
.inventory
.and_then(|inv| inv.equipped(EquipSlot::Glider))
.is_none()
{
update.character = CharacterState::Idle;
update.ori = update.ori.to_horizontal();

View File

@ -31,7 +31,11 @@ impl CharacterBehavior for Data {
{
update.character = CharacterState::Idle;
}
if data.inventory.equipped(EquipSlot::Glider).is_none() {
if data
.inventory
.and_then(|inv| inv.equipped(EquipSlot::Glider))
.is_none()
{
update.character = CharacterState::Idle
};

View File

@ -205,7 +205,7 @@ fn use_item(data: &JoinData, update: &mut StateUpdate, state: &Data) {
// Check if the same item is in the slot
let item_is_same = data
.inventory
.get(state.static_data.inv_slot)
.and_then(|inv| inv.get(state.static_data.inv_slot))
.map_or(false, |item| {
item.item_definition_id() == state.static_data.item_definition_id
});

View File

@ -486,7 +486,7 @@ pub fn attempt_wield(data: &JoinData<'_>, update: &mut StateUpdate) {
// equip slot
let equip_time = |equip_slot| {
data.inventory
.equipped(equip_slot)
.and_then(|inv| inv.equipped(equip_slot))
.and_then(|item| match item.kind() {
ItemKind::Tool(tool) => Some((item, tool)),
_ => None,
@ -568,11 +568,11 @@ pub fn handle_climb(data: &JoinData<'_>, update: &mut StateUpdate) -> bool {
pub fn attempt_swap_equipped_weapons(data: &JoinData<'_>, update: &mut StateUpdate) {
if data
.inventory
.equipped(EquipSlot::InactiveMainhand)
.and_then(|inv| inv.equipped(EquipSlot::InactiveMainhand))
.is_some()
|| data
.inventory
.equipped(EquipSlot::InactiveOffhand)
.and_then(|inv| inv.equipped(EquipSlot::InactiveOffhand))
.is_some()
{
update.swap_equipped_weapons = true;
@ -592,7 +592,7 @@ pub fn handle_manipulate_loadout(
// the loadout will have effects that are desired to be non-instantaneous
if let Some((item_kind, item)) = data
.inventory
.get(inv_slot)
.and_then(|inv| inv.get(inv_slot))
.and_then(|item| Option::<ItemUseKind>::from(item.kind()).zip(Some(item)))
{
let (buildup_duration, use_duration, recover_duration) = item_kind.durations();
@ -628,7 +628,10 @@ pub fn handle_manipulate_loadout(
/// Checks that player can wield the glider and updates `CharacterState` if so
pub fn attempt_glide_wield(data: &JoinData<'_>, update: &mut StateUpdate) {
if data.inventory.equipped(EquipSlot::Glider).is_some()
if data
.inventory
.and_then(|inv| inv.equipped(EquipSlot::Glider))
.is_some()
&& !data
.physics
.in_liquid()
@ -681,7 +684,7 @@ fn handle_ability(data: &JoinData<'_>, update: &mut StateUpdate, input: InputKin
if let Some(equip_slot) = equip_slot {
if let Some(ability) = data
.inventory
.equipped(equip_slot)
.and_then(|inv| inv.equipped(equip_slot))
.map(|i| &i.item_config_expect().abilities)
.and_then(|abilities| match input {
InputKind::Primary => Some(abilities.primary.clone()),
@ -801,7 +804,11 @@ pub fn is_strafing(data: &JoinData<'_>, update: &StateUpdate) -> bool {
}
pub fn unwrap_tool_data<'a>(data: &'a JoinData, equip_slot: EquipSlot) -> Option<&'a Tool> {
if let Some(ItemKind::Tool(tool)) = data.inventory.equipped(equip_slot).map(|i| i.kind()) {
if let Some(ItemKind::Tool(tool)) = data
.inventory
.and_then(|inv| inv.equipped(equip_slot))
.map(|i| i.kind())
{
Some(tool)
} else {
None
@ -810,7 +817,11 @@ pub fn unwrap_tool_data<'a>(data: &'a JoinData, equip_slot: EquipSlot) -> Option
pub fn get_hands(data: &JoinData<'_>) -> (Option<Hands>, Option<Hands>) {
let hand = |slot| {
if let Some(ItemKind::Tool(tool)) = data.inventory.equipped(slot).map(|i| i.kind()) {
if let Some(ItemKind::Tool(tool)) = data
.inventory
.and_then(|inv| inv.equipped(slot))
.map(|i| i.kind())
{
Some(tool.hands)
} else {
None
@ -833,7 +844,7 @@ pub fn get_crit_data(data: &JoinData<'_>, ai: AbilityInfo) -> (f32, f32) {
HandInfo::TwoHanded | HandInfo::MainHand => EquipSlot::ActiveMainhand,
HandInfo::OffHand => EquipSlot::ActiveOffhand,
})
.and_then(|slot| data.inventory.equipped(slot))
.and_then(|slot| data.inventory.and_then(|inv| inv.equipped(slot)))
.and_then(|item| {
if let ItemKind::Tool(tool) = item.kind() {
Some(tool.base_crit_chance(data.msm, item.components()))
@ -843,7 +854,7 @@ pub fn get_crit_data(data: &JoinData<'_>, ai: AbilityInfo) -> (f32, f32) {
})
.unwrap_or(DEFAULT_CRIT_CHANCE);
let crit_mult = combat::compute_crit_mult(Some(data.inventory));
let crit_mult = combat::compute_crit_mult(data.inventory);
(crit_chance, crit_mult)
}
@ -855,7 +866,7 @@ pub fn get_buff_strength(data: &JoinData<'_>, ai: AbilityInfo) -> f32 {
HandInfo::TwoHanded | HandInfo::MainHand => EquipSlot::ActiveMainhand,
HandInfo::OffHand => EquipSlot::ActiveOffhand,
})
.and_then(|slot| data.inventory.equipped(slot))
.and_then(|slot| data.inventory.and_then(|inv| inv.equipped(slot)))
.and_then(|item| {
if let ItemKind::Tool(tool) = item.kind() {
Some(tool.base_buff_strength(data.msm, item.components()))

View File

@ -6,10 +6,10 @@ use specs::{
use common::{
comp::{
self, inventory::item::MaterialStatManifest, Beam, Body, CharacterState, Combo, Controller,
Density, Energy, Health, Inventory, Mass, Melee, Mounting, Ori, PhysicsState, Poise,
PoiseState, Pos, SkillSet, StateUpdate, Stats, Vel,
Density, Energy, Health, Inventory, InventoryManip, Mass, Melee, Mounting, Ori,
PhysicsState, Poise, PoiseState, Pos, SkillSet, StateUpdate, Stats, Vel,
},
event::{EventBus, LocalEvent, ServerEvent},
event::{Emitter, EventBus, LocalEvent, ServerEvent},
outcome::Outcome,
resources::DeltaTime,
states::{
@ -22,7 +22,11 @@ use common::{
use common_ecs::{Job, Origin, Phase, System};
use std::time::Duration;
fn incorporate_update(join: &mut JoinStruct, mut state_update: StateUpdate) {
fn incorporate_update(
join: &mut JoinStruct,
mut state_update: StateUpdate,
server_emitter: &mut Emitter<ServerEvent>,
) {
// TODO: if checking equality is expensive use optional field in StateUpdate
if join.char_state.get_unchecked() != &state_update.character {
*join.char_state.get_mut_unchecked() = state_update.character
@ -42,9 +46,10 @@ fn incorporate_update(join: &mut JoinStruct, mut state_update: StateUpdate) {
join.controller.queued_inputs.remove(&input);
}
if state_update.swap_equipped_weapons {
let mut inventory = join.inventory.get_mut_unchecked();
let inventory = &mut *inventory;
inventory.swap_equipped_weapons();
server_emitter.emit(ServerEvent::InventoryManip(
join.entity,
InventoryManip::SwapEquippedWeapons,
));
}
}
@ -69,6 +74,7 @@ pub struct ReadData<'a> {
combos: ReadStorage<'a, Combo>,
alignments: ReadStorage<'a, comp::Alignment>,
terrain: ReadExpect<'a, TerrainGrid>,
inventories: ReadStorage<'a, Inventory>,
}
/// ## Character Behavior System
@ -87,7 +93,6 @@ impl<'a> System<'a> for Sys {
WriteStorage<'a, Ori>,
WriteStorage<'a, Density>,
WriteStorage<'a, Energy>,
WriteStorage<'a, Inventory>,
WriteStorage<'a, Controller>,
WriteStorage<'a, Poise>,
Write<'a, Vec<Outcome>>,
@ -107,7 +112,6 @@ impl<'a> System<'a> for Sys {
mut orientations,
mut densities,
mut energies,
mut inventories,
mut controllers,
mut poises,
mut outcomes,
@ -143,7 +147,7 @@ impl<'a> System<'a> for Sys {
&read_data.masses,
&mut densities,
&mut energies.restrict_mut(),
&mut inventories.restrict_mut(),
read_data.inventories.maybe(),
&mut controllers,
read_data.healths.maybe(),
&read_data.bodies,
@ -346,7 +350,7 @@ impl<'a> System<'a> for Sys {
};
local_emitter.append(&mut state_update.local_events);
server_emitter.append(&mut state_update.server_events);
incorporate_update(&mut join_struct, state_update);
incorporate_update(&mut join_struct, state_update, &mut server_emitter);
}
// Mounted occurs after control actions have been handled
@ -402,7 +406,7 @@ impl<'a> System<'a> for Sys {
local_emitter.append(&mut state_update.local_events);
server_emitter.append(&mut state_update.server_events);
incorporate_update(&mut join_struct, state_update);
incorporate_update(&mut join_struct, state_update, &mut server_emitter);
}
}
}

View File

@ -659,6 +659,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
inventory.sort();
drop(inventories);
},
comp::InventoryManip::SwapEquippedWeapons => {
inventory.swap_equipped_weapons();
drop(inventories);
},
}
// Drop items, Debug items should simply disappear when dropped