2019-08-19 12:39:23 +00:00
#![ deny(unsafe_code) ]
2019-10-03 21:28:17 +00:00
#![ feature(drain_filter) ]
2019-01-02 17:23:31 +00:00
2019-08-08 22:24:14 +00:00
pub mod auth_provider ;
2019-03-03 22:02:38 +00:00
pub mod client ;
2019-04-23 09:53:45 +00:00
pub mod cmd ;
2019-03-03 22:02:38 +00:00
pub mod error ;
pub mod input ;
2019-09-06 14:21:09 +00:00
pub mod metrics ;
2019-09-07 13:10:57 +00:00
pub mod settings ;
2019-03-03 22:02:38 +00:00
// Reexports
2019-06-29 16:41:26 +00:00
pub use crate ::{ error ::Error , input ::Input , settings ::ServerSettings } ;
2019-03-03 22:02:38 +00:00
2019-05-09 17:58:16 +00:00
use crate ::{
2019-08-08 22:24:14 +00:00
auth_provider ::AuthProvider ,
2019-10-06 17:35:47 +00:00
client ::{ Client , Clients , RegionSubscription } ,
2019-05-09 17:58:16 +00:00
cmd ::CHAT_COMMANDS ,
2019-05-06 14:26:10 +00:00
} ;
2019-03-03 22:02:38 +00:00
use common ::{
2019-03-04 19:50:26 +00:00
comp ,
2019-09-25 15:52:58 +00:00
effect ::Effect ,
2019-08-25 14:49:54 +00:00
event ::{ EventBus , ServerEvent } ,
2019-10-04 14:26:22 +00:00
msg ::{ validate_chat_msg , ChatMsgValidationError , MAX_BYTES_CHAT_MSG } ,
2019-07-04 16:14:45 +00:00
msg ::{ ClientMsg , ClientState , RequestStateError , ServerError , ServerInfo , ServerMsg } ,
2019-03-03 22:02:38 +00:00
net ::PostOffice ,
2019-08-07 17:17:04 +00:00
state ::{ BlockChange , State , TimeOfDay , Uid } ,
2019-09-09 08:46:58 +00:00
terrain ::{ block ::Block , TerrainChunk , TerrainChunkSize , TerrainGrid } ,
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
vol ::{ ReadVol , RectVolSize , Vox } ,
2019-03-03 22:02:38 +00:00
} ;
2019-08-15 22:10:46 +00:00
use crossbeam ::channel ;
2019-09-16 02:05:36 +00:00
use hashbrown ::{ hash_map ::Entry , HashMap } ;
2019-10-11 12:19:55 +00:00
use log ::{ debug , trace } ;
2019-09-07 13:10:57 +00:00
use metrics ::ServerMetrics ;
2019-07-29 17:23:26 +00:00
use rand ::Rng ;
2019-09-07 13:10:57 +00:00
use specs ::{ join ::Join , world ::EntityBuilder as EcsEntityBuilder , Builder , Entity as EcsEntity } ;
use std ::{
i32 ,
2019-09-16 02:01:05 +00:00
sync ::{
atomic ::{ AtomicBool , Ordering } ,
Arc ,
} ,
2019-09-07 13:10:57 +00:00
time ::{ Duration , Instant } ,
} ;
2019-07-12 16:30:15 +00:00
use uvth ::{ ThreadPool , ThreadPoolBuilder } ;
2019-05-09 17:58:16 +00:00
use vek ::* ;
2019-08-02 14:37:26 +00:00
use world ::{ ChunkSupplement , World } ;
2019-04-16 13:06:30 +00:00
2019-04-17 14:46:04 +00:00
const CLIENT_TIMEOUT : f64 = 20.0 ; // Seconds
2019-01-02 17:23:31 +00:00
2019-03-03 22:02:38 +00:00
pub enum Event {
2019-05-27 17:45:43 +00:00
ClientConnected {
entity : EcsEntity ,
} ,
ClientDisconnected {
entity : EcsEntity ,
} ,
Chat {
entity : Option < EcsEntity > ,
msg : String ,
} ,
2019-01-02 17:23:31 +00:00
}
2019-05-21 22:04:39 +00:00
#[ derive(Copy, Clone) ]
struct SpawnPoint ( Vec3 < f32 > ) ;
2019-01-02 17:23:31 +00:00
pub struct Server {
2019-01-02 19:22:01 +00:00
state : State ,
2019-05-16 17:40:32 +00:00
world : Arc < World > ,
2019-01-02 17:23:31 +00:00
2019-03-03 22:02:38 +00:00
postoffice : PostOffice < ServerMsg , ClientMsg > ,
2019-03-04 19:50:26 +00:00
clients : Clients ,
2019-04-10 23:16:29 +00:00
thread_pool : ThreadPool ,
2019-09-16 02:01:05 +00:00
chunk_tx : channel ::Sender < (
Vec2 < i32 > ,
2019-09-16 02:05:36 +00:00
Result < ( TerrainChunk , ChunkSupplement ) , EcsEntity > ,
2019-09-16 02:01:05 +00:00
) > ,
chunk_rx : channel ::Receiver < (
Vec2 < i32 > ,
2019-09-16 02:05:36 +00:00
Result < ( TerrainChunk , ChunkSupplement ) , EcsEntity > ,
2019-09-16 02:01:05 +00:00
) > ,
2019-09-16 01:38:53 +00:00
pending_chunks : HashMap < Vec2 < i32 > , Arc < AtomicBool > > ,
2019-05-08 16:22:52 +00:00
2019-06-29 16:41:26 +00:00
server_settings : ServerSettings ,
2019-05-08 16:22:52 +00:00
server_info : ServerInfo ,
2019-09-06 14:21:09 +00:00
metrics : ServerMetrics ,
2019-08-08 03:56:02 +00:00
2019-10-06 17:35:47 +00:00
// Tick count used for throttling network updates
// Note this doesn't account for dt (so update rate changes with tick rate)
tick : u64 ,
2019-08-08 03:56:02 +00:00
// TODO: anything but this
2019-08-08 22:24:14 +00:00
accounts : AuthProvider ,
2019-01-02 17:23:31 +00:00
}
impl Server {
2019-10-11 12:19:55 +00:00
/// Create a new `Server`
2019-06-29 16:41:26 +00:00
pub fn new ( settings : ServerSettings ) -> Result < Self , Error > {
2019-08-15 22:10:46 +00:00
let ( chunk_tx , chunk_rx ) = channel ::unbounded ( ) ;
2019-04-10 23:16:29 +00:00
2019-07-01 20:42:43 +00:00
let mut state = State ::default ( ) ;
2019-05-21 22:31:38 +00:00
state
. ecs_mut ( )
2019-10-16 11:39:41 +00:00
. add_resource ( SpawnPoint ( Vec3 ::new ( 16_384.0 , 16_384.0 , 600.0 ) ) ) ;
2019-08-25 12:27:17 +00:00
state
. ecs_mut ( )
. add_resource ( EventBus ::< ServerEvent > ::default ( ) ) ;
2019-10-06 17:35:47 +00:00
state . ecs_mut ( ) . register ::< RegionSubscription > ( ) ;
2019-04-16 21:06:33 +00:00
2019-07-12 13:03:35 +00:00
// Set starting time for the server.
state . ecs_mut ( ) . write_resource ::< TimeOfDay > ( ) . 0 = settings . start_time ;
2019-06-06 14:48:41 +00:00
let this = Self {
2019-04-16 21:06:33 +00:00
state ,
2019-06-29 16:41:26 +00:00
world : Arc ::new ( World ::generate ( settings . world_seed ) ) ,
2019-03-03 22:02:38 +00:00
2019-10-11 12:19:55 +00:00
postoffice : PostOffice ::bind ( settings . gameserver_address ) ? ,
2019-03-04 19:50:26 +00:00
clients : Clients ::empty ( ) ,
2019-04-10 23:16:29 +00:00
2019-07-12 19:29:16 +00:00
thread_pool : ThreadPoolBuilder ::new ( )
. name ( " veloren-worker " . into ( ) )
. build ( ) ,
2019-04-10 23:16:29 +00:00
chunk_tx ,
chunk_rx ,
2019-09-16 01:38:53 +00:00
pending_chunks : HashMap ::new ( ) ,
2019-05-08 16:22:52 +00:00
server_info : ServerInfo {
2019-07-01 09:37:17 +00:00
name : settings . server_name . clone ( ) ,
description : settings . server_description . clone ( ) ,
2019-07-21 17:45:31 +00:00
git_hash : common ::util ::GIT_HASH . to_string ( ) ,
2019-10-18 13:32:26 +00:00
git_date : common ::util ::GIT_DATE . to_string ( ) ,
2019-05-08 16:22:52 +00:00
} ,
2019-10-23 18:23:31 +00:00
metrics : ServerMetrics ::new ( settings . metrics_address )
. expect ( " Failed to initialize server metrics submodule. " ) ,
2019-10-06 17:35:47 +00:00
tick : 0 ,
2019-08-08 22:24:14 +00:00
accounts : AuthProvider ::new ( ) ,
2019-10-11 12:19:55 +00:00
server_settings : settings . clone ( ) ,
2019-04-16 21:06:33 +00:00
} ;
2019-10-11 12:19:55 +00:00
debug! ( " created veloren server " ) ;
trace! ( " server configuration: {:?} " , & settings ) ;
2019-04-16 21:06:33 +00:00
Ok ( this )
2019-01-02 17:23:31 +00:00
}
2019-06-05 13:13:24 +00:00
pub fn with_thread_pool ( mut self , thread_pool : ThreadPool ) -> Self {
self . thread_pool = thread_pool ;
self
}
2019-01-15 15:13:11 +00:00
/// Get a reference to the server's game state.
2019-04-16 13:06:30 +00:00
pub fn state ( & self ) -> & State {
& self . state
}
2019-01-15 15:13:11 +00:00
/// Get a mutable reference to the server's game state.
2019-04-16 13:06:30 +00:00
pub fn state_mut ( & mut self ) -> & mut State {
& mut self . state
}
2019-01-12 15:57:19 +00:00
2019-01-15 15:13:11 +00:00
/// Get a reference to the server's world.
2019-04-16 13:06:30 +00:00
pub fn world ( & self ) -> & World {
& self . world
}
2019-01-15 15:13:11 +00:00
2019-05-17 09:22:32 +00:00
/// Build a non-player character.
2019-05-25 21:13:38 +00:00
pub fn create_npc (
& mut self ,
2019-06-14 15:27:05 +00:00
pos : comp ::Pos ,
2019-08-25 09:35:54 +00:00
stats : comp ::Stats ,
2019-05-25 21:13:38 +00:00
body : comp ::Body ,
) -> EcsEntityBuilder {
2019-04-16 21:06:33 +00:00
self . state
. ecs_mut ( )
. create_entity_synced ( )
2019-05-25 21:13:38 +00:00
. with ( pos )
2019-06-14 15:27:05 +00:00
. with ( comp ::Vel ( Vec3 ::zero ( ) ) )
. with ( comp ::Ori ( Vec3 ::unit_y ( ) ) )
2019-06-09 14:20:20 +00:00
. with ( comp ::Controller ::default ( ) )
2019-06-30 11:48:28 +00:00
. with ( body )
2019-08-25 09:35:54 +00:00
. with ( stats )
2019-10-17 20:59:36 +00:00
. with ( comp ::Gravity ( 1.0 ) )
2019-08-23 10:11:37 +00:00
. with ( comp ::CharacterState ::default ( ) )
2019-04-16 21:06:33 +00:00
}
2019-07-21 12:42:45 +00:00
/// Build a static object entity
pub fn create_object (
& mut self ,
pos : comp ::Pos ,
object : comp ::object ::Body ,
) -> EcsEntityBuilder {
self . state
. ecs_mut ( )
. create_entity_synced ( )
. with ( pos )
. with ( comp ::Vel ( Vec3 ::zero ( ) ) )
. with ( comp ::Ori ( Vec3 ::unit_y ( ) ) )
. with ( comp ::Body ::Object ( object ) )
2019-10-23 19:40:45 +00:00
. with ( comp ::Mass ( 100.0 ) )
. with ( comp ::Gravity ( 1.0 ) )
2019-08-25 09:35:54 +00:00
//.with(comp::LightEmitter::default())
2019-07-21 12:42:45 +00:00
}
2019-08-25 12:27:17 +00:00
/// Build a projectile
pub fn create_projectile (
state : & mut State ,
pos : comp ::Pos ,
vel : comp ::Vel ,
body : comp ::Body ,
2019-09-17 12:43:19 +00:00
projectile : comp ::Projectile ,
2019-08-25 12:27:17 +00:00
) -> EcsEntityBuilder {
2019-10-17 20:59:36 +00:00
state
2019-08-25 12:27:17 +00:00
. ecs_mut ( )
. create_entity_synced ( )
. with ( pos )
. with ( vel )
2019-09-28 18:07:13 +00:00
. with ( comp ::Ori ( vel . 0. normalized ( ) ) )
2019-10-02 19:28:35 +00:00
. with ( comp ::Mass ( 0.0 ) )
2019-08-25 12:27:17 +00:00
. with ( body )
2019-09-17 12:43:19 +00:00
. with ( projectile )
2019-10-17 20:59:36 +00:00
. with ( comp ::Sticky )
2019-08-25 12:27:17 +00:00
}
2019-04-23 09:53:45 +00:00
pub fn create_player_character (
state : & mut State ,
entity : EcsEntity ,
client : & mut Client ,
2019-05-12 21:21:18 +00:00
name : String ,
2019-05-15 16:06:58 +00:00
body : comp ::Body ,
2019-08-27 19:56:46 +00:00
main : Option < comp ::Item > ,
2019-08-12 14:05:58 +00:00
server_settings : & ServerSettings ,
2019-04-23 09:53:45 +00:00
) {
2019-05-21 22:04:39 +00:00
let spawn_point = state . ecs ( ) . read_resource ::< SpawnPoint > ( ) . 0 ;
2019-06-30 11:48:28 +00:00
state . write_component ( entity , body ) ;
2019-08-27 19:56:46 +00:00
state . write_component ( entity , comp ::Stats ::new ( name , main ) ) ;
2019-06-09 14:20:20 +00:00
state . write_component ( entity , comp ::Controller ::default ( ) ) ;
2019-06-14 15:27:05 +00:00
state . write_component ( entity , comp ::Pos ( spawn_point ) ) ;
state . write_component ( entity , comp ::Vel ( Vec3 ::zero ( ) ) ) ;
state . write_component ( entity , comp ::Ori ( Vec3 ::unit_y ( ) ) ) ;
2019-10-17 20:59:36 +00:00
state . write_component ( entity , comp ::Gravity ( 1.0 ) ) ;
2019-08-23 10:11:37 +00:00
state . write_component ( entity , comp ::CharacterState ::default ( ) ) ;
2019-07-25 14:48:27 +00:00
state . write_component ( entity , comp ::Inventory ::default ( ) ) ;
2019-07-25 17:41:06 +00:00
state . write_component ( entity , comp ::InventoryUpdate ) ;
2019-05-22 20:53:24 +00:00
// Make sure physics are accepted.
2019-06-14 15:27:05 +00:00
state . write_component ( entity , comp ::ForceUpdate ) ;
2019-04-19 07:35:23 +00:00
2019-08-14 15:51:59 +00:00
// Give the Admin component to the player if their name exists in admin list
2019-08-12 16:11:06 +00:00
if server_settings . admins . contains (
& state
. ecs ( )
. read_storage ::< comp ::Player > ( )
. get ( entity )
2019-10-23 16:38:09 +00:00
. expect ( " Failed to fetch entity. " )
2019-08-12 16:11:06 +00:00
. alias ,
) {
2019-08-14 15:51:59 +00:00
state . write_component ( entity , comp ::Admin ) ;
2019-08-12 14:05:58 +00:00
}
2019-05-17 09:22:32 +00:00
// Tell the client its request was successful.
2019-05-19 22:20:25 +00:00
client . allow_state ( ClientState ::Character ) ;
2019-04-19 07:35:23 +00:00
}
2019-08-07 15:39:16 +00:00
/// Handle events coming through via the event bus
fn handle_events ( & mut self ) {
2019-08-25 12:27:17 +00:00
let events = self
. state
. ecs ( )
. read_resource ::< EventBus < ServerEvent > > ( )
. recv_all ( ) ;
2019-08-23 20:50:35 +00:00
for event in events {
2019-08-25 12:27:17 +00:00
let state = & mut self . state ;
let clients = & mut self . clients ;
2019-08-23 20:50:35 +00:00
let mut todo_remove = None ;
2019-08-25 12:27:17 +00:00
match event {
ServerEvent ::Explosion { pos , radius } = > {
const RAYS : usize = 500 ;
for _ in 0 .. RAYS {
let dir = Vec3 ::new (
rand ::random ::< f32 > ( ) - 0.5 ,
rand ::random ::< f32 > ( ) - 0.5 ,
rand ::random ::< f32 > ( ) - 0.5 ,
)
. normalized ( ) ;
let ecs = state . ecs_mut ( ) ;
let mut block_change = ecs . write_resource ::< BlockChange > ( ) ;
let _ = 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
. read_resource ::< TerrainGrid > ( )
2019-08-25 12:27:17 +00:00
. ray ( pos , pos + dir * radius )
. until ( | _ | rand ::random ::< f32 > ( ) < 0.05 )
. for_each ( | pos | block_change . set ( pos , Block ::empty ( ) ) )
. cast ( ) ;
2019-08-07 17:17:04 +00:00
}
2019-08-25 12:27:17 +00:00
}
2019-09-28 19:35:28 +00:00
ServerEvent ::Shoot {
entity ,
dir ,
2019-10-11 04:30:34 +00:00
body ,
light ,
2019-09-28 19:35:28 +00:00
projectile ,
2019-10-17 20:59:36 +00:00
gravity ,
2019-09-28 19:35:28 +00:00
} = > {
2019-10-06 19:21:33 +00:00
let mut pos = state
2019-08-25 12:27:17 +00:00
. ecs ( )
. read_storage ::< comp ::Pos > ( )
. get ( entity )
2019-10-23 16:38:09 +00:00
. expect ( " Failed to fetch entity " )
2019-08-25 12:27:17 +00:00
. 0 ;
2019-10-06 19:21:33 +00:00
// TODO: Player height
pos . z + = 1.2 ;
2019-10-17 20:59:36 +00:00
let mut builder = Self ::create_projectile (
2019-08-25 12:27:17 +00:00
state ,
comp ::Pos ( pos ) ,
2019-09-16 15:58:40 +00:00
comp ::Vel ( dir * 100.0 ) ,
2019-10-11 04:30:34 +00:00
body ,
2019-09-28 19:35:28 +00:00
projectile ,
2019-10-17 20:59:36 +00:00
) ;
if let Some ( light ) = light {
builder = builder . with ( light )
}
if let Some ( gravity ) = gravity {
builder = builder . with ( gravity )
}
builder . build ( ) ;
2019-08-25 12:27:17 +00:00
}
2019-08-23 10:11:37 +00:00
2019-10-17 20:59:36 +00:00
ServerEvent ::Damage { uid , change } = > {
2019-09-21 12:43:24 +00:00
let ecs = state . ecs_mut ( ) ;
if let Some ( entity ) = ecs . entity_from_uid ( uid . into ( ) ) {
if let Some ( stats ) = ecs . write_storage ::< comp ::Stats > ( ) . get_mut ( entity ) {
2019-10-17 20:59:36 +00:00
stats . health . change_by ( change ) ;
2019-09-21 12:43:24 +00:00
}
}
}
2019-09-17 12:43:19 +00:00
ServerEvent ::Destroy { entity , cause } = > {
2019-08-25 12:27:17 +00:00
let ecs = state . ecs_mut ( ) ;
// Chat message
if let Some ( player ) = ecs . read_storage ::< comp ::Player > ( ) . get ( entity ) {
let msg = if let comp ::HealthSource ::Attack { by } = cause {
ecs . entity_from_uid ( by . into ( ) ) . and_then ( | attacker | {
ecs . read_storage ::< comp ::Player > ( ) . get ( attacker ) . map (
| attacker_alias | {
format! (
" {} was killed by {} " ,
& player . alias , & attacker_alias . alias
)
} ,
)
} )
} else {
None
2019-08-23 20:50:35 +00:00
}
2019-08-25 12:27:17 +00:00
. unwrap_or ( format! ( " {} died " , & player . alias ) ) ;
clients . notify_registered ( ServerMsg ::kill ( msg ) ) ;
}
2019-08-23 10:11:37 +00:00
2019-09-17 12:43:19 +00:00
// Give EXP to the killer if entity had stats
2019-08-23 20:50:35 +00:00
let mut stats = ecs . write_storage ::< comp ::Stats > ( ) ;
if let Some ( entity_stats ) = stats . get ( entity ) . cloned ( ) {
if let comp ::HealthSource ::Attack { by } = cause {
ecs . entity_from_uid ( by . into ( ) ) . map ( | attacker | {
if let Some ( attacker_stats ) = stats . get_mut ( attacker ) {
// TODO: Discuss whether we should give EXP by Player Killing or not.
2019-10-08 16:12:08 +00:00
attacker_stats
. exp
. change_by ( ( entity_stats . level . level ( ) * 10 ) as i64 ) ;
2019-08-23 20:50:35 +00:00
}
} ) ;
2019-08-23 10:11:37 +00:00
}
}
2019-08-25 12:27:17 +00:00
if let Some ( client ) = clients . get_mut ( & entity ) {
let _ = ecs . write_storage ( ) . insert ( entity , comp ::Vel ( Vec3 ::zero ( ) ) ) ;
let _ = ecs . write_storage ( ) . insert ( entity , comp ::ForceUpdate ) ;
client . force_state ( ClientState ::Dead ) ;
} else {
2019-08-23 20:50:35 +00:00
todo_remove = Some ( entity . clone ( ) ) ;
2019-08-23 10:11:37 +00:00
}
}
2019-08-23 20:50:35 +00:00
2019-08-25 12:27:17 +00:00
ServerEvent ::Respawn ( entity ) = > {
// Only clients can respawn
if let Some ( client ) = clients . get_mut ( & entity ) {
2019-09-25 20:22:39 +00:00
let respawn_point = state
. read_component_cloned ::< comp ::Waypoint > ( entity )
. map ( | wp | wp . get_pos ( ) )
. unwrap_or ( state . ecs ( ) . read_resource ::< SpawnPoint > ( ) . 0 ) ;
2019-08-25 12:27:17 +00:00
client . allow_state ( ClientState ::Character ) ;
state
. ecs_mut ( )
. write_storage ::< comp ::Stats > ( )
. get_mut ( entity )
. map ( | stats | stats . revive ( ) ) ;
state
. ecs_mut ( )
. write_storage ::< comp ::Pos > ( )
. get_mut ( entity )
2019-09-25 20:22:39 +00:00
. map ( | pos | pos . 0 = respawn_point ) ;
2019-08-25 12:27:17 +00:00
let _ = state
. ecs_mut ( )
. write_storage ( )
. insert ( entity , comp ::ForceUpdate ) ;
}
}
2019-10-08 16:50:26 +00:00
ServerEvent ::LandOnGround { entity , vel } = > {
if vel . z < = - 25.0 {
if let Some ( stats ) = state
. ecs_mut ( )
. write_storage ::< comp ::Stats > ( )
. get_mut ( entity )
{
let falldmg = ( vel . z / 5.0 ) as i32 ;
if falldmg < 0 {
2019-10-17 20:59:36 +00:00
stats . health . change_by ( comp ::HealthChange {
amount : falldmg ,
cause : comp ::HealthSource ::World ,
} ) ;
2019-10-08 16:50:26 +00:00
}
}
}
}
2019-09-09 19:11:40 +00:00
ServerEvent ::Mount ( mounter , mountee ) = > {
if state
. ecs ( )
. read_storage ::< comp ::Mounting > ( )
. get ( mounter )
. is_none ( )
{
let not_mounting_yet = if let Some ( comp ::MountState ::Unmounted ) = state
. ecs ( )
. write_storage ::< comp ::MountState > ( )
. get_mut ( mountee )
. cloned ( )
{
true
} else {
false
} ;
if not_mounting_yet {
if let ( Some ( mounter_uid ) , Some ( mountee_uid ) ) = (
state . ecs ( ) . uid_from_entity ( mounter ) ,
state . ecs ( ) . uid_from_entity ( mountee ) ,
) {
state . write_component (
mountee ,
comp ::MountState ::MountedBy ( mounter_uid . into ( ) ) ,
) ;
state . write_component ( mounter , comp ::Mounting ( mountee_uid . into ( ) ) ) ;
}
}
}
}
ServerEvent ::Unmount ( mounter ) = > {
let mountee_entity = state
. ecs ( )
. write_storage ::< comp ::Mounting > ( )
. get ( mounter )
. and_then ( | mountee | state . ecs ( ) . entity_from_uid ( mountee . 0. into ( ) ) ) ;
if let Some ( mountee_entity ) = mountee_entity {
state
. ecs_mut ( )
. write_storage ::< comp ::MountState > ( )
. get_mut ( mountee_entity )
. map ( | ms | * ms = comp ::MountState ::Unmounted ) ;
}
state . delete_component ::< comp ::Mounting > ( mounter ) ;
}
2019-10-11 04:30:34 +00:00
ServerEvent ::Possess ( possessor_uid , possesse_uid ) = > {
if let ( Some ( possessor ) , Some ( possesse ) ) = (
state . ecs ( ) . entity_from_uid ( possessor_uid . into ( ) ) ,
state . ecs ( ) . entity_from_uid ( possesse_uid . into ( ) ) ,
) {
// You can't possess other players
if clients . get ( & possesse ) . is_none ( ) {
if let Some ( mut client ) = clients . remove ( & possessor ) {
client . notify ( ServerMsg ::SetPlayerEntity ( possesse_uid . into ( ) ) ) ;
clients . add ( possesse , client ) ;
// Create inventory if it doesn't exist
{
let mut inventories =
state . ecs_mut ( ) . write_storage ::< comp ::Inventory > ( ) ;
2019-10-11 23:30:05 +00:00
if let Some ( inventory ) = inventories . get_mut ( possesse ) {
2019-10-11 04:30:34 +00:00
inventory
. push ( comp ::Item ::Debug ( comp ::item ::Debug ::Possess ) ) ;
} else {
let _ = inventories . insert (
possesse ,
comp ::Inventory {
slots : vec ! [ Some ( comp ::Item ::Debug (
comp ::item ::Debug ::Possess ,
) ) ] ,
} ,
) ;
}
}
let _ = state
. ecs_mut ( )
. write_storage ::< comp ::InventoryUpdate > ( )
. insert ( possesse , comp ::InventoryUpdate ) ;
// Move player component
{
let mut players =
state . ecs_mut ( ) . write_storage ::< comp ::Player > ( ) ;
if let Some ( player ) = players . get ( possessor ) . cloned ( ) {
let _ = players . insert ( possesse , player ) ;
}
}
// Remove will of the entity
let _ = state
. ecs_mut ( )
. write_storage ::< comp ::Agent > ( )
. remove ( possesse ) ;
// Transfer admin powers
{
let mut admins = state . ecs_mut ( ) . write_storage ::< comp ::Admin > ( ) ;
if let Some ( admin ) = admins . remove ( possessor ) {
let _ = admins . insert ( possesse , admin ) ;
}
}
// Transfer waypoint
{
let mut waypoints =
state . ecs_mut ( ) . write_storage ::< comp ::Waypoint > ( ) ;
if let Some ( waypoint ) = waypoints . remove ( possessor ) {
let _ = waypoints . insert ( possesse , waypoint ) ;
}
}
}
}
}
}
2019-08-23 20:50:35 +00:00
}
2019-08-23 20:50:35 +00:00
if let Some ( entity ) = todo_remove {
let _ = state . ecs_mut ( ) . delete_entity_synced ( entity ) ;
}
2019-08-07 15:39:16 +00:00
}
}
2019-05-17 09:22:32 +00:00
/// Execute a single server tick, handle input and update the game state by the given duration.
2019-07-01 16:38:19 +00:00
pub fn tick ( & mut self , _input : Input , dt : Duration ) -> Result < Vec < Event > , Error > {
2019-10-06 17:35:47 +00:00
self . tick + = 1 ;
2019-01-02 17:23:31 +00:00
// This tick function is the centre of the Veloren universe. Most server-side things are
// managed from here, and as such it's important that it stays organised. Please consult
// the core developers before making significant changes to this code. Here is the
// approximate order of things. Please update it as this code changes.
//
// 1) Collect input from the frontend, apply input effects to the state of the game
// 2) Go through any events (timer-driven or otherwise) that need handling and apply them
// to the state of the game
// 3) Go through all incoming client network communications, apply them to the game state
// 4) Perform a single LocalState tick (i.e: update the world and entities in the world)
// 5) Go through the terrain update queue and apply all changes to the terrain
// 6) Send relevant state updates to all clients
2019-09-06 14:21:09 +00:00
// 7) Update Metrics with current data
// 8) Finish the tick, passing control of the main thread back to the frontend
2019-01-02 17:23:31 +00:00
2019-09-07 13:10:57 +00:00
let before_tick_1 = Instant ::now ( ) ;
2019-05-17 20:47:58 +00:00
// 1) Build up a list of events for this frame, to be passed to the frontend.
2019-03-03 22:02:38 +00:00
let mut frontend_events = Vec ::new ( ) ;
2019-05-17 09:22:32 +00:00
// If networking has problems, handle them.
2019-03-05 18:39:18 +00:00
if let Some ( err ) = self . postoffice . error ( ) {
2019-03-03 22:02:38 +00:00
return Err ( err . into ( ) ) ;
}
2019-05-17 20:47:58 +00:00
// 2)
2019-03-03 22:02:38 +00:00
2019-05-17 20:47:58 +00:00
// 3) Handle inputs from clients
frontend_events . append ( & mut self . handle_new_connections ( ) ? ) ;
2019-03-03 22:02:38 +00:00
frontend_events . append ( & mut self . handle_new_messages ( ) ? ) ;
2019-08-07 17:17:04 +00:00
// Handle game events
self . handle_events ( ) ;
2019-09-07 13:10:57 +00:00
let before_tick_4 = Instant ::now ( ) ;
2019-05-17 20:47:58 +00:00
// 4) Tick the client's LocalState.
2019-01-02 17:23:31 +00:00
self . state . tick ( dt ) ;
2019-05-18 08:59:58 +00:00
// Tick the world
self . world . tick ( dt ) ;
2019-09-07 13:10:57 +00:00
let before_tick_5 = Instant ::now ( ) ;
2019-05-17 20:47:58 +00:00
// 5) Fetch any generated `TerrainChunk`s and insert them into the terrain.
2019-05-17 09:22:32 +00:00
// Also, send the chunk data to anybody that is close by.
2019-09-16 01:38:53 +00:00
' insert_terrain_chunks : while let Ok ( ( key , res ) ) = self . chunk_rx . try_recv ( ) {
let ( chunk , supplement ) = match res {
Ok ( ( chunk , supplement ) ) = > ( chunk , supplement ) ,
Err ( entity ) = > {
self . clients . notify (
entity ,
ServerMsg ::TerrainChunkUpdate {
key ,
chunk : Err ( ( ) ) ,
} ,
) ;
2019-09-16 02:18:40 +00:00
continue 'insert_terrain_chunks ;
2019-09-16 01:38:53 +00:00
}
} ;
2019-05-17 09:22:32 +00:00
// Send the chunk to all nearby players.
2019-05-27 17:01:00 +00:00
for ( entity , view_distance , pos ) in (
2019-04-22 08:20:25 +00:00
& self . state . ecs ( ) . entities ( ) ,
& self . state . ecs ( ) . read_storage ::< comp ::Player > ( ) ,
2019-06-14 15:27:05 +00:00
& self . state . ecs ( ) . read_storage ::< comp ::Pos > ( ) ,
2019-04-29 20:37:19 +00:00
)
. join ( )
2019-05-27 17:01:00 +00:00
. filter_map ( | ( entity , player , pos ) | {
player . view_distance . map ( | vd | ( entity , vd , pos ) )
} )
2019-04-29 20:37:19 +00:00
{
2019-04-25 19:25:22 +00:00
let chunk_pos = self . state . terrain ( ) . pos_key ( pos . 0. map ( | e | e as i32 ) ) ;
2019-06-23 19:49:15 +00:00
let adjusted_dist_sqr = ( Vec2 ::from ( chunk_pos ) - Vec2 ::from ( key ) )
. map ( | e : i32 | ( e . abs ( ) as u32 ) . checked_sub ( 2 ) . unwrap_or ( 0 ) )
. magnitude_squared ( ) ;
2019-04-25 19:25:22 +00:00
2019-06-23 19:49:15 +00:00
if adjusted_dist_sqr < = view_distance . pow ( 2 ) {
2019-04-29 20:37:19 +00:00
self . clients . notify (
entity ,
ServerMsg ::TerrainChunkUpdate {
key ,
2019-09-16 01:38:53 +00:00
chunk : Ok ( Box ::new ( chunk . clone ( ) ) ) ,
2019-04-29 20:37:19 +00:00
} ,
) ;
2019-04-25 19:25:22 +00:00
}
2019-04-10 23:16:29 +00:00
}
self . state . insert_chunk ( key , chunk ) ;
2019-04-25 19:08:26 +00:00
self . pending_chunks . remove ( & key ) ;
2019-08-02 14:37:26 +00:00
// Handle chunk supplement
for npc in supplement . npcs {
2019-08-03 10:35:16 +00:00
let ( mut stats , mut body ) = if rand ::random ( ) {
2019-08-27 19:56:46 +00:00
let stats = comp ::Stats ::new (
" Humanoid " . to_string ( ) ,
Some ( comp ::Item ::Tool {
kind : comp ::item ::Tool ::Sword ,
2019-09-29 11:41:04 +00:00
power : 5 ,
2019-10-09 19:28:05 +00:00
stamina : 0 ,
strength : 0 ,
dexterity : 0 ,
intelligence : 0 ,
2019-08-27 19:56:46 +00:00
} ) ,
) ;
2019-08-03 10:35:16 +00:00
let body = comp ::Body ::Humanoid ( comp ::humanoid ::Body ::random ( ) ) ;
( stats , body )
} else {
2019-08-27 19:56:46 +00:00
let stats = comp ::Stats ::new ( " Wolf " . to_string ( ) , None ) ;
2019-08-03 10:35:16 +00:00
let body = comp ::Body ::QuadrupedMedium ( comp ::quadruped_medium ::Body ::random ( ) ) ;
( stats , body )
} ;
2019-08-02 18:56:37 +00:00
let mut scale = 1.0 ;
2019-10-04 16:07:26 +00:00
// TODO: Remove this and implement scaling or level depending on stuff like species instead
2019-10-08 16:12:08 +00:00
stats . level . set_level ( rand ::thread_rng ( ) . gen_range ( 1 , 3 ) ) ;
2019-10-04 16:07:26 +00:00
2019-08-02 18:56:37 +00:00
if npc . boss {
2019-08-02 20:31:22 +00:00
if rand ::random ::< f32 > ( ) < 0.8 {
2019-08-27 19:56:46 +00:00
stats = comp ::Stats ::new (
" Humanoid " . to_string ( ) ,
Some ( comp ::Item ::Tool {
kind : comp ::item ::Tool ::Sword ,
power : 10 ,
2019-10-09 19:28:05 +00:00
stamina : 0 ,
strength : 0 ,
dexterity : 0 ,
intelligence : 0 ,
2019-08-27 19:56:46 +00:00
} ) ,
) ;
2019-08-02 20:31:22 +00:00
body = comp ::Body ::Humanoid ( comp ::humanoid ::Body ::random ( ) ) ;
}
2019-10-08 16:12:08 +00:00
stats . level . set_level ( rand ::thread_rng ( ) . gen_range ( 10 , 50 ) ) ;
2019-08-04 09:32:50 +00:00
scale = 2.5 + rand ::random ::< f32 > ( ) ;
2019-08-02 18:56:37 +00:00
}
2019-10-08 16:12:08 +00:00
stats . update_max_hp ( ) ;
2019-10-09 15:09:25 +00:00
stats
. health
. set_to ( stats . health . maximum ( ) , comp ::HealthSource ::Revive ) ;
2019-08-25 09:35:54 +00:00
self . create_npc ( comp ::Pos ( npc . pos ) , stats , body )
2019-08-02 18:56:37 +00:00
. with ( comp ::Agent ::enemy ( ) )
. with ( comp ::Scale ( scale ) )
2019-08-02 14:37:26 +00:00
. build ( ) ;
}
2019-04-25 19:08:26 +00:00
}
2019-07-01 13:38:45 +00:00
fn chunk_in_vd (
player_pos : Vec3 < f32 > ,
chunk_pos : Vec2 < i32 > ,
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 : & TerrainGrid ,
2019-07-01 13:38:45 +00:00
vd : u32 ,
) -> bool {
2019-07-01 13:36:45 +00:00
let player_chunk_pos = terrain . pos_key ( player_pos . map ( | e | e as i32 ) ) ;
let adjusted_dist_sqr = Vec2 ::from ( player_chunk_pos - chunk_pos )
. map ( | e : i32 | ( e . abs ( ) as u32 ) . checked_sub ( 2 ) . unwrap_or ( 0 ) )
. magnitude_squared ( ) ;
adjusted_dist_sqr < = vd . pow ( 2 )
}
2019-05-17 09:22:32 +00:00
// Remove chunks that are too far from players.
2019-04-25 19:08:26 +00:00
let mut chunks_to_remove = Vec ::new ( ) ;
2019-09-16 02:01:05 +00:00
self . state
. terrain ( )
. iter ( )
. map ( | ( k , _ ) | k )
2019-09-16 01:38:53 +00:00
. chain ( self . pending_chunks . keys ( ) . cloned ( ) )
. for_each ( | chunk_key | {
let mut should_drop = true ;
// For each player with a position, calculate the distance.
for ( player , pos ) in (
& self . state . ecs ( ) . read_storage ::< comp ::Player > ( ) ,
& self . state . ecs ( ) . read_storage ::< comp ::Pos > ( ) ,
)
. join ( )
2019-06-05 16:32:33 +00:00
{
2019-09-16 01:38:53 +00:00
if player
. view_distance
. map ( | vd | chunk_in_vd ( pos . 0 , chunk_key , & self . state . terrain ( ) , vd ) )
. unwrap_or ( false )
{
should_drop = false ;
break ;
}
2019-05-19 00:45:02 +00:00
}
2019-04-25 19:08:26 +00:00
2019-09-16 01:38:53 +00:00
if should_drop {
chunks_to_remove . push ( chunk_key ) ;
}
} ) ;
2019-04-25 19:08:26 +00:00
for key in chunks_to_remove {
self . state . remove_chunk ( key ) ;
2019-09-16 01:38:53 +00:00
if let Some ( cancel ) = self . pending_chunks . remove ( & key ) {
cancel . store ( true , Ordering ::Relaxed ) ;
}
2019-04-10 23:16:29 +00:00
}
2019-09-07 13:10:57 +00:00
let before_tick_6 = Instant ::now ( ) ;
2019-05-17 20:47:58 +00:00
// 6) Synchronise clients with the new state of the world.
2019-03-04 19:50:26 +00:00
self . sync_clients ( ) ;
2019-07-01 13:36:45 +00:00
// Sync changed chunks
2019-07-20 15:37:01 +00:00
' chunk : for chunk_key in & self . state . terrain_changes ( ) . modified_chunks {
2019-07-01 13:36:45 +00:00
let terrain = self . state . terrain ( ) ;
for ( entity , player , pos ) in (
& self . state . ecs ( ) . entities ( ) ,
& self . state . ecs ( ) . read_storage ::< comp ::Player > ( ) ,
& self . state . ecs ( ) . read_storage ::< comp ::Pos > ( ) ,
)
. join ( )
{
if player
. view_distance
. map ( | vd | chunk_in_vd ( pos . 0 , * chunk_key , & terrain , vd ) )
. unwrap_or ( false )
{
self . clients . notify (
entity ,
ServerMsg ::TerrainChunkUpdate {
key : * chunk_key ,
2019-09-16 01:38:53 +00:00
chunk : Ok ( Box ::new ( match self . state . terrain ( ) . get_key ( * chunk_key ) {
2019-07-01 13:36:45 +00:00
Some ( chunk ) = > chunk . clone ( ) ,
None = > break 'chunk ,
2019-09-16 01:38:53 +00:00
} ) ) ,
2019-07-01 13:36:45 +00:00
} ,
) ;
}
}
}
2019-08-02 17:48:14 +00:00
2019-07-20 15:37:01 +00:00
// Sync changed blocks
let msg =
ServerMsg ::TerrainBlockUpdates ( self . state . terrain_changes ( ) . modified_blocks . clone ( ) ) ;
for ( entity , player ) in (
& self . state . ecs ( ) . entities ( ) ,
& self . state . ecs ( ) . read_storage ::< comp ::Player > ( ) ,
)
. join ( )
{
if player . view_distance . is_some ( ) {
self . clients . notify ( entity , msg . clone ( ) ) ;
}
}
2019-07-01 13:36:45 +00:00
2019-08-02 17:48:14 +00:00
// Remove NPCs that are outside the view distances of all players
2019-10-06 17:35:47 +00:00
// This is done by removing NPCs in unloaded chunks
2019-08-02 17:48:14 +00:00
let to_delete = {
let terrain = self . state . terrain ( ) ;
(
& self . state . ecs ( ) . entities ( ) ,
& self . state . ecs ( ) . read_storage ::< comp ::Pos > ( ) ,
& self . state . ecs ( ) . read_storage ::< comp ::Agent > ( ) ,
)
. join ( )
. filter ( | ( _ , pos , _ ) | terrain . get ( pos . 0. map ( | e | e . floor ( ) as i32 ) ) . is_err ( ) )
. map ( | ( entity , _ , _ ) | entity )
. collect ::< Vec < _ > > ( )
} ;
for entity in to_delete {
2019-10-07 13:15:29 +00:00
let _ = self . state . ecs_mut ( ) . delete_entity_synced ( entity ) ;
2019-08-02 17:48:14 +00:00
}
2019-09-07 13:10:57 +00:00
let before_tick_7 = Instant ::now ( ) ;
2019-09-06 14:21:09 +00:00
// 7) Update Metrics
2019-09-07 13:10:57 +00:00
self . metrics
. tick_time
. with_label_values ( & [ " input " ] )
. set ( ( before_tick_4 - before_tick_1 ) . as_nanos ( ) as i64 ) ;
self . metrics
. tick_time
. with_label_values ( & [ " world " ] )
. set ( ( before_tick_5 - before_tick_4 ) . as_nanos ( ) as i64 ) ;
self . metrics
. tick_time
. with_label_values ( & [ " terrain " ] )
. set ( ( before_tick_6 - before_tick_5 ) . as_nanos ( ) as i64 ) ;
self . metrics
. tick_time
. with_label_values ( & [ " sync " ] )
. set ( ( before_tick_7 - before_tick_6 ) . as_nanos ( ) as i64 ) ;
self . metrics . player_online . set ( self . clients . len ( ) as i64 ) ;
2019-09-10 13:22:34 +00:00
self . metrics
. time_of_day
. set ( self . state . ecs ( ) . read_resource ::< TimeOfDay > ( ) . 0 ) ;
if self . metrics . is_100th_tick ( ) {
let mut chonk_cnt = 0 ;
let chunk_cnt = self . state . terrain ( ) . iter ( ) . fold ( 0 , | a , ( _ , c ) | {
chonk_cnt + = 1 ;
a + c . sub_chunks_len ( )
} ) ;
self . metrics . chonks_count . set ( chonk_cnt as i64 ) ;
self . metrics . chunks_count . set ( chunk_cnt as i64 ) ;
}
2019-09-07 13:10:57 +00:00
//self.metrics.entity_count.set(self.state.);
self . metrics
. tick_time
. with_label_values ( & [ " metrics " ] )
. set ( before_tick_7 . elapsed ( ) . as_nanos ( ) as i64 ) ;
2019-09-06 14:21:09 +00:00
// 8) Finish the tick, pass control back to the frontend.
2019-05-25 21:13:38 +00:00
2019-03-03 22:02:38 +00:00
Ok ( frontend_events )
2019-01-02 17:23:31 +00:00
}
2019-01-30 12:11:34 +00:00
2019-05-17 09:22:32 +00:00
/// Clean up the server after a tick.
2019-01-30 12:11:34 +00:00
pub fn cleanup ( & mut self ) {
// Cleanup the local state
self . state . cleanup ( ) ;
}
2019-03-03 22:02:38 +00:00
2019-05-17 09:22:32 +00:00
/// Handle new client connections.
2019-03-03 22:02:38 +00:00
fn handle_new_connections ( & mut self ) -> Result < Vec < Event > , Error > {
let mut frontend_events = Vec ::new ( ) ;
2019-06-06 14:48:41 +00:00
for postbox in self . postoffice . new_postboxes ( ) {
2019-04-16 13:06:30 +00:00
let entity = self . state . ecs_mut ( ) . create_entity_synced ( ) . build ( ) ;
2019-04-21 18:12:29 +00:00
let mut client = Client {
client_state : ClientState ::Connected ,
postbox ,
last_ping : self . state . get_time ( ) ,
} ;
2019-07-01 11:19:26 +00:00
if self . server_settings . max_players < = self . clients . len ( ) {
2019-07-04 16:14:45 +00:00
client . notify ( ServerMsg ::Error ( ServerError ::TooManyPlayers ) ) ;
2019-07-01 11:19:26 +00:00
} else {
// Return the state of the current world (all of the components that Sphynx tracks).
2019-10-16 11:39:41 +00:00
log ::info! ( " Starting initial sync with client. " ) ;
2019-07-01 11:19:26 +00:00
client . notify ( ServerMsg ::InitialSync {
ecs_state : self . state . ecs ( ) . gen_state_package ( ) ,
entity_uid : self . state . ecs ( ) . uid_from_entity ( entity ) . unwrap ( ) . into ( ) , // Can't fail.
server_info : self . server_info . clone ( ) ,
2019-10-16 11:39:41 +00:00
// world_map: (WORLD_SIZE/*, self.world.sim().get_map()*/),
2019-07-01 11:19:26 +00:00
} ) ;
2019-10-16 11:39:41 +00:00
log ::info! ( " Done initial sync with client. " ) ;
2019-07-01 11:19:26 +00:00
frontend_events . push ( Event ::ClientConnected { entity } ) ;
}
2019-03-05 00:00:11 +00:00
2019-04-23 09:53:45 +00:00
self . clients . add ( entity , client ) ;
2019-03-03 22:02:38 +00:00
}
Ok ( frontend_events )
}
2019-05-17 09:22:32 +00:00
/// Handle new client messages.
2019-03-03 22:02:38 +00:00
fn handle_new_messages ( & mut self ) -> Result < Vec < Event > , Error > {
let mut frontend_events = Vec ::new ( ) ;
2019-08-08 03:56:02 +00:00
let accounts = & mut self . accounts ;
2019-08-12 14:05:58 +00:00
let server_settings = & self . server_settings ;
2019-08-08 03:56:02 +00:00
2019-03-03 22:02:38 +00:00
let state = & mut self . state ;
let mut new_chat_msgs = Vec ::new ( ) ;
2019-03-05 00:00:11 +00:00
let mut disconnected_clients = Vec ::new ( ) ;
2019-04-10 23:41:37 +00:00
let mut requested_chunks = Vec ::new ( ) ;
2019-07-26 21:01:41 +00:00
let mut dropped_items = Vec ::new ( ) ;
2019-03-03 22:02:38 +00:00
2019-04-10 23:16:29 +00:00
self . clients . remove_if ( | entity , client | {
2019-04-10 17:23:27 +00:00
let mut disconnect = false ;
2019-03-03 22:02:38 +00:00
let new_msgs = client . postbox . new_messages ( ) ;
2019-05-17 09:22:32 +00:00
// Update client ping.
2019-03-03 22:02:38 +00:00
if new_msgs . len ( ) > 0 {
client . last_ping = state . get_time ( ) ;
2019-05-17 09:22:32 +00:00
// Process incoming messages.
2019-03-03 22:02:38 +00:00
for msg in new_msgs {
2019-04-19 19:32:47 +00:00
match msg {
ClientMsg ::RequestState ( requested_state ) = > match requested_state {
2019-04-21 18:12:29 +00:00
ClientState ::Connected = > disconnect = true , // Default state
2019-04-21 15:52:15 +00:00
ClientState ::Registered = > match client . client_state {
2019-05-17 09:22:32 +00:00
// Use ClientMsg::Register instead.
2019-04-29 20:37:19 +00:00
ClientState ::Connected = > {
client . error_state ( RequestStateError ::WrongMessage )
}
ClientState ::Registered = > {
client . error_state ( RequestStateError ::Already )
}
2019-05-23 15:14:39 +00:00
ClientState ::Spectator
| ClientState ::Character
| ClientState ::Dead = > client . allow_state ( ClientState ::Registered ) ,
2019-05-24 19:10:18 +00:00
ClientState ::Pending = > { }
2019-04-20 17:54:37 +00:00
} ,
ClientState ::Spectator = > match requested_state {
2019-05-17 09:22:32 +00:00
// Become Registered first.
2019-04-29 20:37:19 +00:00
ClientState ::Connected = > {
client . error_state ( RequestStateError ::Impossible )
}
ClientState ::Spectator = > {
client . error_state ( RequestStateError ::Already )
}
2019-05-23 15:14:39 +00:00
ClientState ::Registered
| ClientState ::Character
| ClientState ::Dead = > client . allow_state ( ClientState ::Spectator ) ,
2019-05-24 19:10:18 +00:00
ClientState ::Pending = > { }
2019-04-20 17:54:37 +00:00
} ,
2019-05-17 09:22:32 +00:00
// Use ClientMsg::Character instead.
2019-04-29 20:37:19 +00:00
ClientState ::Character = > {
client . error_state ( RequestStateError ::WrongMessage )
}
2019-05-23 15:14:39 +00:00
ClientState ::Dead = > client . error_state ( RequestStateError ::Impossible ) ,
2019-05-24 19:10:18 +00:00
ClientState ::Pending = > { }
2019-04-19 19:32:47 +00:00
} ,
2019-06-29 15:04:06 +00:00
// Valid player
2019-08-08 03:56:02 +00:00
ClientMsg ::Register { player , password } if player . is_valid ( ) = > {
2019-08-08 22:24:14 +00:00
if ! accounts . query ( player . alias . clone ( ) , password ) {
client . error_state ( RequestStateError ::Denied ) ;
break ;
2019-08-08 03:56:02 +00:00
}
2019-06-29 15:48:34 +00:00
match client . client_state {
ClientState ::Connected = > {
Self ::initialize_player ( state , entity , client , player ) ;
2019-05-27 17:45:43 +00:00
}
2019-06-29 15:48:34 +00:00
// Use RequestState instead (No need to send `player` again).
_ = > client . error_state ( RequestStateError ::Impossible ) ,
2019-04-29 20:37:19 +00:00
}
2019-08-08 15:23:58 +00:00
//client.allow_state(ClientState::Registered);
2019-06-29 15:48:34 +00:00
}
2019-06-29 15:04:06 +00:00
// Invalid player
2019-07-01 16:38:19 +00:00
ClientMsg ::Register { .. } = > {
2019-06-29 15:48:34 +00:00
client . error_state ( RequestStateError ::Impossible )
}
2019-05-19 00:45:02 +00:00
ClientMsg ::SetViewDistance ( view_distance ) = > match client . client_state {
ClientState ::Character { .. } = > {
state
. ecs_mut ( )
. write_storage ::< comp ::Player > ( )
. get_mut ( entity )
. map ( | player | player . view_distance = Some ( view_distance ) ) ;
}
_ = > { }
} ,
2019-08-30 20:46:45 +00:00
ClientMsg ::UseInventorySlot ( x ) = > {
2019-08-30 19:54:33 +00:00
let item = state
2019-08-28 20:47:52 +00:00
. ecs ( )
. write_storage ::< comp ::Inventory > ( )
. get_mut ( entity )
. and_then ( | inv | inv . remove ( x ) ) ;
2019-08-30 19:54:33 +00:00
match item {
Some ( comp ::Item ::Tool { .. } ) | Some ( comp ::Item ::Debug ( _ ) ) = > {
if let Some ( stats ) =
state . ecs ( ) . write_storage ::< comp ::Stats > ( ) . get_mut ( entity )
{
2019-08-30 22:09:25 +00:00
// Insert old item into inventory
if let Some ( old_item ) = stats . equipment . main . take ( ) {
state
. ecs ( )
. write_storage ::< comp ::Inventory > ( )
. get_mut ( entity )
. map ( | inv | inv . insert ( x , old_item ) ) ;
}
2019-08-30 19:54:33 +00:00
stats . equipment . main = item ;
2019-08-28 20:47:52 +00:00
}
}
2019-09-25 15:52:58 +00:00
Some ( comp ::Item ::Consumable { effect , .. } ) = > {
state . apply_effect ( entity , effect ) ;
}
2019-09-25 22:13:45 +00:00
Some ( item ) = > {
// Re-insert it if unused
let _ = state
. ecs ( )
. write_storage ::< comp ::Inventory > ( )
. get_mut ( entity )
. map ( | inv | inv . insert ( x , item ) ) ;
}
2019-08-30 19:54:33 +00:00
_ = > { }
2019-08-28 20:47:52 +00:00
}
2019-08-30 19:54:33 +00:00
state . write_component ( entity , comp ::InventoryUpdate ) ;
2019-08-28 20:47:52 +00:00
}
2019-07-25 22:52:28 +00:00
ClientMsg ::SwapInventorySlots ( a , b ) = > {
state
. ecs ( )
. write_storage ::< comp ::Inventory > ( )
. get_mut ( entity )
. map ( | inv | inv . swap_slots ( a , b ) ) ;
state . write_component ( entity , comp ::InventoryUpdate ) ;
}
2019-07-26 17:08:40 +00:00
ClientMsg ::DropInventorySlot ( x ) = > {
2019-07-26 21:01:41 +00:00
let item = state
2019-07-26 17:08:40 +00:00
. ecs ( )
. write_storage ::< comp ::Inventory > ( )
. get_mut ( entity )
2019-07-26 21:01:41 +00:00
. and_then ( | inv | inv . remove ( x ) ) ;
2019-07-26 17:08:40 +00:00
state . write_component ( entity , comp ::InventoryUpdate ) ;
2019-07-26 21:01:41 +00:00
2019-07-29 16:19:08 +00:00
if let ( Some ( item ) , Some ( pos ) ) =
( item , state . ecs ( ) . read_storage ::< comp ::Pos > ( ) . get ( entity ) )
2019-07-26 21:01:41 +00:00
{
2019-07-29 16:19:08 +00:00
dropped_items . push ( (
* pos ,
state
. ecs ( )
. read_storage ::< comp ::Ori > ( )
. get ( entity )
. copied ( )
. unwrap_or ( comp ::Ori ( Vec3 ::unit_y ( ) ) ) ,
item ,
) ) ;
2019-07-26 21:01:41 +00:00
}
2019-07-26 17:08:40 +00:00
}
2019-07-29 16:19:08 +00:00
ClientMsg ::PickUp ( uid ) = > {
let item_entity = state . ecs_mut ( ) . entity_from_uid ( uid ) ;
let ecs = state . ecs_mut ( ) ;
let item_entity = if let ( Some ( ( item , item_entity ) ) , Some ( inv ) ) = (
item_entity . and_then ( | item_entity | {
ecs . write_storage ::< comp ::Item > ( )
. get_mut ( item_entity )
2019-08-27 19:56:46 +00:00
. map ( | item | ( item . clone ( ) , item_entity ) )
2019-07-29 16:19:08 +00:00
} ) ,
ecs . write_storage ::< comp ::Inventory > ( ) . get_mut ( entity ) ,
) {
2019-08-28 20:47:52 +00:00
if inv . push ( item ) . is_none ( ) {
2019-07-29 16:19:08 +00:00
Some ( item_entity )
} else {
None
}
} else {
None
} ;
if let Some ( item_entity ) = item_entity {
let _ = ecs . delete_entity_synced ( item_entity ) ;
}
state . write_component ( entity , comp ::InventoryUpdate ) ;
}
2019-08-27 19:56:46 +00:00
ClientMsg ::Character { name , body , main } = > match client . client_state {
2019-05-17 09:22:32 +00:00
// Become Registered first.
2019-04-29 20:37:19 +00:00
ClientState ::Connected = > {
client . error_state ( RequestStateError ::Impossible )
}
2019-05-23 15:14:39 +00:00
ClientState ::Registered
| ClientState ::Spectator
| ClientState ::Dead = > {
2019-10-06 16:26:51 +00:00
if let ( Some ( player ) , None ) = (
state . ecs ( ) . read_storage ::< comp ::Player > ( ) . get ( entity ) ,
// Only send login message if the player didn't have a body
// previously
state . ecs ( ) . read_storage ::< comp ::Body > ( ) . get ( entity ) ,
) {
new_chat_msgs . push ( (
None ,
ServerMsg ::broadcast ( format! (
" [{}] is now online. " ,
& player . alias
) ) ,
) ) ;
}
2019-08-12 14:05:58 +00:00
Self ::create_player_character (
state ,
entity ,
client ,
name ,
body ,
2019-10-09 19:28:05 +00:00
main . map ( | t | comp ::Item ::Tool {
kind : t ,
power : 10 ,
stamina : 0 ,
strength : 0 ,
dexterity : 0 ,
intelligence : 0 ,
} ) ,
2019-08-12 14:05:58 +00:00
& server_settings ,
) ;
2019-10-06 17:35:47 +00:00
Self ::initialize_region_subscription ( state , client , entity ) ;
2019-04-29 20:37:19 +00:00
}
ClientState ::Character = > {
client . error_state ( RequestStateError ::Already )
}
2019-05-24 19:10:18 +00:00
ClientState ::Pending = > { }
2019-04-19 19:32:47 +00:00
} ,
2019-06-09 14:20:20 +00:00
ClientMsg ::Controller ( controller ) = > match client . client_state {
ClientState ::Connected
| ClientState ::Registered
| ClientState ::Spectator = > {
client . error_state ( RequestStateError ::Impossible )
2019-05-25 21:13:38 +00:00
}
2019-06-09 14:20:20 +00:00
ClientState ::Dead | ClientState ::Character = > {
state . write_component ( entity , controller ) ;
2019-05-25 21:13:38 +00:00
}
2019-06-09 14:20:20 +00:00
ClientState ::Pending = > { }
2019-05-25 21:13:38 +00:00
} ,
2019-07-21 18:34:52 +00:00
ClientMsg ::ChatMsg { chat_type , message } = > match client . client_state {
2019-04-29 20:37:19 +00:00
ClientState ::Connected = > {
client . error_state ( RequestStateError ::Impossible )
}
2019-04-23 09:53:45 +00:00
ClientState ::Registered
| ClientState ::Spectator
2019-05-23 15:14:39 +00:00
| ClientState ::Dead
2019-08-14 04:38:54 +00:00
| ClientState ::Character = > match validate_chat_msg ( & message ) {
Ok ( ( ) ) = > new_chat_msgs . push ( (
Some ( entity ) ,
ServerMsg ::ChatMsg { chat_type , message } ,
) ) ,
Err ( ChatMsgValidationError ::TooLong ) = > log ::warn! (
" Recieved a chat message that's too long (max:{} len:{}) " ,
MAX_BYTES_CHAT_MSG ,
message . len ( )
) ,
} ,
2019-05-24 19:10:18 +00:00
ClientState ::Pending = > { }
2019-04-19 19:32:47 +00:00
} ,
2019-05-31 18:45:16 +00:00
ClientMsg ::PlayerPhysics { pos , vel , ori } = > match client . client_state {
2019-04-19 19:32:47 +00:00
ClientState ::Character = > {
2019-04-10 23:16:29 +00:00
state . write_component ( entity , pos ) ;
state . write_component ( entity , vel ) ;
2019-05-31 18:45:16 +00:00
state . write_component ( entity , ori ) ;
2019-04-23 09:53:45 +00:00
}
2019-05-17 09:22:32 +00:00
// Only characters can send positions.
2019-04-22 14:25:37 +00:00
_ = > client . error_state ( RequestStateError ::Impossible ) ,
2019-04-19 19:32:47 +00:00
} ,
2019-07-02 18:19:16 +00:00
ClientMsg ::BreakBlock ( pos ) = > {
if state
. ecs_mut ( )
. read_storage ::< comp ::CanBuild > ( )
. get ( entity )
. is_some ( )
{
2019-09-25 21:17:43 +00:00
state . set_block ( pos , Block ::empty ( ) ) ;
2019-07-02 18:19:16 +00:00
}
}
ClientMsg ::PlaceBlock ( pos , block ) = > {
if state
. ecs_mut ( )
. read_storage ::< comp ::CanBuild > ( )
. get ( entity )
. is_some ( )
{
2019-09-25 14:56:57 +00:00
state . try_set_block ( pos , block ) ;
2019-07-02 18:19:16 +00:00
}
}
2019-09-25 21:17:43 +00:00
ClientMsg ::CollectBlock ( pos ) = > {
let block = state . terrain ( ) . get ( pos ) . ok ( ) . copied ( ) ;
if let Some ( block ) = block {
2019-09-25 22:53:43 +00:00
if block . is_collectible ( )
& & state
. ecs ( )
. read_storage ::< comp ::Inventory > ( )
. get ( entity )
. map ( | inv | ! inv . is_full ( ) )
. unwrap_or ( false )
{
2019-09-25 21:17:43 +00:00
if state . try_set_block ( pos , Block ::empty ( ) ) . is_some ( ) {
comp ::Item ::try_reclaim_from_block ( block )
. map ( | item | state . give_item ( entity , item ) ) ;
}
}
}
}
2019-04-19 19:32:47 +00:00
ClientMsg ::TerrainChunkRequest { key } = > match client . client_state {
2019-05-23 15:14:39 +00:00
ClientState ::Connected
| ClientState ::Registered
| ClientState ::Dead = > {
2019-04-22 14:25:37 +00:00
client . error_state ( RequestStateError ::Impossible ) ;
}
2019-04-19 19:32:47 +00:00
ClientState ::Spectator | ClientState ::Character = > {
2019-04-16 13:06:30 +00:00
match state . terrain ( ) . get_key ( key ) {
2019-04-29 20:37:19 +00:00
Some ( chunk ) = > {
client . postbox . send_message ( ServerMsg ::TerrainChunkUpdate {
key ,
2019-09-16 01:38:53 +00:00
chunk : Ok ( Box ::new ( chunk . clone ( ) ) ) ,
2019-04-29 20:37:19 +00:00
} )
}
2019-09-16 02:01:05 +00:00
None = > requested_chunks . push ( ( entity , key ) ) ,
2019-04-16 13:06:30 +00:00
}
2019-04-23 09:53:45 +00:00
}
2019-05-24 19:10:18 +00:00
ClientState ::Pending = > { }
2019-04-23 09:53:45 +00:00
} ,
2019-05-17 09:22:32 +00:00
// Always possible.
2019-04-20 17:54:37 +00:00
ClientMsg ::Ping = > client . postbox . send_message ( ServerMsg ::Pong ) ,
2019-04-23 09:53:45 +00:00
ClientMsg ::Pong = > { }
2019-05-27 17:45:43 +00:00
ClientMsg ::Disconnect = > {
disconnect = true ;
}
2019-03-03 22:02:38 +00:00
}
}
2019-04-16 13:06:30 +00:00
} else if state . get_time ( ) - client . last_ping > CLIENT_TIMEOUT | | // Timeout
client . postbox . error ( ) . is_some ( )
// Postbox error
2019-03-03 22:02:38 +00:00
{
2019-04-10 17:23:27 +00:00
disconnect = true ;
2019-03-06 13:51:01 +00:00
} else if state . get_time ( ) - client . last_ping > CLIENT_TIMEOUT * 0.5 {
2019-05-17 09:22:32 +00:00
// Try pinging the client if the timeout is nearing.
2019-04-11 22:26:43 +00:00
client . postbox . send_message ( ServerMsg ::Ping ) ;
2019-03-03 22:02:38 +00:00
}
2019-04-10 17:23:27 +00:00
if disconnect {
2019-10-06 15:34:51 +00:00
if let ( Some ( player ) , Some ( _ ) ) = (
state . ecs ( ) . read_storage ::< comp ::Player > ( ) . get ( entity ) ,
// It only shows a message if you had a body (not in char selection)
state . ecs ( ) . read_storage ::< comp ::Body > ( ) . get ( entity ) ,
) {
2019-07-21 18:34:52 +00:00
new_chat_msgs . push ( (
None ,
2019-07-29 14:40:46 +00:00
ServerMsg ::broadcast ( format! ( " {} went offline. " , & player . alias ) ) ,
2019-07-21 18:34:52 +00:00
) ) ;
2019-05-27 17:45:43 +00:00
}
2019-04-10 23:16:29 +00:00
disconnected_clients . push ( entity ) ;
2019-04-23 12:01:16 +00:00
client . postbox . send_message ( ServerMsg ::Disconnect ) ;
2019-03-03 22:02:38 +00:00
true
} else {
false
}
} ) ;
2019-05-17 09:22:32 +00:00
// Handle new chat messages.
2019-04-10 17:23:27 +00:00
for ( entity , msg ) in new_chat_msgs {
2019-07-21 18:34:52 +00:00
match msg {
ServerMsg ::ChatMsg { chat_type , message } = > {
if let Some ( entity ) = entity {
// Handle chat commands.
if message . starts_with ( " / " ) & & message . len ( ) > 1 {
let argv = String ::from ( & message [ 1 .. ] ) ;
self . process_chat_cmd ( entity , argv ) ;
} else {
let message =
match self . state . ecs ( ) . read_storage ::< comp ::Player > ( ) . get ( entity ) {
2019-08-14 15:51:59 +00:00
Some ( player ) = > {
if self . entity_is_admin ( entity ) {
2019-08-12 16:11:06 +00:00
format! ( " [ADMIN][ {} ] {} " , & player . alias , message )
2019-08-14 15:51:59 +00:00
} else {
format! ( " [ {} ] {} " , & player . alias , message )
2019-08-12 16:11:06 +00:00
}
2019-08-14 15:51:59 +00:00
}
2019-07-29 14:40:46 +00:00
None = > format! ( " [<Unknown>] {} " , message ) ,
2019-07-21 18:34:52 +00:00
} ;
self . clients
. notify_registered ( ServerMsg ::ChatMsg { chat_type , message } ) ;
}
} else {
self . clients
. notify_registered ( ServerMsg ::ChatMsg { chat_type , message } ) ;
}
}
_ = > {
panic! ( " Invalid message type. " ) ;
2019-05-27 17:45:43 +00:00
}
2019-04-16 13:06:30 +00:00
}
2019-03-03 22:02:38 +00:00
}
2019-05-17 09:22:32 +00:00
// Handle client disconnects.
2019-04-10 17:23:27 +00:00
for entity in disconnected_clients {
2019-06-06 14:48:41 +00:00
if let Err ( err ) = self . state . ecs_mut ( ) . delete_entity_synced ( entity ) {
2019-06-29 20:40:40 +00:00
debug! ( " Failed to delete disconnected client: {:?} " , err ) ;
2019-06-06 14:48:41 +00:00
}
2019-03-05 00:00:11 +00:00
2019-04-16 13:06:30 +00:00
frontend_events . push ( Event ::ClientDisconnected { entity } ) ;
2019-03-05 00:00:11 +00:00
}
2019-05-17 09:22:32 +00:00
// Generate requested chunks.
2019-09-16 01:38:53 +00:00
for ( entity , key ) in requested_chunks {
self . generate_chunk ( entity , key ) ;
2019-04-10 23:41:37 +00:00
}
2019-07-29 16:19:08 +00:00
for ( pos , ori , item ) in dropped_items {
2019-07-29 17:23:26 +00:00
let vel = ori . 0. normalized ( ) * 5.0
+ Vec3 ::unit_z ( ) * 10.0
+ Vec3 ::< f32 > ::zero ( ) . map ( | _ | rand ::thread_rng ( ) . gen ::< f32 > ( ) - 0.5 ) * 4.0 ;
2019-07-29 16:19:08 +00:00
self . create_object ( Default ::default ( ) , comp ::object ::Body ::Pouch )
2019-07-29 17:23:26 +00:00
. with ( comp ::Pos ( pos . 0 + Vec3 ::unit_z ( ) * 0.25 ) )
2019-07-26 21:01:41 +00:00
. with ( item )
2019-07-29 17:23:26 +00:00
. with ( comp ::Vel ( vel ) )
2019-07-26 21:01:41 +00:00
. build ( ) ;
}
2019-03-03 22:02:38 +00:00
Ok ( frontend_events )
}
2019-03-04 19:50:26 +00:00
2019-05-17 09:22:32 +00:00
/// Initialize a new client states with important information.
2019-04-21 18:12:29 +00:00
fn initialize_player (
2019-04-17 19:22:34 +00:00
state : & mut State ,
entity : specs ::Entity ,
client : & mut Client ,
player : comp ::Player ,
) {
2019-05-17 09:22:32 +00:00
// Save player metadata (for example the username).
2019-04-17 19:22:34 +00:00
state . write_component ( entity , player ) ;
2019-05-17 09:22:32 +00:00
// Tell the client its request was successful.
2019-04-21 15:52:15 +00:00
client . allow_state ( ClientState ::Registered ) ;
2019-04-17 19:22:34 +00:00
}
2019-10-06 17:35:47 +00:00
/// Initialize region subscription, entity should be the client's entity
fn initialize_region_subscription (
state : & mut State ,
client : & mut Client ,
entity : specs ::Entity ,
) {
let mut subscription = None ;
if let ( Some ( client_pos ) , Some ( client_vd ) ) = (
state . ecs ( ) . read_storage ::< comp ::Pos > ( ) . get ( entity ) ,
state
. ecs ( )
. read_storage ::< comp ::Player > ( )
. get ( entity )
. map ( | pl | pl . view_distance )
. and_then ( | v | v ) ,
) {
use common ::region ::RegionMap ;
let fuzzy_chunk = ( Vec2 ::< f32 > ::from ( client_pos . 0 ) )
. map2 ( TerrainChunkSize ::RECT_SIZE , | e , sz | e as i32 / sz as i32 ) ;
let chunk_size = TerrainChunkSize ::RECT_SIZE . reduce_max ( ) as f32 ;
let regions = common ::region ::regions_in_vd (
client_pos . 0 ,
( client_vd as f32 * chunk_size ) as f32
+ ( client ::CHUNK_FUZZ as f32 + chunk_size ) * 2.0 f32 . sqrt ( ) ,
) ;
for ( _ , region ) in state
. ecs ( )
. read_resource ::< RegionMap > ( )
. iter ( )
. filter ( | ( key , _ ) | regions . contains ( key ) )
{
// Sync physics of all entities in this region
for ( & uid , & pos , vel , ori , character_state , _ ) in (
& state . ecs ( ) . read_storage ::< Uid > ( ) ,
& state . ecs ( ) . read_storage ::< comp ::Pos > ( ) , // We assume all these entities have a position
state . ecs ( ) . read_storage ::< comp ::Vel > ( ) . maybe ( ) ,
state . ecs ( ) . read_storage ::< comp ::Ori > ( ) . maybe ( ) ,
state . ecs ( ) . read_storage ::< comp ::CharacterState > ( ) . maybe ( ) ,
region . entities ( ) ,
)
. join ( )
{
client . notify ( ServerMsg ::EntityPos {
entity : uid . into ( ) ,
pos ,
} ) ;
if let Some ( vel ) = vel . copied ( ) {
client . notify ( ServerMsg ::EntityVel {
entity : uid . into ( ) ,
vel ,
} ) ;
}
if let Some ( ori ) = ori . copied ( ) {
client . notify ( ServerMsg ::EntityOri {
entity : uid . into ( ) ,
ori ,
} ) ;
}
if let Some ( character_state ) = character_state . copied ( ) {
client . notify ( ServerMsg ::EntityCharacterState {
entity : uid . into ( ) ,
character_state ,
} ) ;
}
}
}
subscription = Some ( RegionSubscription {
fuzzy_chunk ,
regions ,
} ) ;
}
if let Some ( subscription ) = subscription {
state . write_component ( entity , subscription ) ;
}
}
2019-05-17 09:22:32 +00:00
/// Sync client states with the most up to date information.
2019-03-04 19:50:26 +00:00
fn sync_clients ( & mut self ) {
2019-10-06 17:35:47 +00:00
use common ::region ::{ region_in_vd , regions_in_vd , Event as RegionEvent , RegionMap } ;
//use hibitset::BitSetLike;
2019-04-14 20:30:27 +00:00
2019-07-30 05:24:36 +00:00
let ecs = self . state . ecs_mut ( ) ;
2019-10-06 17:35:47 +00:00
let clients = & mut self . clients ;
2019-06-30 20:25:37 +00:00
2019-10-06 17:35:47 +00:00
// Sync 'logical' state using Sphynx.
clients . notify_registered ( ServerMsg ::EcsSync ( ecs . next_sync_package ( ) ) ) ;
// To update subscriptions
// 1. Iterate through clients
// 2. Calculate current chunk position
// 3. If chunk is the same return, otherwise continue (use fuzzyiness)
// 4. Iterate through subscribed regions
// 5. Check if region is still in range (use fuzzyiness)
// 6. If not in range
// - remove from hashset
// - inform client of which entities to remove
// 7. Determine list of regions that are in range and iterate through it
// - check if in hashset (hash calc) if not add it
let mut regions_to_remove = Vec ::new ( ) ;
for ( entity , subscription , pos , vd ) in (
2019-07-30 05:24:36 +00:00
& ecs . entities ( ) ,
2019-10-06 17:35:47 +00:00
& mut ecs . write_storage ::< RegionSubscription > ( ) ,
2019-07-30 05:24:36 +00:00
& ecs . read_storage ::< comp ::Pos > ( ) ,
2019-10-06 17:35:47 +00:00
& ecs . read_storage ::< comp ::Player > ( ) ,
2019-06-30 20:25:37 +00:00
)
. join ( )
2019-10-06 17:35:47 +00:00
. filter_map ( | ( e , s , pos , player ) | player . view_distance . map ( | v | ( e , s , pos , v ) ) )
2019-06-30 20:25:37 +00:00
{
2019-10-06 17:35:47 +00:00
let chunk = ( Vec2 ::< f32 > ::from ( pos . 0 ) )
. map2 ( TerrainChunkSize ::RECT_SIZE , | e , sz | e as i32 / sz as i32 ) ;
if chunk ! = subscription . fuzzy_chunk
& & ( subscription
. fuzzy_chunk
. map2 ( TerrainChunkSize ::RECT_SIZE , | e , sz | {
( e as f32 + 0.5 ) * sz as f32
} )
- Vec2 ::from ( pos . 0 ) )
. map2 ( TerrainChunkSize ::RECT_SIZE , | e , sz | {
e . abs ( ) > ( sz / 2 + client ::CHUNK_FUZZ ) as f32
} )
. reduce_or ( )
{
let chunk_size = TerrainChunkSize ::RECT_SIZE . reduce_max ( ) as f32 ;
for key in & subscription . regions {
if ! region_in_vd (
* key ,
pos . 0 ,
( vd as f32 * chunk_size )
+ ( client ::CHUNK_FUZZ as f32 + client ::REGION_FUZZ as f32 + chunk_size )
* 2.0 f32 . sqrt ( ) ,
) {
regions_to_remove . push ( * key ) ;
}
}
2019-06-30 20:25:37 +00:00
2019-10-06 17:35:47 +00:00
let mut client = clients . get_mut ( & entity ) ;
for key in regions_to_remove . drain ( .. ) {
subscription . regions . remove ( & key ) ;
// Inform the client to delete these entities
if let ( Some ( ref mut client ) , Some ( region ) ) =
( & mut client , ecs . read_resource ::< RegionMap > ( ) . get ( key ) )
2019-07-30 05:24:36 +00:00
{
2019-10-06 17:35:47 +00:00
// Process entity left events since they won't be processed below because this region is no longer subscribed to
for event in region . events ( ) {
match event {
RegionEvent ::Entered ( _ , _ ) = > { } // These don't need to be processed because this region is being thrown out anyway
RegionEvent ::Left ( id , maybe_key ) = > {
// Lookup UID for entity
if let Some ( & uid ) =
ecs . read_storage ::< Uid > ( ) . get ( ecs . entities ( ) . entity ( * id ) )
{
if ! maybe_key
. as_ref ( )
. map ( | key | subscription . regions . contains ( key ) )
. unwrap_or ( false )
{
client . notify ( ServerMsg ::DeleteEntity ( uid . into ( ) ) ) ;
}
}
}
}
}
for ( & uid , _ ) in ( & ecs . read_storage ::< Uid > ( ) , region . entities ( ) ) . join ( ) {
client . notify ( ServerMsg ::DeleteEntity ( uid . into ( ) ) )
}
2019-07-30 05:24:36 +00:00
}
2019-07-30 08:49:41 +00:00
}
2019-10-06 17:35:47 +00:00
for key in regions_in_vd (
pos . 0 ,
( vd as f32 * chunk_size )
+ ( client ::CHUNK_FUZZ as f32 + chunk_size ) * 2.0 f32 . sqrt ( ) ,
) {
if subscription . regions . insert ( key ) {
// TODO: send the client initial infromation for all the entities in this region
2019-07-30 11:35:16 +00:00
}
}
2019-07-31 18:08:15 +00:00
}
2019-10-06 17:35:47 +00:00
}
2019-07-30 11:35:16 +00:00
2019-10-06 17:35:47 +00:00
// To send entity updates
// 1. Iterate through regions
// 2. Iterate through region subscribers (ie clients)
// - Collect a list of entity ids for clients who are subscribed to this region (hash calc to check each)
// 3. Iterate through events from that region
// - For each entity left event, iterate through the client list and check if they are subscribed to the destination (hash calc per subscribed client per entity left event)
// - Do something with entity entered events when sphynx is removed??
// 4. Iterate through entities in that region
// 5. Inform clients of the component changes for that entity
// - Throttle update rate base on distance to each client
// Sync physics
// via iterating through regions
for ( key , region ) in ecs . read_resource ::< RegionMap > ( ) . iter ( ) {
let subscriptions = ecs . read_storage ::< RegionSubscription > ( ) ;
let subscribers = (
& ecs . entities ( ) ,
& subscriptions ,
& ecs . read_storage ::< comp ::Pos > ( ) ,
)
. join ( )
. filter_map ( | ( entity , subscription , pos ) | {
if subscription . regions . contains ( & key ) {
clients
. get_client_index_ingame ( & entity )
. map ( | index | ( index , & subscription . regions , entity , * pos ) )
} else {
None
}
} )
. collect ::< Vec < _ > > ( ) ;
for event in region . events ( ) {
match event {
RegionEvent ::Entered ( _ , _ ) = > { } // TODO use this
RegionEvent ::Left ( id , maybe_key ) = > {
// Lookup UID for entity
if let Some ( & uid ) =
ecs . read_storage ::< Uid > ( ) . get ( ecs . entities ( ) . entity ( * id ) )
{
for ( client_index , regions , _ , _ ) in & subscribers {
if ! maybe_key
. as_ref ( )
. map ( | key | regions . contains ( key ) )
. unwrap_or ( false )
{
clients . notify_index (
* client_index ,
ServerMsg ::DeleteEntity ( uid . into ( ) ) ,
) ;
}
}
}
2019-07-30 11:35:16 +00:00
}
}
2019-07-31 18:08:15 +00:00
}
2019-07-30 11:35:16 +00:00
2019-10-06 17:35:47 +00:00
let tick = self . tick ;
let send_msg = | msg : ServerMsg ,
entity : EcsEntity ,
pos : comp ::Pos ,
force_update ,
clients : & mut Clients | {
for ( index , _ , client_entity , client_pos ) in & subscribers {
2019-07-30 11:35:16 +00:00
match force_update {
2019-10-06 17:35:47 +00:00
None if client_entity = = & entity = > { }
_ = > {
let distance_sq = client_pos . 0. distance_squared ( pos . 0 ) ;
// Throttle update rate based on distance to player
let update = if distance_sq < 100.0 f32 . powi ( 2 ) {
true // Closer than 100.0 blocks
} else if distance_sq < 150.0 f32 . powi ( 2 ) {
tick + entity . id ( ) as u64 % 2 = = 0
} else if distance_sq < 200.0 f32 . powi ( 2 ) {
tick + entity . id ( ) as u64 % 4 = = 0
} else if distance_sq < 250.0 f32 . powi ( 2 ) {
tick + entity . id ( ) as u64 % 8 = = 0
} else if distance_sq < 300.0 f32 . powi ( 2 ) {
tick + entity . id ( ) as u64 % 8 = = 0
} else {
tick + entity . id ( ) as u64 % 16 = = 0
} ;
if update {
clients . notify_index ( * index , msg . clone ( ) ) ;
}
}
2019-07-30 11:35:16 +00:00
}
}
2019-10-06 17:35:47 +00:00
} ;
2019-07-30 11:35:16 +00:00
2019-10-06 17:35:47 +00:00
for ( _ , entity , & uid , & pos , maybe_vel , maybe_ori , character_state , force_update ) in (
region . entities ( ) ,
& ecs . entities ( ) ,
& ecs . read_storage ::< Uid > ( ) ,
& ecs . read_storage ::< comp ::Pos > ( ) ,
ecs . read_storage ::< comp ::Vel > ( ) . maybe ( ) ,
ecs . read_storage ::< comp ::Ori > ( ) . maybe ( ) ,
ecs . read_storage ::< comp ::CharacterState > ( ) . maybe ( ) ,
ecs . read_storage ::< comp ::ForceUpdate > ( ) . maybe ( ) ,
)
. join ( )
2019-08-23 10:11:37 +00:00
{
2019-10-06 17:35:47 +00:00
let mut last_pos = ecs . write_storage ::< comp ::Last < comp ::Pos > > ( ) ;
let mut last_vel = ecs . write_storage ::< comp ::Last < comp ::Vel > > ( ) ;
let mut last_ori = ecs . write_storage ::< comp ::Last < comp ::Ori > > ( ) ;
let mut last_character_state =
ecs . write_storage ::< comp ::Last < comp ::CharacterState > > ( ) ;
if last_pos . get ( entity ) . map ( | & l | l . 0 ! = pos ) . unwrap_or ( true ) {
let _ = last_pos . insert ( entity , comp ::Last ( pos ) ) ;
send_msg (
ServerMsg ::EntityPos {
entity : uid . into ( ) ,
pos ,
} ,
entity ,
pos ,
force_update ,
clients ,
) ;
}
if let Some ( & vel ) = maybe_vel {
if last_vel . get ( entity ) . map ( | & l | l . 0 ! = vel ) . unwrap_or ( true ) {
let _ = last_vel . insert ( entity , comp ::Last ( vel ) ) ;
send_msg (
ServerMsg ::EntityVel {
entity : uid . into ( ) ,
vel ,
} ,
entity ,
pos ,
force_update ,
clients ,
) ;
}
}
if let Some ( & ori ) = maybe_ori {
if last_ori . get ( entity ) . map ( | & l | l . 0 ! = ori ) . unwrap_or ( true ) {
let _ = last_ori . insert ( entity , comp ::Last ( ori ) ) ;
send_msg (
ServerMsg ::EntityOri {
entity : uid . into ( ) ,
ori ,
} ,
entity ,
pos ,
force_update ,
clients ,
) ;
}
}
if let Some ( & character_state ) = character_state {
if last_character_state
. get ( entity )
. map ( | & l | ! character_state . is_same_state ( & l . 0 ) )
. unwrap_or ( true )
{
let _ = last_character_state . insert ( entity , comp ::Last ( character_state ) ) ;
send_msg (
ServerMsg ::EntityCharacterState {
entity : uid . into ( ) ,
character_state ,
} ,
entity ,
pos ,
force_update ,
clients ,
) ;
2019-07-30 08:49:41 +00:00
}
2019-07-30 05:24:36 +00:00
}
2019-04-17 17:32:29 +00:00
}
}
2019-07-25 14:48:27 +00:00
// Sync inventories
for ( entity , inventory , _ ) in (
& self . state . ecs ( ) . entities ( ) ,
& self . state . ecs ( ) . read_storage ::< comp ::Inventory > ( ) ,
& self . state . ecs ( ) . read_storage ::< comp ::InventoryUpdate > ( ) ,
)
. join ( )
{
self . clients
. notify ( entity , ServerMsg ::InventoryUpdate ( inventory . clone ( ) ) ) ;
}
2019-05-17 09:22:32 +00:00
// Remove all force flags.
2019-04-29 20:37:19 +00:00
self . state
. ecs_mut ( )
2019-06-14 15:27:05 +00:00
. write_storage ::< comp ::ForceUpdate > ( )
2019-04-29 20:37:19 +00:00
. clear ( ) ;
2019-07-25 14:48:27 +00:00
self . state
. ecs_mut ( )
. write_storage ::< comp ::InventoryUpdate > ( )
. clear ( ) ;
2019-03-04 19:50:26 +00:00
}
2019-04-10 23:41:37 +00:00
2019-09-16 01:38:53 +00:00
pub fn generate_chunk ( & mut self , entity : EcsEntity , key : Vec2 < i32 > ) {
2019-09-16 02:01:05 +00:00
let v = if let Entry ::Vacant ( v ) = self . pending_chunks . entry ( key ) {
v
} else {
return ;
} ;
2019-09-16 01:38:53 +00:00
let cancel = Arc ::new ( AtomicBool ::new ( false ) ) ;
v . insert ( Arc ::clone ( & cancel ) ) ;
let chunk_tx = self . chunk_tx . clone ( ) ;
let world = self . world . clone ( ) ;
self . thread_pool . execute ( move | | {
2019-09-16 13:11:47 +00:00
let payload = world
. generate_chunk ( key , | | cancel . load ( Ordering ::Relaxed ) )
. map_err ( | _ | entity ) ;
let _ = chunk_tx . send ( ( key , payload ) ) ;
2019-09-16 01:38:53 +00:00
} ) ;
2019-04-16 13:06:30 +00:00
}
2019-04-16 15:38:01 +00:00
fn process_chat_cmd ( & mut self , entity : EcsEntity , cmd : String ) {
2019-05-17 09:22:32 +00:00
// Separate string into keyword and arguments.
2019-04-16 13:06:30 +00:00
let sep = cmd . find ( ' ' ) ;
let ( kwd , args ) = match sep {
Some ( i ) = > ( cmd [ .. i ] . to_string ( ) , cmd [ ( i + 1 ) .. ] . to_string ( ) ) ,
None = > ( cmd , " " . to_string ( ) ) ,
} ;
2019-04-16 15:38:01 +00:00
2019-05-17 09:22:32 +00:00
// Find the command object and run its handler.
2019-04-16 13:06:30 +00:00
let action_opt = CHAT_COMMANDS . iter ( ) . find ( | x | x . keyword = = kwd ) ;
match action_opt {
2019-04-16 15:38:01 +00:00
Some ( action ) = > action . execute ( self , entity , args ) ,
2019-05-17 09:22:32 +00:00
// Unknown command
2019-04-16 13:06:30 +00:00
None = > {
self . clients . notify (
entity ,
2019-08-01 17:53:34 +00:00
ServerMsg ::private ( format! (
2019-08-18 18:07:21 +00:00
" Unknown command '/{}'. \n Type '/help' for available commands " ,
2019-04-16 13:06:30 +00:00
kwd
) ) ,
) ;
}
2019-04-10 23:41:37 +00:00
}
}
2019-08-14 15:51:59 +00:00
fn entity_is_admin ( & self , entity : EcsEntity ) -> bool {
self . state
. read_storage ::< comp ::Admin > ( )
. get ( entity )
. is_some ( )
}
2019-01-02 17:23:31 +00:00
}
2019-03-05 18:39:18 +00:00
impl Drop for Server {
fn drop ( & mut self ) {
2019-04-22 00:38:29 +00:00
self . clients . notify_registered ( ServerMsg ::Shutdown ) ;
2019-03-05 18:39:18 +00:00
}
}
2019-09-25 14:56:57 +00:00
trait StateExt {
2019-09-25 22:53:43 +00:00
fn give_item ( & mut self , entity : EcsEntity , item : comp ::Item ) -> bool ;
2019-09-25 15:52:58 +00:00
fn apply_effect ( & mut self , entity : EcsEntity , effect : Effect ) ;
2019-09-25 14:56:57 +00:00
}
impl StateExt for State {
2019-09-25 22:53:43 +00:00
fn give_item ( & mut self , entity : EcsEntity , item : comp ::Item ) -> bool {
let success = self
. ecs ( )
2019-09-25 14:56:57 +00:00
. write_storage ::< comp ::Inventory > ( )
. get_mut ( entity )
2019-09-25 22:53:43 +00:00
. map ( | inv | inv . push ( item ) . is_none ( ) )
. unwrap_or ( false ) ;
if success {
self . write_component ( entity , comp ::InventoryUpdate ) ;
}
success
2019-09-25 14:56:57 +00:00
}
2019-09-25 15:52:58 +00:00
fn apply_effect ( & mut self , entity : EcsEntity , effect : Effect ) {
match effect {
2019-10-17 20:59:36 +00:00
Effect ::Health ( change ) = > {
2019-09-25 15:52:58 +00:00
self . ecs_mut ( )
. write_storage ::< comp ::Stats > ( )
. get_mut ( entity )
2019-10-17 20:59:36 +00:00
. map ( | stats | stats . health . change_by ( change ) ) ;
2019-09-25 15:52:58 +00:00
}
Effect ::Xp ( xp ) = > {
self . ecs_mut ( )
. write_storage ::< comp ::Stats > ( )
. get_mut ( entity )
. map ( | stats | stats . exp . change_by ( xp ) ) ;
}
}
}
2019-09-25 14:56:57 +00:00
}