Networking improvement and player nickname change console command

split TcpStream into read and write halves, so it's possible to recv and send concurrently
implement 'player nickname' command
This commit is contained in:
xeon 2024-07-23 00:04:59 +03:00
parent 9af61cd33f
commit 4b86d62d3f
3 changed files with 66 additions and 10 deletions

View file

@ -79,5 +79,6 @@ impl CommandManager {
commands! {
player::avatar "[player_uid] [avatar_id]" "changes player avatar for main city";
player::nickname "[player_uid] [nickname]" "changes player nickname";
}
}

View file

@ -1,4 +1,5 @@
use data::tables;
use proto::PlayerSyncScNotify;
use crate::ServerState;
@ -37,3 +38,54 @@ pub async fn avatar(
"changed frontend_avatar_id, you have to re-enter main city now"
))
}
pub async fn nickname(
args: &[&str],
state: &ServerState,
) -> Result<String, Box<dyn std::error::Error>> {
const USAGE: &str = "Usage: player nickname [uid] [nickname]";
if args.len() != 2 {
return Ok(USAGE.to_string());
}
let uid = args[0].parse::<u32>()?;
let nickname = args[1].trim();
if !matches!(nickname.len(), (4..=15)) {
return Ok(String::from(
"nickname should contain at least 4 and not more than 15 characters",
));
}
let Some(player_lock) = state.player_mgr.get_player(uid).await else {
return Ok(String::from("player not found"));
};
let (session_id, basic_info) = {
let mut player = player_lock.lock().await;
player.basic_data_model.nick_name = Some(nickname.to_string());
(
player.current_session_id(),
player.basic_data_model.player_basic_info(),
)
};
if let Some(session_id) = session_id {
if let Some(session) = state.session_mgr.get(session_id) {
session
.notify(PlayerSyncScNotify {
basic_info: Some(basic_info),
..Default::default()
})
.await?;
}
} else {
state.player_mgr.save_and_remove(uid).await;
}
Ok(format!(
"successfully changed player {uid} nickname to {nickname}"
))
}

View file

@ -9,8 +9,11 @@ use common::{
use proto::{CmdID, NapMessage, PacketHead};
use tokio::{
io::AsyncWriteExt,
net::TcpStream,
sync::{Mutex, MutexGuard, OnceCell},
net::{
tcp::{OwnedReadHalf, OwnedWriteHalf},
TcpStream,
},
sync::{Mutex, OnceCell},
};
use crate::{
@ -30,7 +33,8 @@ static SECRET_KEY: LazyLock<MhyXorpad> = LazyLock::new(|| {
pub struct NetSession {
id: u64,
stream: Mutex<TcpStream>,
reader: Mutex<OwnedReadHalf>,
writer: Mutex<OwnedWriteHalf>,
session_key: OnceCell<MhyXorpad>,
packet_id_counter: AtomicU32,
state: AtomicNetSessionState,
@ -75,9 +79,12 @@ pub enum SessionError {
impl NetSession {
pub fn new(id: u64, stream: TcpStream) -> Self {
let (reader, writer) = stream.into_split();
Self {
id,
stream: Mutex::new(stream),
reader: Mutex::new(reader),
writer: Mutex::new(writer),
session_key: OnceCell::new(),
packet_id_counter: AtomicU32::new(0),
state: AtomicNetSessionState::new(NetSessionState::StartEnterGameWorld),
@ -90,7 +97,7 @@ impl NetSession {
let mut last_save_time = util::cur_timestamp();
let result = loop {
let packet = match NetPacket::read(&mut *self.stream().await).await {
let packet = match NetPacket::read(&mut *self.reader.lock().await).await {
Ok(packet) => packet,
Err(DecodeError::IoError(_)) => break Ok(()),
Err(err) => break Err(SessionError::PacketDecode(err)),
@ -195,11 +202,7 @@ impl NetSession {
self.xor_payload(packet.cmd_id, &mut packet.body);
let buf = packet.encode();
self.stream().await.write_all(&buf).await
}
async fn stream(&self) -> MutexGuard<'_, TcpStream> {
self.stream.lock().await
self.writer.lock().await.write_all(&buf).await
}
pub fn id(&self) -> u64 {