2019-04-14 20:30:27 +00:00
|
|
|
// Reexports
|
|
|
|
pub use sphynx::Uid;
|
|
|
|
|
2019-04-29 20:37:19 +00:00
|
|
|
use crate::{
|
2019-06-28 23:42:51 +00:00
|
|
|
comp,
|
2019-08-25 14:49:54 +00:00
|
|
|
event::{EventBus, LocalEvent, ServerEvent},
|
2019-05-16 23:05:46 +00:00
|
|
|
msg::{EcsCompPacket, EcsResPacket},
|
2019-10-06 17:35:47 +00:00
|
|
|
region::RegionMap,
|
2019-04-29 20:37:19 +00:00
|
|
|
sys,
|
common: Rework volume API
See the doc comments in `common/src/vol.rs` for more information on
the API itself.
The changes include:
* Consistent `Err`/`Error` naming.
* Types are named `...Error`.
* `enum` variants are named `...Err`.
* Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation
to an upcoming change where a “map” in the game related sense will
be added.
* Add volume iterators. There are two types of them:
* _Position_ iterators obtained from the trait `IntoPosIterator`
using the method
`fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `Vec3<i32>`.
* _Volume_ iterators obtained from the trait `IntoVolIterator`
using the method
`fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `(Vec3<i32>, &Self::Vox)`.
Those traits will usually be implemented by references to volume
types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some
type which usually implements several volume traits, such as `Chunk`).
* _Position_ iterators iterate over the positions valid for that
volume.
* _Volume_ iterators do the same but return not only the position
but also the voxel at that position, in each iteration.
* Introduce trait `RectSizedVol` for the use case which we have with
`Chonk`: A `Chonk` is sized only in x and y direction.
* Introduce traits `RasterableVol`, `RectRasterableVol`
* `RasterableVol` represents a volume that is compile-time sized and has
its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen
because such a volume can be used with `VolGrid3d`.
* `RectRasterableVol` represents a volume that is compile-time sized at
least in x and y direction and has its lower bound at `(0, 0, z)`.
There's no requirement on he lower bound or size in z direction.
The name `RectRasterableVol` was chosen because such a volume can be
used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
|
|
|
terrain::{Block, TerrainChunk, TerrainGrid},
|
2019-07-01 13:36:45 +00:00
|
|
|
vol::WriteVol,
|
2019-04-10 23:16:29 +00:00
|
|
|
};
|
2019-08-11 20:38:28 +00:00
|
|
|
use hashbrown::{HashMap, HashSet};
|
2019-05-01 18:10:43 +00:00
|
|
|
use rayon::{ThreadPool, ThreadPoolBuilder};
|
2019-05-16 23:05:46 +00:00
|
|
|
use serde_derive::{Deserialize, Serialize};
|
2019-03-04 19:50:26 +00:00
|
|
|
use specs::{
|
2019-09-09 19:11:40 +00:00
|
|
|
saveload::Marker,
|
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},
|
2019-09-09 19:11:40 +00:00
|
|
|
Component, DispatcherBuilder, Entity as EcsEntity, Join,
|
2019-03-04 19:50:26 +00:00
|
|
|
};
|
2019-04-10 17:23:27 +00:00
|
|
|
use sphynx;
|
2019-07-20 15:37:01 +00:00
|
|
|
use std::{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?
|
2019-05-17 09:22:32 +00:00
|
|
|
// TODO: Don't hard-code this.
|
2019-06-21 08:42:16 +00:00
|
|
|
const DAY_CYCLE_FACTOR: f64 = 24.0 * 2.0;
|
2019-01-12 15:57:19 +00:00
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
/// A resource that stores the time of day.
|
2019-05-16 23:05:46 +00:00
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
2019-06-21 08:42:16 +00:00
|
|
|
pub struct TimeOfDay(pub f64);
|
2019-01-12 15:57:19 +00:00
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
/// A resource that stores the tick (i.e: physics) time.
|
2019-05-19 17:58:32 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
2019-05-19 20:14:18 +00:00
|
|
|
pub struct Time(pub f64);
|
2019-01-23 20:01:58 +00:00
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
/// A resource that stores the time since the previous tick.
|
2019-03-02 03:48:30 +00:00
|
|
|
#[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
|
2019-05-17 09:22:32 +00:00
|
|
|
/// too fast, we'd skip important physics events like collisions. This constant determines the
|
|
|
|
/// upper limit. If delta time exceeds this value, the game's physics will begin to produce time
|
|
|
|
/// lag. Ideally, we'd avoid such a situation.
|
2019-06-27 14:21:41 +00:00
|
|
|
const MAX_DELTA_TIME: f32 = 1.0;
|
2019-09-09 19:11:40 +00:00
|
|
|
const HUMANOID_JUMP_ACCEL: f32 = 16.0;
|
2019-03-02 03:48:30 +00:00
|
|
|
|
2019-08-07 15:39:16 +00:00
|
|
|
#[derive(Default)]
|
2019-07-20 15:37:01 +00:00
|
|
|
pub struct BlockChange {
|
2019-08-11 20:38:28 +00:00
|
|
|
blocks: HashMap<Vec3<i32>, Block>,
|
2019-07-01 13:36:45 +00:00
|
|
|
}
|
|
|
|
|
2019-07-20 15:37:01 +00:00
|
|
|
impl BlockChange {
|
2019-07-01 13:36:45 +00:00
|
|
|
pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
|
|
|
|
self.blocks.insert(pos, block);
|
|
|
|
}
|
|
|
|
|
2019-09-25 14:56:57 +00:00
|
|
|
pub fn try_set(&mut self, pos: Vec3<i32>, block: Block) -> Option<()> {
|
|
|
|
if !self.blocks.contains_key(&pos) {
|
|
|
|
self.blocks.insert(pos, block);
|
|
|
|
Some(())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-01 13:36:45 +00:00
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.blocks.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-07 15:39:16 +00:00
|
|
|
#[derive(Default)]
|
2019-07-20 15:37:01 +00:00
|
|
|
pub struct TerrainChanges {
|
2019-08-11 20:38:28 +00:00
|
|
|
pub new_chunks: HashSet<Vec2<i32>>,
|
|
|
|
pub modified_chunks: HashSet<Vec2<i32>>,
|
|
|
|
pub removed_chunks: HashSet<Vec2<i32>>,
|
|
|
|
pub modified_blocks: HashMap<Vec3<i32>, Block>,
|
2019-01-23 20:01:58 +00:00
|
|
|
}
|
|
|
|
|
2019-07-20 15:37:01 +00:00
|
|
|
impl TerrainChanges {
|
2019-07-01 13:36:45 +00:00
|
|
|
pub fn clear(&mut self) {
|
2019-01-23 20:01:58 +00:00
|
|
|
self.new_chunks.clear();
|
2019-07-01 13:36:45 +00:00
|
|
|
self.modified_chunks.clear();
|
2019-01-23 20:01:58 +00:00
|
|
|
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
|
2019-05-17 09:22:32 +00:00
|
|
|
/// things like entity components, terrain data, and global states like weather, time of day, etc.
|
2019-01-02 19:22:01 +00:00
|
|
|
pub struct State {
|
2019-05-16 23:05:46 +00:00
|
|
|
ecs: sphynx::World<EcsCompPacket, EcsResPacket>,
|
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-02 19:22:01 +00:00
|
|
|
}
|
|
|
|
|
2019-07-01 20:42:43 +00:00
|
|
|
impl Default for State {
|
2019-01-12 15:57:19 +00:00
|
|
|
/// Create a new `State`.
|
2019-07-01 20:42:43 +00:00
|
|
|
fn default() -> 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
|
|
|
}
|
|
|
|
}
|
2019-07-01 20:42:43 +00:00
|
|
|
}
|
2019-01-02 19:22:01 +00:00
|
|
|
|
2019-07-01 20:42:43 +00:00
|
|
|
impl State {
|
2019-05-17 09:22:32 +00:00
|
|
|
/// Create a new `State` from an ECS state package.
|
2019-05-16 23:05:46 +00:00
|
|
|
pub fn from_state_package(
|
|
|
|
state_package: sphynx::StatePackage<EcsCompPacket, EcsResPacket>,
|
|
|
|
) -> 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-02 19:22:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
// Create a new Sphynx ECS world.
|
2019-08-25 14:49:54 +00:00
|
|
|
// TODO: Split up registering into server and client (e.g. move EventBus<ServerEvent> to the server)
|
2019-05-16 23:05:46 +00:00
|
|
|
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsCompPacket, EcsResPacket>) {
|
2019-07-30 05:24:36 +00:00
|
|
|
// Register server -> all clients synced components.
|
2019-06-30 11:48:28 +00:00
|
|
|
ecs.register_synced::<comp::Body>();
|
2019-04-10 17:23:27 +00:00
|
|
|
ecs.register_synced::<comp::Player>();
|
2019-05-13 13:58:01 +00:00
|
|
|
ecs.register_synced::<comp::Stats>();
|
2019-07-02 18:52:44 +00:00
|
|
|
ecs.register_synced::<comp::CanBuild>();
|
2019-07-21 16:50:13 +00:00
|
|
|
ecs.register_synced::<comp::LightEmitter>();
|
2019-07-26 21:01:41 +00:00
|
|
|
ecs.register_synced::<comp::Item>();
|
2019-08-02 18:56:37 +00:00
|
|
|
ecs.register_synced::<comp::Scale>();
|
2019-09-09 19:11:40 +00:00
|
|
|
ecs.register_synced::<comp::Mounting>();
|
|
|
|
ecs.register_synced::<comp::MountState>();
|
2019-09-25 20:22:39 +00:00
|
|
|
ecs.register_synced::<comp::Mass>();
|
2019-10-02 19:28:35 +00:00
|
|
|
ecs.register_synced::<comp::Sticky>();
|
2019-10-17 20:59:36 +00:00
|
|
|
ecs.register_synced::<comp::Gravity>();
|
2019-09-28 18:31:35 +00:00
|
|
|
ecs.register_synced::<comp::Projectile>();
|
2019-04-10 17:23:27 +00:00
|
|
|
|
2019-07-30 05:24:36 +00:00
|
|
|
// Register components send from clients -> server
|
|
|
|
ecs.register::<comp::Controller>();
|
|
|
|
|
|
|
|
// Register components send directly from server -> all but one client
|
2019-08-23 10:11:37 +00:00
|
|
|
ecs.register::<comp::CharacterState>();
|
|
|
|
ecs.register::<comp::PhysicsState>();
|
2019-07-30 05:24:36 +00:00
|
|
|
|
|
|
|
// Register components synced from client -> server -> all other clients
|
2019-06-14 15:27:05 +00:00
|
|
|
ecs.register::<comp::Pos>();
|
|
|
|
ecs.register::<comp::Vel>();
|
|
|
|
ecs.register::<comp::Ori>();
|
2019-07-25 14:48:27 +00:00
|
|
|
ecs.register::<comp::Inventory>();
|
2019-05-27 17:45:43 +00:00
|
|
|
|
|
|
|
// Register server-local components
|
2019-07-30 05:24:36 +00:00
|
|
|
ecs.register::<comp::Last<comp::Pos>>();
|
|
|
|
ecs.register::<comp::Last<comp::Vel>>();
|
|
|
|
ecs.register::<comp::Last<comp::Ori>>();
|
2019-08-23 10:11:37 +00:00
|
|
|
ecs.register::<comp::Last<comp::CharacterState>>();
|
2019-05-27 17:45:43 +00:00
|
|
|
ecs.register::<comp::Agent>();
|
2019-06-16 17:00:44 +00:00
|
|
|
ecs.register::<comp::ForceUpdate>();
|
2019-07-25 14:48:27 +00:00
|
|
|
ecs.register::<comp::InventoryUpdate>();
|
2019-06-28 23:42:51 +00:00
|
|
|
ecs.register::<comp::Inventory>();
|
2019-08-14 15:51:59 +00:00
|
|
|
ecs.register::<comp::Admin>();
|
2019-09-25 20:22:39 +00:00
|
|
|
ecs.register::<comp::Waypoint>();
|
2019-04-14 20:30:27 +00:00
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
// Register synced resources used by the ECS.
|
2019-09-09 19:11:40 +00:00
|
|
|
ecs.insert_synced(TimeOfDay(0.0));
|
2019-05-16 23:05:46 +00:00
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
// Register unsynced resources used by the ECS.
|
2019-08-01 23:32:33 +00:00
|
|
|
ecs.add_resource(Time(0.0));
|
|
|
|
ecs.add_resource(DeltaTime(0.0));
|
common: Rework volume API
See the doc comments in `common/src/vol.rs` for more information on
the API itself.
The changes include:
* Consistent `Err`/`Error` naming.
* Types are named `...Error`.
* `enum` variants are named `...Err`.
* Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation
to an upcoming change where a “map” in the game related sense will
be added.
* Add volume iterators. There are two types of them:
* _Position_ iterators obtained from the trait `IntoPosIterator`
using the method
`fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `Vec3<i32>`.
* _Volume_ iterators obtained from the trait `IntoVolIterator`
using the method
`fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `(Vec3<i32>, &Self::Vox)`.
Those traits will usually be implemented by references to volume
types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some
type which usually implements several volume traits, such as `Chunk`).
* _Position_ iterators iterate over the positions valid for that
volume.
* _Volume_ iterators do the same but return not only the position
but also the voxel at that position, in each iteration.
* Introduce trait `RectSizedVol` for the use case which we have with
`Chonk`: A `Chonk` is sized only in x and y direction.
* Introduce traits `RasterableVol`, `RectRasterableVol`
* `RasterableVol` represents a volume that is compile-time sized and has
its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen
because such a volume can be used with `VolGrid3d`.
* `RectRasterableVol` represents a volume that is compile-time sized at
least in x and y direction and has its lower bound at `(0, 0, z)`.
There's no requirement on he lower bound or size in z direction.
The name `RectRasterableVol` was chosen because such a volume can be
used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
|
|
|
ecs.add_resource(TerrainGrid::new().unwrap());
|
2019-08-01 23:32:33 +00:00
|
|
|
ecs.add_resource(BlockChange::default());
|
|
|
|
ecs.add_resource(TerrainChanges::default());
|
2019-08-25 14:49:54 +00:00
|
|
|
ecs.add_resource(EventBus::<ServerEvent>::default());
|
|
|
|
ecs.add_resource(EventBus::<LocalEvent>::default());
|
2019-10-06 17:35:47 +00:00
|
|
|
ecs.add_resource(RegionMap::new());
|
2019-04-10 17:23:27 +00:00
|
|
|
}
|
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
/// Register a component with the state's ECS.
|
2019-03-03 22:02:38 +00:00
|
|
|
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-05-17 09:22:32 +00:00
|
|
|
/// Write a component attributed to a particular entity.
|
2019-03-04 19:50:26 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-09-09 19:11:40 +00:00
|
|
|
/// Delete a component attributed to a particular entity.
|
|
|
|
pub fn delete_component<C: Component>(&mut self, entity: EcsEntity) -> Option<C> {
|
|
|
|
self.ecs.write_storage().remove(entity)
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
/// Read a component attributed to a particular entity.
|
2019-04-10 17:23:27 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
/// Get a read-only reference to the storage of a particular component type.
|
2019-03-04 19:50:26 +00:00
|
|
|
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-05-17 09:22:32 +00:00
|
|
|
/// Get a reference to the internal ECS world.
|
2019-05-16 23:05:46 +00:00
|
|
|
pub fn ecs(&self) -> &sphynx::World<EcsCompPacket, EcsResPacket> {
|
2019-04-10 17:23:27 +00:00
|
|
|
&self.ecs
|
2019-03-02 03:48:30 +00:00
|
|
|
}
|
2019-01-23 20:01:58 +00:00
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
/// Get a mutable reference to the internal ECS world.
|
2019-05-16 23:05:46 +00:00
|
|
|
pub fn ecs_mut(&mut self) -> &mut sphynx::World<EcsCompPacket, EcsResPacket> {
|
2019-04-10 17:23:27 +00:00
|
|
|
&mut self.ecs
|
2019-03-04 19:50:26 +00:00
|
|
|
}
|
|
|
|
|
2019-07-20 15:37:01 +00:00
|
|
|
/// Get a reference to the `TerrainChanges` structure of the state. This contains
|
|
|
|
/// information about terrain state that has changed since the last game tick.
|
|
|
|
pub fn terrain_changes(&self) -> Fetch<TerrainChanges> {
|
2019-07-01 13:36:45 +00:00
|
|
|
self.ecs.read_resource()
|
2019-03-02 03:48:30 +00:00
|
|
|
}
|
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-06-25 21:00:26 +00:00
|
|
|
/// Get the current delta time.
|
|
|
|
pub fn get_delta_time(&self) -> f32 {
|
|
|
|
self.ecs.read_resource::<DeltaTime>().0
|
|
|
|
}
|
|
|
|
|
2019-01-14 18:49:53 +00:00
|
|
|
/// Get a reference to this state's terrain.
|
common: Rework volume API
See the doc comments in `common/src/vol.rs` for more information on
the API itself.
The changes include:
* Consistent `Err`/`Error` naming.
* Types are named `...Error`.
* `enum` variants are named `...Err`.
* Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation
to an upcoming change where a “map” in the game related sense will
be added.
* Add volume iterators. There are two types of them:
* _Position_ iterators obtained from the trait `IntoPosIterator`
using the method
`fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `Vec3<i32>`.
* _Volume_ iterators obtained from the trait `IntoVolIterator`
using the method
`fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `(Vec3<i32>, &Self::Vox)`.
Those traits will usually be implemented by references to volume
types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some
type which usually implements several volume traits, such as `Chunk`).
* _Position_ iterators iterate over the positions valid for that
volume.
* _Volume_ iterators do the same but return not only the position
but also the voxel at that position, in each iteration.
* Introduce trait `RectSizedVol` for the use case which we have with
`Chonk`: A `Chonk` is sized only in x and y direction.
* Introduce traits `RasterableVol`, `RectRasterableVol`
* `RasterableVol` represents a volume that is compile-time sized and has
its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen
because such a volume can be used with `VolGrid3d`.
* `RectRasterableVol` represents a volume that is compile-time sized at
least in x and y direction and has its lower bound at `(0, 0, z)`.
There's no requirement on he lower bound or size in z direction.
The name `RectRasterableVol` was chosen because such a volume can be
used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
|
|
|
pub fn terrain(&self) -> Fetch<TerrainGrid> {
|
2019-07-01 13:36:45 +00:00
|
|
|
self.ecs.read_resource()
|
2019-04-10 23:16:29 +00:00
|
|
|
}
|
|
|
|
|
2019-05-19 23:00:31 +00:00
|
|
|
/// Get a writable reference to this state's terrain.
|
common: Rework volume API
See the doc comments in `common/src/vol.rs` for more information on
the API itself.
The changes include:
* Consistent `Err`/`Error` naming.
* Types are named `...Error`.
* `enum` variants are named `...Err`.
* Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation
to an upcoming change where a “map” in the game related sense will
be added.
* Add volume iterators. There are two types of them:
* _Position_ iterators obtained from the trait `IntoPosIterator`
using the method
`fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `Vec3<i32>`.
* _Volume_ iterators obtained from the trait `IntoVolIterator`
using the method
`fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `(Vec3<i32>, &Self::Vox)`.
Those traits will usually be implemented by references to volume
types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some
type which usually implements several volume traits, such as `Chunk`).
* _Position_ iterators iterate over the positions valid for that
volume.
* _Volume_ iterators do the same but return not only the position
but also the voxel at that position, in each iteration.
* Introduce trait `RectSizedVol` for the use case which we have with
`Chonk`: A `Chonk` is sized only in x and y direction.
* Introduce traits `RasterableVol`, `RectRasterableVol`
* `RasterableVol` represents a volume that is compile-time sized and has
its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen
because such a volume can be used with `VolGrid3d`.
* `RectRasterableVol` represents a volume that is compile-time sized at
least in x and y direction and has its lower bound at `(0, 0, z)`.
There's no requirement on he lower bound or size in z direction.
The name `RectRasterableVol` was chosen because such a volume can be
used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
|
|
|
pub fn terrain_mut(&self) -> FetchMut<TerrainGrid> {
|
2019-07-01 13:36:45 +00:00
|
|
|
self.ecs.write_resource()
|
2019-05-19 23:00:31 +00:00
|
|
|
}
|
|
|
|
|
2019-09-25 14:56:57 +00:00
|
|
|
/// Set a block in this state's terrain.
|
2019-07-20 15:37:01 +00:00
|
|
|
pub fn set_block(&mut self, pos: Vec3<i32>, block: Block) {
|
|
|
|
self.ecs.write_resource::<BlockChange>().set(pos, block);
|
|
|
|
}
|
|
|
|
|
2019-09-25 14:56:57 +00:00
|
|
|
/// Set a block in this state's terrain. Will return `None` if the block has already been modified this tick.
|
|
|
|
pub fn try_set_block(&mut self, pos: Vec3<i32>, block: Block) -> Option<()> {
|
|
|
|
self.ecs.write_resource::<BlockChange>().try_set(pos, block)
|
|
|
|
}
|
|
|
|
|
2019-05-25 12:23:56 +00:00
|
|
|
/// Removes every chunk of the terrain.
|
|
|
|
pub fn clear_terrain(&mut self) {
|
2019-05-25 21:13:38 +00:00
|
|
|
let keys = self
|
|
|
|
.terrain_mut()
|
2019-05-25 12:23:56 +00:00
|
|
|
.drain()
|
|
|
|
.map(|(key, _)| key)
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
for key in keys {
|
|
|
|
self.remove_chunk(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-10 23:16:29 +00:00
|
|
|
/// Insert the provided chunk into this state's terrain.
|
2019-05-17 17:44:30 +00:00
|
|
|
pub fn insert_chunk(&mut self, key: Vec2<i32>, chunk: TerrainChunk) {
|
2019-04-29 20:37:19 +00:00
|
|
|
if self
|
|
|
|
.ecs
|
common: Rework volume API
See the doc comments in `common/src/vol.rs` for more information on
the API itself.
The changes include:
* Consistent `Err`/`Error` naming.
* Types are named `...Error`.
* `enum` variants are named `...Err`.
* Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation
to an upcoming change where a “map” in the game related sense will
be added.
* Add volume iterators. There are two types of them:
* _Position_ iterators obtained from the trait `IntoPosIterator`
using the method
`fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `Vec3<i32>`.
* _Volume_ iterators obtained from the trait `IntoVolIterator`
using the method
`fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `(Vec3<i32>, &Self::Vox)`.
Those traits will usually be implemented by references to volume
types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some
type which usually implements several volume traits, such as `Chunk`).
* _Position_ iterators iterate over the positions valid for that
volume.
* _Volume_ iterators do the same but return not only the position
but also the voxel at that position, in each iteration.
* Introduce trait `RectSizedVol` for the use case which we have with
`Chonk`: A `Chonk` is sized only in x and y direction.
* Introduce traits `RasterableVol`, `RectRasterableVol`
* `RasterableVol` represents a volume that is compile-time sized and has
its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen
because such a volume can be used with `VolGrid3d`.
* `RectRasterableVol` represents a volume that is compile-time sized at
least in x and y direction and has its lower bound at `(0, 0, z)`.
There's no requirement on he lower bound or size in z direction.
The name `RectRasterableVol` was chosen because such a volume can be
used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
|
|
|
.write_resource::<TerrainGrid>()
|
2019-05-12 18:32:57 +00:00
|
|
|
.insert(key, Arc::new(chunk))
|
2019-04-10 23:16:29 +00:00
|
|
|
.is_some()
|
|
|
|
{
|
2019-07-01 13:36:45 +00:00
|
|
|
self.ecs
|
2019-07-20 15:37:01 +00:00
|
|
|
.write_resource::<TerrainChanges>()
|
2019-07-01 13:38:45 +00:00
|
|
|
.modified_chunks
|
|
|
|
.insert(key);
|
2019-04-10 23:16:29 +00:00
|
|
|
} else {
|
2019-07-01 13:36:45 +00:00
|
|
|
self.ecs
|
2019-07-20 15:37:01 +00:00
|
|
|
.write_resource::<TerrainChanges>()
|
2019-07-01 13:38:45 +00:00
|
|
|
.new_chunks
|
|
|
|
.insert(key);
|
2019-04-10 23:16:29 +00:00
|
|
|
}
|
2019-01-23 20:01:58 +00:00
|
|
|
}
|
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
/// Remove the chunk with the given key from this state's terrain, if it exists.
|
2019-05-17 17:44:30 +00:00
|
|
|
pub fn remove_chunk(&mut self, key: Vec2<i32>) {
|
2019-04-29 20:37:19 +00:00
|
|
|
if self
|
|
|
|
.ecs
|
common: Rework volume API
See the doc comments in `common/src/vol.rs` for more information on
the API itself.
The changes include:
* Consistent `Err`/`Error` naming.
* Types are named `...Error`.
* `enum` variants are named `...Err`.
* Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation
to an upcoming change where a “map” in the game related sense will
be added.
* Add volume iterators. There are two types of them:
* _Position_ iterators obtained from the trait `IntoPosIterator`
using the method
`fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `Vec3<i32>`.
* _Volume_ iterators obtained from the trait `IntoVolIterator`
using the method
`fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `(Vec3<i32>, &Self::Vox)`.
Those traits will usually be implemented by references to volume
types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some
type which usually implements several volume traits, such as `Chunk`).
* _Position_ iterators iterate over the positions valid for that
volume.
* _Volume_ iterators do the same but return not only the position
but also the voxel at that position, in each iteration.
* Introduce trait `RectSizedVol` for the use case which we have with
`Chonk`: A `Chonk` is sized only in x and y direction.
* Introduce traits `RasterableVol`, `RectRasterableVol`
* `RasterableVol` represents a volume that is compile-time sized and has
its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen
because such a volume can be used with `VolGrid3d`.
* `RectRasterableVol` represents a volume that is compile-time sized at
least in x and y direction and has its lower bound at `(0, 0, z)`.
There's no requirement on he lower bound or size in z direction.
The name `RectRasterableVol` was chosen because such a volume can be
used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
|
|
|
.write_resource::<TerrainGrid>()
|
2019-04-25 19:08:26 +00:00
|
|
|
.remove(key)
|
|
|
|
.is_some()
|
|
|
|
{
|
2019-07-01 13:36:45 +00:00
|
|
|
self.ecs
|
2019-07-20 15:37:01 +00:00
|
|
|
.write_resource::<TerrainChanges>()
|
2019-07-01 13:38:45 +00:00
|
|
|
.removed_chunks
|
|
|
|
.insert(key);
|
2019-04-25 19:08:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-05-17 09:22:32 +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-17 09:22:32 +00:00
|
|
|
// Update delta time.
|
|
|
|
// Beyond a delta time of MAX_DELTA_TIME, start lagging to avoid skipping important physics events.
|
2019-05-01 11:28:26 +00:00
|
|
|
self.ecs.write_resource::<DeltaTime>().0 = dt.as_secs_f32().min(MAX_DELTA_TIME);
|
2019-03-02 03:48:30 +00:00
|
|
|
|
2019-09-09 19:11:40 +00:00
|
|
|
// Mounted entities. We handle this here because we need access to the Uid registry and I
|
|
|
|
// forgot how to access that within a system. Anyhow, here goes.
|
|
|
|
for (entity, mount_state) in (
|
|
|
|
&self.ecs.entities(),
|
|
|
|
&mut self.ecs.write_storage::<comp::MountState>(),
|
|
|
|
)
|
|
|
|
.join()
|
|
|
|
{
|
|
|
|
match mount_state {
|
|
|
|
comp::MountState::Unmounted => {}
|
|
|
|
comp::MountState::MountedBy(mounter) => {
|
|
|
|
if let Some((controller, mounter)) =
|
|
|
|
self.ecs.entity_from_uid(mounter.id()).and_then(|mounter| {
|
|
|
|
self.ecs
|
|
|
|
.read_storage::<comp::Controller>()
|
|
|
|
.get(mounter)
|
|
|
|
.cloned()
|
|
|
|
.map(|x| (x, mounter))
|
|
|
|
})
|
|
|
|
{
|
|
|
|
let pos = self.ecs.read_storage::<comp::Pos>().get(entity).copied();
|
|
|
|
let ori = self.ecs.read_storage::<comp::Ori>().get(entity).copied();
|
|
|
|
let vel = self.ecs.read_storage::<comp::Vel>().get(entity).copied();
|
|
|
|
if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) {
|
|
|
|
let _ = self
|
|
|
|
.ecs
|
|
|
|
.write_storage()
|
|
|
|
.insert(mounter, comp::Pos(pos.0 + Vec3::unit_z() * 1.0));
|
|
|
|
let _ = self.ecs.write_storage().insert(mounter, ori);
|
|
|
|
let _ = self.ecs.write_storage().insert(mounter, vel);
|
|
|
|
}
|
|
|
|
let _ = self
|
|
|
|
.ecs
|
|
|
|
.write_storage::<comp::Controller>()
|
|
|
|
.insert(entity, controller);
|
|
|
|
} else {
|
|
|
|
*mount_state = comp::MountState::Unmounted;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut to_unmount = Vec::new();
|
|
|
|
for (entity, comp::Mounting(mountee)) in (
|
|
|
|
&self.ecs.entities(),
|
|
|
|
&self.ecs.read_storage::<comp::Mounting>(),
|
|
|
|
)
|
|
|
|
.join()
|
|
|
|
{
|
|
|
|
if self
|
|
|
|
.ecs
|
|
|
|
.entity_from_uid(mountee.id())
|
|
|
|
.filter(|mountee| self.ecs.is_alive(*mountee))
|
|
|
|
.is_none()
|
|
|
|
{
|
|
|
|
to_unmount.push(entity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for entity in to_unmount {
|
|
|
|
self.ecs.write_storage::<comp::Mounting>().remove(entity);
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
// Run systems to update the world.
|
|
|
|
// Create and run a 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);
|
2019-05-17 09:22:32 +00:00
|
|
|
// This dispatches all the systems in parallel.
|
2019-08-01 23:32:33 +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-07-01 13:36:45 +00:00
|
|
|
|
2019-10-06 17:35:47 +00:00
|
|
|
// Run RegionMap tick to update enitity region occupancy
|
|
|
|
self.ecs.write_resource::<RegionMap>().tick(
|
|
|
|
self.ecs.read_storage::<comp::Pos>(),
|
|
|
|
self.ecs.read_storage::<comp::Vel>(),
|
|
|
|
self.ecs.entities(),
|
|
|
|
);
|
|
|
|
|
2019-07-01 13:36:45 +00:00
|
|
|
// Apply terrain changes
|
common: Rework volume API
See the doc comments in `common/src/vol.rs` for more information on
the API itself.
The changes include:
* Consistent `Err`/`Error` naming.
* Types are named `...Error`.
* `enum` variants are named `...Err`.
* Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation
to an upcoming change where a “map” in the game related sense will
be added.
* Add volume iterators. There are two types of them:
* _Position_ iterators obtained from the trait `IntoPosIterator`
using the method
`fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `Vec3<i32>`.
* _Volume_ iterators obtained from the trait `IntoVolIterator`
using the method
`fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `(Vec3<i32>, &Self::Vox)`.
Those traits will usually be implemented by references to volume
types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some
type which usually implements several volume traits, such as `Chunk`).
* _Position_ iterators iterate over the positions valid for that
volume.
* _Volume_ iterators do the same but return not only the position
but also the voxel at that position, in each iteration.
* Introduce trait `RectSizedVol` for the use case which we have with
`Chonk`: A `Chonk` is sized only in x and y direction.
* Introduce traits `RasterableVol`, `RectRasterableVol`
* `RasterableVol` represents a volume that is compile-time sized and has
its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen
because such a volume can be used with `VolGrid3d`.
* `RectRasterableVol` represents a volume that is compile-time sized at
least in x and y direction and has its lower bound at `(0, 0, z)`.
There's no requirement on he lower bound or size in z direction.
The name `RectRasterableVol` was chosen because such a volume can be
used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
|
|
|
let mut terrain = self.ecs.write_resource::<TerrainGrid>();
|
2019-07-01 13:36:45 +00:00
|
|
|
self.ecs
|
2019-07-20 15:37:01 +00:00
|
|
|
.read_resource::<BlockChange>()
|
2019-07-01 13:36:45 +00:00
|
|
|
.blocks
|
2019-07-20 15:37:01 +00:00
|
|
|
.iter()
|
2019-07-01 13:38:45 +00:00
|
|
|
.for_each(|(pos, block)| {
|
2019-07-20 18:08:28 +00:00
|
|
|
let _ = terrain.set(*pos, *block);
|
2019-07-01 13:36:45 +00:00
|
|
|
});
|
2019-08-07 17:17:04 +00:00
|
|
|
self.ecs.write_resource::<TerrainChanges>().modified_blocks = std::mem::replace(
|
2019-07-20 15:37:01 +00:00
|
|
|
&mut self.ecs.write_resource::<BlockChange>().blocks,
|
2019-08-07 17:17:04 +00:00
|
|
|
Default::default(),
|
|
|
|
);
|
2019-08-25 16:48:12 +00:00
|
|
|
|
2019-08-25 14:49:54 +00:00
|
|
|
// Process local events
|
|
|
|
let events = self.ecs.read_resource::<EventBus<LocalEvent>>().recv_all();
|
|
|
|
for event in events {
|
2019-08-25 18:31:56 +00:00
|
|
|
let mut velocities = self.ecs.write_storage::<comp::Vel>();
|
2019-09-09 19:11:40 +00:00
|
|
|
let mut controllers = self.ecs.write_storage::<comp::Controller>();
|
2019-08-25 18:31:56 +00:00
|
|
|
match event {
|
|
|
|
LocalEvent::Jump(entity) => {
|
|
|
|
if let Some(vel) = velocities.get_mut(entity) {
|
|
|
|
vel.0.z = HUMANOID_JUMP_ACCEL;
|
|
|
|
}
|
|
|
|
}
|
2019-09-09 19:11:40 +00:00
|
|
|
LocalEvent::WallLeap { entity, wall_dir } => {
|
|
|
|
if let (Some(vel), Some(_controller)) =
|
|
|
|
(velocities.get_mut(entity), controllers.get_mut(entity))
|
|
|
|
{
|
|
|
|
let hspeed = Vec2::<f32>::from(vel.0).magnitude();
|
|
|
|
if hspeed > 0.001 && hspeed < 0.5 {
|
|
|
|
vel.0 += vel.0.normalized()
|
|
|
|
* Vec3::new(1.0, 1.0, 0.0)
|
|
|
|
* HUMANOID_JUMP_ACCEL
|
|
|
|
* 1.5
|
|
|
|
- wall_dir * 0.03;
|
|
|
|
vel.0.z = HUMANOID_JUMP_ACCEL * 0.5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-28 20:47:52 +00:00
|
|
|
LocalEvent::Boost {
|
|
|
|
entity,
|
|
|
|
vel: extra_vel,
|
|
|
|
} => {
|
|
|
|
if let Some(vel) = velocities.get_mut(entity) {
|
|
|
|
vel.0 += extra_vel;
|
|
|
|
}
|
|
|
|
}
|
2019-08-25 14:49:54 +00:00
|
|
|
}
|
|
|
|
}
|
2019-01-23 20:01:58 +00:00
|
|
|
}
|
2019-08-25 16:48:12 +00:00
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
/// Clean up the state after a tick.
|
2019-01-23 20:01:58 +00:00
|
|
|
pub fn cleanup(&mut self) {
|
2019-05-17 09:22:32 +00:00
|
|
|
// Clean up data structures from the last tick.
|
2019-07-20 15:37:01 +00:00
|
|
|
self.ecs.write_resource::<TerrainChanges>().clear();
|
2019-01-02 19:22:01 +00:00
|
|
|
}
|
|
|
|
}
|