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
|
||||
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).
|
||||
- Moderators and admins are no longer blocked from logging in when there are too many players.
|
||||
|
||||
## [0.13.0] - 2022-07-23
|
||||
|
||||
|
@ -51,19 +51,20 @@ fn main() {
|
||||
|
||||
// Create a client.
|
||||
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");
|
||||
|
||||
println!("Server info: {:?}", client.server_info());
|
||||
|
||||
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();
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
|
@ -13,6 +13,7 @@ mod settings;
|
||||
mod tui;
|
||||
|
||||
use common::comp::body::humanoid::Body;
|
||||
use common_net::msg::ServerInfo;
|
||||
use settings::Settings;
|
||||
use tui::Cmd;
|
||||
|
||||
@ -46,31 +47,47 @@ pub fn main() {
|
||||
pub struct BotClient {
|
||||
settings: Settings,
|
||||
runtime: Arc<Runtime>,
|
||||
menu_client: Client,
|
||||
server_info: ServerInfo,
|
||||
bot_clients: HashMap<String, Client>,
|
||||
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 addr = ConnectionArgs::Tcp {
|
||||
prefer_ipv6: false,
|
||||
hostname: server.to_owned(),
|
||||
};
|
||||
runtime
|
||||
.block_on(Client::new(addr, runtime_clone, &mut None))
|
||||
.expect("Failed to connect to server")
|
||||
.block_on(Client::new(
|
||||
addr,
|
||||
runtime_clone,
|
||||
server_info,
|
||||
username,
|
||||
password,
|
||||
|_| true,
|
||||
))
|
||||
.ok()
|
||||
}
|
||||
|
||||
impl BotClient {
|
||||
pub fn new(settings: Settings) -> BotClient {
|
||||
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));
|
||||
BotClient {
|
||||
settings,
|
||||
runtime,
|
||||
menu_client,
|
||||
server_info,
|
||||
bot_clients: HashMap::new(),
|
||||
clock,
|
||||
}
|
||||
@ -106,7 +123,7 @@ impl BotClient {
|
||||
None => vec![prefix.to_string()],
|
||||
};
|
||||
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 = scheme
|
||||
.parse::<authc::Scheme>()
|
||||
@ -156,20 +173,16 @@ impl BotClient {
|
||||
for cred in creds.iter() {
|
||||
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
|
||||
.bot_clients
|
||||
.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();
|
||||
client.create_character(
|
||||
cred.username.clone(),
|
||||
|
@ -108,15 +108,17 @@ fn run_client(
|
||||
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");
|
||||
|
||||
// Login
|
||||
// NOTE: use a no-auth server
|
||||
runtime
|
||||
.block_on(client.register(username.clone(), String::new(), |_| false))
|
||||
.expect("Failed to log in");
|
||||
let mut client = runtime
|
||||
.block_on(Client::new(
|
||||
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));
|
||||
|
||||
|
@ -275,6 +275,9 @@ impl Client {
|
||||
runtime: Arc<Runtime>,
|
||||
// TODO: refactor to avoid needing to use this out parameter
|
||||
mismatched_server_info: &mut Option<ServerInfo>,
|
||||
username: &str,
|
||||
password: &str,
|
||||
auth_trusted: impl FnMut(&str) -> bool,
|
||||
) -> Result<Self, Error> {
|
||||
let network = Network::new(Pid::new(), &runtime);
|
||||
|
||||
@ -317,15 +320,24 @@ impl Client {
|
||||
common::util::GIT_HASH.to_string(),
|
||||
common::util::GIT_DATE.to_string(),
|
||||
);
|
||||
|
||||
// Pass the server info back to the caller to ensure they can access it even
|
||||
// if this function errors.
|
||||
mem::swap(mismatched_server_info, &mut Some(server_info.clone()));
|
||||
}
|
||||
// Pass the server info back to the caller to ensure they can access it even
|
||||
// if this function errors.
|
||||
mem::swap(mismatched_server_info, &mut Some(server_info.clone()));
|
||||
debug!("Auth Server: {:?}", server_info.auth_provider);
|
||||
|
||||
ping_stream.send(PingMsg::Ping)?;
|
||||
|
||||
// Register client
|
||||
Self::register(
|
||||
username,
|
||||
password,
|
||||
auth_trusted,
|
||||
&server_info,
|
||||
&mut register_stream,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Wait for initial sync
|
||||
let mut ping_interval = tokio::time::interval(Duration::from_secs(1));
|
||||
let (
|
||||
@ -632,7 +644,7 @@ impl Client {
|
||||
let map_bounds = Vec2::new(sea_level, max_height);
|
||||
debug!("Done preparing image...");
|
||||
|
||||
Ok((
|
||||
(
|
||||
state,
|
||||
lod_base,
|
||||
lod_alt,
|
||||
@ -644,16 +656,15 @@ impl Client {
|
||||
component_recipe_book,
|
||||
max_group_size,
|
||||
client_timeout,
|
||||
))
|
||||
)
|
||||
},
|
||||
ServerInit::TooManyPlayers => Err(Error::TooManyPlayers),
|
||||
}?;
|
||||
};
|
||||
ping_stream.send(PingMsg::Ping)?;
|
||||
|
||||
debug!("Initial sync done");
|
||||
|
||||
Ok(Self {
|
||||
registered: false,
|
||||
registered: true,
|
||||
presence: None,
|
||||
runtime,
|
||||
server_info,
|
||||
@ -722,14 +733,15 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Request a state transition to `ClientState::Registered`.
|
||||
pub async fn register(
|
||||
&mut self,
|
||||
username: String,
|
||||
password: String,
|
||||
async fn register(
|
||||
username: &str,
|
||||
password: &str,
|
||||
mut auth_trusted: impl FnMut(&str) -> bool,
|
||||
server_info: &ServerInfo,
|
||||
register_stream: &mut Stream,
|
||||
) -> Result<(), Error> {
|
||||
// Authentication
|
||||
let token_or_username = match &self.server_info.auth_provider {
|
||||
let token_or_username = match &server_info.auth_provider {
|
||||
Some(addr) => {
|
||||
// Query whether this is a trusted auth server
|
||||
if auth_trusted(addr) {
|
||||
@ -749,26 +761,29 @@ impl Client {
|
||||
};
|
||||
|
||||
Ok(authc::AuthClient::new(scheme, authority)?
|
||||
.sign_in(&username, &password)
|
||||
.sign_in(username, password)
|
||||
.await?
|
||||
.serialize())
|
||||
} else {
|
||||
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::InvalidCharacter) => Err(Error::InvalidCharacter),
|
||||
Err(RegisterError::NotOnWhitelist) => Err(Error::NotOnWhitelist),
|
||||
Err(RegisterError::Kicked(err)) => Err(Error::Kicked(err)),
|
||||
Err(RegisterError::Banned(reason)) => Err(Error::Banned(reason)),
|
||||
Err(RegisterError::TooManyPlayers) => Err(Error::TooManyPlayers),
|
||||
Ok(()) => {
|
||||
self.registered = true;
|
||||
debug!("Client registered successfully.");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
@ -2917,6 +2932,9 @@ mod tests {
|
||||
|
||||
let runtime = Arc::new(Runtime::new().unwrap());
|
||||
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(
|
||||
ConnectionArgs::Tcp {
|
||||
hostname: "127.0.0.1:9000".to_owned(),
|
||||
@ -2924,18 +2942,12 @@ mod tests {
|
||||
},
|
||||
runtime2,
|
||||
&mut None,
|
||||
username,
|
||||
password,
|
||||
|suggestion: &str| suggestion == auth_server,
|
||||
));
|
||||
|
||||
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
|
||||
let mut clock = Clock::new(Duration::from_secs_f64(SPT));
|
||||
|
||||
|
@ -55,7 +55,6 @@ pub struct ServerInfo {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum ServerInit {
|
||||
TooManyPlayers,
|
||||
GameSync {
|
||||
entity_package: sync::EntityPackage<EcsCompPacket>,
|
||||
time_of_day: TimeOfDay,
|
||||
@ -275,6 +274,7 @@ pub enum RegisterError {
|
||||
Kicked(String),
|
||||
InvalidCharacter,
|
||||
NotOnWhitelist,
|
||||
TooManyPlayers,
|
||||
//TODO: InvalidAlias,
|
||||
}
|
||||
|
||||
|
@ -42,9 +42,6 @@ macro_rules! synced_components {
|
||||
shockwave: Shockwave,
|
||||
beam_segment: BeamSegment,
|
||||
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
|
||||
// from other entities (e.g. just keys needed to show appearance
|
||||
// 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;
|
||||
}
|
||||
|
||||
impl NetSync for Player {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||
}
|
||||
|
||||
impl NetSync for Inventory {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||
}
|
||||
@ -222,7 +215,7 @@ impl NetSync for SkillSet {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||
}
|
||||
|
||||
// These are synced only from the client's own entity.
|
||||
// These are synced only from the client's own entity.
|
||||
|
||||
impl NetSync for Combo {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::ClientEntity;
|
||||
|
@ -67,7 +67,7 @@ use crate::{
|
||||
presence::{Presence, RegionSubscription, RepositionOnChunkLoad},
|
||||
rtsim::RtSim,
|
||||
state_ext::StateExt,
|
||||
sys::sentinel::{DeletedEntities, TrackedStorages},
|
||||
sys::sentinel::DeletedEntities,
|
||||
};
|
||||
use censor::Censor;
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
@ -79,7 +79,6 @@ use common::{
|
||||
cmd::ServerChatCommand,
|
||||
comp,
|
||||
event::{EventBus, ServerEvent},
|
||||
recipe::{default_component_recipe_book, default_recipe_book},
|
||||
resources::{BattleMode, Time, TimeOfDay},
|
||||
rtsim::RtSimEntity,
|
||||
slowjob::SlowJobPool,
|
||||
@ -88,9 +87,7 @@ use common::{
|
||||
};
|
||||
use common_ecs::run_now;
|
||||
use common_net::{
|
||||
msg::{
|
||||
ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerInit, ServerMsg, WorldMapMsg,
|
||||
},
|
||||
msg::{ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerMsg},
|
||||
sync::WorldSyncExt,
|
||||
};
|
||||
use common_state::{BuildAreas, State};
|
||||
@ -103,7 +100,7 @@ use persistence::{
|
||||
};
|
||||
use prometheus::Registry;
|
||||
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::{
|
||||
i32,
|
||||
ops::{Deref, DerefMut},
|
||||
@ -201,7 +198,6 @@ pub struct Server {
|
||||
state: State,
|
||||
world: Arc<World>,
|
||||
index: IndexOwned,
|
||||
map: WorldMapMsg,
|
||||
|
||||
connection_handler: ConnectionHandler,
|
||||
|
||||
@ -387,6 +383,8 @@ impl Server {
|
||||
pois: Vec::new(),
|
||||
};
|
||||
|
||||
state.ecs_mut().insert(map);
|
||||
|
||||
#[cfg(feature = "worldgen")]
|
||||
let spawn_point = SpawnPoint({
|
||||
let index = index.as_index_ref();
|
||||
@ -562,8 +560,6 @@ impl Server {
|
||||
state,
|
||||
world,
|
||||
index,
|
||||
map,
|
||||
|
||||
connection_handler,
|
||||
runtime,
|
||||
|
||||
@ -630,9 +626,6 @@ impl Server {
|
||||
/// Get a reference to the server's 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
|
||||
/// the given duration.
|
||||
pub fn tick(&mut self, _input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
|
||||
@ -1029,19 +1022,7 @@ impl Server {
|
||||
.map(|mut t| t.maintain());
|
||||
}
|
||||
|
||||
fn initialize_client(
|
||||
&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);
|
||||
}
|
||||
|
||||
fn initialize_client(&mut self, client: connection_handler::IncomingClient) -> Entity {
|
||||
let entity = self
|
||||
.state
|
||||
.ecs_mut()
|
||||
@ -1053,43 +1034,7 @@ impl Server {
|
||||
.read_resource::<metrics::PlayerMetrics>()
|
||||
.clients_connected
|
||||
.inc();
|
||||
// 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.");
|
||||
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))
|
||||
entity
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
match self.initialize_client(incoming) {
|
||||
Ok(None) => (),
|
||||
Ok(Some(entity)) => {
|
||||
frontend_events.push(Event::ClientConnected { entity });
|
||||
debug!("Done initial sync with client.");
|
||||
},
|
||||
Err(e) => {
|
||||
debug!(?e, "failed initializing a new client");
|
||||
},
|
||||
}
|
||||
let entity = self.initialize_client(incoming);
|
||||
frontend_events.push(Event::ClientConnected { entity });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ impl LoginProvider {
|
||||
PendingLogin { pending_r }
|
||||
}
|
||||
|
||||
pub fn login(
|
||||
pub(crate) fn login(
|
||||
&mut self,
|
||||
pending: &mut PendingLogin,
|
||||
#[cfg(feature = "plugins")] world: &EcsWorld,
|
||||
@ -105,6 +105,7 @@ impl LoginProvider {
|
||||
admins: &HashMap<Uuid, AdminRecord>,
|
||||
whitelist: &HashMap<Uuid, WhitelistRecord>,
|
||||
banlist: &HashMap<Uuid, BanEntry>,
|
||||
player_count_exceeded: bool,
|
||||
) -> Option<Result<(String, Uuid), RegisterError>> {
|
||||
match pending.pending_r.try_recv() {
|
||||
Ok(Err(e)) => Some(Err(e)),
|
||||
@ -137,6 +138,11 @@ impl LoginProvider {
|
||||
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")]
|
||||
{
|
||||
// Plugin player join hooks execute for all players, but are only allowed to
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
calendar::Calendar,
|
||||
comp::{Collider, ForceUpdate, InventoryUpdate, Last, Ori, Pos, Vel},
|
||||
comp::{Collider, ForceUpdate, InventoryUpdate, Last, Ori, Player, Pos, Vel},
|
||||
event::EventBus,
|
||||
outcome::Outcome,
|
||||
region::{Event as RegionEvent, RegionMap},
|
||||
@ -40,6 +40,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Vel>,
|
||||
ReadStorage<'a, Ori>,
|
||||
ReadStorage<'a, RegionSubscription>,
|
||||
ReadStorage<'a, Player>,
|
||||
ReadStorage<'a, Presence>,
|
||||
ReadStorage<'a, Client>,
|
||||
WriteStorage<'a, Last<Pos>>,
|
||||
@ -70,6 +71,7 @@ impl<'a> System<'a> for Sys {
|
||||
velocities,
|
||||
orientations,
|
||||
subscriptions,
|
||||
players,
|
||||
presences,
|
||||
clients,
|
||||
mut last_pos,
|
||||
@ -88,7 +90,6 @@ impl<'a> System<'a> for Sys {
|
||||
let uids = &tracked_storages.uid;
|
||||
let colliders = &tracked_storages.collider;
|
||||
let inventories = &tracked_storages.inventory;
|
||||
let players = &tracked_storages.player;
|
||||
let is_rider = &tracked_storages.is_rider;
|
||||
|
||||
// To send entity updates
|
||||
|
@ -2,17 +2,20 @@ use crate::{
|
||||
client::Client,
|
||||
login_provider::{LoginProvider, PendingLogin},
|
||||
metrics::PlayerMetrics,
|
||||
sys::sentinel::TrackedStorages,
|
||||
EditableSettings, Settings,
|
||||
};
|
||||
use common::{
|
||||
comp::{Admin, Player, Stats},
|
||||
comp::{self, Admin, Player, Stats},
|
||||
event::{EventBus, ServerEvent},
|
||||
recipe::{default_component_recipe_book, default_recipe_book},
|
||||
resources::TimeOfDay,
|
||||
uid::{Uid, UidAllocator},
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
use common_net::msg::{
|
||||
CharacterInfo, ClientRegister, DisconnectReason, PlayerInfo, PlayerListUpdate, RegisterError,
|
||||
ServerGeneral,
|
||||
ServerGeneral, ServerInit, WorldMapMsg,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use plugin_api::Health;
|
||||
@ -20,7 +23,7 @@ use specs::{
|
||||
shred::ResourceId, storage::StorageEntry, Entities, Join, Read, ReadExpect, ReadStorage,
|
||||
SystemData, World, WriteExpect, WriteStorage,
|
||||
};
|
||||
use tracing::trace;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
#[cfg(feature = "plugins")]
|
||||
use {common_state::plugin::memory_manager::EcsWorld, common_state::plugin::PluginMgr};
|
||||
@ -40,6 +43,11 @@ pub struct ReadData<'a> {
|
||||
player_metrics: ReadExpect<'a, PlayerMetrics>,
|
||||
settings: ReadExpect<'a, Settings>,
|
||||
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
|
||||
_plugin_mgr: ReadPlugin<'a>, // 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 retries = vec![];
|
||||
let mut player_count = player_list.len();
|
||||
let max_players = read_data.settings.max_players;
|
||||
for (entity, client, pending) in
|
||||
(&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.whitelist,
|
||||
&*read_data.editable_settings.banlist,
|
||||
player_count >= max_players,
|
||||
) {
|
||||
None => return Ok(()),
|
||||
Some(r) => {
|
||||
@ -190,6 +201,7 @@ impl<'a> System<'a> for Sys {
|
||||
if let Ok(StorageEntry::Vacant(v)) = players.entry(entity) {
|
||||
// Add Player component to this client, if the entity exists.
|
||||
v.insert(player);
|
||||
player_count += 1;
|
||||
read_data.player_metrics.players_connected.inc();
|
||||
|
||||
// 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.
|
||||
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
|
||||
client.send(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init(
|
||||
player_list.clone(),
|
||||
|
@ -1278,7 +1278,6 @@ impl Hud {
|
||||
let bodies = ecs.read_storage::<comp::Body>();
|
||||
let items = ecs.read_storage::<Item>();
|
||||
let inventories = ecs.read_storage::<comp::Inventory>();
|
||||
let players = ecs.read_storage::<comp::Player>();
|
||||
let msm = ecs.read_resource::<MaterialStatManifest>();
|
||||
let entities = ecs.entities();
|
||||
let me = info.viewpoint_entity;
|
||||
@ -1978,7 +1977,6 @@ impl Hud {
|
||||
&mut hp_floater_lists,
|
||||
&uids,
|
||||
&inventories,
|
||||
players.maybe(),
|
||||
poises.maybe(),
|
||||
(alignments.maybe(), is_mount.maybe()),
|
||||
)
|
||||
@ -2002,7 +2000,6 @@ impl Hud {
|
||||
hpfl,
|
||||
uid,
|
||||
inventory,
|
||||
player,
|
||||
poise,
|
||||
(alignment, is_mount),
|
||||
)| {
|
||||
@ -2013,7 +2010,8 @@ impl Hud {
|
||||
// TODO: once the site2 rework lands and merchants have dedicated stalls or
|
||||
// buildings, they no longer need to be emphasized via the higher overhead
|
||||
// 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);
|
||||
// Determine whether to display nametag and healthbar based on whether the
|
||||
// entity has been damaged, is targeted/selected, or is in your group
|
||||
|
@ -77,17 +77,13 @@ impl ClientInit {
|
||||
connection_args.clone(),
|
||||
Arc::clone(&runtime2),
|
||||
&mut mismatched_server_info,
|
||||
&username,
|
||||
&password,
|
||||
trust_fn,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(mut 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;
|
||||
}
|
||||
Ok(client) => {
|
||||
let _ = tx.send(Msg::Done(Ok(client)));
|
||||
tokio::task::block_in_place(move || drop(runtime2));
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user