add chat commands, rustfmt

Former-commit-id: 64941127598b53c64fe2e0e7c167f0ce3f358060
This commit is contained in:
Sebastian Venter 2019-04-16 14:06:30 +01:00 committed by sxv20_
parent 19d6b7b38a
commit 031e4b36d6
3 changed files with 268 additions and 91 deletions

11
Cargo.lock generated
View File

@ -1499,6 +1499,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"
@ -1811,6 +1819,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)",
@ -2208,6 +2218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"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 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"

View File

@ -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"

View File

@ -5,53 +5,80 @@ pub mod error;
pub mod input; pub mod input;
// Reexports // Reexports
pub use crate::{ pub use crate::{error::Error, input::Input};
error::Error,
input::Input,
};
use std::{ use crate::client::{Client, ClientState, Clients};
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;
use lazy_static::lazy_static;
use scan_fmt::scan_fmt;
const CLIENT_TIMEOUT: f64 = 5.0; // Seconds const CLIENT_TIMEOUT: f64 = 5.0; // Seconds
struct ChatCommand {
keyword: &'static str,
arg_fmt: &'static str,
help_string: &'static str,
}
impl ChatCommand {
pub fn new(keyword: &'static str, arg_fmt: &'static str, help_string: &'static str) -> Self {
Self {
keyword,
arg_fmt,
help_string,
}
}
}
lazy_static! {
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]"
),
ChatCommand::new(
"goto",
"{d} {d} {d}",
"goto: teleport to a given position\n
Usage: /goto [x] [y] [z]"
),
ChatCommand::new(
"alias",
"{}",
"alias: change your player name (cannot contain spaces)\n
Usage: /alias [name]"
),
ChatCommand::new(
"tp",
"{}",
"tp: teleport to a named player\n
Usage: /tp [name]"
),
ChatCommand::new("help", "", "help: display this message")
];
}
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 +100,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 +118,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 +179,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 +220,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(); entity,
Client {
self.clients.add(entity, Client {
state: ClientState::Connecting, state: ClientState::Connecting,
postbox, postbox,
last_ping: self.state.get_time(), last_ping: self.state.get_time(),
}); },
);
frontend_events.push(Event::ClientConnected { frontend_events.push(Event::ClientConnected { entity });
entity,
});
} }
Ok(frontend_events) Ok(frontend_events)
@ -236,7 +269,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 +281,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,7 +328,14 @@ 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
if msg.starts_with("/") && msg.len() > 1 {
let argv = String::from(&msg[1..]);
self.process_chat_cmd(entity, argv);
} else {
self.clients.notify_connected(ServerMsg::Chat(
match self
.state
.ecs() .ecs()
.internal() .internal()
.read_storage::<comp::Player>() .read_storage::<comp::Player>()
@ -302,21 +343,18 @@ impl Server {
{ {
Some(player) => format!("[{}] {}", &player.alias, msg), Some(player) => format!("[{}] {}", &player.alias, msg),
None => format!("[<anon>] {}", 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 +413,133 @@ 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<'a>(&mut self, entity: EcsEntity, cmd: String) {
let sep = cmd.find(' ');
let (kwd, args) = match sep {
Some(i) => (cmd[..i].to_string(), cmd[(i + 1)..].to_string()),
None => (cmd, "".to_string()),
};
let action_opt = CHAT_COMMANDS.iter().find(|x| x.keyword == kwd);
match action_opt {
Some(action) => match action.keyword {
"jump" => {
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)) => {
if let Some(current_pos) =
self.state.read_component_cloned::<comp::phys::Pos>(entity)
{
self.state.write_component(
entity,
comp::phys::Pos(current_pos.0 + Vec3::new(x, y, z)),
)
} else {
self.clients.notify(
entity,
ServerMsg::Chat(String::from(
"Command 'jump' invalid in current state",
)),
)
}
}
_ => self
.clients
.notify(entity, ServerMsg::Chat(String::from(action.help_string))),
}
}
"goto" => {
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)) => self
.state
.write_component(entity, comp::phys::Pos(Vec3::new(x, y, z))),
_ => self
.clients
.notify(entity, ServerMsg::Chat(String::from(action.help_string))),
}
}
"alias" => {
let opt_alias = scan_fmt!(&args, action.arg_fmt, String);
match opt_alias {
Some(alias) => self
.state
.write_component(entity, comp::player::Player { alias }),
None => self
.clients
.notify(entity, ServerMsg::Chat(String::from(action.help_string))),
}
}
"tp" => {
let opt_alias = scan_fmt!(&args, action.arg_fmt, String);
match opt_alias {
Some(alias) => {
let ecs = self.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) => {
if let Some(pos) =
self.state.read_component_cloned::<comp::phys::Pos>(player)
{
self.state.write_component(entity, pos);
} else {
self.clients.notify(
entity,
ServerMsg::Chat(format!(
"Unable to teleport to player '{}'",
alias
)),
);
}
}
None => {
self.clients.notify(
entity,
ServerMsg::Chat(format!("Player '{}' not found!", alias)),
);
self.clients.notify(
entity,
ServerMsg::Chat(String::from(action.help_string)),
);
}
}
}
None => self
.clients
.notify(entity, ServerMsg::Chat(String::from(action.help_string))),
}
}
"help" => {
for cmd in CHAT_COMMANDS.iter() {
self.clients
.notify(entity, ServerMsg::Chat(String::from(cmd.help_string)));
}
}
_ => {}
},
// unknown command
None => {
self.clients.notify(
entity,
ServerMsg::Chat(format!(
"Unrecognised command: '/{}'\nAvailable commands:",
kwd
)),
);
for cmd in CHAT_COMMANDS.iter() {
self.clients
.notify(entity, ServerMsg::Chat(String::from(cmd.keyword)));
}
}
} }
} }
} }