wicked-waifus-rs/gateway-server/src/handler/client_request_handler.rs
xavo95 96d1994fe2 Support for Nested combat messages and JSPatch Notify from files (#2)
Reviewed-on: Shorekeeper/Shorekeeper#2
Co-authored-by: xavo95 <xavo95@xeondev.com>
Co-committed-by: xavo95 <xavo95@xeondev.com>
2024-09-14 09:05:17 +00:00

215 lines
6.6 KiB
Rust

use common::time_util;
use shorekeeper_database::{models, query_as};
use shorekeeper_network::ServiceMessage;
use shorekeeper_protocol::{
message::Message, CreateCharacterRequest, CreatePlayerDataRequest, EnterGameRequest, ErrorCode,
HeartbeatRequest, HeartbeatResponse, LoginRequest, LoginResponse, MessageID, ProtoKeyRequest,
ProtoKeyResponse, Protobuf, StartPlayerSessionRequest,
};
use crate::session::Session;
use super::game_server_connection;
macro_rules! requests {
($($name:ident;)*) => {
async fn handle_request_impl(session: &mut crate::session::Session, mut msg: Message) -> Result<(), crate::session::SessionError> {
use ::shorekeeper_protocol::{MessageID, Protobuf};
::paste::paste! {
match msg.get_message_id() {
$(
::shorekeeper_protocol::[<$name Request>]::MESSAGE_ID => {
let request = ::shorekeeper_protocol::[<$name Request>]::decode(&*msg.remove_payload())?;
let mut response = ::shorekeeper_protocol::[<$name Response>]::default();
[<on_ $name:snake _request>](session, request, &mut response).await;
session.send_response(response, msg.get_rpc_id()).await?;
},
)*
unhandled => ::tracing::warn!("can't find handler for request with message_id={unhandled}")
}
}
Ok(())
}
};
}
requests! {
ProtoKey;
Login;
Heartbeat;
}
pub async fn handle_request(
session: &mut Session,
msg: Message,
) -> Result<(), crate::session::SessionError> {
match msg.get_message_id() {
CreateCharacterRequest::MESSAGE_ID => on_create_character_request(session, msg).await,
EnterGameRequest::MESSAGE_ID => on_enter_game_request(session, msg).await,
_ => handle_request_impl(session, msg).await,
}
}
async fn on_proto_key_request(
session: &mut Session,
request: ProtoKeyRequest,
response: &mut ProtoKeyResponse,
) {
tracing::debug!("on_proto_key_request: {request:?}");
let Ok(key) = session.generate_session_key() else {
response.error_code = ErrorCode::InternalError.into();
return;
};
response.r#type = 2;
response.key = key;
}
async fn on_login_request(
session: &mut Session,
request: LoginRequest,
response: &mut LoginResponse,
) {
tracing::debug!("on_login: {request:?}");
let Some(account): Option<models::AccountRow> =
query_as("SELECT * FROM t_user_account WHERE user_id = ($1)")
.bind(&request.account)
.fetch_optional(session.database.as_ref())
.await
.inspect_err(|err| tracing::error!("failed to fetch account: {err}"))
.ok()
.flatten()
else {
tracing::debug!("login: account '{}' not found", &request.account);
response.code = ErrorCode::InvalidUserId.into();
return;
};
// TODO: token check. We don't have proper sdk/login system yet.
let last_login_trace_id = account.last_login_trace_id.unwrap_or_default();
if last_login_trace_id != request.login_trace_id {
tracing::debug!(
"login: trace_id mismatch! Server: {}, client: {}, user_id: {}",
&last_login_trace_id,
&request.login_trace_id,
&account.user_id
);
response.code = ErrorCode::LoginRetry.into();
return;
}
if let Some(ban_time_stamp) = account.ban_time_stamp {
let cur_time_stamp = time_util::unix_timestamp() as i64;
if ban_time_stamp > cur_time_stamp {
tracing::debug!(
"login: account with id {} is banned until {} ({} seconds remaining)",
&account.user_id,
ban_time_stamp,
ban_time_stamp - cur_time_stamp
);
response.code = ErrorCode::AccountIsBlocked.into();
return;
}
}
session.user_id = Some(request.account.clone());
let player_id = query_as("SELECT * from t_user_uid WHERE user_id = ($1)")
.bind(&request.account)
.fetch_optional(session.database.as_ref())
.await
.inspect_err(|err| tracing::error!("failed to fetch player_id: {err}"))
.ok()
.flatten()
.map(|u: models::UserUidRow| u.player_id);
let Some(player_id) = player_id else {
tracing::debug!(
"login: first login on account {}, awaiting create character request",
&account.user_id
);
response.code = ErrorCode::HaveNoCharacter.into();
return;
};
session.player_id = Some(player_id);
response.code = ErrorCode::Success.into();
response.timestamp = time_util::unix_timestamp_ms() as i64;
tracing::info!(
"login success, user_id: {}, player_id: {}",
&request.account,
player_id
);
}
async fn on_heartbeat_request(
session: &mut Session,
_: HeartbeatRequest,
_: &mut HeartbeatResponse,
) {
session.update_last_heartbeat_time();
}
async fn on_enter_game_request(
session: &mut Session,
message: Message,
) -> Result<(), crate::session::SessionError> {
let Some(player_id) = session.player_id else {
tracing::debug!(
"EnterGameRequest: player_id is None, session_id: {}",
session.get_conv_id()
);
return Ok(());
};
game_server_connection::push_message(ServiceMessage {
src_service_id: 0,
rpc_id: message.get_rpc_id(),
message_id: StartPlayerSessionRequest::MESSAGE_ID,
data: StartPlayerSessionRequest {
gateway_session_id: session.get_conv_id(),
player_id,
}
.encode_to_vec()
.into_boxed_slice(),
})
.await;
Ok(())
}
async fn on_create_character_request(
session: &mut Session,
mut message: Message,
) -> Result<(), crate::session::SessionError> {
let client_request = CreateCharacterRequest::decode(message.remove_payload().as_ref())?;
let Some(user_id) = session.user_id.clone() else {
tracing::error!("create_character: session.user_id is None");
return Ok(());
};
let create_player_request = CreatePlayerDataRequest {
session_id: session.get_conv_id(),
user_id,
sex: client_request.sex,
name: client_request.name,
};
game_server_connection::push_message(ServiceMessage {
src_service_id: 0,
rpc_id: message.get_rpc_id(),
message_id: CreatePlayerDataRequest::MESSAGE_ID,
data: create_player_request.encode_to_vec().into_boxed_slice(),
})
.await;
Ok(())
}