2019-10-20 07:20:21 +00:00
use super ::SysTimer ;
2019-10-15 04:06:14 +00:00
use crate ::{
client ::{ Client , RegionSubscription } ,
Tick ,
} ;
use common ::{
comp ::{ CharacterState , ForceUpdate , Inventory , InventoryUpdate , Last , Ori , Pos , Vel } ,
msg ::ServerMsg ,
region ::{ Event as RegionEvent , RegionMap } ,
state ::Uid ,
} ;
use specs ::{
2019-10-20 07:20:21 +00:00
Entities , Entity as EcsEntity , Join , Read , ReadExpect , ReadStorage , System , Write , WriteStorage ,
2019-10-15 04:06:14 +00:00
} ;
/// This system will send physics updates to the client
pub struct Sys ;
impl < ' a > System < ' a > for Sys {
type SystemData = (
Entities < ' a > ,
Read < ' a , Tick > ,
ReadExpect < ' a , RegionMap > ,
2019-10-20 07:20:21 +00:00
Write < ' a , SysTimer < Self > > ,
2019-10-15 04:06:14 +00:00
ReadStorage < ' a , Uid > ,
ReadStorage < ' a , Pos > ,
ReadStorage < ' a , Vel > ,
ReadStorage < ' a , Ori > ,
ReadStorage < ' a , CharacterState > ,
ReadStorage < ' a , Inventory > ,
ReadStorage < ' a , RegionSubscription > ,
WriteStorage < ' a , Last < Pos > > ,
WriteStorage < ' a , Last < Vel > > ,
WriteStorage < ' a , Last < Ori > > ,
WriteStorage < ' a , Last < CharacterState > > ,
WriteStorage < ' a , Client > ,
WriteStorage < ' a , ForceUpdate > ,
WriteStorage < ' a , InventoryUpdate > ,
) ;
fn run (
& mut self ,
(
entities ,
tick ,
region_map ,
2019-10-20 07:20:21 +00:00
mut timer ,
2019-10-15 04:06:14 +00:00
uids ,
positions ,
velocities ,
orientations ,
character_states ,
inventories ,
subscriptions ,
mut last_pos ,
mut last_vel ,
mut last_ori ,
mut last_character_state ,
mut clients ,
mut force_updates ,
mut inventory_updates ,
) : Self ::SystemData ,
) {
2019-10-20 07:20:21 +00:00
timer . start ( ) ;
2019-10-15 04:06:14 +00:00
let tick = tick . 0 ;
// 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 region_map . iter ( ) {
let mut subscribers = ( & mut clients , & entities , & subscriptions , & positions )
. join ( )
. filter_map ( | ( client , entity , subscription , pos ) | {
if client . is_ingame ( ) & & subscription . regions . contains ( & key ) {
Some ( ( client , & subscription . regions , entity , * pos ) )
} else {
None
}
} )
. collect ::< Vec < _ > > ( ) ;
for event in region . events ( ) {
match event {
2019-10-25 05:35:15 +00:00
RegionEvent ::Entered ( id , maybe_key ) = > {
let entity = entities . entity ( * id ) ;
if let Some ( ( uid , pos , vel , ori , character_state ) ) =
uids . get ( entity ) . and_then ( | uid | {
positions . get ( entity ) . map ( | pos | {
(
uid ,
pos ,
velocities . get ( entity ) ,
orientations . get ( entity ) ,
character_states . get ( entity ) ,
)
} )
} )
{
for ( client , regions , _ , _ ) in & mut subscribers {
if maybe_key
. as_ref ( )
. map ( | key | ! regions . contains ( key ) )
. unwrap_or ( true )
{
send_initial_unsynced_components (
client ,
uid ,
pos ,
vel ,
ori ,
character_state ,
) ;
}
}
}
}
2019-10-15 04:06:14 +00:00
RegionEvent ::Left ( id , maybe_key ) = > {
// Lookup UID for entity
if let Some ( & uid ) = uids . get ( entities . entity ( * id ) ) {
for ( client , regions , _ , _ ) in & mut subscribers {
2019-10-25 05:35:15 +00:00
if maybe_key
2019-10-15 04:06:14 +00:00
. as_ref ( )
2019-10-25 05:35:15 +00:00
. map ( | key | ! regions . contains ( key ) )
. unwrap_or ( true )
2019-10-15 04:06:14 +00:00
{
client . notify ( ServerMsg ::DeleteEntity ( uid . into ( ) ) ) ;
}
}
}
}
}
}
let mut send_msg =
| msg : ServerMsg , entity : EcsEntity , pos : Pos , force_update , throttle : bool | {
for ( client , _ , client_entity , client_pos ) in & mut subscribers {
match force_update {
None if client_entity = = & entity = > { }
_ = > {
let distance_sq = client_pos . 0. distance_squared ( pos . 0 ) ;
// Throttle update rate based on distance to player
2019-10-25 05:35:15 +00:00
// TODO: more entities will be farther away so it could be more
// efficient to reverse the order of these checks
2019-10-15 04:06:14 +00:00
let update = if ! throttle | | 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 ) % 16 = = 0
} else {
( tick + entity . id ( ) as u64 ) % 32 = = 0
} ;
if update {
client . notify ( msg . clone ( ) ) ;
}
}
}
}
} ;
for ( _ , entity , & uid , & pos , maybe_vel , maybe_ori , character_state , force_update ) in (
region . entities ( ) ,
& entities ,
& uids ,
& positions ,
velocities . maybe ( ) ,
orientations . maybe ( ) ,
character_states . maybe ( ) ,
force_updates . maybe ( ) ,
)
. join ( )
{
2019-10-25 05:35:15 +00:00
// TODO: An entity that stoppped moving on a tick that it wasn't sent to the player
// will never have it's position updated
2019-10-15 04:06:14 +00:00
if last_pos . get ( entity ) . map ( | & l | l . 0 ! = pos ) . unwrap_or ( true ) {
let _ = last_pos . insert ( entity , Last ( pos ) ) ;
send_msg (
ServerMsg ::EntityPos {
entity : uid . into ( ) ,
pos ,
} ,
entity ,
pos ,
force_update ,
true ,
) ;
}
if let Some ( & vel ) = maybe_vel {
if last_vel . get ( entity ) . map ( | & l | l . 0 ! = vel ) . unwrap_or ( true ) {
let _ = last_vel . insert ( entity , Last ( vel ) ) ;
send_msg (
ServerMsg ::EntityVel {
entity : uid . into ( ) ,
vel ,
} ,
entity ,
pos ,
force_update ,
true ,
) ;
}
}
if let Some ( & ori ) = maybe_ori {
if last_ori . get ( entity ) . map ( | & l | l . 0 ! = ori ) . unwrap_or ( true ) {
let _ = last_ori . insert ( entity , Last ( ori ) ) ;
send_msg (
ServerMsg ::EntityOri {
entity : uid . into ( ) ,
ori ,
} ,
entity ,
pos ,
force_update ,
true ,
) ;
}
}
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 , Last ( character_state ) ) ;
send_msg (
ServerMsg ::EntityCharacterState {
entity : uid . into ( ) ,
character_state ,
} ,
entity ,
pos ,
force_update ,
false ,
) ;
}
}
}
}
// Sync inventories
for ( client , inventory , _ ) in ( & mut clients , & inventories , & inventory_updates ) . join ( ) {
client . notify ( ServerMsg ::InventoryUpdate ( inventory . clone ( ) ) ) ;
}
// Remove all force flags.
force_updates . clear ( ) ;
inventory_updates . clear ( ) ;
2019-10-20 07:20:21 +00:00
timer . end ( ) ;
2019-10-15 04:06:14 +00:00
}
}
2019-10-25 05:35:15 +00:00
pub fn send_initial_unsynced_components (
client : & mut Client ,
uid : & Uid ,
pos : & Pos ,
vel : Option < & Vel > ,
ori : Option < & Ori > ,
character_state : Option < & CharacterState > ,
) {
let entity = ( * uid ) . into ( ) ;
client . notify ( ServerMsg ::EntityPos { entity , pos : * pos } ) ;
if let Some ( & vel ) = vel {
client . notify ( ServerMsg ::EntityVel { entity , vel } ) ;
}
if let Some ( & ori ) = ori {
client . notify ( ServerMsg ::EntityOri { entity , ori } ) ;
}
if let Some ( & character_state ) = character_state {
client . notify ( ServerMsg ::EntityCharacterState {
entity ,
character_state ,
} ) ;
}
}