mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sharp/fix-full-server' into 'master'
Allow mods/admins to log in when server is full. See merge request veloren/veloren!3600
This commit is contained in:
commit
c95c08ee54
@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Fixed bug where the view distance selection was not immediately applied to entity syncing when
|
- Fixed bug where the view distance selection was not immediately applied to entity syncing when
|
||||||
first joining a server and when changing the view distance (previously this required moving to a
|
first joining a server and when changing the view distance (previously this required moving to a
|
||||||
new chunk for the initial setting or subsequent change to apply).
|
new chunk for the initial setting or subsequent change to apply).
|
||||||
|
- Moderators and admins are no longer blocked from logging in when there are too many players.
|
||||||
|
|
||||||
## [0.13.0] - 2022-07-23
|
## [0.13.0] - 2022-07-23
|
||||||
|
|
||||||
|
@ -51,19 +51,20 @@ fn main() {
|
|||||||
|
|
||||||
// Create a client.
|
// Create a client.
|
||||||
let mut client = runtime
|
let mut client = runtime
|
||||||
.block_on(Client::new(addr, runtime2, &mut None))
|
.block_on(Client::new(
|
||||||
|
addr,
|
||||||
|
runtime2,
|
||||||
|
&mut None,
|
||||||
|
&username,
|
||||||
|
&password,
|
||||||
|
|provider| provider == "https://auth.veloren.net",
|
||||||
|
))
|
||||||
.expect("Failed to create client instance");
|
.expect("Failed to create client instance");
|
||||||
|
|
||||||
println!("Server info: {:?}", client.server_info());
|
println!("Server info: {:?}", client.server_info());
|
||||||
|
|
||||||
println!("Players online: {:?}", client.players().collect::<Vec<_>>());
|
println!("Players online: {:?}", client.players().collect::<Vec<_>>());
|
||||||
|
|
||||||
runtime
|
|
||||||
.block_on(client.register(username, password, |provider| {
|
|
||||||
provider == "https://auth.veloren.net"
|
|
||||||
}))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
|
@ -13,6 +13,7 @@ mod settings;
|
|||||||
mod tui;
|
mod tui;
|
||||||
|
|
||||||
use common::comp::body::humanoid::Body;
|
use common::comp::body::humanoid::Body;
|
||||||
|
use common_net::msg::ServerInfo;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use tui::Cmd;
|
use tui::Cmd;
|
||||||
|
|
||||||
@ -46,31 +47,47 @@ pub fn main() {
|
|||||||
pub struct BotClient {
|
pub struct BotClient {
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
runtime: Arc<Runtime>,
|
runtime: Arc<Runtime>,
|
||||||
menu_client: Client,
|
server_info: ServerInfo,
|
||||||
bot_clients: HashMap<String, Client>,
|
bot_clients: HashMap<String, Client>,
|
||||||
clock: Clock,
|
clock: Clock,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_client(runtime: &Arc<Runtime>, server: &str) -> Client {
|
pub fn make_client(
|
||||||
|
runtime: &Arc<Runtime>,
|
||||||
|
server: &str,
|
||||||
|
server_info: &mut Option<ServerInfo>,
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
) -> Option<Client> {
|
||||||
let runtime_clone = Arc::clone(runtime);
|
let runtime_clone = Arc::clone(runtime);
|
||||||
let addr = ConnectionArgs::Tcp {
|
let addr = ConnectionArgs::Tcp {
|
||||||
prefer_ipv6: false,
|
prefer_ipv6: false,
|
||||||
hostname: server.to_owned(),
|
hostname: server.to_owned(),
|
||||||
};
|
};
|
||||||
runtime
|
runtime
|
||||||
.block_on(Client::new(addr, runtime_clone, &mut None))
|
.block_on(Client::new(
|
||||||
.expect("Failed to connect to server")
|
addr,
|
||||||
|
runtime_clone,
|
||||||
|
server_info,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
|_| true,
|
||||||
|
))
|
||||||
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BotClient {
|
impl BotClient {
|
||||||
pub fn new(settings: Settings) -> BotClient {
|
pub fn new(settings: Settings) -> BotClient {
|
||||||
let runtime = Arc::new(Runtime::new().unwrap());
|
let runtime = Arc::new(Runtime::new().unwrap());
|
||||||
let menu_client: Client = make_client(&runtime, &settings.server);
|
let mut server_info = None;
|
||||||
|
// Don't care if we connect, just trying to grab the server info.
|
||||||
|
let _ = make_client(&runtime, &settings.server, &mut server_info, "", "");
|
||||||
|
let server_info = server_info.expect("Failed to connect to server.");
|
||||||
let clock = Clock::new(Duration::from_secs_f64(1.0 / 60.0));
|
let clock = Clock::new(Duration::from_secs_f64(1.0 / 60.0));
|
||||||
BotClient {
|
BotClient {
|
||||||
settings,
|
settings,
|
||||||
runtime,
|
runtime,
|
||||||
menu_client,
|
server_info,
|
||||||
bot_clients: HashMap::new(),
|
bot_clients: HashMap::new(),
|
||||||
clock,
|
clock,
|
||||||
}
|
}
|
||||||
@ -106,7 +123,7 @@ impl BotClient {
|
|||||||
None => vec![prefix.to_string()],
|
None => vec![prefix.to_string()],
|
||||||
};
|
};
|
||||||
info!("usernames: {:?}", usernames);
|
info!("usernames: {:?}", usernames);
|
||||||
if let Some(auth_addr) = self.menu_client.server_info().auth_provider.as_ref() {
|
if let Some(auth_addr) = self.server_info.auth_provider.as_ref() {
|
||||||
let (scheme, authority) = auth_addr.split_once("://").expect("invalid auth url");
|
let (scheme, authority) = auth_addr.split_once("://").expect("invalid auth url");
|
||||||
let scheme = scheme
|
let scheme = scheme
|
||||||
.parse::<authc::Scheme>()
|
.parse::<authc::Scheme>()
|
||||||
@ -156,20 +173,16 @@ impl BotClient {
|
|||||||
for cred in creds.iter() {
|
for cred in creds.iter() {
|
||||||
let runtime = Arc::clone(&self.runtime);
|
let runtime = Arc::clone(&self.runtime);
|
||||||
|
|
||||||
let server = self.settings.server.clone();
|
let server = &self.settings.server;
|
||||||
|
// TODO: log the clients in in parallel instead of in series
|
||||||
let client = self
|
let client = self
|
||||||
.bot_clients
|
.bot_clients
|
||||||
.entry(cred.username.clone())
|
.entry(cred.username.clone())
|
||||||
.or_insert_with(|| make_client(&runtime, &server));
|
.or_insert_with(|| {
|
||||||
|
make_client(&runtime, server, &mut None, &cred.username, &cred.password)
|
||||||
|
.expect("Failed to connect to server")
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: log the clients in in parallel instead of in series
|
|
||||||
if let Err(e) = runtime.block_on(client.register(
|
|
||||||
cred.username.clone(),
|
|
||||||
cred.password.clone(),
|
|
||||||
|_| true,
|
|
||||||
)) {
|
|
||||||
warn!("error logging in {:?}: {:?}", cred.username, e);
|
|
||||||
}
|
|
||||||
let body = BotClient::create_default_body();
|
let body = BotClient::create_default_body();
|
||||||
client.create_character(
|
client.create_character(
|
||||||
cred.username.clone(),
|
cred.username.clone(),
|
||||||
|
@ -108,15 +108,17 @@ fn run_client(
|
|||||||
hostname: "localhost".into(),
|
hostname: "localhost".into(),
|
||||||
};
|
};
|
||||||
let runtime_clone = Arc::clone(&runtime);
|
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");
|
|
||||||
|
|
||||||
// Login
|
|
||||||
// NOTE: use a no-auth server
|
// NOTE: use a no-auth server
|
||||||
runtime
|
let mut client = runtime
|
||||||
.block_on(client.register(username.clone(), String::new(), |_| false))
|
.block_on(Client::new(
|
||||||
.expect("Failed to log in");
|
addr,
|
||||||
|
runtime_clone,
|
||||||
|
&mut None,
|
||||||
|
&username,
|
||||||
|
"",
|
||||||
|
|_| false,
|
||||||
|
))
|
||||||
|
.expect("Failed to connect to the server");
|
||||||
|
|
||||||
let mut clock = common::clock::Clock::new(Duration::from_secs_f32(1.0 / 30.0));
|
let mut clock = common::clock::Clock::new(Duration::from_secs_f32(1.0 / 30.0));
|
||||||
|
|
||||||
|
@ -275,6 +275,9 @@ impl Client {
|
|||||||
runtime: Arc<Runtime>,
|
runtime: Arc<Runtime>,
|
||||||
// TODO: refactor to avoid needing to use this out parameter
|
// TODO: refactor to avoid needing to use this out parameter
|
||||||
mismatched_server_info: &mut Option<ServerInfo>,
|
mismatched_server_info: &mut Option<ServerInfo>,
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
auth_trusted: impl FnMut(&str) -> bool,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let network = Network::new(Pid::new(), &runtime);
|
let network = Network::new(Pid::new(), &runtime);
|
||||||
|
|
||||||
@ -317,15 +320,24 @@ impl Client {
|
|||||||
common::util::GIT_HASH.to_string(),
|
common::util::GIT_HASH.to_string(),
|
||||||
common::util::GIT_DATE.to_string(),
|
common::util::GIT_DATE.to_string(),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// Pass the server info back to the caller to ensure they can access it even
|
// Pass the server info back to the caller to ensure they can access it even
|
||||||
// if this function errors.
|
// if this function errors.
|
||||||
mem::swap(mismatched_server_info, &mut Some(server_info.clone()));
|
mem::swap(mismatched_server_info, &mut Some(server_info.clone()));
|
||||||
}
|
|
||||||
debug!("Auth Server: {:?}", server_info.auth_provider);
|
debug!("Auth Server: {:?}", server_info.auth_provider);
|
||||||
|
|
||||||
ping_stream.send(PingMsg::Ping)?;
|
ping_stream.send(PingMsg::Ping)?;
|
||||||
|
|
||||||
|
// Register client
|
||||||
|
Self::register(
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
auth_trusted,
|
||||||
|
&server_info,
|
||||||
|
&mut register_stream,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Wait for initial sync
|
// Wait for initial sync
|
||||||
let mut ping_interval = tokio::time::interval(Duration::from_secs(1));
|
let mut ping_interval = tokio::time::interval(Duration::from_secs(1));
|
||||||
let (
|
let (
|
||||||
@ -632,7 +644,7 @@ impl Client {
|
|||||||
let map_bounds = Vec2::new(sea_level, max_height);
|
let map_bounds = Vec2::new(sea_level, max_height);
|
||||||
debug!("Done preparing image...");
|
debug!("Done preparing image...");
|
||||||
|
|
||||||
Ok((
|
(
|
||||||
state,
|
state,
|
||||||
lod_base,
|
lod_base,
|
||||||
lod_alt,
|
lod_alt,
|
||||||
@ -644,16 +656,15 @@ impl Client {
|
|||||||
component_recipe_book,
|
component_recipe_book,
|
||||||
max_group_size,
|
max_group_size,
|
||||||
client_timeout,
|
client_timeout,
|
||||||
))
|
)
|
||||||
},
|
},
|
||||||
ServerInit::TooManyPlayers => Err(Error::TooManyPlayers),
|
};
|
||||||
}?;
|
|
||||||
ping_stream.send(PingMsg::Ping)?;
|
ping_stream.send(PingMsg::Ping)?;
|
||||||
|
|
||||||
debug!("Initial sync done");
|
debug!("Initial sync done");
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
registered: false,
|
registered: true,
|
||||||
presence: None,
|
presence: None,
|
||||||
runtime,
|
runtime,
|
||||||
server_info,
|
server_info,
|
||||||
@ -722,14 +733,15 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Request a state transition to `ClientState::Registered`.
|
/// Request a state transition to `ClientState::Registered`.
|
||||||
pub async fn register(
|
async fn register(
|
||||||
&mut self,
|
username: &str,
|
||||||
username: String,
|
password: &str,
|
||||||
password: String,
|
|
||||||
mut auth_trusted: impl FnMut(&str) -> bool,
|
mut auth_trusted: impl FnMut(&str) -> bool,
|
||||||
|
server_info: &ServerInfo,
|
||||||
|
register_stream: &mut Stream,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// Authentication
|
// Authentication
|
||||||
let token_or_username = match &self.server_info.auth_provider {
|
let token_or_username = match &server_info.auth_provider {
|
||||||
Some(addr) => {
|
Some(addr) => {
|
||||||
// Query whether this is a trusted auth server
|
// Query whether this is a trusted auth server
|
||||||
if auth_trusted(addr) {
|
if auth_trusted(addr) {
|
||||||
@ -749,26 +761,29 @@ impl Client {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(authc::AuthClient::new(scheme, authority)?
|
Ok(authc::AuthClient::new(scheme, authority)?
|
||||||
.sign_in(&username, &password)
|
.sign_in(username, password)
|
||||||
.await?
|
.await?
|
||||||
.serialize())
|
.serialize())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::AuthServerNotTrusted)
|
Err(Error::AuthServerNotTrusted)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => Ok(username),
|
None => Ok(username.to_owned()),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
self.send_msg_err(ClientRegister { token_or_username })?;
|
debug!("Registering client...");
|
||||||
|
|
||||||
match self.register_stream.recv::<ServerRegisterAnswer>().await? {
|
register_stream.send(ClientRegister { token_or_username })?;
|
||||||
|
|
||||||
|
match register_stream.recv::<ServerRegisterAnswer>().await? {
|
||||||
Err(RegisterError::AuthError(err)) => Err(Error::AuthErr(err)),
|
Err(RegisterError::AuthError(err)) => Err(Error::AuthErr(err)),
|
||||||
Err(RegisterError::InvalidCharacter) => Err(Error::InvalidCharacter),
|
Err(RegisterError::InvalidCharacter) => Err(Error::InvalidCharacter),
|
||||||
Err(RegisterError::NotOnWhitelist) => Err(Error::NotOnWhitelist),
|
Err(RegisterError::NotOnWhitelist) => Err(Error::NotOnWhitelist),
|
||||||
Err(RegisterError::Kicked(err)) => Err(Error::Kicked(err)),
|
Err(RegisterError::Kicked(err)) => Err(Error::Kicked(err)),
|
||||||
Err(RegisterError::Banned(reason)) => Err(Error::Banned(reason)),
|
Err(RegisterError::Banned(reason)) => Err(Error::Banned(reason)),
|
||||||
|
Err(RegisterError::TooManyPlayers) => Err(Error::TooManyPlayers),
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.registered = true;
|
debug!("Client registered successfully.");
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -2917,6 +2932,9 @@ mod tests {
|
|||||||
|
|
||||||
let runtime = Arc::new(Runtime::new().unwrap());
|
let runtime = Arc::new(Runtime::new().unwrap());
|
||||||
let runtime2 = Arc::clone(&runtime);
|
let runtime2 = Arc::clone(&runtime);
|
||||||
|
let username = "Foo";
|
||||||
|
let password = "Bar";
|
||||||
|
let auth_server = "auth.veloren.net";
|
||||||
let veloren_client: Result<Client, Error> = runtime.block_on(Client::new(
|
let veloren_client: Result<Client, Error> = runtime.block_on(Client::new(
|
||||||
ConnectionArgs::Tcp {
|
ConnectionArgs::Tcp {
|
||||||
hostname: "127.0.0.1:9000".to_owned(),
|
hostname: "127.0.0.1:9000".to_owned(),
|
||||||
@ -2924,18 +2942,12 @@ mod tests {
|
|||||||
},
|
},
|
||||||
runtime2,
|
runtime2,
|
||||||
&mut None,
|
&mut None,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
|suggestion: &str| suggestion == auth_server,
|
||||||
));
|
));
|
||||||
|
|
||||||
let _ = veloren_client.map(|mut client| {
|
let _ = veloren_client.map(|mut client| {
|
||||||
//register
|
|
||||||
let username: String = "Foo".to_string();
|
|
||||||
let password: String = "Bar".to_string();
|
|
||||||
let auth_server: String = "auth.veloren.net".to_string();
|
|
||||||
let _result: Result<(), Error> =
|
|
||||||
runtime.block_on(client.register(username, password, |suggestion: &str| {
|
|
||||||
suggestion == auth_server
|
|
||||||
}));
|
|
||||||
|
|
||||||
//clock
|
//clock
|
||||||
let mut clock = Clock::new(Duration::from_secs_f64(SPT));
|
let mut clock = Clock::new(Duration::from_secs_f64(SPT));
|
||||||
|
|
||||||
|
@ -55,7 +55,6 @@ pub struct ServerInfo {
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum ServerInit {
|
pub enum ServerInit {
|
||||||
TooManyPlayers,
|
|
||||||
GameSync {
|
GameSync {
|
||||||
entity_package: sync::EntityPackage<EcsCompPacket>,
|
entity_package: sync::EntityPackage<EcsCompPacket>,
|
||||||
time_of_day: TimeOfDay,
|
time_of_day: TimeOfDay,
|
||||||
@ -275,6 +274,7 @@ pub enum RegisterError {
|
|||||||
Kicked(String),
|
Kicked(String),
|
||||||
InvalidCharacter,
|
InvalidCharacter,
|
||||||
NotOnWhitelist,
|
NotOnWhitelist,
|
||||||
|
TooManyPlayers,
|
||||||
//TODO: InvalidAlias,
|
//TODO: InvalidAlias,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,9 +42,6 @@ macro_rules! synced_components {
|
|||||||
shockwave: Shockwave,
|
shockwave: Shockwave,
|
||||||
beam_segment: BeamSegment,
|
beam_segment: BeamSegment,
|
||||||
alignment: Alignment,
|
alignment: Alignment,
|
||||||
// TODO: evaluate if this is used on the client,
|
|
||||||
// and if so what it is used for
|
|
||||||
player: Player,
|
|
||||||
// TODO: change this to `SyncFrom::ClientEntity` and sync the bare minimum
|
// TODO: change this to `SyncFrom::ClientEntity` and sync the bare minimum
|
||||||
// from other entities (e.g. just keys needed to show appearance
|
// from other entities (e.g. just keys needed to show appearance
|
||||||
// based on their loadout). Also, it looks like this actually has
|
// based on their loadout). Also, it looks like this actually has
|
||||||
@ -210,10 +207,6 @@ impl NetSync for Alignment {
|
|||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetSync for Player {
|
|
||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetSync for Inventory {
|
impl NetSync for Inventory {
|
||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ use crate::{
|
|||||||
presence::{Presence, RegionSubscription, RepositionOnChunkLoad},
|
presence::{Presence, RegionSubscription, RepositionOnChunkLoad},
|
||||||
rtsim::RtSim,
|
rtsim::RtSim,
|
||||||
state_ext::StateExt,
|
state_ext::StateExt,
|
||||||
sys::sentinel::{DeletedEntities, TrackedStorages},
|
sys::sentinel::DeletedEntities,
|
||||||
};
|
};
|
||||||
use censor::Censor;
|
use censor::Censor;
|
||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
@ -79,7 +79,6 @@ use common::{
|
|||||||
cmd::ServerChatCommand,
|
cmd::ServerChatCommand,
|
||||||
comp,
|
comp,
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
recipe::{default_component_recipe_book, default_recipe_book},
|
|
||||||
resources::{BattleMode, Time, TimeOfDay},
|
resources::{BattleMode, Time, TimeOfDay},
|
||||||
rtsim::RtSimEntity,
|
rtsim::RtSimEntity,
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
@ -88,9 +87,7 @@ use common::{
|
|||||||
};
|
};
|
||||||
use common_ecs::run_now;
|
use common_ecs::run_now;
|
||||||
use common_net::{
|
use common_net::{
|
||||||
msg::{
|
msg::{ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerMsg},
|
||||||
ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerInit, ServerMsg, WorldMapMsg,
|
|
||||||
},
|
|
||||||
sync::WorldSyncExt,
|
sync::WorldSyncExt,
|
||||||
};
|
};
|
||||||
use common_state::{BuildAreas, State};
|
use common_state::{BuildAreas, State};
|
||||||
@ -103,7 +100,7 @@ use persistence::{
|
|||||||
};
|
};
|
||||||
use prometheus::Registry;
|
use prometheus::Registry;
|
||||||
use prometheus_hyper::Server as PrometheusServer;
|
use prometheus_hyper::Server as PrometheusServer;
|
||||||
use specs::{join::Join, Builder, Entity as EcsEntity, Entity, SystemData, WorldExt};
|
use specs::{join::Join, Builder, Entity as EcsEntity, Entity, WorldExt};
|
||||||
use std::{
|
use std::{
|
||||||
i32,
|
i32,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
@ -201,7 +198,6 @@ pub struct Server {
|
|||||||
state: State,
|
state: State,
|
||||||
world: Arc<World>,
|
world: Arc<World>,
|
||||||
index: IndexOwned,
|
index: IndexOwned,
|
||||||
map: WorldMapMsg,
|
|
||||||
|
|
||||||
connection_handler: ConnectionHandler,
|
connection_handler: ConnectionHandler,
|
||||||
|
|
||||||
@ -387,6 +383,8 @@ impl Server {
|
|||||||
pois: Vec::new(),
|
pois: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
state.ecs_mut().insert(map);
|
||||||
|
|
||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
let spawn_point = SpawnPoint({
|
let spawn_point = SpawnPoint({
|
||||||
let index = index.as_index_ref();
|
let index = index.as_index_ref();
|
||||||
@ -562,8 +560,6 @@ impl Server {
|
|||||||
state,
|
state,
|
||||||
world,
|
world,
|
||||||
index,
|
index,
|
||||||
map,
|
|
||||||
|
|
||||||
connection_handler,
|
connection_handler,
|
||||||
runtime,
|
runtime,
|
||||||
|
|
||||||
@ -630,9 +626,6 @@ impl Server {
|
|||||||
/// Get a reference to the server's world.
|
/// Get a reference to the server's world.
|
||||||
pub fn world(&self) -> &World { &self.world }
|
pub fn world(&self) -> &World { &self.world }
|
||||||
|
|
||||||
/// Get a reference to the server's world map.
|
|
||||||
pub fn map(&self) -> &WorldMapMsg { &self.map }
|
|
||||||
|
|
||||||
/// Execute a single server tick, handle input and update the game state by
|
/// Execute a single server tick, handle input and update the game state by
|
||||||
/// the given duration.
|
/// the given duration.
|
||||||
pub fn tick(&mut self, _input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
|
pub fn tick(&mut self, _input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
|
||||||
@ -1029,19 +1022,7 @@ impl Server {
|
|||||||
.map(|mut t| t.maintain());
|
.map(|mut t| t.maintain());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_client(
|
fn initialize_client(&mut self, client: connection_handler::IncomingClient) -> Entity {
|
||||||
&mut self,
|
|
||||||
client: connection_handler::IncomingClient,
|
|
||||||
) -> Result<Option<Entity>, Error> {
|
|
||||||
if self.settings().max_players <= self.state.ecs().read_storage::<Client>().join().count() {
|
|
||||||
trace!(
|
|
||||||
?client.participant,
|
|
||||||
"to many players, wont allow participant to connect"
|
|
||||||
);
|
|
||||||
client.send(ServerInit::TooManyPlayers)?;
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let entity = self
|
let entity = self
|
||||||
.state
|
.state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
@ -1053,43 +1034,7 @@ impl Server {
|
|||||||
.read_resource::<metrics::PlayerMetrics>()
|
.read_resource::<metrics::PlayerMetrics>()
|
||||||
.clients_connected
|
.clients_connected
|
||||||
.inc();
|
.inc();
|
||||||
// Send client all the tracked components currently attached to its entity as
|
entity
|
||||||
// well as synced resources (currently only `TimeOfDay`)
|
|
||||||
debug!("Starting initial sync with client.");
|
|
||||||
self.state
|
|
||||||
.ecs()
|
|
||||||
.read_storage::<Client>()
|
|
||||||
.get(entity)
|
|
||||||
.expect(
|
|
||||||
"We just created this entity with a Client component using build(), and we have \
|
|
||||||
&mut access to the ecs so it can't have been deleted yet.",
|
|
||||||
)
|
|
||||||
.send(ServerInit::GameSync {
|
|
||||||
// Send client their entity
|
|
||||||
entity_package: TrackedStorages::fetch(self.state.ecs())
|
|
||||||
.create_entity_package(entity, None, None, None)
|
|
||||||
.expect(
|
|
||||||
"We just created this entity as marked() (using create_entity_synced) so \
|
|
||||||
it definitely has a uid",
|
|
||||||
),
|
|
||||||
time_of_day: *self.state.ecs().read_resource(),
|
|
||||||
max_group_size: self.settings().max_player_group_size,
|
|
||||||
client_timeout: self.settings().client_timeout,
|
|
||||||
world_map: self.map.clone(),
|
|
||||||
recipe_book: default_recipe_book().cloned(),
|
|
||||||
component_recipe_book: default_component_recipe_book().cloned(),
|
|
||||||
material_stats: (&*self
|
|
||||||
.state
|
|
||||||
.ecs()
|
|
||||||
.read_resource::<comp::item::MaterialStatManifest>())
|
|
||||||
.clone(),
|
|
||||||
ability_map: (&*self
|
|
||||||
.state
|
|
||||||
.ecs()
|
|
||||||
.read_resource::<comp::item::tool::AbilityMap>())
|
|
||||||
.clone(),
|
|
||||||
})?;
|
|
||||||
Ok(Some(entity))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disconnects all clients if requested by either an admin command or
|
/// Disconnects all clients if requested by either an admin command or
|
||||||
@ -1155,16 +1100,8 @@ impl Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while let Ok(incoming) = self.connection_handler.client_receiver.try_recv() {
|
while let Ok(incoming) = self.connection_handler.client_receiver.try_recv() {
|
||||||
match self.initialize_client(incoming) {
|
let entity = self.initialize_client(incoming);
|
||||||
Ok(None) => (),
|
|
||||||
Ok(Some(entity)) => {
|
|
||||||
frontend_events.push(Event::ClientConnected { entity });
|
frontend_events.push(Event::ClientConnected { entity });
|
||||||
debug!("Done initial sync with client.");
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
debug!(?e, "failed initializing a new client");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ impl LoginProvider {
|
|||||||
PendingLogin { pending_r }
|
PendingLogin { pending_r }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn login(
|
pub(crate) fn login(
|
||||||
&mut self,
|
&mut self,
|
||||||
pending: &mut PendingLogin,
|
pending: &mut PendingLogin,
|
||||||
#[cfg(feature = "plugins")] world: &EcsWorld,
|
#[cfg(feature = "plugins")] world: &EcsWorld,
|
||||||
@ -105,6 +105,7 @@ impl LoginProvider {
|
|||||||
admins: &HashMap<Uuid, AdminRecord>,
|
admins: &HashMap<Uuid, AdminRecord>,
|
||||||
whitelist: &HashMap<Uuid, WhitelistRecord>,
|
whitelist: &HashMap<Uuid, WhitelistRecord>,
|
||||||
banlist: &HashMap<Uuid, BanEntry>,
|
banlist: &HashMap<Uuid, BanEntry>,
|
||||||
|
player_count_exceeded: bool,
|
||||||
) -> Option<Result<(String, Uuid), RegisterError>> {
|
) -> Option<Result<(String, Uuid), RegisterError>> {
|
||||||
match pending.pending_r.try_recv() {
|
match pending.pending_r.try_recv() {
|
||||||
Ok(Err(e)) => Some(Err(e)),
|
Ok(Err(e)) => Some(Err(e)),
|
||||||
@ -137,6 +138,11 @@ impl LoginProvider {
|
|||||||
return Some(Err(RegisterError::NotOnWhitelist));
|
return Some(Err(RegisterError::NotOnWhitelist));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// non-admins can only join if the player count has not been exceeded.
|
||||||
|
if admin.is_none() && player_count_exceeded {
|
||||||
|
return Some(Err(RegisterError::TooManyPlayers));
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
{
|
{
|
||||||
// Plugin player join hooks execute for all players, but are only allowed to
|
// Plugin player join hooks execute for all players, but are only allowed to
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
calendar::Calendar,
|
calendar::Calendar,
|
||||||
comp::{Collider, ForceUpdate, InventoryUpdate, Last, Ori, Pos, Vel},
|
comp::{Collider, ForceUpdate, InventoryUpdate, Last, Ori, Player, Pos, Vel},
|
||||||
event::EventBus,
|
event::EventBus,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
region::{Event as RegionEvent, RegionMap},
|
region::{Event as RegionEvent, RegionMap},
|
||||||
@ -40,6 +40,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, Vel>,
|
ReadStorage<'a, Vel>,
|
||||||
ReadStorage<'a, Ori>,
|
ReadStorage<'a, Ori>,
|
||||||
ReadStorage<'a, RegionSubscription>,
|
ReadStorage<'a, RegionSubscription>,
|
||||||
|
ReadStorage<'a, Player>,
|
||||||
ReadStorage<'a, Presence>,
|
ReadStorage<'a, Presence>,
|
||||||
ReadStorage<'a, Client>,
|
ReadStorage<'a, Client>,
|
||||||
WriteStorage<'a, Last<Pos>>,
|
WriteStorage<'a, Last<Pos>>,
|
||||||
@ -70,6 +71,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
velocities,
|
velocities,
|
||||||
orientations,
|
orientations,
|
||||||
subscriptions,
|
subscriptions,
|
||||||
|
players,
|
||||||
presences,
|
presences,
|
||||||
clients,
|
clients,
|
||||||
mut last_pos,
|
mut last_pos,
|
||||||
@ -88,7 +90,6 @@ impl<'a> System<'a> for Sys {
|
|||||||
let uids = &tracked_storages.uid;
|
let uids = &tracked_storages.uid;
|
||||||
let colliders = &tracked_storages.collider;
|
let colliders = &tracked_storages.collider;
|
||||||
let inventories = &tracked_storages.inventory;
|
let inventories = &tracked_storages.inventory;
|
||||||
let players = &tracked_storages.player;
|
|
||||||
let is_rider = &tracked_storages.is_rider;
|
let is_rider = &tracked_storages.is_rider;
|
||||||
|
|
||||||
// To send entity updates
|
// To send entity updates
|
||||||
|
@ -2,17 +2,20 @@ use crate::{
|
|||||||
client::Client,
|
client::Client,
|
||||||
login_provider::{LoginProvider, PendingLogin},
|
login_provider::{LoginProvider, PendingLogin},
|
||||||
metrics::PlayerMetrics,
|
metrics::PlayerMetrics,
|
||||||
|
sys::sentinel::TrackedStorages,
|
||||||
EditableSettings, Settings,
|
EditableSettings, Settings,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{Admin, Player, Stats},
|
comp::{self, Admin, Player, Stats},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
|
recipe::{default_component_recipe_book, default_recipe_book},
|
||||||
|
resources::TimeOfDay,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use common_net::msg::{
|
use common_net::msg::{
|
||||||
CharacterInfo, ClientRegister, DisconnectReason, PlayerInfo, PlayerListUpdate, RegisterError,
|
CharacterInfo, ClientRegister, DisconnectReason, PlayerInfo, PlayerListUpdate, RegisterError,
|
||||||
ServerGeneral,
|
ServerGeneral, ServerInit, WorldMapMsg,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use plugin_api::Health;
|
use plugin_api::Health;
|
||||||
@ -20,7 +23,7 @@ use specs::{
|
|||||||
shred::ResourceId, storage::StorageEntry, Entities, Join, Read, ReadExpect, ReadStorage,
|
shred::ResourceId, storage::StorageEntry, Entities, Join, Read, ReadExpect, ReadStorage,
|
||||||
SystemData, World, WriteExpect, WriteStorage,
|
SystemData, World, WriteExpect, WriteStorage,
|
||||||
};
|
};
|
||||||
use tracing::trace;
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
use {common_state::plugin::memory_manager::EcsWorld, common_state::plugin::PluginMgr};
|
use {common_state::plugin::memory_manager::EcsWorld, common_state::plugin::PluginMgr};
|
||||||
@ -40,6 +43,11 @@ pub struct ReadData<'a> {
|
|||||||
player_metrics: ReadExpect<'a, PlayerMetrics>,
|
player_metrics: ReadExpect<'a, PlayerMetrics>,
|
||||||
settings: ReadExpect<'a, Settings>,
|
settings: ReadExpect<'a, Settings>,
|
||||||
editable_settings: ReadExpect<'a, EditableSettings>,
|
editable_settings: ReadExpect<'a, EditableSettings>,
|
||||||
|
time_of_day: Read<'a, TimeOfDay>,
|
||||||
|
material_stats: ReadExpect<'a, comp::item::MaterialStatManifest>,
|
||||||
|
ability_map: ReadExpect<'a, comp::item::tool::AbilityMap>,
|
||||||
|
map: ReadExpect<'a, WorldMapMsg>,
|
||||||
|
trackers: TrackedStorages<'a>,
|
||||||
_healths: ReadStorage<'a, Health>, // used by plugin feature
|
_healths: ReadStorage<'a, Health>, // used by plugin feature
|
||||||
_plugin_mgr: ReadPlugin<'a>, // used by plugin feature
|
_plugin_mgr: ReadPlugin<'a>, // used by plugin feature
|
||||||
_uid_allocator: Read<'a, UidAllocator>, // used by plugin feature
|
_uid_allocator: Read<'a, UidAllocator>, // used by plugin feature
|
||||||
@ -107,6 +115,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
let mut finished_pending = vec![];
|
let mut finished_pending = vec![];
|
||||||
let mut retries = vec![];
|
let mut retries = vec![];
|
||||||
|
let mut player_count = player_list.len();
|
||||||
|
let max_players = read_data.settings.max_players;
|
||||||
for (entity, client, pending) in
|
for (entity, client, pending) in
|
||||||
(&read_data.entities, &read_data.clients, &mut pending_logins).join()
|
(&read_data.entities, &read_data.clients, &mut pending_logins).join()
|
||||||
{
|
{
|
||||||
@ -129,6 +139,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
&*read_data.editable_settings.admins,
|
&*read_data.editable_settings.admins,
|
||||||
&*read_data.editable_settings.whitelist,
|
&*read_data.editable_settings.whitelist,
|
||||||
&*read_data.editable_settings.banlist,
|
&*read_data.editable_settings.banlist,
|
||||||
|
player_count >= max_players,
|
||||||
) {
|
) {
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
Some(r) => {
|
Some(r) => {
|
||||||
@ -190,6 +201,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
if let Ok(StorageEntry::Vacant(v)) = players.entry(entity) {
|
if let Ok(StorageEntry::Vacant(v)) = players.entry(entity) {
|
||||||
// Add Player component to this client, if the entity exists.
|
// Add Player component to this client, if the entity exists.
|
||||||
v.insert(player);
|
v.insert(player);
|
||||||
|
player_count += 1;
|
||||||
read_data.player_metrics.players_connected.inc();
|
read_data.player_metrics.players_connected.inc();
|
||||||
|
|
||||||
// Give the Admin component to the player if their name exists in
|
// Give the Admin component to the player if their name exists in
|
||||||
@ -203,6 +215,31 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Tell the client its request was successful.
|
// Tell the client its request was successful.
|
||||||
client.send(Ok(()))?;
|
client.send(Ok(()))?;
|
||||||
|
|
||||||
|
// Send client all the tracked components currently attached to its entity as
|
||||||
|
// well as synced resources (currently only `TimeOfDay`)
|
||||||
|
debug!("Starting initial sync with client.");
|
||||||
|
client.send(ServerInit::GameSync {
|
||||||
|
// Send client their entity
|
||||||
|
entity_package:
|
||||||
|
read_data.trackers
|
||||||
|
.create_entity_package(entity, None, None, None)
|
||||||
|
// NOTE: We are apparently okay with crashing if a uid is removed from
|
||||||
|
// a non-logged-in player without removing the whole thing.
|
||||||
|
.expect(
|
||||||
|
"We created this entity as marked() (using create_entity_synced) so \
|
||||||
|
it definitely has a uid",
|
||||||
|
),
|
||||||
|
time_of_day: *read_data.time_of_day,
|
||||||
|
max_group_size: read_data.settings.max_player_group_size,
|
||||||
|
client_timeout: read_data.settings.client_timeout,
|
||||||
|
world_map: (&*read_data.map).clone(),
|
||||||
|
recipe_book: default_recipe_book().cloned(),
|
||||||
|
component_recipe_book: default_component_recipe_book().cloned(),
|
||||||
|
material_stats: (&*read_data.material_stats).clone(),
|
||||||
|
ability_map: (&*read_data.ability_map).clone(),
|
||||||
|
})?;
|
||||||
|
debug!("Done initial sync with client.");
|
||||||
|
|
||||||
// Send initial player list
|
// Send initial player list
|
||||||
client.send(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init(
|
client.send(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init(
|
||||||
player_list.clone(),
|
player_list.clone(),
|
||||||
|
@ -1278,7 +1278,6 @@ impl Hud {
|
|||||||
let bodies = ecs.read_storage::<comp::Body>();
|
let bodies = ecs.read_storage::<comp::Body>();
|
||||||
let items = ecs.read_storage::<Item>();
|
let items = ecs.read_storage::<Item>();
|
||||||
let inventories = ecs.read_storage::<comp::Inventory>();
|
let inventories = ecs.read_storage::<comp::Inventory>();
|
||||||
let players = ecs.read_storage::<comp::Player>();
|
|
||||||
let msm = ecs.read_resource::<MaterialStatManifest>();
|
let msm = ecs.read_resource::<MaterialStatManifest>();
|
||||||
let entities = ecs.entities();
|
let entities = ecs.entities();
|
||||||
let me = info.viewpoint_entity;
|
let me = info.viewpoint_entity;
|
||||||
@ -1978,7 +1977,6 @@ impl Hud {
|
|||||||
&mut hp_floater_lists,
|
&mut hp_floater_lists,
|
||||||
&uids,
|
&uids,
|
||||||
&inventories,
|
&inventories,
|
||||||
players.maybe(),
|
|
||||||
poises.maybe(),
|
poises.maybe(),
|
||||||
(alignments.maybe(), is_mount.maybe()),
|
(alignments.maybe(), is_mount.maybe()),
|
||||||
)
|
)
|
||||||
@ -2002,7 +2000,6 @@ impl Hud {
|
|||||||
hpfl,
|
hpfl,
|
||||||
uid,
|
uid,
|
||||||
inventory,
|
inventory,
|
||||||
player,
|
|
||||||
poise,
|
poise,
|
||||||
(alignment, is_mount),
|
(alignment, is_mount),
|
||||||
)| {
|
)| {
|
||||||
@ -2013,7 +2010,8 @@ impl Hud {
|
|||||||
// TODO: once the site2 rework lands and merchants have dedicated stalls or
|
// TODO: once the site2 rework lands and merchants have dedicated stalls or
|
||||||
// buildings, they no longer need to be emphasized via the higher overhead
|
// buildings, they no longer need to be emphasized via the higher overhead
|
||||||
// text radius relative to other NPCs
|
// text radius relative to other NPCs
|
||||||
let is_merchant = stats.name == "Merchant" && player.is_none();
|
let is_merchant =
|
||||||
|
stats.name == "Merchant" && client.player_list().get(uid).is_none();
|
||||||
let dist_sqr = pos.distance_squared(player_pos);
|
let dist_sqr = pos.distance_squared(player_pos);
|
||||||
// Determine whether to display nametag and healthbar based on whether the
|
// Determine whether to display nametag and healthbar based on whether the
|
||||||
// entity has been damaged, is targeted/selected, or is in your group
|
// entity has been damaged, is targeted/selected, or is in your group
|
||||||
|
@ -77,17 +77,13 @@ impl ClientInit {
|
|||||||
connection_args.clone(),
|
connection_args.clone(),
|
||||||
Arc::clone(&runtime2),
|
Arc::clone(&runtime2),
|
||||||
&mut mismatched_server_info,
|
&mut mismatched_server_info,
|
||||||
|
&username,
|
||||||
|
&password,
|
||||||
|
trust_fn,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(mut client) => {
|
Ok(client) => {
|
||||||
if let Err(e) = client.register(username, password, trust_fn).await {
|
|
||||||
last_err = Some(Error::ClientError {
|
|
||||||
error: e,
|
|
||||||
mismatched_server_info: None,
|
|
||||||
});
|
|
||||||
break 'tries;
|
|
||||||
}
|
|
||||||
let _ = tx.send(Msg::Done(Ok(client)));
|
let _ = tx.send(Msg::Done(Ok(client)));
|
||||||
tokio::task::block_in_place(move || drop(runtime2));
|
tokio::task::block_in_place(move || drop(runtime2));
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user