Added /rtsim_purge command

This commit is contained in:
Joshua Barretto 2023-03-31 23:20:52 +01:00
parent 66710d5bc2
commit 64324262c7
12 changed files with 171 additions and 85 deletions

View File

@ -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",

View File

@ -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<f32>) -> Self {
Self {
travel_to: Some(pos),
personality:Personality::default(),
personality: Personality::default(),
heading_to: None,
speed_factor: 0.5,
events: Vec::new(),

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -44,6 +44,7 @@ impl Data {
},
time_of_day: TimeOfDay(settings.start_time),
should_purge: false,
};
let initial_factions = (0..16)

View File

@ -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<String>,
action: &ServerChatCommand,
) -> CmdResult<()> {
use crate::rtsim2::RtSim;
if let Some(should_purge) = parse_cmd_args!(args, bool) {
server
.state
.ecs()
.write_resource::<RtSim>()
.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,

View File

@ -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::<rtsim2::RtSim>()
.save(true);
}
}
}

View File

@ -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<Option<LoadedChunkState>>);

View File

@ -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::<ChunkStates>();

View File

@ -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 {

View File

@ -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"
},