veloren_rtsim/rule/
cleanup.rs

1use crate::{RtState, Rule, RuleError, event::OnTick};
2use rand::prelude::*;
3use rand_chacha::ChaChaRng;
4
5/// Prevent performing cleanup for every NPC every tick
6const NPC_SENTIMENT_TICK_SKIP: u64 = 30;
7const NPC_CLEANUP_TICK_SKIP: u64 = 100;
8const FACTION_CLEANUP_TICK_SKIP: u64 = 30;
9const SITE_CLEANUP_TICK_SKIP: u64 = 30;
10
11/// A rule that cleans up data structures in rtsim: removing old reports,
12/// irrelevant sentiments, etc.
13///
14/// Also performs sentiment decay (although this should be moved elsewhere)
15pub struct CleanUp;
16
17impl Rule for CleanUp {
18    fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
19        rtstate.bind::<Self, OnTick>(|ctx| {
20            let data = &mut *ctx.state.data_mut();
21            let mut rng = ChaChaRng::from_seed(thread_rng().gen::<[u8; 32]>());
22
23            // TODO: Use `.into_par_iter()` for these by implementing rayon traits in upstream slotmap.
24
25            // Decay NPC sentiments
26            data.npcs
27                .iter_mut()
28                // Only cleanup NPCs every few ticks
29                .filter(|(_, npc)| (npc.seed as u64 + ctx.event.tick) % NPC_SENTIMENT_TICK_SKIP == 0)
30                .for_each(|(_, npc)| npc.sentiments.decay(&mut rng, ctx.event.dt * NPC_SENTIMENT_TICK_SKIP as f32));
31
32            // Remove dead NPCs
33            // TODO: Don't do this every tick, find a sensible way to gradually remove dead NPCs after they've been
34            // forgotten
35            data.npcs
36                .retain(|npc_id, npc| if npc.is_dead() {
37                    // Remove NPC from home population
38                    if let Some(home) = npc.home.and_then(|home| data.sites.get_mut(home)) {
39                        home.population.remove(&npc_id);
40                    }
41                    tracing::debug!(?npc_id, "Cleaning up dead NPC");
42                    false
43                } else {
44                    true
45                });
46
47            // Clean up entities
48            data.npcs
49                .iter_mut()
50                .filter(|(_, npc)| (npc.seed as u64 + ctx.event.tick) % NPC_CLEANUP_TICK_SKIP == 0)
51                .for_each(|(_, npc)| npc.cleanup(&data.reports));
52
53            // Clean up factions
54            data.factions
55                .iter_mut()
56                .filter(|(_, faction)| (faction.seed as u64 + ctx.event.tick) % FACTION_CLEANUP_TICK_SKIP == 0)
57                .for_each(|(_, faction)| faction.cleanup());
58
59            // Clean up sites
60            data.sites
61                .iter_mut()
62                .filter(|(_, site)| (site.seed as u64 + ctx.event.tick) % SITE_CLEANUP_TICK_SKIP == 0)
63                .for_each(|(_, site)| site.cleanup(&data.reports));
64
65            // Clean up old reports
66            data.reports.cleanup(data.time_of_day);
67        });
68
69        Ok(Self)
70    }
71}