From 64324262c72da069a67b1922ac3ecf0e06918587 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 31 Mar 2023 23:20:52 +0100 Subject: [PATCH] Added /rtsim_purge command --- common/src/cmd.rs | 23 ++++-- common/src/rtsim.rs | 60 +++++++++----- common/src/states/behavior.rs | 4 +- common/systems/src/character_behavior.rs | 4 +- rtsim/src/data/mod.rs | 2 + rtsim/src/gen/mod.rs | 1 + server/src/cmd.rs | 39 ++++++++- server/src/lib.rs | 11 ++- server/src/rtsim2/mod.rs | 22 +++++- server/src/rtsim2/tick.rs | 4 +- server/src/sys/agent/behavior_tree.rs | 7 +- .../sys/agent/behavior_tree/interaction.rs | 79 +++++++++---------- 12 files changed, 171 insertions(+), 85 deletions(-) diff --git a/common/src/cmd.rs b/common/src/cmd.rs index fb2b8068dc..270b60377a 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -310,8 +310,9 @@ pub enum ServerChatCommand { Tell, Time, Tp, - TpNpc, - NpcInfo, + RtsimTp, + RtsimInfo, + RtsimPurge, RtsimChunk, Unban, Version, @@ -682,16 +683,25 @@ impl ServerChatCommand { "Teleport to another player", Some(Moderator), ), - ServerChatCommand::TpNpc => cmd( + ServerChatCommand::RtsimTp => cmd( vec![Integer("npc index", 0, Required)], "Teleport to an rtsim npc", Some(Moderator), ), - ServerChatCommand::NpcInfo => cmd( + ServerChatCommand::RtsimInfo => cmd( vec![Integer("npc index", 0, Required)], "Display information about an rtsim NPC", Some(Moderator), ), + ServerChatCommand::RtsimPurge => cmd( + vec![Boolean( + "whether purging of rtsim data should occur on next startup", + true.to_string(), + Required, + )], + "Purge rtsim data on next startup", + Some(Admin), + ), ServerChatCommand::RtsimChunk => cmd( vec![], "Display information about the current chunk from rtsim", @@ -824,8 +834,9 @@ impl ServerChatCommand { ServerChatCommand::Tell => "tell", ServerChatCommand::Time => "time", ServerChatCommand::Tp => "tp", - ServerChatCommand::TpNpc => "tp_npc", - ServerChatCommand::NpcInfo => "npc_info", + ServerChatCommand::RtsimTp => "rtsim_tp", + ServerChatCommand::RtsimInfo => "rtsim_info", + ServerChatCommand::RtsimPurge => "rtsim_purge", ServerChatCommand::RtsimChunk => "rtsim_chunk", ServerChatCommand::Unban => "unban", ServerChatCommand::Version => "version", diff --git a/common/src/rtsim.rs b/common/src/rtsim.rs index e50dc9b920..9fef179a51 100644 --- a/common/src/rtsim.rs +++ b/common/src/rtsim.rs @@ -3,7 +3,7 @@ // `Agent`). When possible, this should be moved to the `rtsim` // module in `server`. -use rand::{Rng, seq::IteratorRandom}; +use rand::{seq::IteratorRandom, Rng}; use serde::{Deserialize, Serialize}; use specs::Component; use strum::{EnumIter, IntoEnumIterator}; @@ -56,7 +56,6 @@ pub enum MemoryItem { Mood { state: MoodState }, } - #[derive(EnumIter, Clone, Copy)] pub enum PersonalityTrait { Open, @@ -88,7 +87,9 @@ pub struct Personality { fn distributed(min: u8, max: u8, rng: &mut impl Rng) -> u8 { let l = max - min; - min + rng.gen_range(0..=l / 3) + rng.gen_range(0..=l / 3 + l % 3 % 2) + rng.gen_range(0..=l / 3 + l % 3 / 2) + min + rng.gen_range(0..=l / 3) + + rng.gen_range(0..=l / 3 + l % 3 % 2) + + rng.gen_range(0..=l / 3 + l % 3 / 2) } impl Personality { @@ -96,13 +97,11 @@ impl Personality { pub const LITTLE_HIGH: u8 = Self::MID + (Self::MAX - Self::MIN) / 20; pub const LITTLE_LOW: u8 = Self::MID - (Self::MAX - Self::MIN) / 20; pub const LOW_THRESHOLD: u8 = (Self::MAX - Self::MIN) / 5 * 2 + Self::MIN; - const MIN: u8 = 0; - pub const MID: u8 = (Self::MAX - Self::MIN) / 2; const MAX: u8 = 255; + pub const MID: u8 = (Self::MAX - Self::MIN) / 2; + const MIN: u8 = 0; - fn distributed_value(rng: &mut impl Rng) -> u8 { - distributed(Self::MIN, Self::MAX, rng) - } + fn distributed_value(rng: &mut impl Rng) -> u8 { distributed(Self::MIN, Self::MAX, rng) } pub fn random(rng: &mut impl Rng) -> Self { Self { @@ -130,27 +129,43 @@ impl Personality { extraversion: Self::distributed_value(rng), neuroticism: Self::distributed_value(rng), agreeableness: Self::distributed_value(rng), - conscientiousness: distributed(Self::LOW_THRESHOLD, Self::MAX, rng), + conscientiousness: distributed(Self::LOW_THRESHOLD, Self::MAX, rng), } } pub fn is(&self, trait_: PersonalityTrait) -> bool { match trait_ { PersonalityTrait::Open => self.openness > Personality::HIGH_THRESHOLD, - PersonalityTrait::Adventurous => self.openness > Personality::HIGH_THRESHOLD && self.neuroticism < Personality::MID, + PersonalityTrait::Adventurous => { + self.openness > Personality::HIGH_THRESHOLD && self.neuroticism < Personality::MID + }, PersonalityTrait::Closed => self.openness < Personality::LOW_THRESHOLD, PersonalityTrait::Conscientious => self.conscientiousness > Personality::HIGH_THRESHOLD, PersonalityTrait::Busybody => self.agreeableness < Personality::LOW_THRESHOLD, - PersonalityTrait::Unconscientious => self.conscientiousness < Personality::LOW_THRESHOLD, + PersonalityTrait::Unconscientious => { + self.conscientiousness < Personality::LOW_THRESHOLD + }, PersonalityTrait::Extroverted => self.extraversion > Personality::HIGH_THRESHOLD, PersonalityTrait::Introverted => self.extraversion < Personality::LOW_THRESHOLD, PersonalityTrait::Agreeable => self.agreeableness > Personality::HIGH_THRESHOLD, - PersonalityTrait::Sociable => self.agreeableness > Personality::HIGH_THRESHOLD && self.extraversion > Personality::MID, + PersonalityTrait::Sociable => { + self.agreeableness > Personality::HIGH_THRESHOLD + && self.extraversion > Personality::MID + }, PersonalityTrait::Disagreeable => self.agreeableness < Personality::LOW_THRESHOLD, PersonalityTrait::Neurotic => self.neuroticism > Personality::HIGH_THRESHOLD, - PersonalityTrait::Seeker => self.neuroticism > Personality::HIGH_THRESHOLD && self.openness > Personality::LITTLE_HIGH, - PersonalityTrait::Worried => self.neuroticism > Personality::HIGH_THRESHOLD && self.agreeableness > Personality::LITTLE_HIGH, - PersonalityTrait::SadLoner => self.neuroticism > Personality::HIGH_THRESHOLD && self.extraversion < Personality::LITTLE_LOW, + PersonalityTrait::Seeker => { + self.neuroticism > Personality::HIGH_THRESHOLD + && self.openness > Personality::LITTLE_HIGH + }, + PersonalityTrait::Worried => { + self.neuroticism > Personality::HIGH_THRESHOLD + && self.agreeableness > Personality::LITTLE_HIGH + }, + PersonalityTrait::SadLoner => { + self.neuroticism > Personality::HIGH_THRESHOLD + && self.extraversion < Personality::LITTLE_LOW + }, PersonalityTrait::Stable => self.neuroticism < Personality::LOW_THRESHOLD, } } @@ -160,14 +175,19 @@ impl Personality { } pub fn will_ambush(&self) -> bool { - self.agreeableness < Self::LOW_THRESHOLD - && self.conscientiousness < Self::LOW_THRESHOLD + self.agreeableness < Self::LOW_THRESHOLD && self.conscientiousness < Self::LOW_THRESHOLD } } impl Default for Personality { fn default() -> Self { - Self { openness: Personality::MID, conscientiousness: Personality::MID, extraversion: Personality::MID, agreeableness: Personality::MID, neuroticism: Personality::MID } + Self { + openness: Personality::MID, + conscientiousness: Personality::MID, + extraversion: Personality::MID, + agreeableness: Personality::MID, + neuroticism: Personality::MID, + } } } @@ -198,7 +218,7 @@ impl Default for RtSimController { fn default() -> Self { Self { travel_to: None, - personality:Personality::default(), + personality: Personality::default(), heading_to: None, speed_factor: 1.0, events: Vec::new(), @@ -210,7 +230,7 @@ impl RtSimController { pub fn with_destination(pos: Vec3) -> Self { Self { travel_to: Some(pos), - personality:Personality::default(), + personality: Personality::default(), heading_to: None, speed_factor: 0.5, events: Vec::new(), diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index d97e1bf92d..dd52911ca8 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -5,8 +5,8 @@ use crate::{ item::{tool::AbilityMap, MaterialStatManifest}, ActiveAbilities, Beam, Body, CharacterState, Combo, ControlAction, Controller, ControllerInputs, Density, Energy, Health, InputAttr, InputKind, Inventory, - InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, Scale, SkillSet, Stance, StateUpdate, Stats, - Vel, + InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, Scale, SkillSet, Stance, StateUpdate, + Stats, Vel, }, link::Is, mounting::Rider, diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index 28c3393df6..aaad927eaa 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -9,8 +9,8 @@ use common::{ character_state::OutputEvents, inventory::item::{tool::AbilityMap, MaterialStatManifest}, ActiveAbilities, Beam, Body, CharacterState, Combo, Controller, Density, Energy, Health, - Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos, Scale, SkillSet, Stance, - StateUpdate, Stats, Vel, + Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos, Scale, SkillSet, + Stance, StateUpdate, Stats, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, link::Is, diff --git a/rtsim/src/data/mod.rs b/rtsim/src/data/mod.rs index 3f2bcae678..5b537c1ac6 100644 --- a/rtsim/src/data/mod.rs +++ b/rtsim/src/data/mod.rs @@ -43,6 +43,8 @@ pub struct Data { pub factions: Factions, pub time_of_day: TimeOfDay, + // If true, rtsim data will be ignored (and, hence, overwritten on next save) on load. + pub should_purge: bool, } pub type ReadError = rmp_serde::decode::Error; diff --git a/rtsim/src/gen/mod.rs b/rtsim/src/gen/mod.rs index bbe7d43d6c..c3977eb851 100644 --- a/rtsim/src/gen/mod.rs +++ b/rtsim/src/gen/mod.rs @@ -44,6 +44,7 @@ impl Data { }, time_of_day: TimeOfDay(settings.start_time), + should_purge: false, }; let initial_factions = (0..16) diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 5cade6e2a9..82eb3345bf 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -184,8 +184,9 @@ fn do_command( ServerChatCommand::Tell => handle_tell, ServerChatCommand::Time => handle_time, ServerChatCommand::Tp => handle_tp, - ServerChatCommand::TpNpc => handle_tp_npc, - ServerChatCommand::NpcInfo => handle_npc_info, + ServerChatCommand::RtsimTp => handle_rtsim_tp, + ServerChatCommand::RtsimInfo => handle_rtsim_info, + ServerChatCommand::RtsimPurge => handle_rtsim_purge, ServerChatCommand::RtsimChunk => handle_rtsim_chunk, ServerChatCommand::Unban => handle_unban, ServerChatCommand::Version => handle_version, @@ -1185,7 +1186,7 @@ fn handle_tp( }) } -fn handle_tp_npc( +fn handle_rtsim_tp( server: &mut Server, _client: EcsEntity, target: EcsEntity, @@ -1214,7 +1215,7 @@ fn handle_tp_npc( }) } -fn handle_npc_info( +fn handle_rtsim_info( server: &mut Server, client: EcsEntity, target: EcsEntity, @@ -1263,6 +1264,36 @@ fn handle_npc_info( } } +fn handle_rtsim_purge( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: Vec, + action: &ServerChatCommand, +) -> CmdResult<()> { + use crate::rtsim2::RtSim; + if let Some(should_purge) = parse_cmd_args!(args, bool) { + server + .state + .ecs() + .write_resource::() + .set_should_purge(should_purge); + server.notify_client( + client, + ServerGeneral::server_msg( + ChatType::CommandInfo, + format!( + "Rtsim data {} be purged on next startup", + if should_purge { "WILL" } else { "will NOT" }, + ), + ), + ); + Ok(()) + } else { + return Err(action.help_string()); + } +} + fn handle_rtsim_chunk( server: &mut Server, client: EcsEntity, diff --git a/server/src/lib.rs b/server/src/lib.rs index 44332db921..0a5ddb03b7 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -80,8 +80,8 @@ use common::{ comp, event::{EventBus, ServerEvent}, resources::{BattleMode, GameMode, Time, TimeOfDay}, - shared_server_config::ServerConstants, rtsim::{RtSimEntity, RtSimVehicle}, + shared_server_config::ServerConstants, slowjob::SlowJobPool, terrain::{Block, TerrainChunk, TerrainChunkSize}, vol::RectRasterableVol, @@ -1463,6 +1463,15 @@ impl Drop for Server { info!("Unloading terrain persistence..."); terrain_persistence.unload_all() }); + + #[cfg(feature = "worldgen")] + { + info!("Saving rtsim state..."); + self.state + .ecs() + .write_resource::() + .save(true); + } } } diff --git a/server/src/rtsim2/mod.rs b/server/src/rtsim2/mod.rs index 3073939e6f..a4bffb6164 100644 --- a/server/src/rtsim2/mod.rs +++ b/server/src/rtsim2/mod.rs @@ -54,7 +54,14 @@ impl RtSim { match Data::from_reader(io::BufReader::new(file)) { Ok(data) => { info!("Rtsim data loaded."); - break 'load data; + if data.should_purge { + warn!( + "The should_purge flag was set on the rtsim data, \ + generating afresh" + ); + } else { + break 'load data; + } }, Err(e) => { error!("Rtsim data failed to load: {}", e); @@ -171,14 +178,14 @@ impl RtSim { self.state.data_mut().npcs.remove(entity.0); } - pub fn save(&mut self, slowjob_pool: &SlowJobPool) { + pub fn save(&mut self, /* slowjob_pool: &SlowJobPool, */ wait_until_finished: bool) { info!("Saving rtsim data..."); let file_path = self.file_path.clone(); let data = self.state.data().clone(); debug!("Starting rtsim data save job..."); // TODO: Use slow job // slowjob_pool.spawn("RTSIM_SAVE", move || { - std::thread::spawn(move || { + let handle = std::thread::spawn(move || { let tmp_file_name = "data_tmp.dat"; if let Err(e) = file_path .parent() @@ -203,6 +210,11 @@ impl RtSim { error!("Saving rtsim data failed: {}", e); } }); + + if wait_until_finished { + handle.join(); + } + self.last_saved = Some(Instant::now()); } @@ -212,6 +224,10 @@ impl RtSim { } pub fn state(&self) -> &RtState { &self.state } + + pub fn set_should_purge(&mut self, should_purge: bool) { + self.state.data_mut().should_purge = should_purge; + } } pub struct ChunkStates(pub Grid>); diff --git a/server/src/rtsim2/tick.rs b/server/src/rtsim2/tick.rs index 4949c4d959..b6a93d7f20 100644 --- a/server/src/rtsim2/tick.rs +++ b/server/src/rtsim2/tick.rs @@ -223,7 +223,9 @@ impl<'a> System<'a> for Sys { .last_saved .map_or(true, |ls| ls.elapsed() > Duration::from_secs(60)) { - rtsim.save(&slow_jobs); + // TODO: Use slow jobs + let _ = slow_jobs; + rtsim.save(/* &slow_jobs, */ false); } let chunk_states = rtsim.state.resource::(); diff --git a/server/src/sys/agent/behavior_tree.rs b/server/src/sys/agent/behavior_tree.rs index 5c262bf3ef..5d0a6ef07c 100644 --- a/server/src/sys/agent/behavior_tree.rs +++ b/server/src/sys/agent/behavior_tree.rs @@ -741,12 +741,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool { read_data.time.0 - selected_at > RETARGETING_THRESHOLD_SECONDS; if !in_aggro_range && is_time_to_retarget { - agent_data.choose_target( - agent, - controller, - read_data, - event_emitter, - ); + agent_data.choose_target(agent, controller, read_data, event_emitter); } if aggro_on { diff --git a/server/src/sys/agent/behavior_tree/interaction.rs b/server/src/sys/agent/behavior_tree/interaction.rs index cb7ef5b431..f477037ff0 100644 --- a/server/src/sys/agent/behavior_tree/interaction.rs +++ b/server/src/sys/agent/behavior_tree/interaction.rs @@ -9,7 +9,7 @@ use common::{ BehaviorState, ControlAction, Item, TradingBehavior, UnresolvedChatMsg, UtteranceKind, }, event::ServerEvent, - rtsim::{Memory, MemoryItem, RtSimEvent, PersonalityTrait}, + rtsim::{Memory, MemoryItem, PersonalityTrait, RtSimEvent}, trade::{TradeAction, TradePhase, TradeResult}, }; use rand::{thread_rng, Rng}; @@ -106,65 +106,64 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool { match subject { Subject::Regular => { if let Some(tgt_stats) = read_data.stats.get(target) { - agent.rtsim_controller.events.push(RtSimEvent::AddMemory( - Memory { + agent + .rtsim_controller + .events + .push(RtSimEvent::AddMemory(Memory { item: MemoryItem::CharacterInteraction { name: tgt_stats.name.clone(), }, time_to_forget: read_data.time.0 + 600.0, - }, - )); + })); if let Some(destination_name) = &agent.rtsim_controller.heading_to { let personality = &agent.rtsim_controller.personality; let standard_response_msg = || -> String { if personality.will_ambush() { format!( - "I'm heading to {}! Want to come along? We'll make \ - great travel buddies, hehe.", + "I'm heading to {}! Want to come along? We'll \ + make great travel buddies, hehe.", destination_name ) - } else if personality.is(PersonalityTrait::Extroverted) - { + } else if personality.is(PersonalityTrait::Extroverted) { format!( "I'm heading to {}! Want to come along?", destination_name ) - } else if personality.is(PersonalityTrait::Disagreeable) - { + } else if personality.is(PersonalityTrait::Disagreeable) { "Hrm.".to_string() } else { "Hello!".to_string() } }; - let msg = if false /* TODO: Remembers character */ { - if personality.will_ambush() { - "Just follow me a bit more, hehe.".to_string() - } else if personality.is(PersonalityTrait::Extroverted) + let msg = if false + /* TODO: Remembers character */ + { + if personality.will_ambush() { + "Just follow me a bit more, hehe.".to_string() + } else if personality.is(PersonalityTrait::Extroverted) { + if personality.is(PersonalityTrait::Extroverted) { + format!( + "Greetings fair {}! It has been far too long \ + since last I saw you. I'm going to {} right \ + now.", + &tgt_stats.name, destination_name + ) + } else if personality.is(PersonalityTrait::Disagreeable) { - if personality.is(PersonalityTrait::Extroverted) - { - format!( - "Greetings fair {}! It has been far \ - too long since last I saw you. I'm \ - going to {} right now.", - &tgt_stats.name, destination_name - ) - } else if personality.is(PersonalityTrait::Disagreeable) - { - "Oh. It's you again.".to_string() - } else { - format!( - "Hi again {}! Unfortunately I'm in a \ - hurry right now. See you!", - &tgt_stats.name - ) - } + "Oh. It's you again.".to_string() } else { - standard_response_msg() + format!( + "Hi again {}! Unfortunately I'm in a hurry \ + right now. See you!", + &tgt_stats.name + ) } } else { standard_response_msg() - }; + } + } else { + standard_response_msg() + }; agent_data.chat_npc(msg, event_emitter); } /*else if agent.behavior.can_trade(agent_data.alignment.copied(), by) { @@ -183,14 +182,14 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool { }; agent_data.chat_npc(msg, event_emitter); } - }*/ else { + }*/ + else { let mut rng = thread_rng(); - if let Some(extreme_trait) = agent.rtsim_controller.personality.chat_trait(&mut rng) + if let Some(extreme_trait) = + agent.rtsim_controller.personality.chat_trait(&mut rng) { let msg = match extreme_trait { - PersonalityTrait::Open => { - "npc-speech-villager_open" - }, + PersonalityTrait::Open => "npc-speech-villager_open", PersonalityTrait::Adventurous => { "npc-speech-villager_adventurous" },