From 0e0a78abaab683890cd18f898205295fc7f2406c Mon Sep 17 00:00:00 2001 From: xeon Date: Sat, 25 May 2024 13:07:56 +0300 Subject: [PATCH] Proper character HP in battle, fix some ordering issues --- Cargo.toml | 2 + common/src/util.rs | 16 +++ gameserver/Cargo.toml | 1 + gameserver/src/game/manager/dungeon.rs | 35 +++++- gameserver/src/game/manager/unique_id.rs | 16 +-- gameserver/src/net/gateway.rs | 2 +- .../src/net/handlers/beginner_procedure.rs | 12 +- gameserver/src/net/handlers/hollow.rs | 29 ++++- gameserver/src/net/handlers/world.rs | 2 +- gameserver/src/net/session.rs | 31 ++--- protocol/src/enums.rs | 112 ++++++++++++++++-- protocol/src/polymorphic.rs | 24 ++++ qwer/Cargo.toml | 1 + qwer/src/collection.rs | 6 +- qwer/src/fastoct.rs | 9 +- 15 files changed, 234 insertions(+), 64 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f6c97d7..1e8c8c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,8 @@ tracing-subscriber = { version = "0.3.18", features = [ ] } tracing-bunyan-formatter = "0.3.9" +itertools = "0.13.0" + common = { version = "0.1.0", path = "common" } protocol = { version = "0.1.0", path = "protocol" } qwer = { version = "0.1.0", path = "qwer", features = ["full"] } diff --git a/common/src/util.rs b/common/src/util.rs index 685ebdd..71b4ef8 100644 --- a/common/src/util.rs +++ b/common/src/util.rs @@ -1,3 +1,5 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + pub fn load_or_create_config(path: &str, defaults: &str) -> String { std::fs::read_to_string(path).map_or_else( |_| { @@ -7,3 +9,17 @@ pub fn load_or_create_config(path: &str, defaults: &str) -> String { |data| data, ) } + +pub fn cur_timestamp_ms() -> i64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as i64 +} + +pub fn cur_timestamp_seconds() -> i64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() as i64 +} diff --git a/gameserver/Cargo.toml b/gameserver/Cargo.toml index a31462e..56267c4 100644 --- a/gameserver/Cargo.toml +++ b/gameserver/Cargo.toml @@ -13,6 +13,7 @@ hex.workspace = true lazy_static.workspace = true paste.workspace = true sysinfo.workspace = true +itertools.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/gameserver/src/game/manager/dungeon.rs b/gameserver/src/game/manager/dungeon.rs index cd52f62..f0c9085 100644 --- a/gameserver/src/game/manager/dungeon.rs +++ b/gameserver/src/game/manager/dungeon.rs @@ -92,21 +92,25 @@ impl DungeonManager { }, }; + let add_dungeon = dungeon_info.clone(); + let add_scene = scene_info.clone(); + let cur_scene_uid = scene_info.get_uid(); + Ok(PlayerOperationResult::with_changes( ptc_enter_scene, PlayerInfo { dungeon_collection: Some(DungeonCollection { dungeons: Some(PropertyHashMap::Modify { - to_add: vec![(dungeon_info.uid, dungeon_info.clone())], + to_add: vec![(add_dungeon.uid, add_dungeon)], to_remove: Vec::new(), }), scenes: Some(PropertyHashMap::Modify { - to_add: vec![(scene_info.get_uid(), scene_info.clone())], + to_add: vec![(add_scene.get_uid(), add_scene)], to_remove: Vec::new(), }), ..Default::default() }), - scene_uid: Some(scene_info.get_uid()), + scene_uid: Some(cur_scene_uid), ..Default::default() }, )) @@ -259,6 +263,8 @@ impl DungeonManager { .get_mut(&cur_scene_uid) .unwrap(); + let dungeon_uid = hollow_scene.get_dungeon_uid(); + if let SceneInfo::Hollow { hollow_system_ui_state, .. @@ -271,12 +277,33 @@ impl DungeonManager { hollow_system_ui_state.insert(HollowSystemType::Menu, HollowSystemUIState::Close); } + let add_scene = hollow_scene.clone(); + + let quest_data = player.quest_data.as_ref().unwrap(); + let mut dungeon_quest = quest_data + .quests + .as_ref() + .unwrap() + .get(&dungeon_uid, &1001000101) + .unwrap() + .clone(); + dungeon_quest.set_progress(1); + dungeon_quest.set_finished_count(1); + dungeon_quest.set_state(QuestState::Finished); + PlayerOperationResult::with_changes( cur_scene_uid, PlayerInfo { dungeon_collection: Some(DungeonCollection { scenes: Some(PropertyHashMap::Modify { - to_add: vec![(cur_scene_uid, hollow_scene.clone())], + to_add: vec![(cur_scene_uid, add_scene)], + to_remove: Vec::new(), + }), + ..Default::default() + }), + quest_data: Some(QuestData { + quests: Some(PropertyDoubleKeyHashMap::Modify { + to_add: vec![(dungeon_uid, 1001000101, dungeon_quest)], to_remove: Vec::new(), }), ..Default::default() diff --git a/gameserver/src/game/manager/unique_id.rs b/gameserver/src/game/manager/unique_id.rs index 4a5917d..2fbc23d 100644 --- a/gameserver/src/game/manager/unique_id.rs +++ b/gameserver/src/game/manager/unique_id.rs @@ -1,22 +1,14 @@ use std::sync::atomic::{AtomicU64, Ordering}; -const BASE_UID: u64 = 1000000; - -pub struct UniqueIDManager { - uid_counter: AtomicU64, -} +pub struct UniqueIDManager(AtomicU64); impl UniqueIDManager { + const BASE_UID: u64 = 1000000; pub const fn new() -> Self { - Self { - uid_counter: AtomicU64::new(BASE_UID), - } + Self(AtomicU64::new(Self::BASE_UID)) } pub fn next(&self) -> u64 { - let uid = self.uid_counter.load(Ordering::SeqCst) + 1; - self.uid_counter.store(uid, Ordering::SeqCst); - - uid + self.0.fetch_add(1, Ordering::SeqCst) + 1 } } diff --git a/gameserver/src/net/gateway.rs b/gameserver/src/net/gateway.rs index 325b28e..b37dd5c 100644 --- a/gameserver/src/net/gateway.rs +++ b/gameserver/src/net/gateway.rs @@ -17,7 +17,7 @@ pub async fn listen(bind_addr: &str) -> Result<()> { tracing::info!("New session from {client_addr}"); - let mut session = NetworkSession::new(client_socket, client_addr); + let mut session = NetworkSession::new(client_socket); tokio::spawn( async move { log_error!( diff --git a/gameserver/src/net/handlers/beginner_procedure.rs b/gameserver/src/net/handlers/beginner_procedure.rs index 36b6837..c3798b2 100644 --- a/gameserver/src/net/handlers/beginner_procedure.rs +++ b/gameserver/src/net/handlers/beginner_procedure.rs @@ -1,4 +1,4 @@ -use std::time::{SystemTime, UNIX_EPOCH}; +use common::util; use super::*; @@ -28,15 +28,11 @@ pub async fn on_rpc_beginnerbattle_begin_arg( session: &NetworkSession, arg: &RpcBeginnerbattleBeginArg, ) -> Result<()> { - let cur_timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - session .send_rpc_ret(RpcBeginnerbattleBeginRet::new(format!( - "{cur_timestamp}-{}", - arg.battle_id + "{}-{}", + arg.battle_id, + util::cur_timestamp_seconds() ))) .await } diff --git a/gameserver/src/net/handlers/hollow.rs b/gameserver/src/net/handlers/hollow.rs index 50183ba..4f19613 100644 --- a/gameserver/src/net/handlers/hollow.rs +++ b/gameserver/src/net/handlers/hollow.rs @@ -1,6 +1,9 @@ +use itertools::Itertools; use qwer::{phashmap, phashset, PropertyHashMap, PropertyHashSet}; use std::collections::HashMap; +use crate::data; + use super::*; pub async fn on_rpc_hollow_move_arg( @@ -59,7 +62,7 @@ pub async fn on_rpc_end_battle_arg(session: &NetworkSession, arg: &RpcEndBattleA quest_id: 1001000101, success: true, reward_items: phashmap![], - statistics: phashmap![], + statistics: phashmap![(QuestStatisticsType::ArrivedLevel, 1)], }; session @@ -211,12 +214,31 @@ pub async fn on_rpc_start_hollow_quest_arg( ) -> Result<()> { tracing::info!("start hollow quest: {arg:?}"); + // Set avatar HP properties for (_idx, avatar_uid) in &arg.avatar_map { - // Set character HP + let player_info = session.get_player(); + let items = player_info.items.as_ref().unwrap(); + let Some(ItemInfo::Avatar { id, .. }) = items + .iter() + .find(|(uid, _)| **uid == *avatar_uid) + .map(|(_, item)| item) + else { + return session + .send_rpc_ret(RpcStartHollowQuestRet::error( + ErrorCode::ObjectNotExist, + Vec::new(), + )) + .await; + }; + + let avatar_config = data::iter_avatar_config_collection() + .find(|c| c.id == *id) + .unwrap(); + let update_properties = PtcPropertyChangedArg { scene_unit_uid: *avatar_uid, is_partial: true, - changed_properties: phashmap![(1, 500), (111, 500)], + changed_properties: phashmap![(1, avatar_config.hp), (111, avatar_config.hp)], }; session.send_rpc_arg(129, &update_properties).await?; @@ -227,6 +249,7 @@ pub async fn on_rpc_start_hollow_quest_arg( let avatars = arg .avatar_map .iter() + .sorted_by_key(|kv| kv.0) .map(|(_idx, uid)| *uid) .collect::>(); let (dungeon_uid, scene_uid) = *dungeon_manager diff --git a/gameserver/src/net/handlers/world.rs b/gameserver/src/net/handlers/world.rs index 1a45c3a..188aef0 100644 --- a/gameserver/src/net/handlers/world.rs +++ b/gameserver/src/net/handlers/world.rs @@ -227,7 +227,7 @@ pub async fn on_rpc_enter_world_arg( let quest_manager = session.context.quest_manager.borrow(); quest_manager.add_world_quest(QuestInfo::MainCity { - id: 10020001, + id: 10020002, finished_count: 0, collection_uid: 0, progress: 0, diff --git a/gameserver/src/net/session.rs b/gameserver/src/net/session.rs index 80d377b..40a8267 100644 --- a/gameserver/src/net/session.rs +++ b/gameserver/src/net/session.rs @@ -3,7 +3,6 @@ use atomic_refcell::{AtomicRef, AtomicRefMut}; use protocol::*; use qwer::{OctData, ProtocolHeader}; use std::io::Cursor; -use std::net::SocketAddr; use std::sync::Arc; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; @@ -16,25 +15,27 @@ use super::{packet::PacketHandler, Packet, RequestBody, ResponseBody}; pub struct NetworkSession { client_socket: Arc>, - client_addr: SocketAddr, cur_rpc_uid: u64, pub ns_prop_mgr: net_stream::PropertyManager, pub context: GameContext, } impl NetworkSession { - pub fn new(client_socket: TcpStream, client_addr: SocketAddr) -> Self { + pub fn new(client_socket: TcpStream) -> Self { let ns_prop_mgr = net_stream::PropertyManager::default(); Self { client_socket: Arc::new(Mutex::new(client_socket)), - client_addr, cur_rpc_uid: 0, context: GameContext::new(ns_prop_mgr.player_info.clone()), ns_prop_mgr, } } + async fn client_socket(&self) -> MutexGuard<'_, TcpStream> { + self.client_socket.lock().await + } + pub fn get_player_uid(&self) -> u64 { self.get_player().uid.unwrap() } @@ -55,20 +56,10 @@ impl NetworkSession { self.ns_prop_mgr.player_info.try_borrow_mut().unwrap() } - pub async fn client_socket(&self) -> MutexGuard<'_, TcpStream> { - self.client_socket.lock().await - } - pub async fn run(&mut self) -> Result<()> { - let channel_id = match self.read_handshake().await { - Ok(channel_id) => channel_id, - Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(()), - Err(e) => return Err(e.into()), + let Some(_channel_id) = self.read_handshake().await? else { + return Ok(()); }; - tracing::info!( - "Session ({}) bound to channel {channel_id}", - self.client_addr - ); loop { let packet = match Packet::read(&mut *self.client_socket().await).await { @@ -83,8 +74,12 @@ impl NetworkSession { } } - async fn read_handshake(&mut self) -> Result { - self.client_socket().await.read_u16_le().await + async fn read_handshake(&mut self) -> Result> { + match self.client_socket().await.read_u16_le().await { + Ok(channel_id) => Ok(Some(channel_id)), + Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => Ok(None), + Err(e) => return Err(e.into()), + } } pub async fn send_rpc_ret(&self, data: impl OctData) -> Result<()> { diff --git a/protocol/src/enums.rs b/protocol/src/enums.rs index 5c55b9c..aa2eb70 100644 --- a/protocol/src/enums.rs +++ b/protocol/src/enums.rs @@ -5,9 +5,101 @@ use super::*; pub enum ErrorCode { Fail = -1, Success = 0, + Timeout = 1, + EntityNotExist = 2, + InvalidVersion = 3, + InvalidTemplateID = 4, + InvalidConfigVersion = 5, + ObjectNotExist = 100, + ConfigError = 101, + NoEnoughRes = 102, + NoEnoughEXP = 103, + NoEnoughItem = 104, + NoEnoughMaterials = 105, + NoEnoughGoods = 106, + NoEnoughCurrency = 107, + CannotFindGoods = 108, + AvatarMaxLevelLimit = 109, + AvatarMaxStarLimit = 110, + AvatarMaxAdvanceLevelLimit = 111, + EquipMaxLevelLimit = 112, + EquipMaxStarLimit = 113, + SceneAlreadyDestroyed = 114, + StageAlreadyDestroyed = 115, + ErrorSrcPosition = 116, + ErrorGraph = 117, + NodeFinished = 118, + NodeRunFailure = 119, + UnknownAvatarSkill = 120, + AvatarSkillMaxLevelLimit = 121, + AvatarTalentMaxLevelLimit = 122, + AvatarStarNotEnough = 123, + InvalidActionMovePath = 124, + ActionIDNotExist = 125, + NickNameMaxLength = 126, + NickNameIllegal = 127, + Ban = 128, + RepeatedLogin = 129, + FuncNotOpen = 130, + TokenError = 131, + PlayerNotExist = 132, + InvalidParam = 133, + ItemBeOccupy = 134, + ItemBeLock = 135, + EquipGachaClose = 136, + InvalidQuestState = 137, + QuestMaxFinishCnt = 138, + NoEnoughTimes = 139, + BattleReportLimit = 140, + BattleReportInvalid = 141, + MaxLevelLimit = 142, + MaxStarLimit = 143, + MaxRefineLimit = 144, + AlreadyGet = 145, + RepeatedModName = 146, + VHSStoreUnlock = 147, + VHSStoreAlreadSlot = 148, + VHSStoreSlotNumErr = 149, + FuncNotUnlock = 150, + VHSStoreRamenContinueEat = 151, + VHSStoreAlreadyUnlock = 152, + AFKGamePlayClose = 153, + InitiativeItemUnlock = 154, + InitiativeItemLevel = 155, + PrepareAvatarsFail = 156, + AlreadyAFK = 157, + HollowDoEvtListNotEmpty = 158, + HollowEvtNotCompleted = 159, + HollowMoveFail = 160, + BuyNumOverflow = 161, + PackgeOverflow = 162, + ReplacePkg = 163, + FileLenghCheckFaild = 164, + HashCheckFaild = 165, + DiskNotEnough = 166, + NotReachable = 167, + ServerException = 168, + RequestException = 169, + OherDownLoadError = 170, + QuestProgressNotEnough = 171, + ConditionComplete = 172, + ConditionNoComplete = 173, + PayErrCode0 = 174, + PayErrCode1 = 175, + PayErrCode2 = 176, + PayErrCode3 = 177, + PayErrCode4 = 178, + PayErrCode5 = 179, + NoWhite = 180, + NoWhiteDevice = 181, + NoWhiteIp = 182, + SdkError = 183, + StopServer = 184, + AccountServerError = 185, + CloseServer = 187, } -#[derive(OctData, Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(OctData, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(i16)] pub enum HollowQuestType { Common = 0, @@ -48,7 +140,7 @@ pub enum FightRanking { S = 5, } -#[derive(OctData, Hash, Clone, Debug, PartialEq, Eq)] +#[derive(OctData, Hash, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] #[repr(i16)] pub enum BattleRewardType { Client = 1, @@ -79,7 +171,7 @@ pub enum HollowBattleEventType { LevelFin = 5, } -#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(OctData, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(i16)] pub enum QuestType { ArchiveFile = 1, @@ -113,7 +205,7 @@ pub enum ActionState { Error = 3, } -#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(OctData, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum DungeonContentDropPoolType { Card = 0, @@ -143,14 +235,14 @@ pub enum UIType { Archive = 3, } -#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(OctData, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(i16)] pub enum ACTPerformShowMoment { Begin = 0, End = 1, } -#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(OctData, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(i16)] pub enum HollowSystemType { Card = 1, @@ -170,7 +262,7 @@ pub enum HollowSystemUIState { Brighten = 2, } -#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(OctData, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(i16)] pub enum HollowShopType { All = 0, @@ -203,7 +295,7 @@ pub enum WeatherType { Thunder = 5, } -#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(OctData, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(i16)] pub enum PropertyType { Hp = 1, @@ -333,7 +425,7 @@ pub enum PropertyType { EnumCount = 10351, } -#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(OctData, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(i16)] pub enum ScenePropertyType { Stamina = 1001, @@ -535,7 +627,7 @@ pub enum QuestState { Finished = 3, } -#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(OctData, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum QuestStatisticsType { ArrivedLevel = 1, diff --git a/protocol/src/polymorphic.rs b/protocol/src/polymorphic.rs index f7db07f..8fb1b4b 100644 --- a/protocol/src/polymorphic.rs +++ b/protocol/src/polymorphic.rs @@ -658,6 +658,30 @@ macro_rules! polymorphic_quest_info { )* } } + + pub fn set_state(&mut self, state: QuestState) { + match self { + $( + $name::$variant { state: ref mut c, .. } => *c = state, + )* + } + } + + pub fn set_progress(&mut self, progress: u16) { + match self { + $( + $name::$variant { progress: ref mut c, .. } => *c = progress, + )* + } + } + + pub fn set_finished_count(&mut self, finished_count: i32) { + match self { + $( + $name::$variant { finished_count: ref mut c, .. } => *c = finished_count, + )* + } + } } }; } diff --git a/qwer/Cargo.toml b/qwer/Cargo.toml index a641050..11cc4f0 100644 --- a/qwer/Cargo.toml +++ b/qwer/Cargo.toml @@ -13,3 +13,4 @@ protocol = [] [dependencies] byteorder.workspace = true qwer-derive.workspace = true +itertools.workspace = true diff --git a/qwer/src/collection.rs b/qwer/src/collection.rs index b7e81a4..05e5db2 100644 --- a/qwer/src/collection.rs +++ b/qwer/src/collection.rs @@ -612,7 +612,7 @@ fn test_dkhashmap_iter() { impl OctData for PropertyHashMap where - K: OctData + Eq + std::hash::Hash, + K: OctData + Eq + Ord + std::hash::Hash, V: OctData, { fn marshal_to(&self, w: &mut W, bt_property_tag: u16) -> Result<()> { @@ -721,8 +721,8 @@ where impl OctData for PropertyDoubleKeyHashMap where - K1: OctData + Eq + std::hash::Hash, - K2: OctData + Eq + std::hash::Hash, + K1: OctData + Eq + Ord + std::hash::Hash, + K2: OctData + Eq + Ord + std::hash::Hash, V: OctData, { fn marshal_to(&self, w: &mut W, bt_property_tag: u16) -> Result<()> { diff --git a/qwer/src/fastoct.rs b/qwer/src/fastoct.rs index ef8e3f6..b9f8465 100644 --- a/qwer/src/fastoct.rs +++ b/qwer/src/fastoct.rs @@ -6,6 +6,7 @@ use std::{ }; use byteorder::{ReadBytesExt, WriteBytesExt}; +use itertools::Itertools; pub use qwer_derive::OctData; @@ -133,12 +134,12 @@ where impl OctData for HashMap where - K: OctData + Eq + Hash, + K: OctData + Eq + Hash + Ord, V: OctData, { default fn marshal_to(&self, w: &mut W, bt_property_tag: u16) -> Result<()> { (self.len() as i32).marshal_to(w, bt_property_tag)?; - for (key, value) in self { + for (key, value) in self.iter().sorted_by_key(|kv| kv.0) { key.marshal_to(w, bt_property_tag)?; value.marshal_to(w, bt_property_tag)?; } @@ -164,8 +165,8 @@ where #[cfg(feature = "collection")] impl OctData for DoubleKeyHashMap where - K1: OctData + Eq + Hash, - K2: OctData + Eq + Hash, + K1: OctData + Eq + Hash + Ord, + K2: OctData + Eq + Hash + Ord, V: OctData, { fn marshal_to(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {