mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'master' into 'master'
add chat commands See merge request veloren/veloren!33 Former-commit-id: dbef1677b800ae3eaecdeba4fda12973654c9149
This commit is contained in:
commit
d4368e2891
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -1552,6 +1552,14 @@ dependencies = [
|
|||||||
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scan_fmt"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped_threadpool"
|
name = "scoped_threadpool"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@ -1907,6 +1915,8 @@ dependencies = [
|
|||||||
name = "veloren-server"
|
name = "veloren-server"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"scan_fmt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"vek 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"vek 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -2329,6 +2339,7 @@ dependencies = [
|
|||||||
"checksum rusttype 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "25951e85bb2647960969f72c559392245a5bd07446a589390bf427dda31cdc4a"
|
"checksum rusttype 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "25951e85bb2647960969f72c559392245a5bd07446a589390bf427dda31cdc4a"
|
||||||
"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
|
"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
|
||||||
"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
|
"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
|
||||||
|
"checksum scan_fmt 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b87497427f9fbe539ee6b9626f5a5e899331fdf1c1d62f14c637a462969db30"
|
||||||
"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||||
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
|
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
|
||||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
@ -11,3 +11,5 @@ world = { package = "veloren-world", path = "../world" }
|
|||||||
specs = "0.14"
|
specs = "0.14"
|
||||||
vek = "0.9"
|
vek = "0.9"
|
||||||
threadpool = "1.7"
|
threadpool = "1.7"
|
||||||
|
lazy_static = "1.3.0"
|
||||||
|
scan_fmt = "0.1.3"
|
178
server/src/cmd.rs
Normal file
178
server/src/cmd.rs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
//! # Implementing new commands
|
||||||
|
//! To implement a new command, add an instance of `ChatCommand` to `CHAT_COMMANDS`
|
||||||
|
//! and provide a handler function.
|
||||||
|
|
||||||
|
use crate::Server;
|
||||||
|
use common::{comp, msg::ServerMsg};
|
||||||
|
use specs::{join::Join, Entity as EcsEntity};
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use scan_fmt::scan_fmt;
|
||||||
|
/// Struct representing a command that a user can run from server chat
|
||||||
|
pub struct ChatCommand {
|
||||||
|
/// The keyword used to invoke the command, omitting the leading '/'
|
||||||
|
pub keyword: &'static str,
|
||||||
|
/// A format string for parsing arguments
|
||||||
|
arg_fmt: &'static str,
|
||||||
|
/// message to explain how the command is used
|
||||||
|
help_string: &'static str,
|
||||||
|
/// Handler function called when the command is executed
|
||||||
|
/// # Arguments
|
||||||
|
/// * `&mut Server` - the `Server` instance executing the command
|
||||||
|
/// * `EcsEntity` - an `Entity` corresponding to the player that invoked the command
|
||||||
|
/// * `String` - a `String` containing the part of the command after the keyword
|
||||||
|
/// * `&ChatCommand` - the command to execute with the above arguments
|
||||||
|
/// Handler functions must parse arguments from the the given `String` (`scan_fmt!` is included for this purpose)
|
||||||
|
handler: fn(&mut Server, EcsEntity, String, &ChatCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChatCommand {
|
||||||
|
/// Creates a new chat command
|
||||||
|
pub fn new(
|
||||||
|
keyword: &'static str,
|
||||||
|
arg_fmt: &'static str,
|
||||||
|
help_string: &'static str,
|
||||||
|
handler: fn(&mut Server, EcsEntity, String, &ChatCommand),
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
keyword,
|
||||||
|
arg_fmt,
|
||||||
|
help_string,
|
||||||
|
handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Calls the contained handler function, passing `&self` as the last argument
|
||||||
|
pub fn execute(&self, server: &mut Server, entity: EcsEntity, args: String) {
|
||||||
|
(self.handler)(server, entity, args, self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
/// Static list of chat commands available to the server
|
||||||
|
pub static ref CHAT_COMMANDS: Vec<ChatCommand> = vec![
|
||||||
|
ChatCommand::new(
|
||||||
|
"jump",
|
||||||
|
"{d} {d} {d}",
|
||||||
|
"jump: offset your current position by a vector\n
|
||||||
|
Usage: /jump [x] [y] [z]",
|
||||||
|
handle_jump
|
||||||
|
),
|
||||||
|
ChatCommand::new(
|
||||||
|
"goto",
|
||||||
|
"{d} {d} {d}",
|
||||||
|
"goto: teleport to a given position\n
|
||||||
|
Usage: /goto [x] [y] [z]",
|
||||||
|
handle_goto
|
||||||
|
),
|
||||||
|
ChatCommand::new(
|
||||||
|
"alias",
|
||||||
|
"{}",
|
||||||
|
"alias: change your player name (cannot contain spaces)\n
|
||||||
|
Usage: /alias [name]",
|
||||||
|
handle_alias
|
||||||
|
),
|
||||||
|
ChatCommand::new(
|
||||||
|
"tp",
|
||||||
|
"{}",
|
||||||
|
"tp: teleport to a named player\n
|
||||||
|
Usage: /tp [name]",
|
||||||
|
handle_tp
|
||||||
|
),
|
||||||
|
ChatCommand::new("help", "", "help: display this message", handle_help)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||||
|
let (opt_x, opt_y, opt_z) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32);
|
||||||
|
match (opt_x, opt_y, opt_z) {
|
||||||
|
(Some(x), Some(y), Some(z)) => {
|
||||||
|
match server
|
||||||
|
.state
|
||||||
|
.read_component_cloned::<comp::phys::Pos>(entity)
|
||||||
|
{
|
||||||
|
Some(current_pos) => server
|
||||||
|
.state
|
||||||
|
.write_component(entity, comp::phys::Pos(current_pos.0 + Vec3::new(x, y, z))),
|
||||||
|
None => server.clients.notify(
|
||||||
|
entity,
|
||||||
|
ServerMsg::Chat(String::from("Command 'jump' invalid in current state")),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => server
|
||||||
|
.clients
|
||||||
|
.notify(entity, ServerMsg::Chat(String::from(action.help_string))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||||
|
let (opt_x, opt_y, opt_z) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32);
|
||||||
|
match (opt_x, opt_y, opt_z) {
|
||||||
|
(Some(x), Some(y), Some(z)) => server
|
||||||
|
.state
|
||||||
|
.write_component(entity, comp::phys::Pos(Vec3::new(x, y, z))),
|
||||||
|
_ => server
|
||||||
|
.clients
|
||||||
|
.notify(entity, ServerMsg::Chat(String::from(action.help_string))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||||
|
let opt_alias = scan_fmt!(&args, action.arg_fmt, String);
|
||||||
|
match opt_alias {
|
||||||
|
Some(alias) => server
|
||||||
|
.state
|
||||||
|
.write_component(entity, comp::player::Player { alias }),
|
||||||
|
None => server
|
||||||
|
.clients
|
||||||
|
.notify(entity, ServerMsg::Chat(String::from(action.help_string))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||||
|
let opt_alias = scan_fmt!(&args, action.arg_fmt, String);
|
||||||
|
match opt_alias {
|
||||||
|
Some(alias) => {
|
||||||
|
let ecs = server.state.ecs().internal();
|
||||||
|
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::player::Player>())
|
||||||
|
.join()
|
||||||
|
.find(|(_, player)| player.alias == alias)
|
||||||
|
.map(|(entity, _)| entity);
|
||||||
|
match opt_player {
|
||||||
|
Some(player) => match server
|
||||||
|
.state
|
||||||
|
.read_component_cloned::<comp::phys::Pos>(player)
|
||||||
|
{
|
||||||
|
Some(pos) => server.state.write_component(entity, pos),
|
||||||
|
None => server.clients.notify(
|
||||||
|
entity,
|
||||||
|
ServerMsg::Chat(format!("Unable to teleport to player '{}'", alias)),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
None => {
|
||||||
|
server.clients.notify(
|
||||||
|
entity,
|
||||||
|
ServerMsg::Chat(format!("Player '{}' not found!", alias)),
|
||||||
|
);
|
||||||
|
server
|
||||||
|
.clients
|
||||||
|
.notify(entity, ServerMsg::Chat(String::from(action.help_string)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => server
|
||||||
|
.clients
|
||||||
|
.notify(entity, ServerMsg::Chat(String::from(action.help_string))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) {
|
||||||
|
for cmd in CHAT_COMMANDS.iter() {
|
||||||
|
server
|
||||||
|
.clients
|
||||||
|
.notify(entity, ServerMsg::Chat(String::from(cmd.help_string)));
|
||||||
|
}
|
||||||
|
}
|
@ -3,55 +3,34 @@
|
|||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
|
pub mod cmd;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use crate::{
|
pub use crate::{error::Error, input::Input};
|
||||||
error::Error,
|
|
||||||
input::Input,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::{
|
use crate::{client::{Client, ClientState, Clients}, cmd::CHAT_COMMANDS};
|
||||||
time::Duration,
|
|
||||||
net::SocketAddr,
|
|
||||||
sync::mpsc,
|
|
||||||
collections::HashSet,
|
|
||||||
};
|
|
||||||
use specs::{
|
|
||||||
Entity as EcsEntity,
|
|
||||||
world::EntityBuilder as EcsEntityBuilder,
|
|
||||||
Builder,
|
|
||||||
join::Join,
|
|
||||||
saveload::MarkedBuilder,
|
|
||||||
};
|
|
||||||
use vek::*;
|
|
||||||
use threadpool::ThreadPool;
|
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
state::{State, Uid},
|
msg::{ClientMsg, ServerMsg},
|
||||||
net::PostOffice,
|
net::PostOffice,
|
||||||
msg::{ServerMsg, ClientMsg},
|
state::State,
|
||||||
terrain::TerrainChunk,
|
terrain::TerrainChunk,
|
||||||
};
|
};
|
||||||
use world::World;
|
use specs::{
|
||||||
use crate::client::{
|
join::Join, saveload::MarkedBuilder, world::EntityBuilder as EcsEntityBuilder, Builder,
|
||||||
ClientState,
|
Entity as EcsEntity,
|
||||||
Client,
|
|
||||||
Clients,
|
|
||||||
};
|
};
|
||||||
|
use std::{collections::HashSet, net::SocketAddr, sync::mpsc, time::Duration};
|
||||||
|
use threadpool::ThreadPool;
|
||||||
|
use vek::*;
|
||||||
|
use world::World;
|
||||||
|
|
||||||
const CLIENT_TIMEOUT: f64 = 5.0; // Seconds
|
const CLIENT_TIMEOUT: f64 = 5.0; // Seconds
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
ClientConnected {
|
ClientConnected { entity: EcsEntity },
|
||||||
entity: EcsEntity,
|
ClientDisconnected { entity: EcsEntity },
|
||||||
},
|
Chat { entity: EcsEntity, msg: String },
|
||||||
ClientDisconnected {
|
|
||||||
entity: EcsEntity,
|
|
||||||
},
|
|
||||||
Chat {
|
|
||||||
entity: EcsEntity,
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
@ -73,11 +52,8 @@ impl Server {
|
|||||||
pub fn new() -> Result<Self, Error> {
|
pub fn new() -> Result<Self, Error> {
|
||||||
let (chunk_tx, chunk_rx) = mpsc::channel();
|
let (chunk_tx, chunk_rx) = mpsc::channel();
|
||||||
|
|
||||||
let mut state = State::new();
|
|
||||||
state.ecs_mut().internal_mut().register::<comp::phys::ForceUpdate>();
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
state,
|
state: State::new(),
|
||||||
world: World::new(),
|
world: World::new(),
|
||||||
|
|
||||||
postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?,
|
postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?,
|
||||||
@ -94,17 +70,25 @@ impl Server {
|
|||||||
|
|
||||||
/// Get a reference to the server's game state.
|
/// Get a reference to the server's game state.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn state(&self) -> &State { &self.state }
|
pub fn state(&self) -> &State {
|
||||||
|
&self.state
|
||||||
|
}
|
||||||
/// Get a mutable reference to the server's game state.
|
/// Get a mutable reference to the server's game state.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
|
pub fn state_mut(&mut self) -> &mut State {
|
||||||
|
&mut self.state
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a reference to the server's world.
|
/// Get a reference to the server's world.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn world(&self) -> &World { &self.world }
|
pub fn world(&self) -> &World {
|
||||||
|
&self.world
|
||||||
|
}
|
||||||
/// Get a mutable reference to the server's world.
|
/// Get a mutable reference to the server's world.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn world_mut(&mut self) -> &mut World { &mut self.world }
|
pub fn world_mut(&mut self) -> &mut World {
|
||||||
|
&mut self.world
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute a single server tick, handle input and update the game state by the given duration
|
/// Execute a single server tick, handle input and update the game state by the given duration
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -147,8 +131,14 @@ impl Server {
|
|||||||
for (entity, player, pos) in (
|
for (entity, player, pos) in (
|
||||||
&self.state.ecs().internal().entities(),
|
&self.state.ecs().internal().entities(),
|
||||||
&self.state.ecs().internal().read_storage::<comp::Player>(),
|
&self.state.ecs().internal().read_storage::<comp::Player>(),
|
||||||
&self.state.ecs().internal().read_storage::<comp::phys::Pos>(),
|
&self
|
||||||
).join() {
|
.state
|
||||||
|
.ecs()
|
||||||
|
.internal()
|
||||||
|
.read_storage::<comp::phys::Pos>(),
|
||||||
|
)
|
||||||
|
.join()
|
||||||
|
{
|
||||||
// TODO: Distance check
|
// TODO: Distance check
|
||||||
// if self.state.terrain().key_pos(key)
|
// if self.state.terrain().key_pos(key)
|
||||||
|
|
||||||
@ -182,23 +172,18 @@ impl Server {
|
|||||||
let mut frontend_events = Vec::new();
|
let mut frontend_events = Vec::new();
|
||||||
|
|
||||||
for mut postbox in self.postoffice.new_postboxes() {
|
for mut postbox in self.postoffice.new_postboxes() {
|
||||||
let entity = self.state
|
let entity = self.state.ecs_mut().create_entity_synced().build();
|
||||||
.ecs_mut()
|
|
||||||
.create_entity_synced()
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// Make sure the entity gets properly created
|
self.clients.add(
|
||||||
self.state.ecs_mut().internal_mut().maintain();
|
|
||||||
|
|
||||||
self.clients.add(entity, Client {
|
|
||||||
state: ClientState::Connecting,
|
|
||||||
postbox,
|
|
||||||
last_ping: self.state.get_time(),
|
|
||||||
});
|
|
||||||
|
|
||||||
frontend_events.push(Event::ClientConnected {
|
|
||||||
entity,
|
entity,
|
||||||
});
|
Client {
|
||||||
|
state: ClientState::Connecting,
|
||||||
|
postbox,
|
||||||
|
last_ping: self.state.get_time(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
frontend_events.push(Event::ClientConnected { entity });
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
@ -236,7 +221,6 @@ impl Server {
|
|||||||
state.write_component(entity, character);
|
state.write_component(entity, character);
|
||||||
|
|
||||||
}
|
}
|
||||||
state.write_component(entity, comp::phys::ForceUpdate);
|
|
||||||
|
|
||||||
client.state = ClientState::Connected;
|
client.state = ClientState::Connected;
|
||||||
|
|
||||||
@ -249,34 +233,36 @@ impl Server {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.into(),
|
.into(),
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
_ => disconnect = true,
|
_ => disconnect = true,
|
||||||
},
|
},
|
||||||
ClientState::Connected => match msg {
|
ClientState::Connected => match msg {
|
||||||
ClientMsg::Connect { .. } => disconnect = true, // Not allowed when already connected
|
ClientMsg::Connect { .. } => disconnect = true, // Not allowed when already connected
|
||||||
ClientMsg::Disconnect => disconnect = true,
|
ClientMsg::Disconnect => disconnect = true,
|
||||||
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
|
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
|
||||||
ClientMsg::Pong => {},
|
ClientMsg::Pong => {}
|
||||||
ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)),
|
ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)),
|
||||||
ClientMsg::PlayerAnimation(animation) => state.write_component(entity, animation),
|
ClientMsg::PlayerAnimation(animation) => state.write_component(entity, animation),
|
||||||
ClientMsg::PlayerPhysics { pos, vel, dir } => {
|
ClientMsg::PlayerPhysics { pos, vel, dir } => {
|
||||||
state.write_component(entity, pos);
|
state.write_component(entity, pos);
|
||||||
state.write_component(entity, vel);
|
state.write_component(entity, vel);
|
||||||
state.write_component(entity, dir);
|
state.write_component(entity, dir);
|
||||||
},
|
}
|
||||||
ClientMsg::TerrainChunkRequest { key } => match state.terrain().get_key(key) {
|
ClientMsg::TerrainChunkRequest { key } => {
|
||||||
Some(chunk) => {}, /*client.postbox.send_message(ServerMsg::TerrainChunkUpdate {
|
match state.terrain().get_key(key) {
|
||||||
|
Some(chunk) => {} /*client.postbox.send_message(ServerMsg::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
chunk: Box::new(chunk.clone()),
|
chunk: Box::new(chunk.clone()),
|
||||||
}),*/
|
}),*/
|
||||||
None => requested_chunks.push(key),
|
None => requested_chunks.push(key),
|
||||||
},
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if
|
} else if state.get_time() - client.last_ping > CLIENT_TIMEOUT || // Timeout
|
||||||
state.get_time() - client.last_ping > CLIENT_TIMEOUT || // Timeout
|
client.postbox.error().is_some()
|
||||||
client.postbox.error().is_some() // Postbox error
|
// Postbox error
|
||||||
{
|
{
|
||||||
disconnect = true;
|
disconnect = true;
|
||||||
} else if state.get_time() - client.last_ping > CLIENT_TIMEOUT * 0.5 {
|
} else if state.get_time() - client.last_ping > CLIENT_TIMEOUT * 0.5 {
|
||||||
@ -294,29 +280,33 @@ impl Server {
|
|||||||
|
|
||||||
// Handle new chat messages
|
// Handle new chat messages
|
||||||
for (entity, msg) in new_chat_msgs {
|
for (entity, msg) in new_chat_msgs {
|
||||||
self.clients.notify_connected(ServerMsg::Chat(match state
|
// Handle chat commands
|
||||||
.ecs()
|
if msg.starts_with("/") && msg.len() > 1 {
|
||||||
.internal()
|
let argv = String::from(&msg[1..]);
|
||||||
.read_storage::<comp::Player>()
|
self.process_chat_cmd(entity, argv);
|
||||||
.get(entity)
|
} else {
|
||||||
{
|
self.clients.notify_connected(ServerMsg::Chat(
|
||||||
Some(player) => format!("[{}] {}", &player.alias, msg),
|
match self
|
||||||
None => format!("[<anon>] {}", msg),
|
.state
|
||||||
}));
|
.ecs()
|
||||||
|
.internal()
|
||||||
|
.read_storage::<comp::Player>()
|
||||||
|
.get(entity)
|
||||||
|
{
|
||||||
|
Some(player) => format!("[{}] {}", &player.alias, msg),
|
||||||
|
None => format!("[<anon>] {}", msg),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
frontend_events.push(Event::Chat {
|
frontend_events.push(Event::Chat { entity, msg });
|
||||||
entity,
|
}
|
||||||
msg,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle client disconnects
|
// Handle client disconnects
|
||||||
for entity in disconnected_clients {
|
for entity in disconnected_clients {
|
||||||
state.ecs_mut().delete_entity_synced(entity).unwrap();
|
self.state.ecs_mut().delete_entity_synced(entity);
|
||||||
|
|
||||||
frontend_events.push(Event::ClientDisconnected {
|
frontend_events.push(Event::ClientDisconnected { entity });
|
||||||
entity,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate requested chunks
|
// Generate requested chunks
|
||||||
@ -375,7 +365,33 @@ impl Server {
|
|||||||
pub fn generate_chunk(&mut self, key: Vec3<i32>) {
|
pub fn generate_chunk(&mut self, key: Vec3<i32>) {
|
||||||
if self.pending_chunks.insert(key) {
|
if self.pending_chunks.insert(key) {
|
||||||
let chunk_tx = self.chunk_tx.clone();
|
let chunk_tx = self.chunk_tx.clone();
|
||||||
self.thread_pool.execute(move || chunk_tx.send((key, World::generate_chunk(key))).unwrap());
|
self.thread_pool
|
||||||
|
.execute(move || chunk_tx.send((key, World::generate_chunk(key))).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_chat_cmd(&mut self, entity: EcsEntity, cmd: String) {
|
||||||
|
// separate string into keyword and arguments
|
||||||
|
let sep = cmd.find(' ');
|
||||||
|
let (kwd, args) = match sep {
|
||||||
|
Some(i) => (cmd[..i].to_string(), cmd[(i + 1)..].to_string()),
|
||||||
|
None => (cmd, "".to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// find command object and run its handler
|
||||||
|
let action_opt = CHAT_COMMANDS.iter().find(|x| x.keyword == kwd);
|
||||||
|
match action_opt {
|
||||||
|
Some(action) => action.execute(self, entity, args),
|
||||||
|
// unknown command
|
||||||
|
None => {
|
||||||
|
self.clients.notify(
|
||||||
|
entity,
|
||||||
|
ServerMsg::Chat(format!(
|
||||||
|
"Unrecognised command: '/{}'\ntype '/help' for a list of available commands",
|
||||||
|
kwd
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user