mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-31 08:22:33 +00:00
Prevent duplicate profiles (#178)
* Implement get_player_by_uuid for server & world Similar to the existing functions named get_player_by_name, these functions will search for a player with a matching Uuid. This implementation is asynchronous and thus better suits being combined with #176, which makes get_player_by_name asynchronous too. * make get_player_by_name async, rename old fn to get_player_by_name_blocking Currently, the existing commands system is all that requires the existing blocking version of get_player_by_name function. After discussing in the Discord, it seems renaming the old blocking method with a suffix of _blocking in addition to adding an async version under the original (unmodified) name of get_player_by_name. * Implement duplicate profile protections * fmt client_packet.rs * use hashmap search for players by uuid; move duplicate uuid check I've confirmed this commit compiles and runs, my client is able to play normally. However, I haven't confirmed it prevents duplicate usernames or UUIDs as of yet. * client_packet: check auth before duplications; check uuid offline too
This commit is contained in:
@@ -151,6 +151,7 @@ impl Client {
|
||||
};
|
||||
|
||||
if BASIC_CONFIG.online_mode {
|
||||
// Online mode auth
|
||||
match self
|
||||
.authenticate(server, &shared_secret, &profile.name)
|
||||
.await
|
||||
@@ -163,6 +164,21 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
// Don't allow duplicate UUIDs
|
||||
if let Some(online_player) = &server.get_player_by_uuid(profile.id).await {
|
||||
log::debug!("Player (IP '{}', username '{}') tried to log in with the same UUID ('{}') as an online player (IP '{}', username '{}')", &self.address.lock().await.to_string(), &profile.name, &profile.id.to_string(), &online_player.client.address.lock().await.to_string(), &online_player.gameprofile.name);
|
||||
self.kick("You are already connected to this server").await;
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't allow a duplicate username
|
||||
if let Some(online_player) = &server.get_player_by_name(&profile.name).await {
|
||||
log::debug!("A player (IP '{}', attempted username '{}') tried to log in with the same username as an online player (UUID '{}', IP '{}', username '{}')", &self.address.lock().await.to_string(), &profile.name, &profile.id.to_string(), &online_player.client.address.lock().await.to_string(), &online_player.gameprofile.name);
|
||||
self.kick("A player with this username is already connected")
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
if ADVANCED_CONFIG.packet_compression.enabled {
|
||||
self.enable_compression().await;
|
||||
}
|
||||
@@ -190,8 +206,8 @@ impl Client {
|
||||
if let Some(auth_client) = &server.auth_client {
|
||||
let hash = server.digest_secret(shared_secret);
|
||||
let ip = self.address.lock().await.ip();
|
||||
|
||||
let profile = authentication::authenticate(username, &hash, &ip, auth_client).await?;
|
||||
|
||||
// Check if player should join
|
||||
if let Some(actions) = &profile.profile_actions {
|
||||
if ADVANCED_CONFIG
|
||||
|
||||
@@ -26,7 +26,7 @@ pub fn consume_arg_player(
|
||||
name => {
|
||||
// todo: implement any other player than sender
|
||||
for world in &server.worlds {
|
||||
if world.get_player_by_name(name).is_some() {
|
||||
if world.get_player_by_name_blocking(name).is_some() {
|
||||
return Ok(name.into());
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ pub fn parse_arg_player(
|
||||
"@a" | "@e" => todo!(), // todo: implement all players target selector
|
||||
name => {
|
||||
for world in &server.worlds {
|
||||
if let Some(player) = world.get_player_by_name(name) {
|
||||
if let Some(player) = world.get_player_by_name_blocking(name) {
|
||||
return Ok(player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,10 +143,20 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
/// Searches every world for a player by name
|
||||
pub fn get_player_by_name(&self, name: &str) -> Option<Arc<Player>> {
|
||||
/// Searches every world for a player by username
|
||||
pub async fn get_player_by_name(&self, name: &str) -> Option<Arc<Player>> {
|
||||
for world in &self.worlds {
|
||||
if let Some(player) = world.get_player_by_name(name) {
|
||||
if let Some(player) = world.get_player_by_name(name).await {
|
||||
return Some(player);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Searches every world for a player by UUID
|
||||
pub async fn get_player_by_uuid(&self, id: uuid::Uuid) -> Option<Arc<Player>> {
|
||||
for world in &self.worlds {
|
||||
if let Some(player) = world.get_player_by_uuid(id).await {
|
||||
return Some(player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,9 +361,19 @@ impl World {
|
||||
None
|
||||
}
|
||||
|
||||
/// Gets a Player by name
|
||||
pub fn get_player_by_name(&self, name: &str) -> Option<Arc<Player>> {
|
||||
// not sure of blocking lock
|
||||
/// Gets a Player by username
|
||||
pub async fn get_player_by_name(&self, name: &str) -> Option<Arc<Player>> {
|
||||
for player in self.current_players.lock().await.values() {
|
||||
if player.gameprofile.name == name {
|
||||
return Some(player.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Gets a Player by username (Blocking - Legacy use only)
|
||||
pub fn get_player_by_name_blocking(&self, name: &str) -> Option<Arc<Player>> {
|
||||
// TODO: Remove this blocking functions when commands are async.
|
||||
for player in self.current_players.blocking_lock().values() {
|
||||
if player.gameprofile.name == name {
|
||||
return Some(player.clone());
|
||||
@@ -372,6 +382,11 @@ impl World {
|
||||
None
|
||||
}
|
||||
|
||||
/// Gets a Player by UUID
|
||||
pub async fn get_player_by_uuid(&self, id: uuid::Uuid) -> Option<Arc<Player>> {
|
||||
return self.current_players.lock().await.get(&id).cloned();
|
||||
}
|
||||
|
||||
pub async fn add_player(&self, uuid: uuid::Uuid, player: Arc<Player>) {
|
||||
self.current_players.lock().await.insert(uuid, player);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user