Implement fast respawn

Former-commit-id: 17f9aceba5c40a5d3ddc2e0fd6795479487753fd
This commit is contained in:
timokoesters 2019-05-23 17:14:39 +02:00
parent 0344f3eba8
commit 1826a2b4f9
9 changed files with 154 additions and 119 deletions

View File

@ -5,6 +5,7 @@ use vek::*;
pub enum InputEvent {
Jump,
Attack,
Respawn,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]

View File

@ -18,5 +18,6 @@ pub use inputs::Actions;
pub use inputs::InputEvent;
pub use inputs::Inputs;
pub use player::Player;
pub use player::Respawn;
pub use stats::Dying;
pub use stats::Stats;

View File

@ -1,4 +1,4 @@
use specs::{Component, FlaggedStorage, VecStorage};
use specs::{Component, NullStorage, FlaggedStorage, VecStorage};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Player {
@ -18,3 +18,10 @@ impl Player {
impl Component for Player {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Respawn;
impl Component for Respawn {
type Storage = NullStorage<Self>;
}

View File

@ -12,5 +12,6 @@ pub enum ClientState {
Connected,
Registered,
Spectator,
Dead,
Character,
}

View File

@ -103,6 +103,7 @@ impl State {
ecs.register_synced::<comp::Actor>();
ecs.register_synced::<comp::Player>();
ecs.register_synced::<comp::Stats>();
ecs.register_synced::<comp::Actions>();
ecs.register::<comp::phys::ForceUpdate>();
// Register unsynced (or synced by other means) components.
@ -111,8 +112,8 @@ impl State {
ecs.register::<comp::phys::Dir>();
ecs.register::<comp::AnimationInfo>();
ecs.register::<comp::Inputs>();
ecs.register::<comp::Actions>();
ecs.register::<comp::Dying>();
ecs.register::<comp::Respawn>();
ecs.register::<comp::Agent>();
ecs.register::<inventory::Inventory>();

View File

@ -6,7 +6,7 @@ use vek::*;
use crate::{
comp::{
phys::{Dir, ForceUpdate, Pos, Vel},
Actions, Animation, AnimationInfo, InputEvent, Inputs, Stats,
Actions, Animation, AnimationInfo, Respawn, InputEvent, Inputs, Stats,
},
state::{DeltaTime, Time},
terrain::TerrainMap,
@ -29,6 +29,7 @@ impl<'a> System<'a> for Sys {
WriteStorage<'a, Dir>,
WriteStorage<'a, AnimationInfo>,
WriteStorage<'a, Stats>,
WriteStorage<'a, Respawn>,
WriteStorage<'a, ForceUpdate>,
);
@ -46,6 +47,7 @@ impl<'a> System<'a> for Sys {
mut directions,
mut animation_infos,
mut stats,
mut respawns,
mut force_updates,
): Self::SystemData,
) {
@ -133,12 +135,9 @@ impl<'a> System<'a> for Sys {
},
);
}
for (entity, inputs, mut action, pos, dir) in (
for (entity, inputs) in (
&entities,
&mut inputs,
&mut actions,
&positions,
&mut directions,
)
.join()
{
@ -147,24 +146,26 @@ impl<'a> System<'a> for Sys {
match event {
InputEvent::Attack => {
// Attack delay
if action.attack_time.is_some() {
continue;
}
for (b, pos_b, mut stat_b, mut vel_b) in
(&entities, &positions, &mut stats, &mut velocities).join()
{
if entity != b
&& pos.0.distance_squared(pos_b.0) < 50.0
&& dir.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0
if let (Some(pos), Some(dir), Some(action)) = (positions.get(entity), directions.get(entity), actions.get_mut(entity)) {
for (b, pos_b, mut stat_b, mut vel_b) in
(&entities, &positions, &mut stats, &mut velocities).join()
{
action.attack_time = Some(0.0);
stat_b.hp.change_by(-10); // TODO: variable damage
vel_b.0 += (pos_b.0 - pos.0).normalized() * 20.0;
vel_b.0.z = 20.0;
force_updates.insert(b, ForceUpdate);
if entity != b
&& pos.0.distance_squared(pos_b.0) < 50.0
&& dir.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0
{
action.attack_time = Some(0.0);
stat_b.hp.change_by(-10); // TODO: variable damage
vel_b.0 += (pos_b.0 - pos.0).normalized() * 20.0;
vel_b.0.z = 20.0;
force_updates.insert(b, ForceUpdate);
}
}
}
}
InputEvent::Respawn => {
respawns.insert(entity, Respawn);
}
InputEvent::Jump => {}
}
}

View File

@ -200,6 +200,52 @@ impl Server {
// Tick the world
self.world.tick(dt);
// Sync deaths.
let todo_kill = (
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<comp::Dying>(),
)
.join()
.map(|(entity, _)| entity)
.collect::<Vec<EcsEntity>>();
for entity in todo_kill {
self.state
.ecs_mut()
.write_storage::<comp::Dying>()
.remove(entity);
self.state
.ecs_mut()
.write_storage::<comp::Actions>()
.remove(entity);
if let Some(client) = self.clients.get_mut(&entity) {
client.force_state(ClientState::Dead);
} else {
//self.state.ecs_mut().delete_entity_synced(entity);
}
}
// Handle respawns
let todo_respawn = (
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<comp::Respawn>(),
)
.join()
.map(|(entity, _)| entity)
.collect::<Vec<EcsEntity>>();
for entity in todo_respawn {
if let Some(client) = self.clients.get_mut(&entity) {
client.allow_state(ClientState::Character);
self.state.ecs_mut().write_storage::<comp::Respawn>().remove(entity);
self.state.write_component(entity, comp::Stats::default());
self.state.write_component(entity, comp::Actions::default());
self.state.write_component(entity, comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)));
self.state.write_component(entity, comp::phys::Vel(Vec3::zero()));
self.state.write_component(entity, comp::phys::ForceUpdate);
}
}
// 5) Fetch any generated `TerrainChunk`s and insert them into the terrain.
// Also, send the chunk data to anybody that is close by.
if let Ok((key, chunk)) = self.chunk_rx.try_recv() {
@ -331,9 +377,9 @@ impl Server {
ClientState::Registered => {
client.error_state(RequestStateError::Already)
}
ClientState::Spectator | ClientState::Character => {
client.allow_state(ClientState::Registered)
}
ClientState::Spectator
| ClientState::Character
| ClientState::Dead => client.allow_state(ClientState::Registered),
},
ClientState::Spectator => match requested_state {
// Become Registered first.
@ -343,14 +389,15 @@ impl Server {
ClientState::Spectator => {
client.error_state(RequestStateError::Already)
}
ClientState::Registered | ClientState::Character => {
client.allow_state(ClientState::Spectator)
}
ClientState::Registered
| ClientState::Character
| ClientState::Dead => client.allow_state(ClientState::Spectator),
},
// Use ClientMsg::Character instead.
ClientState::Character => {
client.error_state(RequestStateError::WrongMessage)
}
ClientState::Dead => client.error_state(RequestStateError::Impossible),
},
ClientMsg::Register { player } => match client.client_state {
ClientState::Connected => {
@ -374,7 +421,9 @@ impl Server {
ClientState::Connected => {
client.error_state(RequestStateError::Impossible)
}
ClientState::Registered | ClientState::Spectator => {
ClientState::Registered
| ClientState::Spectator
| ClientState::Dead => {
Self::create_player_character(state, entity, client, name, body)
}
ClientState::Character => {
@ -387,17 +436,21 @@ impl Server {
}
ClientState::Registered
| ClientState::Spectator
| ClientState::Dead
| ClientState::Character => new_chat_msgs.push((entity, msg)),
},
ClientMsg::PlayerInputs(mut inputs) => {
state
.ecs_mut()
.write_storage::<comp::Inputs>()
.get_mut(entity)
.map(|s| {
s.events.append(&mut inputs.events);
});
}
ClientMsg::PlayerInputs(mut inputs) => match client.client_state {
ClientState::Character | ClientState::Dead => {
state
.ecs_mut()
.write_storage::<comp::Inputs>()
.get_mut(entity)
.map(|s| {
s.events.append(&mut inputs.events);
});
}
_ => client.error_state(RequestStateError::Impossible),
},
ClientMsg::PlayerAnimation(animation_info) => {
match client.client_state {
ClientState::Character => {
@ -417,7 +470,9 @@ impl Server {
_ => client.error_state(RequestStateError::Impossible),
},
ClientMsg::TerrainChunkRequest { key } => match client.client_state {
ClientState::Connected | ClientState::Registered => {
ClientState::Connected
| ClientState::Registered
| ClientState::Dead => {
client.error_state(RequestStateError::Impossible);
}
ClientState::Spectator | ClientState::Character => {
@ -571,58 +626,12 @@ impl Server {
}
}
// Sync deaths.
let todo_kill = (
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<comp::Dying>(),
)
.join()
.map(|(entity, _)| entity)
.collect::<Vec<EcsEntity>>();
for entity in todo_kill {
self.state
.ecs_mut()
.write_storage::<comp::Dying>()
.remove(entity);
self.state
.ecs_mut()
.write_storage::<comp::Actor>()
.remove(entity);
self.state
.ecs_mut()
.write_storage::<comp::Stats>()
.remove(entity);
self.state
.ecs_mut()
.write_storage::<comp::phys::Pos>()
.remove(entity);
self.state
.ecs_mut()
.write_storage::<comp::phys::Vel>()
.remove(entity);
self.state
.ecs_mut()
.write_storage::<comp::phys::Dir>()
.remove(entity);
self.state
.ecs_mut()
.write_storage::<comp::AnimationInfo>()
.remove(entity);
self.state
.ecs_mut()
.write_storage::<comp::Inputs>()
.remove(entity);
if let Some(client) = self.clients.get_mut(&entity) {
client.force_state(ClientState::Registered);
}
}
// Remove all force flags.
self.state
.ecs_mut()
.write_storage::<comp::phys::ForceUpdate>()
.clear();
}
pub fn generate_chunk(&mut self, key: Vec2<i32>) {

View File

@ -12,6 +12,7 @@ use crate::{
};
use client::Client;
use common::{
msg::ClientState,
assets,
comp::{
self,
@ -457,22 +458,26 @@ impl FigureMgr {
let tick = client.get_tick();
let ecs = client.state().ecs();
for (entity, actor) in (&ecs.entities(), &ecs.read_storage::<comp::Actor>()).join() {
match actor {
comp::Actor::Character { body, .. } => {
if let Some((locals, bone_consts)) = match body {
Body::Humanoid(_) => self
.character_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
Body::Quadruped(_) => self
.quadruped_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
} {
let model = self.model_cache.get_or_create_model(renderer, *body, tick);
for (entity, actor, _) in (&ecs.entities(), &ecs.read_storage::<comp::Actor>(), &ecs.read_storage::<comp::Actions>()).join() {
// Check if player is alive
{
match actor {
comp::Actor::Character { body, .. } => {
if let Some((locals, bone_consts)) = match body {
Body::Humanoid(_) => self
.character_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
Body::Quadruped(_) => self
.quadruped_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
} {
let model = self.model_cache.get_or_create_model(renderer, *body, tick);
renderer.render_figure(model, globals, locals, bone_consts);
renderer.render_figure(model, globals, locals, bone_consts);
}
}
}
}

View File

@ -8,7 +8,7 @@ use crate::{
Direction, Error, GlobalState, PlayState, PlayStateResult,
};
use client::{self, Client};
use common::{comp, clock::Clock, msg::ClientState};
use common::{clock::Clock, comp, msg::ClientState};
use glutin::MouseButton;
use std::{cell::RefCell, mem, rc::Rc, time::Duration};
use vek::*;
@ -131,44 +131,53 @@ impl PlayState for SessionState {
// Game loop
while self.client.borrow().is_request_pending()
|| self.client.borrow().get_client_state() == Some(ClientState::Character)
|| self.client.borrow().get_client_state() == Some(ClientState::Dead)
{
let alive = self.client.borrow().get_client_state() == Some(ClientState::Character);
// Handle window events.
for event in global_state.window.fetch_events() {
// Pass all events to the ui first.
if self.hud.handle_event(event.clone(), global_state) {
continue;
}
let _handled = match event {
match event {
Event::Close => {
return PlayStateResult::Shutdown;
}
// Attack key pressed
Event::Click(MouseButton::Left, state) => {
self.input_events.push(comp::InputEvent::Attack)
Event::Click(MouseButton::Left, state) => match alive {
true => {
self.input_events.push(comp::InputEvent::Attack);
}
false => {
self.input_events.push(comp::InputEvent::Respawn);
}
_ => unreachable!()
}
// Movement key pressed
Event::KeyDown(Key::MoveForward) => self.key_state.up = true,
Event::KeyDown(Key::MoveBack) => self.key_state.down = true,
Event::KeyDown(Key::MoveLeft) => self.key_state.left = true,
Event::KeyDown(Key::MoveRight) => self.key_state.right = true,
Event::KeyDown(Key::Jump) => {
Event::KeyDown(Key::MoveForward) if alive => self.key_state.up = true,
Event::KeyDown(Key::MoveBack) if alive => self.key_state.down = true,
Event::KeyDown(Key::MoveLeft) if alive => self.key_state.left = true,
Event::KeyDown(Key::MoveRight) if alive => self.key_state.right = true,
Event::KeyDown(Key::Jump) if alive => {
self.input_events.push(comp::InputEvent::Jump);
self.key_state.jump = true;
}
Event::KeyDown(Key::Glide) => self.key_state.glide = true,
Event::KeyDown(Key::Glide) if alive => self.key_state.glide = true,
// Movement key released
Event::KeyUp(Key::MoveForward) => self.key_state.up = false,
Event::KeyUp(Key::MoveBack) => self.key_state.down = false,
Event::KeyUp(Key::MoveLeft) => self.key_state.left = false,
Event::KeyUp(Key::MoveRight) => self.key_state.right = false,
Event::KeyUp(Key::Jump) => self.key_state.jump = false,
Event::KeyUp(Key::Glide) => self.key_state.glide = false,
Event::KeyUp(Key::MoveForward) if alive => self.key_state.up = false,
Event::KeyUp(Key::MoveBack) if alive => self.key_state.down = false,
Event::KeyUp(Key::MoveLeft) if alive => self.key_state.left = false,
Event::KeyUp(Key::MoveRight) if alive => self.key_state.right = false,
Event::KeyUp(Key::Jump) if alive => self.key_state.jump = false,
Event::KeyUp(Key::Glide) if alive => self.key_state.glide = false,
// Pass all other events to the scene
event => {
self.scene.handle_input_event(event);
}
};
// TODO: Do something if the event wasn't handled?
} // TODO: Do something if the event wasn't handled?
}
}
// Perform an in-game tick.