2019-04-14 20:30:27 +00:00
|
|
|
// Reexports
|
|
|
|
pub use sphynx::Uid;
|
|
|
|
|
2019-04-29 20:37:19 +00:00
|
|
|
use crate::{
|
|
|
|
comp,
|
|
|
|
msg::EcsPacket,
|
|
|
|
sys,
|
|
|
|
terrain::{TerrainChunk, TerrainMap},
|
2019-04-10 23:16:29 +00:00
|
|
|
};
|
2019-05-01 18:10:43 +00:00
|
|
|
use rayon::{ThreadPool, ThreadPoolBuilder};
|
2019-03-04 19:50:26 +00:00
|
|
|
use specs::{
|
2019-03-06 13:48:44 +00:00
|
|
|
saveload::{MarkedBuilder, MarkerAllocator},
|
2019-05-01 18:10:43 +00:00
|
|
|
shred::{Fetch, FetchMut},
|
2019-04-29 20:37:19 +00:00
|
|
|
storage::{MaskedStorage as EcsMaskedStorage, Storage as EcsStorage},
|
|
|
|
Builder, Component, DispatcherBuilder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder,
|
2019-03-04 19:50:26 +00:00
|
|
|
};
|
2019-04-10 17:23:27 +00:00
|
|
|
use sphynx;
|
2019-05-01 18:10:43 +00:00
|
|
|
use std::{collections::HashSet, sync::Arc, time::Duration};
|
2019-01-23 20:01:58 +00:00
|
|
|
use vek::*;
|
2019-01-02 19:22:01 +00:00
|
|
|
|
2019-01-12 15:57:19 +00:00
|
|
|
/// How much faster should an in-game day be compared to a real day?
|
|
|
|
// TODO: Don't hard-code this
|
|
|
|
const DAY_CYCLE_FACTOR: f64 = 24.0 * 60.0;
|
|
|
|
|
|
|
|
/// A resource to store the time of day
|
|
|
|
struct TimeOfDay(f64);
|
|
|
|
|
|
|
|
/// A resource to store the tick (i.e: physics) time
|
2019-01-23 20:01:58 +00:00
|
|
|
struct Time(f64);
|
|
|
|
|
2019-03-02 03:48:30 +00:00
|
|
|
/// A resource used to store the time since the last tick
|
|
|
|
#[derive(Default)]
|
2019-05-01 11:28:26 +00:00
|
|
|
pub struct DeltaTime(pub f32);
|
|
|
|
|
|
|
|
/// At what point should we stop speeding up physics to compensate for lag? If we speed physics up
|
|
|
|
/// too fast, we'd skip important physics events like collisions. This constant determines what
|
|
|
|
/// the upper limit is. If delta time exceeds this value, the game's physics will begin to produce
|
|
|
|
/// time lag. Ideally, we'd avoid such a situation.
|
2019-05-12 05:37:10 +00:00
|
|
|
const MAX_DELTA_TIME: f32 = 0.05;
|
2019-03-02 03:48:30 +00:00
|
|
|
|
2019-01-23 20:01:58 +00:00
|
|
|
pub struct Changes {
|
2019-04-10 23:16:29 +00:00
|
|
|
pub new_chunks: HashSet<Vec3<i32>>,
|
|
|
|
pub changed_chunks: HashSet<Vec3<i32>>,
|
|
|
|
pub removed_chunks: HashSet<Vec3<i32>>,
|
2019-01-23 20:01:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Changes {
|
|
|
|
pub fn default() -> Self {
|
|
|
|
Self {
|
2019-04-10 23:16:29 +00:00
|
|
|
new_chunks: HashSet::new(),
|
|
|
|
changed_chunks: HashSet::new(),
|
|
|
|
removed_chunks: HashSet::new(),
|
2019-01-23 20:01:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn cleanup(&mut self) {
|
|
|
|
self.new_chunks.clear();
|
|
|
|
self.changed_chunks.clear();
|
|
|
|
self.removed_chunks.clear();
|
|
|
|
}
|
|
|
|
}
|
2019-01-12 15:57:19 +00:00
|
|
|
|
|
|
|
/// A type used to represent game state stored on both the client and the server. This includes
|
|
|
|
/// things like entity components, terrain data, and global state like weather, time of day, etc.
|
2019-01-02 19:22:01 +00:00
|
|
|
pub struct State {
|
2019-04-10 17:23:27 +00:00
|
|
|
ecs: sphynx::World<EcsPacket>,
|
2019-05-01 18:10:43 +00:00
|
|
|
// Avoid lifetime annotation by storing a thread pool instead of the whole dispatcher
|
|
|
|
thread_pool: Arc<ThreadPool>,
|
2019-01-23 20:01:58 +00:00
|
|
|
changes: Changes,
|
2019-01-02 19:22:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl State {
|
2019-01-12 15:57:19 +00:00
|
|
|
/// Create a new `State`.
|
2019-01-02 19:22:01 +00:00
|
|
|
pub fn new() -> Self {
|
2019-04-10 17:23:27 +00:00
|
|
|
Self {
|
|
|
|
ecs: sphynx::World::new(specs::World::new(), Self::setup_sphynx_world),
|
2019-05-01 18:10:43 +00:00
|
|
|
thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()),
|
2019-04-10 17:23:27 +00:00
|
|
|
changes: Changes::default(),
|
|
|
|
}
|
|
|
|
}
|
2019-01-02 19:22:01 +00:00
|
|
|
|
2019-04-10 17:23:27 +00:00
|
|
|
/// Create a new `State` from an ECS state package
|
|
|
|
pub fn from_state_package(state_package: sphynx::StatePackage<EcsPacket>) -> Self {
|
2019-01-02 19:22:01 +00:00
|
|
|
Self {
|
2019-04-29 20:37:19 +00:00
|
|
|
ecs: sphynx::World::from_state_package(
|
|
|
|
specs::World::new(),
|
|
|
|
Self::setup_sphynx_world,
|
|
|
|
state_package,
|
|
|
|
),
|
2019-05-01 18:10:43 +00:00
|
|
|
thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()),
|
2019-01-23 20:01:58 +00:00
|
|
|
changes: Changes::default(),
|
2019-01-02 19:22:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-10 17:23:27 +00:00
|
|
|
// Create a new Sphynx ECS world
|
|
|
|
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsPacket>) {
|
|
|
|
// Register synced components
|
2019-05-12 21:21:18 +00:00
|
|
|
ecs.register_synced::<comp::Actor>();
|
2019-04-10 17:23:27 +00:00
|
|
|
ecs.register_synced::<comp::Player>();
|
|
|
|
|
2019-04-14 20:30:27 +00:00
|
|
|
// Register unsynched (or synced by other means) components
|
2019-04-22 08:20:25 +00:00
|
|
|
ecs.register::<comp::phys::Pos>();
|
|
|
|
ecs.register::<comp::phys::Vel>();
|
|
|
|
ecs.register::<comp::phys::Dir>();
|
|
|
|
ecs.register::<comp::AnimationHistory>();
|
|
|
|
ecs.register::<comp::Agent>();
|
|
|
|
ecs.register::<comp::Control>();
|
2019-04-14 20:30:27 +00:00
|
|
|
|
2019-04-10 17:23:27 +00:00
|
|
|
// Register resources used by the ECS
|
2019-04-22 08:20:25 +00:00
|
|
|
ecs.add_resource(TimeOfDay(0.0));
|
|
|
|
ecs.add_resource(Time(0.0));
|
|
|
|
ecs.add_resource(DeltaTime(0.0));
|
|
|
|
ecs.add_resource(TerrainMap::new());
|
2019-04-10 17:23:27 +00:00
|
|
|
}
|
|
|
|
|
2019-03-03 22:02:38 +00:00
|
|
|
/// Register a component with the state's ECS
|
|
|
|
pub fn with_component<T: Component>(mut self) -> Self
|
2019-04-29 20:37:19 +00:00
|
|
|
where
|
|
|
|
<T as Component>::Storage: Default,
|
2019-03-03 22:02:38 +00:00
|
|
|
{
|
2019-04-22 08:20:25 +00:00
|
|
|
self.ecs.register::<T>();
|
2019-03-03 22:02:38 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2019-03-04 19:50:26 +00:00
|
|
|
/// Write a component attributed to a particular entity
|
|
|
|
pub fn write_component<C: Component>(&mut self, entity: EcsEntity, comp: C) {
|
2019-04-22 08:20:25 +00:00
|
|
|
let _ = self.ecs.write_storage().insert(entity, comp);
|
2019-04-10 17:23:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Read a component attributed to a particular entity
|
|
|
|
pub fn read_component_cloned<C: Component + Clone>(&self, entity: EcsEntity) -> Option<C> {
|
2019-04-22 08:20:25 +00:00
|
|
|
self.ecs.read_storage().get(entity).cloned()
|
2019-03-04 19:50:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a read-only reference to the storage of a particular component type
|
|
|
|
pub fn read_storage<C: Component>(&self) -> EcsStorage<C, Fetch<EcsMaskedStorage<C>>> {
|
2019-04-22 08:20:25 +00:00
|
|
|
self.ecs.read_storage::<C>()
|
2019-03-02 03:48:30 +00:00
|
|
|
}
|
|
|
|
|
2019-01-23 20:01:58 +00:00
|
|
|
/// Get a reference to the internal ECS world
|
2019-04-10 17:23:27 +00:00
|
|
|
pub fn ecs(&self) -> &sphynx::World<EcsPacket> {
|
|
|
|
&self.ecs
|
2019-03-02 03:48:30 +00:00
|
|
|
}
|
2019-01-23 20:01:58 +00:00
|
|
|
|
2019-03-04 19:50:26 +00:00
|
|
|
/// Get a mutable reference to the internal ECS world
|
2019-04-10 17:23:27 +00:00
|
|
|
pub fn ecs_mut(&mut self) -> &mut sphynx::World<EcsPacket> {
|
|
|
|
&mut self.ecs
|
2019-03-04 19:50:26 +00:00
|
|
|
}
|
|
|
|
|
2019-01-23 20:01:58 +00:00
|
|
|
/// Get a reference to the `Changes` structure of the state. This contains
|
|
|
|
/// information about state that has changed since the last game tick.
|
2019-03-02 03:48:30 +00:00
|
|
|
pub fn changes(&self) -> &Changes {
|
|
|
|
&self.changes
|
|
|
|
}
|
2019-01-23 20:01:58 +00:00
|
|
|
|
2019-01-12 15:57:19 +00:00
|
|
|
/// Get the current in-game time of day.
|
|
|
|
///
|
|
|
|
/// Note that this should not be used for physics, animations or other such localised timings.
|
|
|
|
pub fn get_time_of_day(&self) -> f64 {
|
2019-04-22 08:20:25 +00:00
|
|
|
self.ecs.read_resource::<TimeOfDay>().0
|
2019-01-12 15:57:19 +00:00
|
|
|
}
|
|
|
|
|
2019-01-23 20:01:58 +00:00
|
|
|
/// Get the current in-game time.
|
2019-01-12 15:57:19 +00:00
|
|
|
///
|
|
|
|
/// Note that this does not correspond to the time of day.
|
2019-01-23 20:01:58 +00:00
|
|
|
pub fn get_time(&self) -> f64 {
|
2019-04-22 08:20:25 +00:00
|
|
|
self.ecs.read_resource::<Time>().0
|
2019-01-12 15:57:19 +00:00
|
|
|
}
|
|
|
|
|
2019-01-14 18:49:53 +00:00
|
|
|
/// Get a reference to this state's terrain.
|
2019-03-02 03:48:30 +00:00
|
|
|
pub fn terrain(&self) -> Fetch<TerrainMap> {
|
2019-04-29 20:37:19 +00:00
|
|
|
self.ecs.read_resource::<TerrainMap>()
|
2019-04-10 23:16:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert the provided chunk into this state's terrain.
|
|
|
|
pub fn insert_chunk(&mut self, key: Vec3<i32>, chunk: TerrainChunk) {
|
2019-04-29 20:37:19 +00:00
|
|
|
if self
|
|
|
|
.ecs
|
2019-04-10 23:16:29 +00:00
|
|
|
.write_resource::<TerrainMap>()
|
2019-05-12 18:32:57 +00:00
|
|
|
.insert(key, Arc::new(chunk))
|
2019-04-10 23:16:29 +00:00
|
|
|
.is_some()
|
|
|
|
{
|
|
|
|
self.changes.changed_chunks.insert(key);
|
|
|
|
} else {
|
|
|
|
self.changes.new_chunks.insert(key);
|
|
|
|
}
|
2019-01-23 20:01:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-25 19:08:26 +00:00
|
|
|
/// Remove the chunk with the given key from this state's terrain, if it exists
|
|
|
|
pub fn remove_chunk(&mut self, key: Vec3<i32>) {
|
2019-04-29 20:37:19 +00:00
|
|
|
if self
|
|
|
|
.ecs
|
2019-04-25 19:08:26 +00:00
|
|
|
.write_resource::<TerrainMap>()
|
|
|
|
.remove(key)
|
|
|
|
.is_some()
|
|
|
|
{
|
|
|
|
self.changes.removed_chunks.insert(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-12 15:57:19 +00:00
|
|
|
/// Execute a single tick, simulating the game state by the given duration.
|
2019-01-02 19:22:01 +00:00
|
|
|
pub fn tick(&mut self, dt: Duration) {
|
2019-01-12 15:57:19 +00:00
|
|
|
// Change the time accordingly
|
2019-04-22 08:20:25 +00:00
|
|
|
self.ecs.write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR;
|
|
|
|
self.ecs.write_resource::<Time>().0 += dt.as_secs_f64();
|
2019-03-02 03:48:30 +00:00
|
|
|
|
2019-05-01 11:28:26 +00:00
|
|
|
// Update delta time
|
|
|
|
// Above a delta time of MAX_DELTA_TIME, start lagging to avoid skipping important physics events
|
|
|
|
self.ecs.write_resource::<DeltaTime>().0 = dt.as_secs_f32().min(MAX_DELTA_TIME);
|
2019-03-02 03:48:30 +00:00
|
|
|
|
2019-05-01 11:28:26 +00:00
|
|
|
// Run systems to update the world
|
2019-03-02 03:48:30 +00:00
|
|
|
// Create and run dispatcher for ecs systems
|
2019-05-01 18:10:43 +00:00
|
|
|
let mut dispatch_builder = DispatcherBuilder::new().with_pool(self.thread_pool.clone());
|
2019-03-02 03:48:30 +00:00
|
|
|
sys::add_local_systems(&mut dispatch_builder);
|
|
|
|
// This dispatches all the systems in parallel
|
2019-04-22 08:20:25 +00:00
|
|
|
dispatch_builder.build().dispatch(&self.ecs.res);
|
2019-03-02 03:48:30 +00:00
|
|
|
|
2019-04-22 08:20:25 +00:00
|
|
|
self.ecs.maintain();
|
2019-01-23 20:01:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Clean up the state after a tick
|
|
|
|
pub fn cleanup(&mut self) {
|
|
|
|
// Clean up data structures from the last tick
|
|
|
|
self.changes.cleanup();
|
2019-01-02 19:22:01 +00:00
|
|
|
}
|
|
|
|
}
|