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