mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'imbris/char-screen-transition' into 'master'
Fix issues regarding going back to the character selection screen Closes #386 See merge request veloren/veloren!700
This commit is contained in:
commit
4fa05150a2
@ -47,6 +47,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Fixed region display name
|
||||
- Fixed the bow fire rate
|
||||
- Healthbars now flash on critical health
|
||||
- Fixed ghosts when going back to character screen
|
||||
- Fixed not being able to unmount
|
||||
- Fixed non-humanoids being able to climb and glide
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -14,10 +14,10 @@ members = [
|
||||
# default profile for devs, fast to compile, okay enough to run, no debug information
|
||||
[profile.dev]
|
||||
opt-level = 2
|
||||
overflow-checks = false
|
||||
overflow-checks = true
|
||||
debug-assertions = true
|
||||
panic = "abort"
|
||||
debug = false
|
||||
debug = false
|
||||
codegen-units = 8
|
||||
lto = false
|
||||
incremental = true
|
||||
@ -44,7 +44,7 @@ opt-level = 2
|
||||
inherits= 'dev'
|
||||
debug = true
|
||||
|
||||
# this profil is used for veloren releases, compile time doesn't matter
|
||||
# this profile is used for veloren releases, compile time doesn't matter
|
||||
# we need stacktraces, light debug information, as much checks as possible
|
||||
# I would like to put it in a seperate `official_release` target, but that doesnt share caches with `cargo test` and `cargo bench`
|
||||
[profile.release]
|
||||
|
@ -5,7 +5,11 @@ pub mod error;
|
||||
|
||||
// Reexports
|
||||
pub use crate::error::Error;
|
||||
pub use specs::{join::Join, saveload::Marker, Entity as EcsEntity, ReadStorage, WorldExt};
|
||||
pub use specs::{
|
||||
join::Join,
|
||||
saveload::{Marker, MarkerAllocator},
|
||||
Builder, Entity as EcsEntity, ReadStorage, WorldExt,
|
||||
};
|
||||
|
||||
use common::{
|
||||
comp::{self, ControlEvent, Controller, ControllerInputs, InventoryManip},
|
||||
@ -15,7 +19,7 @@ use common::{
|
||||
},
|
||||
net::PostBox,
|
||||
state::State,
|
||||
sync::{Uid, WorldSyncExt},
|
||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
||||
terrain::{block::Block, TerrainChunk, TerrainChunkSize},
|
||||
vol::RectVolSize,
|
||||
ChatType,
|
||||
@ -84,7 +88,7 @@ impl Client {
|
||||
time_of_day,
|
||||
// world_map: /*(map_size, world_map)*/map_size,
|
||||
}) => {
|
||||
// TODO: Voxygen should display this.
|
||||
// TODO: Display that versions don't match in Voxygen
|
||||
if server_info.git_hash != common::util::GIT_HASH.to_string() {
|
||||
log::warn!(
|
||||
"Server is running {}[{}], you are running {}[{}], versions might be incompatible!",
|
||||
@ -183,17 +187,15 @@ impl Client {
|
||||
self.client_state = ClientState::Pending;
|
||||
}
|
||||
|
||||
/// Request a state transition to `ClientState::Character`.
|
||||
/// Send disconnect message to the server
|
||||
pub fn request_logout(&mut self) {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::RequestState(ClientState::Connected));
|
||||
self.postbox.send_message(ClientMsg::Disconnect);
|
||||
self.client_state = ClientState::Pending;
|
||||
}
|
||||
|
||||
/// Request a state transition to `ClientState::Character`.
|
||||
/// Request a state transition to `ClientState::Registered` from an ingame state.
|
||||
pub fn request_remove_character(&mut self) {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::RequestState(ClientState::Registered));
|
||||
self.postbox.send_message(ClientMsg::ExitIngame);
|
||||
self.client_state = ClientState::Pending;
|
||||
}
|
||||
|
||||
@ -282,9 +284,9 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Send a chat message to the server.
|
||||
pub fn send_chat(&mut self, msg: String) {
|
||||
match validate_chat_msg(&msg) {
|
||||
Ok(()) => self.postbox.send_message(ClientMsg::chat(msg)),
|
||||
pub fn send_chat(&mut self, message: String) {
|
||||
match validate_chat_msg(&message) {
|
||||
Ok(()) => self.postbox.send_message(ClientMsg::ChatMsg { message }),
|
||||
Err(ChatMsgValidationError::TooLong) => log::warn!(
|
||||
"Attempted to send a message that's too long (Over {} bytes)",
|
||||
MAX_BYTES_CHAT_MSG
|
||||
@ -331,7 +333,7 @@ impl Client {
|
||||
|
||||
// 1) Handle input from frontend.
|
||||
// Pass character actions from frontend input to the player's entity.
|
||||
if let ClientState::Character | ClientState::Dead = self.client_state {
|
||||
if let ClientState::Character = self.client_state {
|
||||
self.state.write_component(
|
||||
self.entity,
|
||||
Controller {
|
||||
@ -560,8 +562,8 @@ impl Client {
|
||||
.duration_since(self.last_server_ping)
|
||||
.as_secs_f64();
|
||||
}
|
||||
ServerMsg::ChatMsg { chat_type, message } => {
|
||||
frontend_events.push(Event::Chat { chat_type, message })
|
||||
ServerMsg::ChatMsg { message, chat_type } => {
|
||||
frontend_events.push(Event::Chat { message, chat_type })
|
||||
}
|
||||
ServerMsg::SetPlayerEntity(uid) => {
|
||||
if let Some(entity) = self.state.ecs().entity_from_uid(uid) {
|
||||
@ -591,6 +593,26 @@ impl Client {
|
||||
.delete_entity_and_clear_from_uid_allocator(entity);
|
||||
}
|
||||
}
|
||||
// Cleanup for when the client goes back to the `Registered` state
|
||||
ServerMsg::ExitIngameCleanup => {
|
||||
// Get client entity Uid
|
||||
let client_uid = self
|
||||
.state
|
||||
.read_component_cloned::<Uid>(self.entity)
|
||||
.map(|u| u.into())
|
||||
.expect("Client doesn't have a Uid!!!");
|
||||
// Clear ecs of all entities
|
||||
self.state.ecs_mut().delete_all();
|
||||
self.state.ecs_mut().maintain();
|
||||
self.state.ecs_mut().insert(UidAllocator::default());
|
||||
// Recreate client entity with Uid
|
||||
let entity_builder = self.state.ecs_mut().create_entity();
|
||||
let uid = entity_builder
|
||||
.world
|
||||
.write_resource::<UidAllocator>()
|
||||
.allocate(entity_builder.entity, Some(client_uid));
|
||||
self.entity = entity_builder.with(uid).build();
|
||||
}
|
||||
ServerMsg::EntityPos { entity, pos } => {
|
||||
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
|
||||
self.state.write_component(entity, pos);
|
||||
@ -641,9 +663,6 @@ impl Client {
|
||||
error, state
|
||||
);
|
||||
}
|
||||
ServerMsg::ForceState(state) => {
|
||||
self.client_state = state;
|
||||
}
|
||||
ServerMsg::Disconnect => {
|
||||
frontend_events.push(Event::Disconnect);
|
||||
}
|
||||
|
@ -88,12 +88,15 @@ pub enum ServerEvent {
|
||||
Mount(EcsEntity, EcsEntity),
|
||||
Unmount(EcsEntity),
|
||||
Possess(Uid, Uid),
|
||||
CreatePlayer {
|
||||
CreateCharacter {
|
||||
entity: EcsEntity,
|
||||
name: String,
|
||||
body: comp::Body,
|
||||
main: Option<String>,
|
||||
},
|
||||
ExitIngame {
|
||||
entity: EcsEntity,
|
||||
},
|
||||
CreateNpc {
|
||||
pos: comp::Pos,
|
||||
stats: comp::Stats,
|
||||
|
@ -1,6 +1,4 @@
|
||||
use super::ClientState;
|
||||
use crate::terrain::block::Block;
|
||||
use crate::{comp, ChatType};
|
||||
use crate::{comp, terrain::block::Block};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@ -14,16 +12,18 @@ pub enum ClientMsg {
|
||||
body: comp::Body,
|
||||
main: Option<String>, // Specifier for the weapon
|
||||
},
|
||||
/// Request `ClientState::Registered` from an ingame state
|
||||
ExitIngame,
|
||||
/// Request `ClientState::Spectator` from a registered or ingame state
|
||||
Spectate,
|
||||
ControllerInputs(comp::ControllerInputs),
|
||||
ControlEvent(comp::ControlEvent),
|
||||
RequestState(ClientState),
|
||||
SetViewDistance(u32),
|
||||
BreakBlock(Vec3<i32>),
|
||||
PlaceBlock(Vec3<i32>, Block),
|
||||
Ping,
|
||||
Pong,
|
||||
ChatMsg {
|
||||
chat_type: ChatType, // This is unused afaik, TODO: remove
|
||||
message: String,
|
||||
},
|
||||
PlayerPhysics {
|
||||
@ -36,42 +36,3 @@ pub enum ClientMsg {
|
||||
},
|
||||
Disconnect,
|
||||
}
|
||||
|
||||
impl ClientMsg {
|
||||
pub fn chat(message: String) -> ClientMsg {
|
||||
ClientMsg::ChatMsg {
|
||||
chat_type: ChatType::Chat,
|
||||
message,
|
||||
}
|
||||
}
|
||||
pub fn tell(message: String) -> ClientMsg {
|
||||
ClientMsg::ChatMsg {
|
||||
chat_type: ChatType::Tell,
|
||||
message,
|
||||
}
|
||||
}
|
||||
pub fn game(message: String) -> ClientMsg {
|
||||
ClientMsg::ChatMsg {
|
||||
chat_type: ChatType::GameUpdate,
|
||||
message,
|
||||
}
|
||||
}
|
||||
pub fn broadcast(message: String) -> ClientMsg {
|
||||
ClientMsg::ChatMsg {
|
||||
chat_type: ChatType::Broadcast,
|
||||
message,
|
||||
}
|
||||
}
|
||||
pub fn private(message: String) -> ClientMsg {
|
||||
ClientMsg::ChatMsg {
|
||||
chat_type: ChatType::Private,
|
||||
message,
|
||||
}
|
||||
}
|
||||
pub fn kill(message: String) -> ClientMsg {
|
||||
ClientMsg::ChatMsg {
|
||||
chat_type: ChatType::Private,
|
||||
message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ pub enum ClientState {
|
||||
Connected,
|
||||
Registered,
|
||||
Spectator,
|
||||
Dead,
|
||||
Character,
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,9 @@ pub enum ServerMsg {
|
||||
},
|
||||
PlayerListUpdate(PlayerListUpdate),
|
||||
StateAnswer(Result<ClientState, (RequestStateError, ClientState)>),
|
||||
ForceState(ClientState),
|
||||
/// Trigger cleanup for when the client goes back to the `Registered` state from an ingame
|
||||
/// state
|
||||
ExitIngameCleanup,
|
||||
Ping,
|
||||
Pong,
|
||||
ChatMsg {
|
||||
|
@ -327,6 +327,28 @@ impl<'a> System<'a> for Sys {
|
||||
// Or do nothing
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process controller events
|
||||
for event in controller.events.drain(..) {
|
||||
match event {
|
||||
ControlEvent::Mount(mountee_uid) => {
|
||||
if let Some(mountee_entity) =
|
||||
uid_allocator.retrieve_entity_internal(mountee_uid.id())
|
||||
{
|
||||
server_emitter.emit(ServerEvent::Mount(entity, mountee_entity));
|
||||
}
|
||||
}
|
||||
ControlEvent::Unmount => server_emitter.emit(ServerEvent::Unmount(entity)),
|
||||
ControlEvent::InventoryManip(manip) => {
|
||||
server_emitter.emit(ServerEvent::InventoryManip(entity, manip))
|
||||
} /*ControlEvent::Respawn => {
|
||||
if state.is_dead {
|
||||
server_emitter.emit(ServerEvent::Respawn(entity)),
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
// If mounted, character state is controlled by mount
|
||||
if mount.is_some() {
|
||||
character.movement = Sit;
|
||||
@ -394,14 +416,14 @@ impl<'a> System<'a> for Sys {
|
||||
// Any Action + Falling
|
||||
(action_state, Fall) => {
|
||||
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
||||
if inputs.glide.is_pressed() {
|
||||
if inputs.glide.is_pressed() && can_glide(body) {
|
||||
character.movement = Glide;
|
||||
continue;
|
||||
}
|
||||
// Try to climb
|
||||
if let (true, Some(_wall_dir)) = (
|
||||
inputs.climb.is_pressed() | inputs.climb_down.is_pressed()
|
||||
&& body.is_humanoid(),
|
||||
(inputs.climb.is_pressed() | inputs.climb_down.is_pressed())
|
||||
&& can_climb(body),
|
||||
physics.on_wall,
|
||||
) {
|
||||
character.movement = Climb;
|
||||
@ -530,8 +552,8 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Try to climb
|
||||
if let (true, Some(_wall_dir)) = (
|
||||
inputs.climb.is_pressed() | inputs.climb_down.is_pressed()
|
||||
&& body.is_humanoid(),
|
||||
(inputs.climb.is_pressed() | inputs.climb_down.is_pressed())
|
||||
&& can_climb(body),
|
||||
physics.on_wall,
|
||||
) {
|
||||
character.movement = Climb;
|
||||
@ -578,9 +600,7 @@ impl<'a> System<'a> for Sys {
|
||||
// While not on ground ...
|
||||
else {
|
||||
// Try to glide
|
||||
if physics.on_wall == None
|
||||
&& inputs.glide.is_pressed()
|
||||
&& body.is_humanoid()
|
||||
if physics.on_wall == None && inputs.glide.is_pressed() && can_glide(&body)
|
||||
{
|
||||
character.movement = Glide;
|
||||
continue;
|
||||
@ -652,7 +672,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
if !inputs.glide.is_pressed() {
|
||||
character.movement = Fall;
|
||||
} else if let Some(_wall_dir) = physics.on_wall {
|
||||
} else if let (Some(_wall_dir), true) = (physics.on_wall, can_climb(body)) {
|
||||
character.movement = Climb;
|
||||
}
|
||||
|
||||
@ -681,23 +701,14 @@ impl<'a> System<'a> for Sys {
|
||||
// character.movement = Fall;
|
||||
// }
|
||||
};
|
||||
|
||||
// Process other controller events
|
||||
for event in controller.events.drain(..) {
|
||||
match event {
|
||||
ControlEvent::Mount(mountee_uid) => {
|
||||
if let Some(mountee_entity) =
|
||||
uid_allocator.retrieve_entity_internal(mountee_uid.id())
|
||||
{
|
||||
server_emitter.emit(ServerEvent::Mount(entity, mountee_entity));
|
||||
}
|
||||
}
|
||||
ControlEvent::Unmount => server_emitter.emit(ServerEvent::Unmount(entity)),
|
||||
ControlEvent::InventoryManip(manip) => {
|
||||
server_emitter.emit(ServerEvent::InventoryManip(entity, manip))
|
||||
} //ControlEvent::Respawn => server_emitter.emit(ServerEvent::Unmount(entity)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn can_glide(body: &Body) -> bool {
|
||||
body.is_humanoid()
|
||||
}
|
||||
|
||||
fn can_climb(body: &Body) -> bool {
|
||||
body.is_humanoid()
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ const CLEANUP_SYS: &str = "cleanup_sys";
|
||||
|
||||
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
|
||||
dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[AGENT_SYS]);
|
||||
dispatch_builder.add(mount::Sys, MOUNT_SYS, &[CONTROLLER_SYS]);
|
||||
dispatch_builder.add(mount::Sys, MOUNT_SYS, &[AGENT_SYS]);
|
||||
dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[AGENT_SYS, MOUNT_SYS]);
|
||||
dispatch_builder.add(movement::Sys, MOVEMENT_SYS, &[]);
|
||||
dispatch_builder.add(combat::Sys, COMBAT_SYS, &[CONTROLLER_SYS]);
|
||||
dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]);
|
||||
|
@ -40,9 +40,15 @@ impl<'a> System<'a> for Sys {
|
||||
match mount_states.get_unchecked() {
|
||||
MountState::Unmounted => {}
|
||||
MountState::MountedBy(mounter_uid) => {
|
||||
if let Some((controller, mounter)) = uid_allocator
|
||||
// Note: currently controller events are not passed through since none of them
|
||||
// are currently relevant to controlling the mounted entity
|
||||
if let Some((inputs, mounter)) = uid_allocator
|
||||
.retrieve_entity_internal(mounter_uid.id())
|
||||
.and_then(|mounter| controllers.get(mounter).cloned().map(|x| (x, mounter)))
|
||||
.and_then(|mounter| {
|
||||
controllers
|
||||
.get(mounter)
|
||||
.map(|c| (c.inputs.clone(), mounter))
|
||||
})
|
||||
{
|
||||
// TODO: consider joining on these? (remember we can use .maybe())
|
||||
let pos = positions.get(entity).copied();
|
||||
@ -53,7 +59,12 @@ impl<'a> System<'a> for Sys {
|
||||
let _ = orientations.insert(mounter, ori);
|
||||
let _ = velocities.insert(mounter, vel);
|
||||
}
|
||||
let _ = controllers.insert(entity, controller);
|
||||
controllers.get_mut(entity).map(|controller| {
|
||||
*controller = Controller {
|
||||
inputs,
|
||||
..Default::default()
|
||||
}
|
||||
});
|
||||
} else {
|
||||
*(mount_states.get_mut_unchecked()) = MountState::Unmounted;
|
||||
}
|
||||
|
@ -215,8 +215,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Climb
|
||||
if let (true, Some(_wall_dir)) = (
|
||||
(inputs.climb.is_pressed() | inputs.climb_down.is_pressed())
|
||||
&& vel.0.z <= CLIMB_SPEED,
|
||||
character.movement == Climb && vel.0.z <= CLIMB_SPEED,
|
||||
physics.on_wall,
|
||||
) {
|
||||
if inputs.climb_down.is_pressed() && !inputs.climb.is_pressed() {
|
||||
|
@ -11,6 +11,7 @@ pub struct Client {
|
||||
pub client_state: ClientState,
|
||||
pub postbox: PostBox<ServerMsg, ClientMsg>,
|
||||
pub last_ping: f64,
|
||||
pub login_msg_sent: bool,
|
||||
}
|
||||
|
||||
impl Component for Client {
|
||||
@ -23,16 +24,13 @@ impl Client {
|
||||
}
|
||||
pub fn is_registered(&self) -> bool {
|
||||
match self.client_state {
|
||||
ClientState::Registered
|
||||
| ClientState::Spectator
|
||||
| ClientState::Dead
|
||||
| ClientState::Character => true,
|
||||
ClientState::Registered | ClientState::Spectator | ClientState::Character => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_ingame(&self) -> bool {
|
||||
match self.client_state {
|
||||
ClientState::Spectator | ClientState::Character | ClientState::Dead => true,
|
||||
ClientState::Spectator | ClientState::Character => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -45,10 +43,6 @@ impl Client {
|
||||
self.postbox
|
||||
.send_message(ServerMsg::StateAnswer(Err((error, self.client_state))));
|
||||
}
|
||||
pub fn force_state(&mut self, new_state: ClientState) {
|
||||
self.client_state = new_state;
|
||||
self.postbox.send_message(ServerMsg::ForceState(new_state));
|
||||
}
|
||||
}
|
||||
|
||||
// Distance from fuzzy_chunk before snapping to current chunk
|
||||
|
@ -28,7 +28,7 @@ use common::{
|
||||
msg::{ClientMsg, ClientState, PlayerListUpdate, ServerError, ServerInfo, ServerMsg},
|
||||
net::PostOffice,
|
||||
state::{BlockChange, State, TimeOfDay},
|
||||
sync::{Uid, WorldSyncExt},
|
||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
||||
terrain::{block::Block, TerrainChunkSize, TerrainGrid},
|
||||
vol::{ReadVol, RectVolSize, Vox},
|
||||
};
|
||||
@ -36,8 +36,8 @@ use log::{debug, error};
|
||||
use metrics::ServerMetrics;
|
||||
use rand::Rng;
|
||||
use specs::{
|
||||
join::Join, world::EntityBuilder as EcsEntityBuilder, Builder, Entity as EcsEntity, RunNow,
|
||||
SystemData, WorldExt,
|
||||
join::Join, saveload::MarkerAllocator, world::EntityBuilder as EcsEntityBuilder, Builder,
|
||||
Entity as EcsEntity, RunNow, SystemData, WorldExt,
|
||||
};
|
||||
use std::{
|
||||
i32,
|
||||
@ -413,10 +413,12 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
let mut remove = true;
|
||||
|
||||
if let Some(client) = state.ecs().write_storage::<Client>().get_mut(entity) {
|
||||
remove = false;
|
||||
if state
|
||||
.ecs()
|
||||
.write_storage::<Client>()
|
||||
.get_mut(entity)
|
||||
.is_some()
|
||||
{
|
||||
state
|
||||
.ecs()
|
||||
.write_storage()
|
||||
@ -431,10 +433,8 @@ impl Server {
|
||||
.map(|err| {
|
||||
error!("Failed to insert ForceUpdate on dead client: {:?}", err)
|
||||
});
|
||||
client.force_state(ClientState::Dead);
|
||||
}
|
||||
|
||||
if remove {
|
||||
} else {
|
||||
// If not a player delete the entity
|
||||
if let Err(err) = state.delete_entity_recorded(entity) {
|
||||
error!("Failed to delete destroyed entity: {:?}", err);
|
||||
}
|
||||
@ -580,13 +580,17 @@ impl Server {
|
||||
|
||||
ServerEvent::Respawn(entity) => {
|
||||
// Only clients can respawn
|
||||
if let Some(client) = state.ecs().write_storage::<Client>().get_mut(entity) {
|
||||
if state
|
||||
.ecs()
|
||||
.write_storage::<Client>()
|
||||
.get_mut(entity)
|
||||
.is_some()
|
||||
{
|
||||
let respawn_point = state
|
||||
.read_component_cloned::<comp::Waypoint>(entity)
|
||||
.map(|wp| wp.get_pos())
|
||||
.unwrap_or(state.ecs().read_resource::<SpawnPoint>().0);
|
||||
|
||||
client.allow_state(ClientState::Character);
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::Stats>()
|
||||
@ -597,10 +601,13 @@ impl Server {
|
||||
.write_storage::<comp::Pos>()
|
||||
.get_mut(entity)
|
||||
.map(|pos| pos.0 = respawn_point);
|
||||
let _ = state
|
||||
state
|
||||
.ecs()
|
||||
.write_storage()
|
||||
.insert(entity, comp::ForceUpdate);
|
||||
.insert(entity, comp::ForceUpdate)
|
||||
.err().map(|err|
|
||||
error!("Error inserting ForceUpdate component when respawning client: {:?}", err)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -730,7 +737,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
ServerEvent::CreatePlayer {
|
||||
ServerEvent::CreateCharacter {
|
||||
entity,
|
||||
name,
|
||||
body,
|
||||
@ -747,6 +754,37 @@ impl Server {
|
||||
sys::subscription::initialize_region_subscription(state.ecs(), entity);
|
||||
}
|
||||
|
||||
ServerEvent::ExitIngame { entity } => {
|
||||
// Create new entity with just `Client`, `Uid`, and `Player` components
|
||||
// Easier than checking and removing all other known components
|
||||
// Note: If other `ServerEvent`s are referring to this entity they will be
|
||||
// disrupted
|
||||
let maybe_client = state.ecs().write_storage::<Client>().remove(entity);
|
||||
let maybe_uid = state.read_component_cloned::<Uid>(entity);
|
||||
let maybe_player = state.ecs().write_storage::<comp::Player>().remove(entity);
|
||||
if let (Some(mut client), Some(uid), Some(player)) =
|
||||
(maybe_client, maybe_uid, maybe_player)
|
||||
{
|
||||
// Tell client its request was successful
|
||||
client.allow_state(ClientState::Registered);
|
||||
// Tell client to clear out other entities and its own components
|
||||
client.notify(ServerMsg::ExitIngameCleanup);
|
||||
|
||||
let entity_builder =
|
||||
state.ecs_mut().create_entity().with(client).with(player);
|
||||
// Ensure UidAllocator maps this uid to the new entity
|
||||
let uid = entity_builder
|
||||
.world
|
||||
.write_resource::<UidAllocator>()
|
||||
.allocate(entity_builder.entity, Some(uid.into()));
|
||||
entity_builder.with(uid).build();
|
||||
}
|
||||
// Delete old entity
|
||||
if let Err(err) = state.delete_entity_recorded(entity) {
|
||||
error!("Failed to delete entity when removing character: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
ServerEvent::CreateNpc {
|
||||
pos,
|
||||
stats,
|
||||
@ -993,6 +1031,7 @@ impl Server {
|
||||
client_state: ClientState::Connected,
|
||||
postbox,
|
||||
last_ping: self.state.get_time(),
|
||||
login_msg_sent: false,
|
||||
};
|
||||
|
||||
if self.server_settings.max_players
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::SysTimer;
|
||||
use crate::{auth_provider::AuthProvider, client::Client, CLIENT_TIMEOUT};
|
||||
use common::{
|
||||
comp::{Admin, Body, CanBuild, Controller, ForceUpdate, Ori, Player, Pos, Vel},
|
||||
comp::{Admin, Body, CanBuild, Controller, ForceUpdate, Ori, Player, Pos, Stats, Vel},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{
|
||||
validate_chat_msg, ChatMsgValidationError, ClientMsg, ClientState, PlayerListUpdate,
|
||||
@ -31,6 +31,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, CanBuild>,
|
||||
ReadStorage<'a, Admin>,
|
||||
ReadStorage<'a, ForceUpdate>,
|
||||
ReadStorage<'a, Stats>,
|
||||
WriteExpect<'a, AuthProvider>,
|
||||
Write<'a, BlockChange>,
|
||||
WriteStorage<'a, Pos>,
|
||||
@ -54,6 +55,7 @@ impl<'a> System<'a> for Sys {
|
||||
can_build,
|
||||
admins,
|
||||
force_updates,
|
||||
stats,
|
||||
mut accounts,
|
||||
mut block_changes,
|
||||
mut positions,
|
||||
@ -98,40 +100,26 @@ impl<'a> System<'a> for Sys {
|
||||
// Process incoming messages.
|
||||
for msg in new_msgs {
|
||||
match msg {
|
||||
ClientMsg::RequestState(requested_state) => match requested_state {
|
||||
ClientState::Connected => disconnect = true, // Default state
|
||||
ClientState::Registered => match client.client_state {
|
||||
// Use ClientMsg::Register instead.
|
||||
ClientState::Connected => {
|
||||
client.error_state(RequestStateError::WrongMessage)
|
||||
}
|
||||
ClientState::Registered => {
|
||||
client.error_state(RequestStateError::Already)
|
||||
}
|
||||
ClientState::Spectator | ClientState::Character | ClientState::Dead => {
|
||||
// TODO: remove position etc here
|
||||
client.allow_state(ClientState::Registered)
|
||||
}
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
ClientState::Spectator => match requested_state {
|
||||
// Become Registered first.
|
||||
ClientState::Connected => {
|
||||
client.error_state(RequestStateError::Impossible)
|
||||
}
|
||||
ClientState::Spectator => {
|
||||
client.error_state(RequestStateError::Already)
|
||||
}
|
||||
ClientState::Registered
|
||||
| ClientState::Character
|
||||
| ClientState::Dead => client.allow_state(ClientState::Spectator),
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
// Use ClientMsg::Character instead.
|
||||
ClientState::Character => {
|
||||
// Go back to registered state (char selection screen)
|
||||
ClientMsg::ExitIngame => match client.client_state {
|
||||
// Use ClientMsg::Register instead.
|
||||
ClientState::Connected => {
|
||||
client.error_state(RequestStateError::WrongMessage)
|
||||
}
|
||||
ClientState::Dead => client.error_state(RequestStateError::Impossible),
|
||||
ClientState::Registered => client.error_state(RequestStateError::Already),
|
||||
ClientState::Spectator | ClientState::Character => {
|
||||
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
||||
}
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
// Request spectator state
|
||||
ClientMsg::Spectate => match client.client_state {
|
||||
// Become Registered first.
|
||||
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
||||
ClientState::Spectator => client.error_state(RequestStateError::Already),
|
||||
ClientState::Registered | ClientState::Character => {
|
||||
client.allow_state(ClientState::Spectator)
|
||||
}
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
// Valid player
|
||||
@ -173,12 +161,12 @@ impl<'a> System<'a> for Sys {
|
||||
ClientMsg::Character { name, body, main } => match client.client_state {
|
||||
// Become Registered first.
|
||||
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
||||
ClientState::Registered | ClientState::Spectator | ClientState::Dead => {
|
||||
if let (Some(player), None) = (
|
||||
ClientState::Registered | ClientState::Spectator => {
|
||||
if let (Some(player), false) = (
|
||||
players.get(entity),
|
||||
// Only send login message if the player didn't have a body
|
||||
// Only send login message if it wasn't already sent
|
||||
// previously
|
||||
bodies.get(entity),
|
||||
client.login_msg_sent,
|
||||
) {
|
||||
new_chat_msgs.push((
|
||||
None,
|
||||
@ -187,9 +175,10 @@ impl<'a> System<'a> for Sys {
|
||||
&player.alias
|
||||
)),
|
||||
));
|
||||
client.login_msg_sent = true;
|
||||
}
|
||||
|
||||
server_emitter.emit(ServerEvent::CreatePlayer {
|
||||
server_emitter.emit(ServerEvent::CreateCharacter {
|
||||
entity,
|
||||
name,
|
||||
body,
|
||||
@ -205,7 +194,7 @@ impl<'a> System<'a> for Sys {
|
||||
| ClientState::Spectator => {
|
||||
client.error_state(RequestStateError::Impossible)
|
||||
}
|
||||
ClientState::Dead | ClientState::Character => {
|
||||
ClientState::Character => {
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.inputs = inputs;
|
||||
}
|
||||
@ -218,21 +207,19 @@ impl<'a> System<'a> for Sys {
|
||||
| ClientState::Spectator => {
|
||||
client.error_state(RequestStateError::Impossible)
|
||||
}
|
||||
ClientState::Dead | ClientState::Character => {
|
||||
ClientState::Character => {
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.events.push(event);
|
||||
}
|
||||
}
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
ClientMsg::ChatMsg { chat_type, message } => match client.client_state {
|
||||
ClientMsg::ChatMsg { message } => match client.client_state {
|
||||
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
||||
ClientState::Registered
|
||||
| ClientState::Spectator
|
||||
| ClientState::Dead
|
||||
| ClientState::Character => match validate_chat_msg(&message) {
|
||||
Ok(()) => new_chat_msgs
|
||||
.push((Some(entity), ServerMsg::ChatMsg { chat_type, message })),
|
||||
Ok(()) => new_chat_msgs.push((Some(entity), ServerMsg::chat(message))),
|
||||
Err(ChatMsgValidationError::TooLong) => log::warn!(
|
||||
"Recieved a chat message that's too long (max:{} len:{})",
|
||||
MAX_BYTES_CHAT_MSG,
|
||||
@ -243,7 +230,9 @@ impl<'a> System<'a> for Sys {
|
||||
},
|
||||
ClientMsg::PlayerPhysics { pos, vel, ori } => match client.client_state {
|
||||
ClientState::Character => {
|
||||
if force_updates.get(entity).is_none() {
|
||||
if force_updates.get(entity).is_none()
|
||||
&& stats.get(entity).map_or(true, |s| !s.is_dead)
|
||||
{
|
||||
let _ = positions.insert(entity, pos);
|
||||
let _ = velocities.insert(entity, vel);
|
||||
let _ = orientations.insert(entity, ori);
|
||||
@ -263,7 +252,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
ClientMsg::TerrainChunkRequest { key } => match client.client_state {
|
||||
ClientState::Connected | ClientState::Registered | ClientState::Dead => {
|
||||
ClientState::Connected | ClientState::Registered => {
|
||||
client.error_state(RequestStateError::Impossible);
|
||||
}
|
||||
ClientState::Spectator | ClientState::Character => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use client::{error::Error as ClientError, Client};
|
||||
use common::comp;
|
||||
use common::{comp, net::PostError};
|
||||
use crossbeam::channel::{unbounded, Receiver, TryRecvError};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
@ -87,6 +87,10 @@ impl ClientInit {
|
||||
}
|
||||
Err(err) => {
|
||||
match err {
|
||||
ClientError::Network(PostError::Bincode(_)) => {
|
||||
last_err = Some(Error::ConnectionFailed(err));
|
||||
break 'tries;
|
||||
}
|
||||
// Assume the connection failed and try again soon
|
||||
ClientError::Network(_) => {}
|
||||
ClientError::TooManyPlayers => {
|
||||
|
@ -121,9 +121,7 @@ impl PlayState for SessionState {
|
||||
|
||||
// Game loop
|
||||
let mut current_client_state = self.client.borrow().get_client_state();
|
||||
while let ClientState::Pending | ClientState::Character | ClientState::Dead =
|
||||
current_client_state
|
||||
{
|
||||
while let ClientState::Pending | ClientState::Character = current_client_state {
|
||||
// Compute camera data
|
||||
let (view_mat, _, cam_pos) = self
|
||||
.scene
|
||||
|
Loading…
Reference in New Issue
Block a user