From a0157ac5f524341c59d6aa6b7e9443857105877d Mon Sep 17 00:00:00 2001 From: Vincent Foulon Date: Tue, 6 Apr 2021 19:18:40 +0200 Subject: [PATCH] use bitflags instead of HashSet --- Cargo.lock | 1 + common/Cargo.toml | 1 + common/src/comp/behavior.rs | 74 +++++++++------------------- common/src/comp/mod.rs | 2 +- common/src/lib.rs | 2 + common/src/states/basic_summon.rs | 4 +- server/src/cmd.rs | 4 +- server/src/events/entity_creation.rs | 7 +-- server/src/events/invite.rs | 8 ++- server/src/events/trade.rs | 4 +- server/src/rtsim/tick.rs | 9 ++-- server/src/sys/agent.rs | 52 +++++++++---------- server/src/sys/terrain.rs | 12 +++-- world/src/index.rs | 20 +++----- 14 files changed, 85 insertions(+), 115 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aea01c2083..9161f94c80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5377,6 +5377,7 @@ dependencies = [ "approx 0.4.0", "arraygen", "assets_manager", + "bitflags", "criterion", "crossbeam-channel", "crossbeam-utils 0.8.3", diff --git a/common/Cargo.toml b/common/Cargo.toml index bca6817839..7e770dd2f7 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -24,6 +24,7 @@ serde = { version = "1.0.110", features = ["derive", "rc"] } approx = "0.4.0" arraygen = "0.1.13" crossbeam-utils = "0.8.1" +bitflags = "1.2" crossbeam-channel = "0.5" enum-iterator = "0.6" lazy_static = "1.4.0" diff --git a/common/src/comp/behavior.rs b/common/src/comp/behavior.rs index 3b2e0bdf0b..305c9aba3b 100644 --- a/common/src/comp/behavior.rs +++ b/common/src/comp/behavior.rs @@ -1,72 +1,44 @@ use specs::Component; use specs_idvs::IdvStorage; -use std::{collections::HashSet, mem}; use crate::trade::SiteId; +bitflags! { + #[derive(Default)] + pub struct BehaviorFlag: u8 { + const CAN_SPEAK = 0b00000001; + const CAN_TRADE = 0b00000010; + const IS_TRADING = 0b00000100; + const IS_TRADING_ISSUER = 0b00001000; + } +} + /// # Behavior Component /// This component allow an Entity to register one or more behavior tags. /// These tags act as flags of what an Entity can do, or what it is doing. /// Behaviors Tags can be added and removed as the Entity lives, to update its /// state when needed -#[derive(Default, Clone, Debug)] +#[derive(Default, Copy, Clone, Debug)] pub struct Behavior { - tags: HashSet, + pub flags: BehaviorFlag, + pub trade_site: Option, } -/// Versatile tags attached to behaviors -#[derive(Hash, Eq, PartialEq, Clone, Debug)] -pub enum BehaviorTag { - /// The entity is allowed to speak - CanSpeak, - /// The entity is able to trade - CanTrade(Option), - - /// The entity is currently trading - IsTrading, - /// The entity has issued a trade - IsTradingIssuer, +impl From for Behavior { + fn from(flags: BehaviorFlag) -> Self { + Behavior { + flags, + trade_site: None, + } + } } impl Behavior { - pub fn new(behavior_tags: &[BehaviorTag]) -> Self { - let mut behavior = Self::default(); - for tag in behavior_tags.iter() { - behavior.add_tag(tag.clone()) - } - behavior - } + pub fn set(&mut self, flags: BehaviorFlag) { self.flags.set(flags, true) } - /// Apply a tag to the Behavior - pub fn add_tag(&mut self, tag: BehaviorTag) { - if !self.has_tag(&tag) { - self.tags.insert(tag); - } - } + pub fn unset(&mut self, flags: BehaviorFlag) { self.flags.set(flags, false) } - /// Revoke a tag to the Behavior - pub fn remove_tag(&mut self, tag: BehaviorTag) { - if self.has_tag(&tag) { - let tag = self.get_tag(&tag).cloned(); - if let Some(tag) = tag { - self.tags.remove(&tag); - } - } - } - - /// Check if the Behavior possess a specific tag - pub fn has_tag(&self, tag: &BehaviorTag) -> bool { - self.tags - .iter() - .any(|behavior_tag| mem::discriminant(behavior_tag) == mem::discriminant(tag)) - } - - /// Get a specific tag by variant - pub fn get_tag(&self, tag: &BehaviorTag) -> Option<&BehaviorTag> { - self.tags - .iter() - .find(|behavior_tag| mem::discriminant(*behavior_tag) == mem::discriminant(tag)) - } + pub fn has(&self, flags: BehaviorFlag) -> bool { self.flags.contains(flags) } } impl Component for Behavior { diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 1bdb3b63aa..b36bae11ff 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -49,7 +49,7 @@ pub use self::{ agent::{Agent, Alignment}, aura::{Aura, AuraChange, AuraKind, Auras}, beam::{Beam, BeamSegment}, - behavior::{Behavior, BehaviorTag}, + behavior::{Behavior, BehaviorFlag}, body::{ biped_large, biped_small, bird_medium, bird_small, dragon, fish_medium, fish_small, golem, humanoid, object, quadruped_low, quadruped_medium, quadruped_small, ship, theropod, diff --git a/common/src/lib.rs b/common/src/lib.rs index c78e861453..a37812d95e 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -18,6 +18,8 @@ type_alias_impl_trait )] +#[macro_use] extern crate bitflags; + /// Re-exported crates pub use uuid; diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 392b2a2242..95bf0d5c3d 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -2,7 +2,7 @@ use crate::{ comp::{ self, inventory::loadout_builder::{LoadoutBuilder, LoadoutConfig}, - Behavior, BehaviorTag, CharacterState, StateUpdate, + Behavior, BehaviorFlag, CharacterState, StateUpdate, }, event::{LocalEvent, ServerEvent}, outcome::Outcome, @@ -105,7 +105,7 @@ impl CharacterBehavior for Data { loadout, body, agent: Some(comp::Agent::new(None, &body, true)), - behavior: Some(Behavior::new(&[BehaviorTag::CanSpeak])), + behavior: Some(Behavior::from(BehaviorFlag::CAN_SPEAK)), alignment: comp::Alignment::Owned(*data.uid), scale: self .static_data diff --git a/server/src/cmd.rs b/server/src/cmd.rs index d0a733525c..93c6c4ffff 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -15,7 +15,7 @@ use common::{ buff::{BuffCategory, BuffData, BuffKind, BuffSource}, inventory::item::MaterialStatManifest, invite::InviteKind, - BehaviorTag, ChatType, Inventory, Item, LightEmitter, WaypointArea, + BehaviorFlag, ChatType, Inventory, Item, LightEmitter, WaypointArea, }, effect::Effect, event::{EventBus, ServerEvent}, @@ -1075,7 +1075,7 @@ fn handle_spawn_airship( }); if let Some(pos) = destination { builder = builder.with(comp::Agent::with_destination(pos)); - builder = builder.with(comp::Behavior::new(&[BehaviorTag::CanSpeak])) + builder = builder.with(comp::Behavior::from(BehaviorFlag::CAN_SPEAK)) } builder.build(); diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index f6315d8ba0..652a045e5a 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -75,11 +75,8 @@ pub fn handle_create_npc( } else { entity }; - let entity = if let Some(behavior) = behavior { - entity.with(behavior) - } else { - entity.with(Behavior::default()) - }; + + let entity = entity.with(behavior.unwrap_or_default()); let entity = if let Some(drop_item) = drop_item { entity.with(ItemDrop(drop_item)) diff --git a/server/src/events/invite.rs b/server/src/events/invite.rs index caac3d3adc..fd8e909c6e 100644 --- a/server/src/events/invite.rs +++ b/server/src/events/invite.rs @@ -226,8 +226,12 @@ pub fn handle_invite_accept(server: &mut Server, entity: specs::Entity) { } let pricing = behaviors .get(inviter) - .and_then(|b| index.get_site_prices(b)) - .or_else(|| behaviors.get(entity).and_then(|b| index.get_site_prices(b))); + .and_then(|b| index.get_site_prices(b.trade_site)) + .or_else(|| { + behaviors + .get(entity) + .and_then(|b| index.get_site_prices(b.trade_site)) + }); clients.get(inviter).map(|c| { c.send(ServerGeneral::UpdatePendingTrade( id, diff --git a/server/src/events/trade.rs b/server/src/events/trade.rs index ade0e6e3e6..c5371481fa 100644 --- a/server/src/events/trade.rs +++ b/server/src/events/trade.rs @@ -35,7 +35,7 @@ fn notify_agent_prices( event: AgentEvent, ) { if let (Some(agent), Some(behavior)) = (agents.get_mut(entity), behaviors.get(entity)) { - let prices = index.get_site_prices(behavior); + let prices = index.get_site_prices(behavior.trade_site); if let AgentEvent::UpdatePendingTrade(boxval) = event { // Box<(tid, pend, _, inventories)>) = event { agent @@ -123,7 +123,7 @@ pub fn handle_process_trade_action( prices = prices.or_else(|| { behaviors .get(e) - .and_then(|b| server.index.get_site_prices(b)) + .and_then(|b| server.index.get_site_prices(b.trade_site)) }); } } diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index 3c8895e2fa..ad922c10fe 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -2,7 +2,7 @@ use super::*; use common::{ - comp::{self, inventory::loadout_builder::LoadoutBuilder, Behavior, BehaviorTag}, + comp::{self, inventory::loadout_builder::LoadoutBuilder, Behavior, BehaviorFlag}, event::{EventBus, ServerEvent}, resources::{DeltaTime, Time}, terrain::TerrainGrid, @@ -104,11 +104,8 @@ impl<'a> System<'a> for Sys { + Vec3::new(0.5, 0.5, body.flying_height()); let pos = comp::Pos(spawn_pos); let agent = Some(comp::Agent::new(None, &body, false)); - let behavior = if matches!(body, comp::Body::Humanoid(_)) { - Some(Behavior::new(&[BehaviorTag::CanSpeak])) - } else { - None - }; + let behavior = matches!(body, comp::Body::Humanoid(_)) + .then(|| Behavior::from(BehaviorFlag::CAN_SPEAK)); let rtsim_entity = Some(RtSimEntity(id)); let event = match body { comp::Body::Ship(ship) => ServerEvent::CreateShip { diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 0aa03c3b87..1a40e344f3 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -14,9 +14,9 @@ use common::{ ItemDesc, ItemKind, }, skills::{AxeSkill, BowSkill, HammerSkill, Skill, StaffSkill, SwordSkill}, - Agent, Alignment, Behavior, BehaviorTag, Body, CharacterState, ControlAction, ControlEvent, - Controller, Energy, Health, InputKind, Inventory, LightEmitter, MountState, Ori, - PhysicsState, Pos, Scale, Stats, UnresolvedChatMsg, Vel, + Agent, Alignment, Behavior, BehaviorFlag, Body, CharacterState, ControlAction, + ControlEvent, Controller, Energy, Health, InputKind, Inventory, LightEmitter, MountState, + Ori, PhysicsState, Pos, Scale, Stats, UnresolvedChatMsg, Vel, }, event::{Emitter, EventBus, ServerEvent}, path::TraversalConfig, @@ -554,7 +554,7 @@ impl<'a> AgentData<'a> { } if agent.action_timer > 0.0 { if agent.action_timer - < (if self.behavior.has_tag(&BehaviorTag::IsTrading) { + < (if self.behavior.has(BehaviorFlag::IS_TRADING) { TRADE_INTERACTION_TIME } else { DEFAULT_INTERACTION_TIME @@ -591,7 +591,7 @@ impl<'a> AgentData<'a> { let dist_sqrd = self.pos.0.distance_squared(tgt_pos.0); // Should the agent flee? if 1.0 - agent.psyche.aggro > self.damage && self.flees { - if agent.action_timer == 0.0 && self.behavior.has_tag(&BehaviorTag::CanSpeak) { + if agent.action_timer == 0.0 && self.behavior.has(BehaviorFlag::CAN_SPEAK) { let msg = "npc.speech.villager_under_attack".to_string(); event_emitter .emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg))); @@ -619,7 +619,7 @@ impl<'a> AgentData<'a> { read_data.buffs.get(target), ) { agent.target = None; - if self.behavior.has_tag(&BehaviorTag::CanSpeak) { + if self.behavior.has(BehaviorFlag::CAN_SPEAK) { let msg = "npc.speech.villager_enemy_killed".to_string(); event_emitter .emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg))); @@ -882,7 +882,7 @@ impl<'a> AgentData<'a> { let msg = agent.inbox.pop_back(); match msg { Some(AgentEvent::Talk(by, subject)) => { - if self.behavior.has_tag(&BehaviorTag::CanSpeak) { + if self.behavior.has(BehaviorFlag::CAN_SPEAK) { if let Some(target) = read_data.uid_allocator.retrieve_entity_internal(by.id()) { agent.target = Some(Target { @@ -935,7 +935,7 @@ impl<'a> AgentData<'a> { event_emitter.emit(ServerEvent::Chat( UnresolvedChatMsg::npc(*self.uid, msg), )); - } else if self.behavior.has_tag(&BehaviorTag::CanTrade(None)) { + } else if self.behavior.has(BehaviorFlag::CAN_TRADE) { let msg = "npc.speech.merchant_advertisement".to_string(); event_emitter.emit(ServerEvent::Chat( UnresolvedChatMsg::npc(*self.uid, msg), @@ -948,8 +948,8 @@ impl<'a> AgentData<'a> { } }, Subject::Trade => { - if self.behavior.has_tag(&BehaviorTag::CanTrade(None)) { - if !self.behavior.has_tag(&BehaviorTag::IsTrading) { + if self.behavior.has(BehaviorFlag::CAN_TRADE) { + if !self.behavior.has(BehaviorFlag::IS_TRADING) { controller.events.push(ControlEvent::InitiateInvite( by, InviteKind::Trade, @@ -1097,8 +1097,8 @@ impl<'a> AgentData<'a> { } }, Some(AgentEvent::TradeInvite(with)) => { - if self.behavior.has_tag(&BehaviorTag::CanTrade(None)) { - if !self.behavior.has_tag(&BehaviorTag::IsTrading) { + if self.behavior.has(BehaviorFlag::CAN_TRADE) { + if !self.behavior.has(BehaviorFlag::IS_TRADING) { // stand still and looking towards the trading player controller.actions.push(ControlAction::Stand); controller.actions.push(ControlAction::Talk); @@ -1114,13 +1114,13 @@ impl<'a> AgentData<'a> { controller .events .push(ControlEvent::InviteResponse(InviteResponse::Accept)); - self.behavior.remove_tag(BehaviorTag::IsTradingIssuer); - self.behavior.add_tag(BehaviorTag::IsTrading); + self.behavior.unset(BehaviorFlag::IS_TRADING_ISSUER); + self.behavior.set(BehaviorFlag::IS_TRADING); } else { controller .events .push(ControlEvent::InviteResponse(InviteResponse::Decline)); - if self.behavior.has_tag(&BehaviorTag::CanSpeak) { + if self.behavior.has(BehaviorFlag::CAN_SPEAK) { event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc( *self.uid, "npc.speech.merchant_busy".to_string(), @@ -1132,7 +1132,7 @@ impl<'a> AgentData<'a> { controller .events .push(ControlEvent::InviteResponse(InviteResponse::Decline)); - if self.behavior.has_tag(&BehaviorTag::CanSpeak) { + if self.behavior.has(BehaviorFlag::CAN_SPEAK) { event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc( *self.uid, "npc.speech.villager_decline_trade".to_string(), @@ -1141,7 +1141,7 @@ impl<'a> AgentData<'a> { } }, Some(AgentEvent::TradeAccepted(with)) => { - if !self.behavior.has_tag(&BehaviorTag::IsTrading) { + if !self.behavior.has(BehaviorFlag::IS_TRADING) { if let Some(target) = read_data.uid_allocator.retrieve_entity_internal(with.id()) { @@ -1151,12 +1151,12 @@ impl<'a> AgentData<'a> { selected_at: read_data.time.0, }); } - self.behavior.add_tag(BehaviorTag::IsTrading); - self.behavior.add_tag(BehaviorTag::IsTradingIssuer); + self.behavior.set(BehaviorFlag::IS_TRADING); + self.behavior.set(BehaviorFlag::IS_TRADING_ISSUER); } }, Some(AgentEvent::FinishedTrade(result)) => { - if self.behavior.has_tag(&BehaviorTag::IsTrading) { + if self.behavior.has(BehaviorFlag::IS_TRADING) { match result { TradeResult::Completed => { event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc( @@ -1169,13 +1169,13 @@ impl<'a> AgentData<'a> { "npc.speech.merchant_trade_declined".to_string(), ))), } - self.behavior.remove_tag(BehaviorTag::IsTrading); + self.behavior.unset(BehaviorFlag::IS_TRADING); } }, Some(AgentEvent::UpdatePendingTrade(boxval)) => { let (tradeid, pending, prices, inventories) = *boxval; - if self.behavior.has_tag(&BehaviorTag::IsTrading) { - let who: usize = if self.behavior.has_tag(&BehaviorTag::IsTradingIssuer) { + if self.behavior.has(BehaviorFlag::IS_TRADING) { + let who: usize = if self.behavior.has(BehaviorFlag::IS_TRADING_ISSUER) { 0 } else { 1 @@ -1209,7 +1209,7 @@ impl<'a> AgentData<'a> { } if pending.phase != TradePhase::Mutate { // we got into the review phase but without balanced goods, decline - self.behavior.remove_tag(BehaviorTag::IsTrading); + self.behavior.unset(BehaviorFlag::IS_TRADING); event_emitter.emit(ServerEvent::ProcessTradeAction( *self.entity, tradeid, @@ -1220,7 +1220,7 @@ impl<'a> AgentData<'a> { } }, None => { - if self.behavior.has_tag(&BehaviorTag::CanSpeak) { + if self.behavior.has(BehaviorFlag::CAN_SPEAK) { // no new events, continue looking towards the last interacting player for some // time if let Some(Target { target, .. }) = &agent.target { @@ -1343,7 +1343,7 @@ impl<'a> AgentData<'a> { ( self.alignment.map_or(false, |alignment| { if matches!(alignment, Alignment::Npc) && e_inventory.equipped_items().filter(|item| item.tags().contains(&ItemTag::Cultist)).count() > 2 { - if self.behavior.has_tag(&BehaviorTag::CanSpeak) { + if self.behavior.has(BehaviorFlag::CAN_SPEAK) { if self.rtsim_entity.is_some() { agent.rtsim_controller.events.push( RtSimEvent::AddMemory(Memory { diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 9bee6fd9e8..0aa2cfbf85 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -3,7 +3,7 @@ use crate::{ }; use common::{ comp::{ - self, bird_medium, inventory::loadout_builder::LoadoutConfig, Alignment, BehaviorTag, Pos, + self, bird_medium, inventory::loadout_builder::LoadoutConfig, Alignment, BehaviorFlag, Pos, }, event::{EventBus, ServerEvent}, generation::get_npc_name, @@ -14,6 +14,7 @@ use common::{ use common_ecs::{Job, Origin, Phase, System}; use common_net::msg::ServerGeneral; use common_sys::state::TerrainChanges; +use comp::Behavior; use specs::{Join, Read, ReadStorage, Write, WriteExpect}; use std::sync::Arc; use vek::*; @@ -203,14 +204,15 @@ impl<'a> System<'a> for Sys { None }, behavior: if entity.has_agency { - let mut behavior_tags = vec![]; + let mut behavior = Behavior::default(); if can_speak { - behavior_tags.push(BehaviorTag::CanSpeak); + behavior.set(BehaviorFlag::CAN_SPEAK); } if trade_for_site.is_some() { - behavior_tags.push(BehaviorTag::CanTrade(trade_for_site)); + behavior.set(BehaviorFlag::CAN_TRADE); + behavior.trade_site = trade_for_site } - Some(comp::Behavior::new(&behavior_tags)) + Some(behavior) } else { None }, diff --git a/world/src/index.rs b/world/src/index.rs index 390a0d293e..71e38edb25 100644 --- a/world/src/index.rs +++ b/world/src/index.rs @@ -4,9 +4,8 @@ use crate::{ }; use common::{ assets::{AssetExt, AssetHandle}, - comp::{Behavior, BehaviorTag}, store::Store, - trade::SitePrices, + trade::{SiteId, SitePrices}, }; use core::ops::Deref; use noise::{Seedable, SuperSimplex}; @@ -72,17 +71,12 @@ impl Index { pub fn colors(&self) -> AssetHandle> { self.colors } - pub fn get_site_prices(&self, behavior: &Behavior) -> Option { - if let Some(BehaviorTag::CanTrade(site_id)) = behavior.get_tag(&BehaviorTag::CanTrade(None)) - { - site_id - .map(|i| self.sites.recreate_id(i)) - .flatten() - .map(|i| self.sites.get(i)) - .map(|s| s.economy.get_site_prices()) - } else { - None - } + pub fn get_site_prices(&self, site_id: Option) -> Option { + site_id + .map(|i| self.sites.recreate_id(i)) + .flatten() + .map(|i| self.sites.get(i)) + .map(|s| s.economy.get_site_prices()) } }