Skip to main content

veloren_common_state/
state.rs

1#[cfg(feature = "plugins")]
2use crate::plugin::PluginMgr;
3#[cfg(feature = "plugins")]
4use crate::plugin::memory_manager::EcsWorld;
5use crate::{BattleModeChangeArea, BuildArea, NoDurabilityArea};
6#[cfg(feature = "plugins")]
7use common::uid::IdMaps;
8use common::{
9    calendar::Calendar,
10    comp::{self, gizmos::RtsimGizmos},
11    event::{BonkEvent, EventBus, LocalEvent},
12    interaction,
13    link::Is,
14    mounting::{Mount, Rider, VolumeRider, VolumeRiders},
15    outcome::Outcome,
16    resources::{
17        DeltaTime, EntitiesDiedLastTick, GameMode, PlayerEntity, PlayerPhysicsSettings,
18        ProgramTime, Time, TimeOfDay, TimeScale,
19    },
20    shared_server_config::ServerConstants,
21    slowjob::SlowJobPool,
22    terrain::{Block, MapSizeLg, TerrainChunk, TerrainGrid, sprite::SpriteAdjecencyRequirement},
23    tether,
24    time::DayPeriod,
25    trade::Trades,
26    util::Dir2,
27    vol::{ReadVol, WriteVol},
28    weather::{Weather, WeatherGrid},
29};
30use common_base::{prof_span, span};
31use common_ecs::{PhysicsMetrics, SysMetrics};
32use common_net::sync::{WorldSyncExt, interpolation as sync_interp};
33use core::{convert::identity, time::Duration};
34use hashbrown::{HashMap, HashSet};
35use rayon::{ThreadPool, ThreadPoolBuilder};
36use specs::{
37    Component, DispatcherBuilder, Entity as EcsEntity, WorldExt,
38    prelude::Resource,
39    shred::{Fetch, FetchMut, SendDispatcher},
40    storage::{MaskedStorage as EcsMaskedStorage, Storage as EcsStorage},
41};
42use std::{
43    sync::{
44        Arc,
45        atomic::{AtomicBool, Ordering},
46    },
47    time::Instant,
48};
49use timer_queue::TimerQueue;
50use vek::*;
51
52/// At what point should we stop speeding up physics to compensate for lag? If
53/// we speed physics up too fast, we'd skip important physics events like
54/// collisions. This constant determines the upper limit. If delta time exceeds
55/// this value, the game's physics will begin to produce time lag. Ideally, we'd
56/// avoid such a situation.
57const MAX_DELTA_TIME: f32 = 1.0;
58/// convert seconds to milliseconds to use in TimerQueue
59const SECONDS_TO_MILLISECONDS: f64 = 1000.0;
60
61#[derive(Default)]
62pub struct BlockChange {
63    blocks: HashMap<Vec3<i32>, Block>,
64}
65
66impl BlockChange {
67    pub fn set(&mut self, pos: Vec3<i32>, block: Block) { self.blocks.insert(pos, block); }
68
69    pub fn try_set(&mut self, pos: Vec3<i32>, block: Block) -> Option<()> {
70        if !self.blocks.contains_key(&pos) {
71            self.blocks.insert(pos, block);
72            Some(())
73        } else {
74            None
75        }
76    }
77
78    /// Check if the block at given position `pos` has already been modified
79    /// this tick.
80    pub fn can_set_block(&self, pos: Vec3<i32>) -> bool { !self.blocks.contains_key(&pos) }
81
82    pub fn clear(&mut self) { self.blocks.clear(); }
83}
84
85#[derive(Default)]
86pub struct ScheduledBlockChange {
87    changes: TimerQueue<HashMap<Vec3<i32>, Block>>,
88    outcomes: TimerQueue<HashMap<Vec3<i32>, Block>>,
89    last_poll_time: u64,
90}
91impl ScheduledBlockChange {
92    pub fn set(&mut self, pos: Vec3<i32>, block: Block, replace_time: f64) {
93        let timer = self.changes.insert(
94            (replace_time * SECONDS_TO_MILLISECONDS) as u64,
95            HashMap::new(),
96        );
97        self.changes.get_mut(timer).insert(pos, block);
98    }
99
100    pub fn outcome_set(&mut self, pos: Vec3<i32>, block: Block, replace_time: f64) {
101        let outcome_timer = self.outcomes.insert(
102            (replace_time * SECONDS_TO_MILLISECONDS) as u64,
103            HashMap::new(),
104        );
105        self.outcomes.get_mut(outcome_timer).insert(pos, block);
106    }
107}
108
109#[derive(Default)]
110pub struct TerrainChanges {
111    pub new_chunks: HashSet<Vec2<i32>>,
112    pub modified_chunks: HashSet<Vec2<i32>>,
113    pub removed_chunks: HashSet<Vec2<i32>>,
114    pub modified_blocks: HashMap<Vec3<i32>, Block>,
115}
116
117impl TerrainChanges {
118    pub fn clear(&mut self) {
119        self.new_chunks.clear();
120        self.modified_chunks.clear();
121        self.removed_chunks.clear();
122    }
123}
124
125#[derive(Clone)]
126pub struct BlockDiff {
127    pub wpos: Vec3<i32>,
128    pub old: Block,
129    pub new: Block,
130}
131
132/// A type used to represent game state stored on both the client and the
133/// server. This includes things like entity components, terrain data, and
134/// global states like weather, time of day, etc.
135pub struct State {
136    ecs: specs::World,
137    // Avoid lifetime annotation by storing a thread pool instead of the whole dispatcher
138    thread_pool: Arc<ThreadPool>,
139    dispatcher: SendDispatcher<'static>,
140}
141
142pub type Pools = Arc<ThreadPool>;
143
144impl State {
145    pub fn pools(game_mode: GameMode) -> Pools {
146        let (thread_name_infix, is_main_task) = match game_mode {
147            GameMode::Server => ("s", true),
148            GameMode::Client => ("c", true),
149            // Note: We don't currently use `Singleplayer`. When we do, server-side tasks should be
150            // deprioritised in favour of things that sit on the main thread!
151            GameMode::Singleplayer => ("sp", false),
152        };
153
154        let is_first_error = Arc::new(AtomicBool::new(true));
155        let set_priority = move || {
156            use thread_priority::*;
157            let priority = if is_main_task {
158                // These threads are critical for the main tick loop, so need a higher priority
159                ThreadPriority::Crossplatform(TryFrom::try_from(50).unwrap())
160            } else {
161                ThreadPriority::Min
162            };
163            let res = cfg_select! {
164                target_os = "linux" => std::thread::current().set_priority_and_policy(
165                    ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::RoundRobin),
166                    priority,
167                ),
168                _ => std::thread::current().set_priority(priority),
169            };
170            if let Err(err) = res
171                && is_first_error.swap(false, Ordering::Relaxed)
172            {
173                tracing::warn!(
174                    "Unable to set priority/schedule policy for dispatcher pool thread: {err}"
175                );
176            }
177        };
178
179        Arc::new(
180            ThreadPoolBuilder::new()
181                .num_threads(num_cpus::get().max(common::consts::MIN_RECOMMENDED_RAYON_THREADS))
182                .thread_name(move |i| format!("rayon-{}-{}", thread_name_infix, i))
183                .spawn_handler(|thread| {
184                    let mut b = std::thread::Builder::new();
185                    if let Some(name) = thread.name() {
186                        b = b.name(name.to_owned());
187                    }
188                    if let Some(stack_size) = thread.stack_size() {
189                        b = b.stack_size(stack_size);
190                    }
191                    let set_priority = set_priority.clone();
192                    b.spawn(move || {
193                        set_priority();
194                        thread.run()
195                    })?;
196                    Ok(())
197                })
198                .build()
199                .unwrap(),
200        )
201    }
202
203    /// Create a new `State` in client mode.
204    pub fn client(
205        pools: Pools,
206        map_size_lg: MapSizeLg,
207        default_chunk: Arc<TerrainChunk>,
208        add_systems: impl Fn(&mut DispatcherBuilder),
209        #[cfg(feature = "plugins")] plugin_mgr: PluginMgr,
210    ) -> Self {
211        Self::new(
212            GameMode::Client,
213            pools,
214            map_size_lg,
215            default_chunk,
216            add_systems,
217            #[cfg(feature = "plugins")]
218            plugin_mgr,
219        )
220    }
221
222    /// Create a new `State` in server mode.
223    pub fn server(
224        pools: Pools,
225        map_size_lg: MapSizeLg,
226        default_chunk: Arc<TerrainChunk>,
227        add_systems: impl Fn(&mut DispatcherBuilder),
228        #[cfg(feature = "plugins")] plugin_mgr: PluginMgr,
229    ) -> Self {
230        Self::new(
231            GameMode::Server,
232            pools,
233            map_size_lg,
234            default_chunk,
235            add_systems,
236            #[cfg(feature = "plugins")]
237            plugin_mgr,
238        )
239    }
240
241    pub fn new(
242        game_mode: GameMode,
243        pools: Pools,
244        map_size_lg: MapSizeLg,
245        default_chunk: Arc<TerrainChunk>,
246        add_systems: impl Fn(&mut DispatcherBuilder),
247        #[cfg(feature = "plugins")] plugin_mgr: PluginMgr,
248    ) -> Self {
249        prof_span!(guard, "create dispatcher");
250        let mut dispatch_builder =
251            DispatcherBuilder::<'static, 'static>::new().with_pool(Arc::clone(&pools));
252        // TODO: Consider alternative ways to do this
253        add_systems(&mut dispatch_builder);
254        let dispatcher = dispatch_builder
255            .build()
256            .try_into_sendable()
257            .unwrap_or_else(|_| panic!("Thread local systems not allowed"));
258        drop(guard);
259
260        Self {
261            ecs: Self::setup_ecs_world(
262                game_mode,
263                Arc::clone(&pools),
264                map_size_lg,
265                default_chunk,
266                #[cfg(feature = "plugins")]
267                plugin_mgr,
268            ),
269            thread_pool: pools,
270            dispatcher,
271        }
272    }
273
274    /// Creates ecs world and registers all the common components and resources
275    // TODO: Split up registering into server and client (e.g. move
276    // EventBus<ServerEvent> to the server)
277    fn setup_ecs_world(
278        game_mode: GameMode,
279        thread_pool: Arc<ThreadPool>,
280        map_size_lg: MapSizeLg,
281        default_chunk: Arc<TerrainChunk>,
282        #[cfg(feature = "plugins")] mut plugin_mgr: PluginMgr,
283    ) -> specs::World {
284        prof_span!("State::setup_ecs_world");
285        let mut ecs = specs::World::new();
286        // Uids for sync
287        ecs.register_sync_marker();
288        // Register server -> all clients synced components.
289        ecs.register::<comp::Body>();
290        ecs.register::<comp::Hardcore>();
291        ecs.register::<comp::body::parts::Heads>();
292        ecs.register::<comp::Player>();
293        ecs.register::<comp::Stats>();
294        ecs.register::<comp::SkillSet>();
295        ecs.register::<comp::ActiveAbilities>();
296        ecs.register::<comp::Buffs>();
297        ecs.register::<comp::Auras>();
298        ecs.register::<comp::EnteredAuras>();
299        ecs.register::<comp::Energy>();
300        ecs.register::<comp::Combo>();
301        ecs.register::<comp::Health>();
302        ecs.register::<comp::Poise>();
303        ecs.register::<comp::CanBuild>();
304        ecs.register::<comp::LightEmitter>();
305        ecs.register::<comp::PickupItem>();
306        ecs.register::<comp::ThrownItem>();
307        ecs.register::<comp::Scale>();
308        ecs.register::<Is<Mount>>();
309        ecs.register::<Is<Rider>>();
310        ecs.register::<Is<VolumeRider>>();
311        ecs.register::<Is<tether::Leader>>();
312        ecs.register::<Is<tether::Follower>>();
313        ecs.register::<Is<interaction::Interactor>>();
314        ecs.register::<interaction::Interactors>();
315        ecs.register::<comp::Mass>();
316        ecs.register::<comp::Density>();
317        ecs.register::<comp::Collider>();
318        ecs.register::<comp::Sticky>();
319        ecs.register::<comp::Immovable>();
320        ecs.register::<comp::CharacterState>();
321        ecs.register::<comp::CharacterActivity>();
322        ecs.register::<comp::Object>();
323        ecs.register::<comp::Group>();
324        ecs.register::<comp::Shockwave>();
325        ecs.register::<comp::ShockwaveHitEntities>();
326        ecs.register::<comp::projectile::ProjectileHitEntities>();
327        ecs.register::<comp::Beam>();
328        ecs.register::<comp::Arcing>();
329        ecs.register::<comp::Pool>();
330        ecs.register::<comp::Alignment>();
331        ecs.register::<comp::LootOwner>();
332        ecs.register::<comp::Admin>();
333        ecs.register::<comp::Stance>();
334        ecs.register::<comp::Teleporting>();
335        ecs.register::<comp::GizmoSubscriber>();
336        ecs.register::<comp::FrontendMarker>();
337
338        // Register components send from clients -> server
339        ecs.register::<comp::Controller>();
340
341        // Register components send directly from server -> all but one client
342        ecs.register::<comp::PhysicsState>();
343
344        // Register components synced from client -> server -> all other clients
345        ecs.register::<comp::Pos>();
346        ecs.register::<comp::Vel>();
347        ecs.register::<comp::Ori>();
348        ecs.register::<comp::Inventory>();
349
350        // Register common unsynced components
351        ecs.register::<comp::PreviousPhysCache>();
352        ecs.register::<comp::PosVelOriDefer>();
353
354        // Register client-local components
355        // TODO: only register on the client
356        ecs.register::<comp::LightAnimation>();
357        ecs.register::<sync_interp::InterpBuffer<comp::Pos>>();
358        ecs.register::<sync_interp::InterpBuffer<comp::Vel>>();
359        ecs.register::<sync_interp::InterpBuffer<comp::Ori>>();
360
361        // Register server-local components
362        // TODO: only register on the server
363        ecs.register::<comp::Last<comp::Pos>>();
364        ecs.register::<comp::Last<comp::Vel>>();
365        ecs.register::<comp::Last<comp::Ori>>();
366        ecs.register::<comp::Agent>();
367        ecs.register::<comp::WaypointArea>();
368        ecs.register::<comp::ForceUpdate>();
369        ecs.register::<comp::InventoryUpdateBuffer>();
370        ecs.register::<comp::Waypoint>();
371        ecs.register::<comp::MapMarker>();
372        ecs.register::<comp::Projectile>();
373        ecs.register::<comp::Melee>();
374        ecs.register::<comp::ItemDrops>();
375        ecs.register::<comp::ChatMode>();
376        ecs.register::<comp::Faction>();
377        ecs.register::<comp::invite::Invite>();
378        ecs.register::<comp::invite::PendingInvites>();
379        ecs.register::<VolumeRiders>();
380        ecs.register::<common::combat::DeathEffects>();
381        ecs.register::<common::combat::RiderEffects>();
382        ecs.register::<comp::SpectatingEntity>();
383
384        // Register synced resources used by the ECS.
385        ecs.insert(TimeOfDay(0.0));
386        ecs.insert(Calendar::default());
387        ecs.insert(WeatherGrid::new(Vec2::zero()));
388        ecs.insert(Time(0.0));
389        ecs.insert(ProgramTime(0.0));
390        ecs.insert(TimeScale(1.0));
391
392        // Register unsynced resources used by the ECS.
393        ecs.insert(DeltaTime(0.0));
394        ecs.insert(PlayerEntity(None));
395        ecs.insert(TerrainGrid::new(map_size_lg, default_chunk).unwrap());
396        ecs.insert(BlockChange::default());
397        ecs.insert(ScheduledBlockChange::default());
398        ecs.insert(crate::special_areas::AreasContainer::<BuildArea>::default());
399        ecs.insert(crate::special_areas::AreasContainer::<NoDurabilityArea>::default());
400        ecs.insert(crate::special_areas::AreasContainer::<BattleModeChangeArea>::default());
401        ecs.insert(TerrainChanges::default());
402        ecs.insert(EventBus::<LocalEvent>::default());
403        ecs.insert(game_mode);
404        ecs.insert(EventBus::<Outcome>::default());
405        ecs.insert(common::CachedSpatialGrid::default());
406        ecs.insert(EntitiesDiedLastTick::default());
407        ecs.insert(RtsimGizmos::default());
408
409        let num_cpu = num_cpus::get() as u64;
410        let slow_limit = (num_cpu / 2 + num_cpu / 4).max(1);
411        tracing::trace!(?slow_limit, "Slow Thread limit");
412        ecs.insert(SlowJobPool::new(slow_limit, 10_000, thread_pool));
413
414        // TODO: only register on the server
415        ecs.insert(comp::group::GroupManager::default());
416        ecs.insert(SysMetrics::default());
417        ecs.insert(PhysicsMetrics::default());
418        ecs.insert(Trades::default());
419        ecs.insert(PlayerPhysicsSettings::default());
420        ecs.insert(VolumeRiders::default());
421
422        // Load plugins from asset directory
423        #[cfg(feature = "plugins")]
424        ecs.insert({
425            let ecs_world = EcsWorld {
426                entities: &ecs.entities(),
427                health: ecs.read_component().into(),
428                uid: ecs.read_component().into(),
429                id_maps: &ecs.read_resource::<IdMaps>().into(),
430                player: ecs.read_component().into(),
431            };
432            if let Err(e) = plugin_mgr.load_event(&ecs_world, game_mode) {
433                tracing::debug!(?e, "Failed to run plugin init");
434                tracing::info!("Plugins disabled, enable debug logging for more information.");
435                PluginMgr::default()
436            } else {
437                plugin_mgr
438            }
439        });
440
441        ecs
442    }
443
444    /// Register a component with the state's ECS.
445    #[must_use]
446    pub fn with_component<T: Component>(mut self) -> Self
447    where
448        <T as Component>::Storage: Default,
449    {
450        self.ecs.register::<T>();
451        self
452    }
453
454    /// Write a component attributed to a particular entity, ignoring errors.
455    ///
456    /// This should be used *only* when we can guarantee that the rest of the
457    /// code does not rely on the insert having succeeded (meaning the
458    /// entity is no longer alive!).
459    ///
460    /// Returns None if the entity was dead or there was no previous entry for
461    /// this component; otherwise, returns Some(old_component).
462    pub fn write_component_ignore_entity_dead<C: Component>(
463        &mut self,
464        entity: EcsEntity,
465        comp: C,
466    ) -> Option<C> {
467        self.ecs
468            .write_storage()
469            .insert(entity, comp)
470            .ok()
471            .and_then(identity)
472    }
473
474    /// Delete a component attributed to a particular entity.
475    pub fn delete_component<C: Component>(&mut self, entity: EcsEntity) -> Option<C> {
476        self.ecs.write_storage().remove(entity)
477    }
478
479    /// Read a component attributed to a particular entity.
480    pub fn read_component_cloned<C: Component + Clone>(&self, entity: EcsEntity) -> Option<C> {
481        self.ecs.read_storage().get(entity).cloned()
482    }
483
484    /// Read a component attributed to a particular entity.
485    pub fn read_component_copied<C: Component + Copy>(&self, entity: EcsEntity) -> Option<C> {
486        self.ecs.read_storage().get(entity).copied()
487    }
488
489    /// # Panics
490    /// Panics if `EventBus<E>` is borrowed
491    pub fn emit_event_now<E>(&self, event: E)
492    where
493        EventBus<E>: Resource,
494    {
495        self.ecs.write_resource::<EventBus<E>>().emit_now(event)
496    }
497
498    /// Given mutable access to the resource R, assuming the resource
499    /// component exists (this is already the behavior of functions like `fetch`
500    /// and `write_component_ignore_entity_dead`).  Since all of our resources
501    /// are generated up front, any failure here is definitely a code bug.
502    pub fn mut_resource<R: Resource>(&mut self) -> &mut R {
503        self.ecs.get_mut::<R>().expect(
504            "Tried to fetch an invalid resource even though all our resources should be known at \
505             compile time.",
506        )
507    }
508
509    /// Get a read-only reference to the storage of a particular component type.
510    pub fn read_storage<C: Component>(&self) -> EcsStorage<'_, C, Fetch<'_, EcsMaskedStorage<C>>> {
511        self.ecs.read_storage::<C>()
512    }
513
514    /// Get a reference to the internal ECS world.
515    pub fn ecs(&self) -> &specs::World { &self.ecs }
516
517    /// Get a mutable reference to the internal ECS world.
518    pub fn ecs_mut(&mut self) -> &mut specs::World { &mut self.ecs }
519
520    pub fn thread_pool(&self) -> &Arc<ThreadPool> { &self.thread_pool }
521
522    /// Get a reference to the `TerrainChanges` structure of the state. This
523    /// contains information about terrain state that has changed since the
524    /// last game tick.
525    pub fn terrain_changes(&self) -> Fetch<'_, TerrainChanges> { self.ecs.read_resource() }
526
527    /// Get a reference the current in-game weather grid.
528    pub fn weather_grid(&self) -> Fetch<'_, WeatherGrid> { self.ecs.read_resource() }
529
530    /// Get a mutable reference the current in-game weather grid.
531    pub fn weather_grid_mut(&mut self) -> FetchMut<'_, WeatherGrid> { self.ecs.write_resource() }
532
533    /// Get the current weather at a position in worldspace.
534    pub fn weather_at(&self, pos: Vec2<f32>) -> Weather {
535        self.weather_grid().get_interpolated(pos)
536    }
537
538    /// Get the max weather near a position in worldspace.
539    pub fn max_weather_near(&self, pos: Vec2<f32>) -> Weather {
540        self.weather_grid().get_max_near(pos)
541    }
542
543    /// Get the current in-game time of day.
544    ///
545    /// Note that this should not be used for physics, animations or other such
546    /// localised timings.
547    pub fn get_time_of_day(&self) -> f64 { self.ecs.read_resource::<TimeOfDay>().0 }
548
549    /// Get the current in-game day period (period of the day/night cycle)
550    pub fn get_day_period(&self) -> DayPeriod { self.get_time_of_day().into() }
551
552    /// Get the current in-game time.
553    ///
554    /// Note that this does not correspond to the time of day.
555    pub fn get_time(&self) -> f64 { self.ecs.read_resource::<Time>().0 }
556
557    /// Get the current true in-game time, unaffected by time_scale.
558    ///
559    /// Note that this does not correspond to the time of day.
560    pub fn get_program_time(&self) -> f64 { self.ecs.read_resource::<ProgramTime>().0 }
561
562    /// Get the current delta time.
563    pub fn get_delta_time(&self) -> f32 { self.ecs.read_resource::<DeltaTime>().0 }
564
565    /// Get a reference to this state's terrain.
566    pub fn terrain(&self) -> Fetch<'_, TerrainGrid> { self.ecs.read_resource() }
567
568    /// Get a reference to this state's terrain.
569    pub fn slow_job_pool(&self) -> Fetch<'_, SlowJobPool> { self.ecs.read_resource() }
570
571    /// Get a writable reference to this state's terrain.
572    pub fn terrain_mut(&self) -> FetchMut<'_, TerrainGrid> { self.ecs.write_resource() }
573
574    /// Get a block in this state's terrain.
575    pub fn get_block(&self, pos: Vec3<i32>) -> Option<Block> {
576        self.terrain().get(pos).ok().copied()
577    }
578
579    /// Set a block in this state's terrain.
580    pub fn set_block(&self, pos: Vec3<i32>, block: Block) {
581        self.ecs.write_resource::<BlockChange>().set(pos, block);
582    }
583
584    /// Set a block in this state's terrain (used to delete temporary summoned
585    /// sprites after a timeout).
586    pub fn schedule_set_block(
587        &self,
588        pos: Vec3<i32>,
589        block: Block,
590        sprite_block: Block,
591        replace_time: f64,
592    ) {
593        self.ecs
594            .write_resource::<ScheduledBlockChange>()
595            .set(pos, block, replace_time);
596        self.ecs
597            .write_resource::<ScheduledBlockChange>()
598            .outcome_set(pos, sprite_block, replace_time);
599    }
600
601    /// Check if the block at given position `pos` has already been modified
602    /// this tick.
603    pub fn can_set_block(&self, pos: Vec3<i32>) -> bool {
604        self.ecs.read_resource::<BlockChange>().can_set_block(pos)
605    }
606
607    /// Removes every chunk of the terrain.
608    pub fn clear_terrain(&mut self) -> usize {
609        let removed_chunks = &mut self.ecs.write_resource::<TerrainChanges>().removed_chunks;
610
611        self.terrain_mut()
612            .drain()
613            .map(|(key, _)| {
614                removed_chunks.insert(key);
615            })
616            .count()
617    }
618
619    /// Insert the provided chunk into this state's terrain.
620    pub fn insert_chunk(&mut self, key: Vec2<i32>, chunk: Arc<TerrainChunk>) {
621        if self
622            .ecs
623            .write_resource::<TerrainGrid>()
624            .insert(key, chunk)
625            .is_some()
626        {
627            self.ecs
628                .write_resource::<TerrainChanges>()
629                .modified_chunks
630                .insert(key);
631        } else {
632            self.ecs
633                .write_resource::<TerrainChanges>()
634                .new_chunks
635                .insert(key);
636        }
637    }
638
639    /// Remove the chunk with the given key from this state's terrain, if it
640    /// exists.
641    pub fn remove_chunk(&mut self, key: Vec2<i32>) -> bool {
642        if self
643            .ecs
644            .write_resource::<TerrainGrid>()
645            .remove(key)
646            .is_some()
647        {
648            self.ecs
649                .write_resource::<TerrainChanges>()
650                .removed_chunks
651                .insert(key);
652
653            true
654        } else {
655            false
656        }
657    }
658
659    // Apply terrain changes
660    pub fn apply_terrain_changes(&self, block_update: impl FnMut(&specs::World, Vec<BlockDiff>)) {
661        self.apply_terrain_changes_internal(false, block_update);
662    }
663
664    /// `during_tick` is true if and only if this is called from within
665    /// [State::tick].
666    ///
667    /// This only happens if [State::tick] is asked to update terrain itself
668    /// (using `update_terrain: true`).  [State::tick] is called from within
669    /// both the client and the server ticks, right after handling terrain
670    /// messages; currently, client sets it to true and server to false.
671    fn apply_terrain_changes_internal(
672        &self,
673        during_tick: bool,
674        mut block_update: impl FnMut(&specs::World, Vec<BlockDiff>),
675    ) {
676        span!(
677            _guard,
678            "apply_terrain_changes",
679            "State::apply_terrain_changes"
680        );
681        let mut terrain = self.ecs.write_resource::<TerrainGrid>();
682        let mut modified_blocks =
683            std::mem::take(&mut self.ecs.write_resource::<BlockChange>().blocks);
684
685        let mut scheduled_changes = self.ecs.write_resource::<ScheduledBlockChange>();
686        let current_time: f64 = self.ecs.read_resource::<Time>().0 * SECONDS_TO_MILLISECONDS;
687        let current_time = current_time as u64;
688        // This is important as the poll function has a debug assert that the new poll
689        // is at a more recent time than the old poll. As Time is synced between server
690        // and client, there is a chance that client dt can get slightly ahead of a
691        // server update, so we do not want to panic in that scenario.
692        if scheduled_changes.last_poll_time < current_time {
693            scheduled_changes.last_poll_time = current_time;
694            while let Some(changes) = scheduled_changes.changes.poll(current_time) {
695                modified_blocks.extend(changes.iter());
696            }
697            let outcome = self.ecs.read_resource::<EventBus<Outcome>>();
698            while let Some(outcomes) = scheduled_changes.outcomes.poll(current_time) {
699                for (pos, block) in outcomes.into_iter() {
700                    if let Some(sprite) = block.get_sprite() {
701                        outcome.emit_now(Outcome::SpriteDelete { pos, sprite });
702                    }
703                }
704            }
705        }
706        // Apply block modifications
707        // Only include in `TerrainChanges` if successful
708        let mut updated_blocks = Vec::with_capacity(modified_blocks.len());
709
710        // All positions that should recieve a block update.
711        let mut block_updates = HashSet::<Vec3<i32>>::default();
712
713        modified_blocks.retain(|wpos, new| {
714            let res = terrain.map(*wpos, |old| {
715                updated_blocks.push(BlockDiff {
716                    wpos: *wpos,
717                    old,
718                    new: *new,
719                });
720                *new
721            });
722
723            if let (&Ok(old), true) = (&res, during_tick) {
724                // NOTE: If the changes are applied during the tick, we push the *old* value as
725                // the modified block (since it otherwise can't be recovered after the tick).
726                // Otherwise, the changes will be applied after the tick, so we push the *new*
727                // value.
728                *new = old;
729            }
730
731            if let (&Ok(old), false) = (&res, during_tick) {
732                let h = old
733                    .get_sprite()
734                    .and_then(|s| s.solid_height())
735                    .unwrap_or(1.0)
736                    .max(
737                        new.get_sprite()
738                            .and_then(|s| s.solid_height())
739                            .unwrap_or(1.0),
740                    )
741                    .ceil() as i32;
742
743                block_updates.extend((-1..=h + 1).map(|z| wpos + Vec3::unit_z() * z).chain(
744                    (0..=h).flat_map(|z| {
745                        Dir2::ALL
746                            .iter()
747                            .map(move |d| wpos + Vec3::unit_z() * z + d.to_vec2())
748                    }),
749                ));
750            };
751
752            res.is_ok()
753        });
754
755        if !updated_blocks.is_empty() {
756            block_update(&self.ecs, updated_blocks);
757        }
758
759        // Only do block updates not during the tick since that's when actual
760        // terrain changes are applied.
761        //
762        // Clients will get these changes since they're just normal block updates
763        // next tick.
764        if !during_tick {
765            prof_span!(_guard, "Indirectly modified sprites");
766
767            // Collects all blocks that are neighbors with a modified block,
768            // where the `adjecency_requirement` is no longer upheld.
769            let indirectly_modified = block_updates
770                .into_iter()
771                // Filter for blocks that have an adjecency requirement.
772                .filter_map(|wpos| {
773                    let block = terrain.get(wpos).ok()?;
774                    Some((wpos, block.get_sprite()?.adjecency_requirement()?, block))
775                })
776                // Check if said adjecency requirement is upheld.
777                .filter(|(wpos, adjecency_requirement, block)| {
778                    let rot_mat = block.rotation_mat();
779                    // Tries to find a solid block for the given adjecent block.
780                    let find_solid = |adj: Vec3<i32>| {
781                        let wpos = wpos + adj;
782
783                        let res = terrain.get(wpos).copied().unwrap_or(Block::empty());
784
785                        // Don't check for sprites if we're checking for a block
786                        // directly above.
787                        let not_above = adj.z <= 0 || adj.x != 0 || adj.y != 0;
788
789                        if not_above && !res.is_solid() {
790                            // Sprites can be taller than 1 block.
791                            for z in 1..=Block::MAX_HEIGHT.ceil() as i32 {
792                                if let Ok(block) = terrain.get(wpos - Vec3::unit_z() * z)
793                                    && let Some(sprite) = block.get_sprite()
794                                    && let Some(h) = sprite.solid_height()
795                                    && h.ceil() as i32 > z
796                                {
797                                    return *block;
798                                }
799                            }
800                        }
801
802                        res
803                    };
804
805                    // Same as `find_solid` but first rotates with the sprites rotation
806                    // and mirroring.
807                    let rel_solid = |adj: Vec3<i32>| find_solid(rot_mat * adj);
808
809                    let valid = match adjecency_requirement {
810                        SpriteAdjecencyRequirement::AllSolid(v) => {
811                            v.iter().all(|v| rel_solid(*v).is_solid())
812                        },
813                        SpriteAdjecencyRequirement::AnySolid(v) => {
814                            v.iter().any(|v| rel_solid(*v).is_solid())
815                        },
816                    };
817
818                    !valid
819                })
820                .map(|(wpos, _, block)| (wpos, block))
821                .collect::<Vec<_>>();
822
823            // If the sprite is bonkable, bonk it.
824            let bonk_event_bus = self.ecs.write_resource::<EventBus<BonkEvent>>();
825            let mut bonk_emitter = bonk_event_bus.emitter();
826
827            let mut block_change = self.ecs.write_resource::<BlockChange>();
828
829            for (wpos, block) in indirectly_modified {
830                if block.is_bonkable() {
831                    bonk_emitter.emit(BonkEvent {
832                        pos: wpos.as_::<f32>() + 0.5,
833                        // TODO: Pass who destroyed the block?
834                        owner: None,
835                        target: None,
836                    });
837                } else {
838                    block_change.blocks.insert(wpos, block.into_vacant());
839                }
840            }
841        }
842
843        self.ecs.write_resource::<TerrainChanges>().modified_blocks = modified_blocks;
844    }
845
846    /// Execute a single tick, simulating the game state by the given duration.
847    pub fn tick(
848        &mut self,
849        dt: Duration,
850        update_terrain: bool,
851        mut metrics: Option<&mut StateTickMetrics>,
852        server_constants: &ServerConstants,
853        block_update: impl FnMut(&specs::World, Vec<BlockDiff>),
854    ) {
855        span!(_guard, "tick", "State::tick");
856
857        // Timing code for server metrics
858        macro_rules! section_span {
859            ($guard:ident, $label:literal) => {
860                span!(span_guard, $label);
861                let metrics_guard = metrics.as_mut().map(|m| MetricsGuard::new($label, m));
862                let $guard = (span_guard, metrics_guard);
863            };
864        }
865
866        // Change the time accordingly.
867        let time_scale = self.ecs.read_resource::<TimeScale>().0;
868        self.ecs.write_resource::<TimeOfDay>().0 +=
869            dt.as_secs_f64() * server_constants.day_cycle_coefficient * time_scale;
870        self.ecs.write_resource::<Time>().0 += dt.as_secs_f64() * time_scale;
871        self.ecs.write_resource::<ProgramTime>().0 += dt.as_secs_f64();
872
873        // Update delta time.
874        // Beyond a delta time of MAX_DELTA_TIME, start lagging to avoid skipping
875        // important physics events.
876        self.ecs.write_resource::<DeltaTime>().0 =
877            (dt.as_secs_f32() * time_scale as f32).min(MAX_DELTA_TIME);
878
879        section_span!(guard, "run systems");
880        // This dispatches all the systems in parallel.
881        self.dispatcher.dispatch(&self.ecs);
882        drop(guard);
883
884        self.maintain_ecs();
885
886        if update_terrain {
887            self.apply_terrain_changes_internal(true, block_update);
888        }
889
890        // Process local events
891        section_span!(guard, "process local events");
892
893        let outcomes = self.ecs.read_resource::<EventBus<Outcome>>();
894        let mut outcomes_emitter = outcomes.emitter();
895
896        let events = self.ecs.read_resource::<EventBus<LocalEvent>>().recv_all();
897        for event in events {
898            let mut velocities = self.ecs.write_storage::<comp::Vel>();
899            let physics = self.ecs.read_storage::<comp::PhysicsState>();
900            match event {
901                LocalEvent::Jump(entity, impulse) => {
902                    if let Some(vel) = velocities.get_mut(entity) {
903                        vel.0.z = impulse + physics.get(entity).map_or(0.0, |ps| ps.ground_vel.z);
904                    }
905                },
906                LocalEvent::ApplyImpulse { entity, impulse } => {
907                    if let Some(vel) = velocities.get_mut(entity) {
908                        vel.0 = impulse;
909                    }
910                },
911                LocalEvent::Boost {
912                    entity,
913                    vel: extra_vel,
914                } => {
915                    if let Some(vel) = velocities.get_mut(entity) {
916                        vel.0 += extra_vel;
917                    }
918                },
919                LocalEvent::CreateOutcome(outcome) => {
920                    outcomes_emitter.emit(outcome);
921                },
922            }
923        }
924        drop(guard);
925    }
926
927    pub fn maintain_ecs(&mut self) {
928        span!(_guard, "maintain ecs");
929        self.ecs.maintain();
930    }
931
932    /// Clean up the state after a tick.
933    pub fn cleanup(&mut self) {
934        span!(_guard, "cleanup", "State::cleanup");
935        // Clean up data structures from the last tick.
936        self.ecs.write_resource::<TerrainChanges>().clear();
937    }
938}
939
940// Timing code for server metrics
941#[derive(Default)]
942pub struct StateTickMetrics {
943    pub timings: Vec<(&'static str, Duration)>,
944}
945
946impl StateTickMetrics {
947    fn add(&mut self, label: &'static str, dur: Duration) {
948        // Check for duplicates!
949        debug_assert!(
950            self.timings.iter().all(|(l, _)| *l != label),
951            "Duplicate label in state tick metrics {label}"
952        );
953        self.timings.push((label, dur));
954    }
955}
956
957struct MetricsGuard<'a> {
958    start: Instant,
959    label: &'static str,
960    metrics: &'a mut StateTickMetrics,
961}
962
963impl<'a> MetricsGuard<'a> {
964    fn new(label: &'static str, metrics: &'a mut StateTickMetrics) -> Self {
965        Self {
966            start: Instant::now(),
967            label,
968            metrics,
969        }
970    }
971}
972
973impl Drop for MetricsGuard<'_> {
974    fn drop(&mut self) { self.metrics.add(self.label, self.start.elapsed()); }
975}