Proper character HP in battle, fix some ordering issues

This commit is contained in:
xeon 2024-05-25 13:07:56 +03:00
parent eb641d67fb
commit 0e0a78abaa
15 changed files with 234 additions and 64 deletions

View file

@ -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"] }

View file

@ -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
}

View file

@ -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

View file

@ -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()

View file

@ -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
}
}

View file

@ -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!(

View file

@ -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
}

View file

@ -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::<Vec<_>>();
let (dungeon_uid, scene_uid) = *dungeon_manager

View file

@ -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,

View file

@ -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<Mutex<TcpStream>>,
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<u16, std::io::Error> {
self.client_socket().await.read_u16_le().await
async fn read_handshake(&mut self) -> Result<Option<u16>> {
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<()> {

View file

@ -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,

View file

@ -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,
)*
}
}
}
};
}

View file

@ -13,3 +13,4 @@ protocol = []
[dependencies]
byteorder.workspace = true
qwer-derive.workspace = true
itertools.workspace = true

View file

@ -612,7 +612,7 @@ fn test_dkhashmap_iter() {
impl<K, V> OctData for PropertyHashMap<K, V>
where
K: OctData + Eq + std::hash::Hash,
K: OctData + Eq + Ord + std::hash::Hash,
V: OctData,
{
fn marshal_to<W: std::io::Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {
@ -721,8 +721,8 @@ where
impl<K1, K2, V> OctData for PropertyDoubleKeyHashMap<K1, K2, V>
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<W: std::io::Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {

View file

@ -6,6 +6,7 @@ use std::{
};
use byteorder::{ReadBytesExt, WriteBytesExt};
use itertools::Itertools;
pub use qwer_derive::OctData;
@ -133,12 +134,12 @@ where
impl<K, V> OctData for HashMap<K, V>
where
K: OctData + Eq + Hash,
K: OctData + Eq + Hash + Ord,
V: OctData,
{
default fn marshal_to<W: Write>(&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<K1, K2, V> OctData for DoubleKeyHashMap<K1, K2, V>
where
K1: OctData + Eq + Hash,
K2: OctData + Eq + Hash,
K1: OctData + Eq + Hash + Ord,
K2: OctData + Eq + Hash + Ord,
V: OctData,
{
fn marshal_to<W: Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {