mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Setup client binary that connects to the server with a group of clients positioned and moving in a few different patterns for profiling the server under network load.
This commit is contained in:
parent
35086c1626
commit
027f2f5719
@ -15,6 +15,7 @@ test-voxygen = "run --bin veloren-voxygen --no-default-features --features simd,
|
|||||||
tracy-voxygen = "-Zunstable-options run --bin veloren-voxygen --no-default-features --features tracy,simd,egui-ui --profile no_overflow"
|
tracy-voxygen = "-Zunstable-options run --bin veloren-voxygen --no-default-features --features tracy,simd,egui-ui --profile no_overflow"
|
||||||
server = "run --bin veloren-server-cli"
|
server = "run --bin veloren-server-cli"
|
||||||
dbg-voxygen = "run --bin veloren-voxygen -Zunstable-options --profile debuginfo"
|
dbg-voxygen = "run --bin veloren-voxygen -Zunstable-options --profile debuginfo"
|
||||||
|
swarm = "run --bin swarm --features client/bin_bot --"
|
||||||
|
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6124,6 +6124,7 @@ dependencies = [
|
|||||||
"rustyline",
|
"rustyline",
|
||||||
"serde",
|
"serde",
|
||||||
"specs",
|
"specs",
|
||||||
|
"structopt",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -7,7 +7,7 @@ edition = "2018"
|
|||||||
[features]
|
[features]
|
||||||
simd = ["vek/platform_intrinsics"]
|
simd = ["vek/platform_intrinsics"]
|
||||||
plugins = ["common-state/plugins"]
|
plugins = ["common-state/plugins"]
|
||||||
bin_bot = ["common-ecs", "serde", "ron", "clap", "rustyline", "common-frontend", "async-channel"]
|
bin_bot = ["common-ecs", "serde", "ron", "clap", "structopt", "rustyline", "common-frontend", "async-channel"]
|
||||||
tracy = ["common-base/tracy"]
|
tracy = ["common-base/tracy"]
|
||||||
|
|
||||||
default = ["simd"]
|
default = ["simd"]
|
||||||
@ -39,6 +39,7 @@ common-ecs = { package = "veloren-common-ecs", path = "../common/ecs", optional
|
|||||||
serde = { version = "1.0", features = [ "rc", "derive" ], optional = true }
|
serde = { version = "1.0", features = [ "rc", "derive" ], optional = true }
|
||||||
ron = { version = "0.7", default-features = false, optional = true }
|
ron = { version = "0.7", default-features = false, optional = true }
|
||||||
clap = { version = "2.33", optional = true }
|
clap = { version = "2.33", optional = true }
|
||||||
|
structopt = { version = "0.3.13", optional = true }
|
||||||
rustyline = { version = "9.0.0", optional = true }
|
rustyline = { version = "9.0.0", optional = true }
|
||||||
## logging
|
## logging
|
||||||
termcolor = { version = "1.1", optional = true }
|
termcolor = { version = "1.1", optional = true }
|
||||||
@ -54,3 +55,7 @@ required-features = ["bin_bot"]
|
|||||||
name = "bot"
|
name = "bot"
|
||||||
#authors = ["Avi Weinstock <aweinstock314@gmail.com>"]
|
#authors = ["Avi Weinstock <aweinstock314@gmail.com>"]
|
||||||
required-features = ["bin_bot"]
|
required-features = ["bin_bot"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "swarm"
|
||||||
|
required-features = ["bin_bot"]
|
||||||
|
@ -56,7 +56,7 @@ fn main() {
|
|||||||
|
|
||||||
println!("Server info: {:?}", client.server_info());
|
println!("Server info: {:?}", client.server_info());
|
||||||
|
|
||||||
println!("Players online: {:?}", client.get_players());
|
println!("Players online: {:?}", client.players().collect::<Vec<_>>());
|
||||||
|
|
||||||
runtime
|
runtime
|
||||||
.block_on(client.register(username, password, |provider| {
|
.block_on(client.register(username, password, |provider| {
|
||||||
|
294
client/src/bin/swarm/main.rs
Normal file
294
client/src/bin/swarm/main.rs
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
use common::comp;
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
use std::{
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicU32, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
thread,
|
||||||
|
time::{Duration, SystemTime},
|
||||||
|
};
|
||||||
|
use structopt::StructOpt;
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
use vek::*;
|
||||||
|
use veloren_client::{addr::ConnectionArgs, Client};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, StructOpt)]
|
||||||
|
struct Opt {
|
||||||
|
/// Number of clients to spin up
|
||||||
|
size: u32,
|
||||||
|
/// View distance of each client
|
||||||
|
vd: u32,
|
||||||
|
/// Distribution of the clients, if not clustered they are dispersed
|
||||||
|
#[structopt(short, long)]
|
||||||
|
clustered: bool,
|
||||||
|
/// Whether the clients should move
|
||||||
|
#[structopt(short, long)]
|
||||||
|
movement: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let opt = Opt::from_args();
|
||||||
|
// Start logging
|
||||||
|
common_frontend::init_stdout(None);
|
||||||
|
// Run clients and stuff
|
||||||
|
//
|
||||||
|
// NOTE: "swarm0" is assumed to be an admin already
|
||||||
|
//
|
||||||
|
// Since this requires a no-auth server use this command to add swarm0 as an
|
||||||
|
// admin:
|
||||||
|
//
|
||||||
|
// --no-auth admin add swarm0 Admin
|
||||||
|
//
|
||||||
|
let admin_username = "swarm0".to_owned();
|
||||||
|
let usernames = (1..opt.size)
|
||||||
|
.map(|i| format!("swarm{}", i))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let to_adminify = usernames.clone();
|
||||||
|
|
||||||
|
let finished_init = Arc::new(AtomicU32::new(0));
|
||||||
|
let runtime = Arc::new(Runtime::new().unwrap());
|
||||||
|
|
||||||
|
// TODO: calculate and log the required chunks per second to maintain the
|
||||||
|
// selected scenario with full vd loaded
|
||||||
|
|
||||||
|
run_client_new_thread(
|
||||||
|
admin_username,
|
||||||
|
0,
|
||||||
|
to_adminify,
|
||||||
|
&runtime,
|
||||||
|
opt,
|
||||||
|
&finished_init,
|
||||||
|
);
|
||||||
|
|
||||||
|
usernames.into_iter().enumerate().for_each(|(index, name)| {
|
||||||
|
run_client_new_thread(
|
||||||
|
name,
|
||||||
|
index as u32,
|
||||||
|
Vec::new(),
|
||||||
|
&runtime,
|
||||||
|
opt,
|
||||||
|
&finished_init,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
loop {
|
||||||
|
thread::sleep(Duration::from_secs_f32(1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_client_new_thread(
|
||||||
|
username: String,
|
||||||
|
index: u32,
|
||||||
|
to_adminify: Vec<String>,
|
||||||
|
runtime: &Arc<Runtime>,
|
||||||
|
opt: Opt,
|
||||||
|
finished_init: &Arc<AtomicU32>,
|
||||||
|
) {
|
||||||
|
let runtime = Arc::clone(runtime);
|
||||||
|
let finished_init = Arc::clone(finished_init);
|
||||||
|
thread::spawn(move || {
|
||||||
|
if let Err(err) = run_client(username, index, to_adminify, runtime, opt, finished_init) {
|
||||||
|
tracing::error!("swarm member {} exited with an error: {:?}", index, err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_client(
|
||||||
|
username: String,
|
||||||
|
index: u32,
|
||||||
|
to_adminify: Vec<String>,
|
||||||
|
runtime: Arc<Runtime>,
|
||||||
|
opt: Opt,
|
||||||
|
finished_init: Arc<AtomicU32>,
|
||||||
|
) -> Result<(), veloren_client::Error> {
|
||||||
|
// Connect to localhost
|
||||||
|
let addr = ConnectionArgs::Tcp {
|
||||||
|
prefer_ipv6: false,
|
||||||
|
hostname: "localhost".into(),
|
||||||
|
};
|
||||||
|
let runtime_clone = Arc::clone(&runtime);
|
||||||
|
let mut client = runtime
|
||||||
|
.block_on(Client::new(addr, runtime_clone, &mut None))
|
||||||
|
.expect("Failed to connect to the server");
|
||||||
|
client.set_view_distance(opt.vd);
|
||||||
|
|
||||||
|
// Login
|
||||||
|
// NOTE: use a no-auth server
|
||||||
|
runtime
|
||||||
|
.block_on(client.register(username.clone(), String::new(), |_| false))
|
||||||
|
.expect("Failed to log in");
|
||||||
|
|
||||||
|
let mut clock = common::clock::Clock::new(Duration::from_secs_f32(1.0 / 30.0));
|
||||||
|
|
||||||
|
let mut tick = |client: &mut Client| -> Result<(), veloren_client::Error> {
|
||||||
|
clock.tick();
|
||||||
|
// TODO make sure this includes terrain loading requests
|
||||||
|
client.tick_network(clock.dt())?;
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wait for character list to load
|
||||||
|
client.load_character_list();
|
||||||
|
while client.character_list().loading {
|
||||||
|
tick(&mut client)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create character if none exist
|
||||||
|
if client.character_list().characters.is_empty() {
|
||||||
|
client.create_character(
|
||||||
|
username.clone(),
|
||||||
|
Some("common.items.weapons.sword.starter".into()),
|
||||||
|
None,
|
||||||
|
body(),
|
||||||
|
);
|
||||||
|
|
||||||
|
client.load_character_list();
|
||||||
|
|
||||||
|
while client.character_list().loading || client.character_list().characters.is_empty() {
|
||||||
|
tick(&mut client)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the first character
|
||||||
|
client.request_character(
|
||||||
|
client
|
||||||
|
.character_list()
|
||||||
|
.characters
|
||||||
|
.first()
|
||||||
|
.expect("Just created new character if non were listed!!!")
|
||||||
|
.character
|
||||||
|
.id
|
||||||
|
.expect("Why is this an option?"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// If this is the admin client then adminify the other swarm members
|
||||||
|
if !to_adminify.is_empty() {
|
||||||
|
// Wait for other clients to connect
|
||||||
|
loop {
|
||||||
|
tick(&mut client)?;
|
||||||
|
// NOTE: it's expected that each swarm member will have a unique alias
|
||||||
|
let players = client.players().collect::<HashSet<&str>>();
|
||||||
|
if to_adminify
|
||||||
|
.iter()
|
||||||
|
.all(|name| players.contains(&name.as_str()))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Assert that we are a moderator (assumes we are an admin if so)
|
||||||
|
assert!(
|
||||||
|
client.is_moderator(),
|
||||||
|
"The user needs to ensure \"{}\" is registered as an admin on the server",
|
||||||
|
username
|
||||||
|
);
|
||||||
|
// Send commands to adminify others
|
||||||
|
to_adminify.iter().for_each(|name| {
|
||||||
|
client.send_command("adminify".into(), vec![name.into(), "admin".into()])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for moderator
|
||||||
|
while !client.is_moderator() {
|
||||||
|
tick(&mut client)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
finished_init.fetch_add(1, Ordering::Relaxed);
|
||||||
|
// Wait for initialization of all other swarm clients to finish
|
||||||
|
while !finished_init.load(Ordering::Relaxed) == opt.size {
|
||||||
|
tick(&mut client)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
let chunk_size = 32.0; // TODO: replace with the actual constant
|
||||||
|
let world_center = client
|
||||||
|
.world_data()
|
||||||
|
.chunk_size()
|
||||||
|
.map(|e| e as f32 * chunk_size)
|
||||||
|
/ 2.0;
|
||||||
|
loop {
|
||||||
|
tick(&mut client)?;
|
||||||
|
let entity = client.entity();
|
||||||
|
// Move or stay still depending on specified options
|
||||||
|
// TODO: make sure server cheat protections aren't triggering
|
||||||
|
let pos = common::comp::Pos(position(index, opt) + world_center);
|
||||||
|
let vel = common::comp::Vel(Default::default());
|
||||||
|
client
|
||||||
|
.state_mut()
|
||||||
|
.write_component_ignore_entity_dead(entity, pos);
|
||||||
|
client
|
||||||
|
.state_mut()
|
||||||
|
.write_component_ignore_entity_dead(entity, vel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use client index, opts, and current system time to determine position
|
||||||
|
fn position(index: u32, opt: Opt) -> Vec3<f32> {
|
||||||
|
// TODO: replace 32 with constant for chunk size
|
||||||
|
let chunk_size = 32.0;
|
||||||
|
|
||||||
|
let width = (opt.size as f32).sqrt().round() as u32;
|
||||||
|
|
||||||
|
let spacing = if opt.clustered {
|
||||||
|
5.0
|
||||||
|
} else {
|
||||||
|
opt.vd as f32 * 3.0 * chunk_size
|
||||||
|
};
|
||||||
|
|
||||||
|
// Offset to center the grid of clients
|
||||||
|
let offset = Vec2::new(
|
||||||
|
width as f32 * spacing / 2.0,
|
||||||
|
(opt.size / width) as f32 / 2.0,
|
||||||
|
);
|
||||||
|
// Position clients in a grid
|
||||||
|
let base_pos = Vec2::new(
|
||||||
|
(index % width) as f32 * spacing,
|
||||||
|
(index / width) as f32 * spacing,
|
||||||
|
) - offset;
|
||||||
|
|
||||||
|
let movement_offset: Vec2<_> = if opt.movement {
|
||||||
|
// blocks per second
|
||||||
|
const SPEED: f32 = 32.0; // a decent amount faster than walking but not super fast
|
||||||
|
|
||||||
|
// move in a square route
|
||||||
|
// in blocks
|
||||||
|
let route_side_length = chunk_size * opt.vd as f32 * 3.0;
|
||||||
|
let route_length = route_side_length * 4.0;
|
||||||
|
// in secs
|
||||||
|
let route_time = route_length / SPEED;
|
||||||
|
let route_progress = (SystemTime::UNIX_EPOCH.elapsed().unwrap().as_secs_f64()
|
||||||
|
% route_time as f64) as f32
|
||||||
|
/ route_time;
|
||||||
|
|
||||||
|
// clockwise square
|
||||||
|
(match route_progress * 4.0 {
|
||||||
|
// going up left side
|
||||||
|
t if t < 1.0 => Vec2::new(0.0, 0.0 + t),
|
||||||
|
// going across top side
|
||||||
|
t if t < 2.0 => Vec2::new(0.0 + (t - 1.0), 1.0),
|
||||||
|
// going down right side
|
||||||
|
t if t < 3.0 => Vec2::new(1.0, 1.0 - (t - 2.0)),
|
||||||
|
// going across bottom
|
||||||
|
t => Vec2::new(1.0 - (t - 3.0), 0.0),
|
||||||
|
}) * route_side_length
|
||||||
|
} else {
|
||||||
|
Vec2::zero()
|
||||||
|
};
|
||||||
|
|
||||||
|
Vec3::from(base_pos + movement_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body() -> comp::Body {
|
||||||
|
comp::body::humanoid::Body {
|
||||||
|
species: comp::body::humanoid::Species::Human,
|
||||||
|
body_type: comp::body::humanoid::BodyType::Male,
|
||||||
|
hair_style: 0,
|
||||||
|
beard: 0,
|
||||||
|
eyes: 0,
|
||||||
|
accessory: 0,
|
||||||
|
hair_color: 0,
|
||||||
|
skin: 0,
|
||||||
|
eye_color: 0,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
@ -1534,6 +1534,54 @@ impl Client {
|
|||||||
.recv_all();
|
.recv_all();
|
||||||
|
|
||||||
// 5) Terrain
|
// 5) Terrain
|
||||||
|
self.tick_terrain()?;
|
||||||
|
|
||||||
|
// Send a ping to the server once every second
|
||||||
|
if self.state.get_time() - self.last_server_ping > 1. {
|
||||||
|
self.send_msg_err(PingMsg::Ping)?;
|
||||||
|
self.last_server_ping = self.state.get_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6) Update the server about the player's physics attributes.
|
||||||
|
if self.presence.is_some() {
|
||||||
|
if let (Some(pos), Some(vel), Some(ori)) = (
|
||||||
|
self.state.read_storage().get(self.entity()).cloned(),
|
||||||
|
self.state.read_storage().get(self.entity()).cloned(),
|
||||||
|
self.state.read_storage().get(self.entity()).cloned(),
|
||||||
|
) {
|
||||||
|
self.in_game_stream
|
||||||
|
.send(ClientGeneral::PlayerPhysics { pos, vel, ori })?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Output debug metrics
|
||||||
|
if log_enabled!(Level::Info) && self.tick % 600 == 0 {
|
||||||
|
let metrics = self
|
||||||
|
.state
|
||||||
|
.terrain()
|
||||||
|
.iter()
|
||||||
|
.fold(ChonkMetrics::default(), |a, (_, c)| a + c.get_metrics());
|
||||||
|
info!("{:?}", metrics);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 7) Finish the tick, pass control back to the frontend.
|
||||||
|
self.tick += 1;
|
||||||
|
Ok(frontend_events)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clean up the client after a tick.
|
||||||
|
pub fn cleanup(&mut self) {
|
||||||
|
// Cleanup the local state
|
||||||
|
self.state.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles terrain addition and removal.
|
||||||
|
///
|
||||||
|
/// Removes old terrain chunks outside the view distance.
|
||||||
|
/// Sends requests for missing chunks within the view distance.
|
||||||
|
fn tick_terrain(&mut self) -> Result<(), Error> {
|
||||||
let pos = self
|
let pos = self
|
||||||
.state
|
.state
|
||||||
.read_storage::<comp::Pos>()
|
.read_storage::<comp::Pos>()
|
||||||
@ -1628,45 +1676,7 @@ impl Client {
|
|||||||
.retain(|_, created| now.duration_since(*created) < Duration::from_secs(3));
|
.retain(|_, created| now.duration_since(*created) < Duration::from_secs(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a ping to the server once every second
|
Ok(())
|
||||||
if self.state.get_time() - self.last_server_ping > 1. {
|
|
||||||
self.send_msg_err(PingMsg::Ping)?;
|
|
||||||
self.last_server_ping = self.state.get_time();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6) Update the server about the player's physics attributes.
|
|
||||||
if self.presence.is_some() {
|
|
||||||
if let (Some(pos), Some(vel), Some(ori)) = (
|
|
||||||
self.state.read_storage().get(self.entity()).cloned(),
|
|
||||||
self.state.read_storage().get(self.entity()).cloned(),
|
|
||||||
self.state.read_storage().get(self.entity()).cloned(),
|
|
||||||
) {
|
|
||||||
self.in_game_stream
|
|
||||||
.send(ClientGeneral::PlayerPhysics { pos, vel, ori })?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Output debug metrics
|
|
||||||
if log_enabled!(Level::Info) && self.tick % 600 == 0 {
|
|
||||||
let metrics = self
|
|
||||||
.state
|
|
||||||
.terrain()
|
|
||||||
.iter()
|
|
||||||
.fold(ChonkMetrics::default(), |a, (_, c)| a + c.get_metrics());
|
|
||||||
info!("{:?}", metrics);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 7) Finish the tick, pass control back to the frontend.
|
|
||||||
self.tick += 1;
|
|
||||||
Ok(frontend_events)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clean up the client after a tick.
|
|
||||||
pub fn cleanup(&mut self) {
|
|
||||||
// Cleanup the local state
|
|
||||||
self.state.cleanup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_server_msg(
|
fn handle_server_msg(
|
||||||
@ -2211,15 +2221,12 @@ impl Client {
|
|||||||
/// Get a mutable reference to the client's game state.
|
/// Get a mutable reference to the client's game state.
|
||||||
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
|
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
|
||||||
|
|
||||||
/// Get a vector of all the players on the server
|
/// Returns an iterator over the aliases of all the online players on the server
|
||||||
pub fn get_players(&mut self) -> Vec<comp::Player> {
|
pub fn players(&self) -> impl Iterator<Item = &str> {
|
||||||
// TODO: Don't clone players.
|
self
|
||||||
self.state
|
.player_list()
|
||||||
.ecs()
|
.values()
|
||||||
.read_storage::<comp::Player>()
|
.filter_map(|player_info| player_info.is_online.then(|| &*player_info.player_alias))
|
||||||
.join()
|
|
||||||
.cloned()
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if this client is a moderator on the server
|
/// Return true if this client is a moderator on the server
|
||||||
@ -2469,6 +2476,57 @@ impl Client {
|
|||||||
comp::ChatType::Meta => message.to_string(),
|
comp::ChatType::Meta => message.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute a single client tick:
|
||||||
|
/// - handles messages from the server
|
||||||
|
/// - sends physics update
|
||||||
|
/// - requests chunks
|
||||||
|
///
|
||||||
|
/// The game state is purposefully not simulated to reduce the overhead of running the client.
|
||||||
|
/// This method is for use in testing a server with many clients connected.
|
||||||
|
pub fn tick_network(
|
||||||
|
&mut self,
|
||||||
|
_dt: Duration,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
span!(_guard, "tick_network", "Client::tick_network");
|
||||||
|
|
||||||
|
// Handle new messages from the server.
|
||||||
|
self.handle_new_messages()?;
|
||||||
|
|
||||||
|
// 5) Terrain
|
||||||
|
self.tick_terrain()?;
|
||||||
|
let empty = Arc::new(TerrainChunk::new(0, Block::empty(), Block::empty(), common::terrain::TerrainChunkMeta::void()));
|
||||||
|
let mut terrain = self.state.terrain_mut();
|
||||||
|
// Replace chunks with empty chunks to save memory
|
||||||
|
let to_clear = terrain.iter().filter_map(|(key, chunk)| (chunk.sub_chunks_len() != 0).then(|| key)).collect::<Vec<_>>();
|
||||||
|
to_clear.into_iter().for_each(|key| { terrain.insert(key, Arc::clone(&empty)); });
|
||||||
|
drop(terrain);
|
||||||
|
|
||||||
|
// Send a ping to the server once every second
|
||||||
|
// TODO: advance state time?
|
||||||
|
if self.state.get_time() - self.last_server_ping > 1. {
|
||||||
|
self.send_msg_err(PingMsg::Ping)?;
|
||||||
|
self.last_server_ping = self.state.get_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6) Update the server about the player's physics attributes.
|
||||||
|
if self.presence.is_some() {
|
||||||
|
if let (Some(pos), Some(vel), Some(ori)) = (
|
||||||
|
self.state.read_storage().get(self.entity()).cloned(),
|
||||||
|
self.state.read_storage().get(self.entity()).cloned(),
|
||||||
|
self.state.read_storage().get(self.entity()).cloned(),
|
||||||
|
) {
|
||||||
|
self.in_game_stream
|
||||||
|
.send(ClientGeneral::PlayerPhysics { pos, vel, ori })?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7) Finish the tick, pass control back to the frontend.
|
||||||
|
self.tick += 1;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Client {
|
impl Drop for Client {
|
||||||
|
@ -85,6 +85,11 @@ fn main() -> io::Result<()> {
|
|||||||
let mut server_settings = server::Settings::load(&server_data_dir);
|
let mut server_settings = server::Settings::load(&server_data_dir);
|
||||||
let mut editable_settings = server::EditableSettings::load(&server_data_dir);
|
let mut editable_settings = server::EditableSettings::load(&server_data_dir);
|
||||||
|
|
||||||
|
// Apply no_auth modifier to the settings
|
||||||
|
if no_auth {
|
||||||
|
server_settings.auth_server_address = None;
|
||||||
|
}
|
||||||
|
|
||||||
// Relative to data_dir
|
// Relative to data_dir
|
||||||
const PERSISTENCE_DB_DIR: &str = "saves";
|
const PERSISTENCE_DB_DIR: &str = "saves";
|
||||||
|
|
||||||
@ -149,10 +154,6 @@ fn main() -> io::Result<()> {
|
|||||||
|
|
||||||
info!("Starting server...");
|
info!("Starting server...");
|
||||||
|
|
||||||
if no_auth {
|
|
||||||
server_settings.auth_server_address = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let server_port = &server_settings.gameserver_address.port();
|
let server_port = &server_settings.gameserver_address.port();
|
||||||
let metrics_port = &server_settings.metrics_address.port();
|
let metrics_port = &server_settings.metrics_address.port();
|
||||||
// Create server
|
// Create server
|
||||||
|
Loading…
Reference in New Issue
Block a user