First push
This commit is contained in:
parent
92a206ba33
commit
6c89b18e7a
60 changed files with 9843 additions and 0 deletions
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
# ---> Rust
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# Visual Studio crap
|
||||
.vs/
|
52
Cargo.toml
Normal file
52
Cargo.toml
Normal file
|
@ -0,0 +1,52 @@
|
|||
[workspace]
|
||||
members = ["gameserver", "protocol", "qwer", "qwer/qwer-derive", "sdkserver"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
ansi_term = "0.12.1"
|
||||
anyhow = "1.0.81"
|
||||
atomic_refcell = "0.1.13"
|
||||
axum = "0.7.4"
|
||||
axum-server = "0.6.0"
|
||||
byteorder = "1.5.0"
|
||||
dirs = "5.0.1"
|
||||
dotenv = "0.15.0"
|
||||
encoding = "0.2.33"
|
||||
env_logger = "0.11.3"
|
||||
heck = "0.5.0"
|
||||
hex = "0.4.3"
|
||||
lazy_static = "1.4.0"
|
||||
leb128 = "0.2.5"
|
||||
paste = "1.0.14"
|
||||
sysinfo = "0.30.7"
|
||||
|
||||
serde = "1.0.197"
|
||||
serde_json = "1.0.114"
|
||||
|
||||
tokio = { version = "1.36.0", features = ["full"] }
|
||||
tokio-util = { version = "0.7.10", features = ["io"] }
|
||||
|
||||
tracing = "0.1.40"
|
||||
tracing-futures = "0.2.5"
|
||||
tracing-log = { version = "0.2.0", features = ["std", "log-tracer"] }
|
||||
tracing-subscriber = { version = "0.3.18", features = [
|
||||
"env-filter",
|
||||
"registry",
|
||||
"std",
|
||||
"tracing",
|
||||
"tracing-log",
|
||||
] }
|
||||
tracing-bunyan-formatter = "0.3.9"
|
||||
|
||||
protocol = { version = "0.1.0", path = "protocol" }
|
||||
qwer = { version = "0.1.0", path = "qwer", features = ["full"] }
|
||||
qwer-derive = { version = "0.1.0", path = "qwer/qwer-derive" }
|
||||
|
||||
[profile.release]
|
||||
strip = true # Automatically strip symbols from the binary.
|
||||
lto = true # Link-time optimization.
|
||||
opt-level = 3 # Optimize for speed.
|
||||
codegen-units = 1 # Maximum size reduction optimizations.
|
1
gameserver/.env
Normal file
1
gameserver/.env
Normal file
|
@ -0,0 +1 @@
|
|||
SKIP_TUTORIAL=0
|
35
gameserver/Cargo.toml
Normal file
35
gameserver/Cargo.toml
Normal file
|
@ -0,0 +1,35 @@
|
|||
[package]
|
||||
name = "gameserver"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
ansi_term.workspace = true
|
||||
anyhow.workspace = true
|
||||
atomic_refcell.workspace = true
|
||||
dirs.workspace = true
|
||||
dotenv.workspace = true
|
||||
env_logger.workspace = true
|
||||
hex.workspace = true
|
||||
lazy_static.workspace = true
|
||||
paste.workspace = true
|
||||
sysinfo.workspace = true
|
||||
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
tokio.workspace = true
|
||||
tokio-util.workspace = true
|
||||
|
||||
tracing.workspace = true
|
||||
tracing-futures.workspace = true
|
||||
tracing-log.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing-bunyan-formatter.workspace = true
|
||||
|
||||
protocol.workspace = true
|
||||
qwer.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "nap-gameserver"
|
||||
path = "src/main.rs"
|
1
gameserver/EventGraphCollection.json
Normal file
1
gameserver/EventGraphCollection.json
Normal file
File diff suppressed because one or more lines are too long
97
gameserver/src/game/context.rs
Normal file
97
gameserver/src/game/context.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use protocol::{PlayerInfo, PtcPlayerInfoChangedArg};
|
||||
|
||||
use crate::net::NetworkSession;
|
||||
|
||||
use super::manager::{
|
||||
DungeonManager, HollowGridManager, ItemManager, QuestManager, SceneUnitManager,
|
||||
UniqueIDManager, UnlockManager, YorozuyaQuestManager,
|
||||
};
|
||||
|
||||
pub struct GameContext {
|
||||
pub player: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
pub uid_manager: Arc<AtomicRefCell<UniqueIDManager>>,
|
||||
pub item_manager: Arc<AtomicRefCell<ItemManager>>,
|
||||
pub dungeon_manager: Arc<AtomicRefCell<DungeonManager>>,
|
||||
pub quest_manager: Arc<AtomicRefCell<QuestManager>>,
|
||||
pub scene_unit_manager: Arc<AtomicRefCell<SceneUnitManager>>,
|
||||
pub hollow_grid_manager: Arc<AtomicRefCell<HollowGridManager>>,
|
||||
pub unlock_manager: Arc<AtomicRefCell<UnlockManager>>,
|
||||
pub yorozuya_quest_manager: Arc<AtomicRefCell<YorozuyaQuestManager>>,
|
||||
}
|
||||
|
||||
impl GameContext {
|
||||
pub fn new(player: Arc<AtomicRefCell<PlayerInfo>>) -> Self {
|
||||
let uid_manager = Arc::new(AtomicRefCell::new(UniqueIDManager::new()));
|
||||
|
||||
Self {
|
||||
player: player.clone(),
|
||||
uid_manager: uid_manager.clone(),
|
||||
item_manager: Arc::new(AtomicRefCell::new(ItemManager::new(
|
||||
uid_manager.clone(),
|
||||
player.clone(),
|
||||
))),
|
||||
dungeon_manager: Arc::new(AtomicRefCell::new(DungeonManager::new(
|
||||
uid_manager.clone(),
|
||||
player.clone(),
|
||||
))),
|
||||
quest_manager: Arc::new(AtomicRefCell::new(QuestManager::new(
|
||||
uid_manager.clone(),
|
||||
player.clone(),
|
||||
))),
|
||||
scene_unit_manager: Arc::new(AtomicRefCell::new(SceneUnitManager::new(uid_manager))),
|
||||
hollow_grid_manager: Arc::new(AtomicRefCell::new(HollowGridManager::new(
|
||||
player.clone(),
|
||||
))),
|
||||
unlock_manager: Arc::new(AtomicRefCell::new(UnlockManager::new(player.clone()))),
|
||||
yorozuya_quest_manager: Arc::new(AtomicRefCell::new(YorozuyaQuestManager::new(player))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PlayerOperationResult<T>
|
||||
where
|
||||
T: Send + Sync,
|
||||
{
|
||||
result: T,
|
||||
player_info_changes: Option<PlayerInfo>,
|
||||
}
|
||||
|
||||
impl<T> PlayerOperationResult<T>
|
||||
where
|
||||
T: Send + Sync,
|
||||
{
|
||||
pub const fn unwrap(&self) -> &T {
|
||||
&self.result
|
||||
}
|
||||
|
||||
pub async fn send_changes(&mut self, session: &NetworkSession) -> Result<&T> {
|
||||
if self.player_info_changes.is_some() {
|
||||
let ptc_player_info_changed = PtcPlayerInfoChangedArg {
|
||||
player_uid: session.get_player_uid(),
|
||||
player_info: self.player_info_changes.take().unwrap(),
|
||||
};
|
||||
|
||||
session.send_rpc_arg(101, &ptc_player_info_changed).await?;
|
||||
}
|
||||
|
||||
Ok(self.unwrap())
|
||||
}
|
||||
|
||||
pub const fn ret(result: T) -> Self {
|
||||
Self {
|
||||
result,
|
||||
player_info_changes: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn with_changes(result: T, player_info_changes: PlayerInfo) -> Self {
|
||||
Self {
|
||||
result,
|
||||
player_info_changes: Some(player_info_changes),
|
||||
}
|
||||
}
|
||||
}
|
18
gameserver/src/game/data.rs
Normal file
18
gameserver/src/game/data.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use lazy_static::lazy_static;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
pub const EVENT_GRAPH_COLLECTION: &str = include_str!("../../EventGraphCollection.json");
|
||||
|
||||
lazy_static! {
|
||||
static ref EVENT_MAP: Map<String, Value> = {
|
||||
serde_json::from_str::<Value>(EVENT_GRAPH_COLLECTION)
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.clone()
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_event_config_json(id: i32) -> &'static Value {
|
||||
EVENT_MAP.get(&id.to_string()).unwrap()
|
||||
}
|
11
gameserver/src/game/globals.rs
Normal file
11
gameserver/src/game/globals.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use std::env;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
static ref SKIP_TUTORIAL: i32 = env::var("SKIP_TUTORIAL").map_or(0, |v| v.parse().unwrap());
|
||||
}
|
||||
|
||||
pub fn should_skip_tutorial() -> bool {
|
||||
*SKIP_TUTORIAL != 0
|
||||
}
|
780
gameserver/src/game/manager/dungeon.rs
Normal file
780
gameserver/src/game/manager/dungeon.rs
Normal file
|
@ -0,0 +1,780 @@
|
|||
use anyhow::{anyhow, bail, Result};
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use protocol::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::game::{manager::UniqueIDManager, util, PlayerOperationResult};
|
||||
use qwer::{
|
||||
pdkhashmap, phashmap, phashset, PropertyDoubleKeyHashMap, PropertyHashMap, PropertyHashSet,
|
||||
};
|
||||
|
||||
pub struct DungeonManager {
|
||||
uid_mgr: Arc<AtomicRefCell<UniqueIDManager>>,
|
||||
player: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
scene_properties: AtomicRefCell<PropertyDoubleKeyHashMap<u64, u16, i32>>,
|
||||
}
|
||||
|
||||
impl DungeonManager {
|
||||
pub fn new(
|
||||
uid_mgr: Arc<AtomicRefCell<UniqueIDManager>>,
|
||||
player: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
uid_mgr,
|
||||
player,
|
||||
scene_properties: AtomicRefCell::new(pdkhashmap![]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_main_city(&self) -> Result<PlayerOperationResult<PtcEnterSceneArg>> {
|
||||
let (player_uid, scene_position, scene_rotation) = {
|
||||
let player = self.player.borrow();
|
||||
let pos_in_main_city = player.pos_in_main_city.as_ref().unwrap();
|
||||
|
||||
(
|
||||
player.uid.unwrap(),
|
||||
*pos_in_main_city.position.as_ref().unwrap(),
|
||||
*pos_in_main_city.rotation.as_ref().unwrap(),
|
||||
)
|
||||
};
|
||||
|
||||
let mut player = self.player.borrow_mut();
|
||||
let default_scene_uid = player
|
||||
.dungeon_collection
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.default_scene_uid
|
||||
.unwrap();
|
||||
|
||||
player.scene_uid.replace(default_scene_uid);
|
||||
let dungeon_collection = player.dungeon_collection.as_mut().unwrap();
|
||||
|
||||
let scene_info = dungeon_collection
|
||||
.scenes
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_mut(&default_scene_uid)
|
||||
.ok_or_else(|| anyhow!("Scene with uid {default_scene_uid} doesn't exist"))?;
|
||||
|
||||
let dungeon_uid = scene_info.get_dungeon_uid();
|
||||
let dungeon_info = dungeon_collection
|
||||
.dungeons
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_mut(&scene_info.get_dungeon_uid())
|
||||
.ok_or_else(|| anyhow!("Dungeon with uid {dungeon_uid} doesn't exist"))?;
|
||||
|
||||
scene_info.set_entered_times(scene_info.get_entered_times() + 1);
|
||||
dungeon_info.entered_times += 1;
|
||||
|
||||
let ptc_enter_scene = PtcEnterSceneArg {
|
||||
player_uid,
|
||||
scene_uid: default_scene_uid,
|
||||
section_id: scene_info.get_section_id(),
|
||||
open_ui: UIType::Default,
|
||||
condition_config_ids: Vec::new(),
|
||||
transform: Transform {
|
||||
position: scene_position,
|
||||
rotation: scene_rotation,
|
||||
},
|
||||
timestamp: util::cur_timestamp_ms(),
|
||||
camera_x: 0,
|
||||
camera_y: 6000,
|
||||
entered_times: scene_info.get_entered_times(),
|
||||
ext: match scene_info {
|
||||
SceneInfo::Hall { .. } => SceneTableExt::Hall {
|
||||
event_graphs_info: EventGraphsInfo {
|
||||
default_event_graph_id: 0,
|
||||
event_graphs_info: phashmap![],
|
||||
},
|
||||
},
|
||||
_ => bail!("Unexpected main city scene type"),
|
||||
},
|
||||
};
|
||||
|
||||
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_remove: Vec::new(),
|
||||
}),
|
||||
scenes: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(scene_info.get_uid(), scene_info.clone())],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
scene_uid: Some(scene_info.get_uid()),
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub fn enter_scene_section(
|
||||
&self,
|
||||
scene_uid: u64,
|
||||
section_id: i32,
|
||||
) -> PlayerOperationResult<PtcEnterSectionArg> {
|
||||
let mut player = self.player.borrow_mut();
|
||||
let scene_info = player
|
||||
.dungeon_collection
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scenes
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_mut(&scene_uid)
|
||||
.unwrap();
|
||||
scene_info.set_section_id(section_id);
|
||||
|
||||
PlayerOperationResult::with_changes(
|
||||
PtcEnterSectionArg { section_id },
|
||||
PlayerInfo {
|
||||
dungeon_collection: Some(DungeonCollection {
|
||||
scenes: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(scene_uid, scene_info.clone())],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn enter_scene(&self, scene_uid: u64) -> Result<PlayerOperationResult<PtcEnterSceneArg>> {
|
||||
let (player_uid, prev_scene_uid) = {
|
||||
let player = self.player.borrow();
|
||||
|
||||
(
|
||||
*player.uid.as_ref().unwrap(),
|
||||
*player.scene_uid.as_ref().unwrap(),
|
||||
)
|
||||
};
|
||||
|
||||
let mut player = self.player.borrow_mut();
|
||||
player.scene_uid.replace(scene_uid);
|
||||
player.prev_scene_uid.replace(prev_scene_uid);
|
||||
let dungeon_collection = player.dungeon_collection.as_mut().unwrap();
|
||||
|
||||
let scene_info = dungeon_collection
|
||||
.scenes
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_mut(&scene_uid)
|
||||
.ok_or_else(|| anyhow!("Scene with uid {scene_uid} doesn't exist"))?;
|
||||
|
||||
let dungeon_uid = scene_info.get_dungeon_uid();
|
||||
let dungeon_info = dungeon_collection
|
||||
.dungeons
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_mut(&scene_info.get_dungeon_uid())
|
||||
.ok_or_else(|| anyhow!("Dungeon with uid {dungeon_uid} doesn't exist"))?;
|
||||
|
||||
scene_info.set_entered_times(scene_info.get_entered_times() + 1);
|
||||
dungeon_info.entered_times += 1;
|
||||
|
||||
if let SceneInfo::Hollow { sections_info, .. } = scene_info {
|
||||
let section = sections_info.get_mut(&1).unwrap();
|
||||
section.entered_times += 1;
|
||||
}
|
||||
|
||||
let ptc_enter_scene = PtcEnterSceneArg {
|
||||
player_uid,
|
||||
scene_uid,
|
||||
section_id: scene_info.get_section_id(),
|
||||
open_ui: UIType::Default,
|
||||
condition_config_ids: Vec::new(),
|
||||
transform: Transform::default(),
|
||||
timestamp: util::cur_timestamp_ms(),
|
||||
camera_x: 0,
|
||||
camera_y: 6000,
|
||||
entered_times: scene_info.get_entered_times(),
|
||||
ext: match scene_info {
|
||||
SceneInfo::Hall { .. } => SceneTableExt::Hall {
|
||||
event_graphs_info: EventGraphsInfo {
|
||||
default_event_graph_id: 0,
|
||||
event_graphs_info: phashmap![],
|
||||
},
|
||||
},
|
||||
SceneInfo::Fresh { .. } => SceneTableExt::Fresh {
|
||||
event_graphs_info: EventGraphsInfo {
|
||||
default_event_graph_id: 0,
|
||||
event_graphs_info: phashmap![],
|
||||
},
|
||||
},
|
||||
SceneInfo::Hollow { .. } => SceneTableExt::Hollow {
|
||||
event_graphs_info: EventGraphsInfo {
|
||||
default_event_graph_id: 0,
|
||||
event_graphs_info: phashmap![],
|
||||
},
|
||||
grid_random_seed: 0,
|
||||
alter_section_id: 0,
|
||||
},
|
||||
SceneInfo::Fight { .. } => SceneTableExt::Fight {
|
||||
event_graphs_info: EventGraphsInfo {
|
||||
default_event_graph_id: 0,
|
||||
event_graphs_info: phashmap![],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
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_remove: Vec::new(),
|
||||
}),
|
||||
scenes: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(scene_info.get_uid(), scene_info.clone())],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
scene_uid: Some(scene_uid),
|
||||
prev_scene_uid: Some(prev_scene_uid),
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_default_scene_uid(&self) -> u64 {
|
||||
self.player
|
||||
.borrow()
|
||||
.dungeon_collection
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.default_scene_uid
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_default_scene_uid_for_dungeon(&self, dungeon_uid: u64) -> u64 {
|
||||
self.player
|
||||
.borrow()
|
||||
.dungeon_collection
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.dungeons
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(&dungeon_uid)
|
||||
.unwrap()
|
||||
.default_scene_uid
|
||||
}
|
||||
|
||||
pub fn get_cur_scene_uid(&self) -> u64 {
|
||||
self.player.borrow().scene_uid.unwrap()
|
||||
}
|
||||
|
||||
fn add_default_hollow_properties(&self, scene_uid: u64) {
|
||||
let mut props = self.scene_properties.borrow_mut();
|
||||
|
||||
for (sub_key, value) in &[
|
||||
(1001, 0),
|
||||
(1002, 100),
|
||||
(1003, 0),
|
||||
(1004, 0),
|
||||
(1019, 10),
|
||||
(1020, 1),
|
||||
(1005, 0),
|
||||
(1006, 0),
|
||||
(1007, 0),
|
||||
(1008, 0),
|
||||
(1009, 0),
|
||||
(1010, 0),
|
||||
(1011, 0),
|
||||
(1012, 0),
|
||||
(1013, 1),
|
||||
(1014, 1),
|
||||
(1015, 0),
|
||||
(1016, 0),
|
||||
(1017, 4),
|
||||
(1018, 10000),
|
||||
(1021, 1),
|
||||
(1025, 1),
|
||||
(1035, 10000),
|
||||
(1041, 10000),
|
||||
(1042, 10000),
|
||||
(1043, 1),
|
||||
(1044, 1),
|
||||
] {
|
||||
props.insert(scene_uid, *sub_key, *value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn leave_battle(&self) -> Result<PlayerOperationResult<PtcEnterSceneArg>> {
|
||||
let back_scene_uid = self.get_back_scene_uid();
|
||||
|
||||
{
|
||||
let mut player = self.player.borrow_mut();
|
||||
|
||||
let hollow_scene = player
|
||||
.dungeon_collection
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scenes
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_mut(&back_scene_uid)
|
||||
.unwrap();
|
||||
|
||||
if let SceneInfo::Hollow {
|
||||
battle_scene_uid, ..
|
||||
} = hollow_scene
|
||||
{
|
||||
*battle_scene_uid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
self.enter_scene(back_scene_uid)
|
||||
}
|
||||
|
||||
fn get_back_scene_uid(&self) -> u64 {
|
||||
let player = self.player.borrow();
|
||||
let fight_scene_uid = player.scene_uid.as_ref().unwrap();
|
||||
let fight_scene = player
|
||||
.dungeon_collection
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.scenes
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(fight_scene_uid)
|
||||
.unwrap();
|
||||
|
||||
fight_scene.get_back_scene_uid()
|
||||
}
|
||||
|
||||
pub fn enter_battle(&self, scene_uid: u64) -> PlayerOperationResult<PtcEnterSceneArg> {
|
||||
let hollow_scene_uid = *self.player.borrow().scene_uid.as_ref().unwrap();
|
||||
let hollow_scene = self.set_cur_hollow_battle(scene_uid, hollow_scene_uid);
|
||||
let ptc_enter_scene = self.enter_scene(scene_uid).unwrap().unwrap().clone();
|
||||
|
||||
let player = self.player.borrow();
|
||||
let dungeon_collection = player.dungeon_collection.as_ref().unwrap();
|
||||
let fight_scene = dungeon_collection
|
||||
.scenes
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(&scene_uid)
|
||||
.unwrap();
|
||||
|
||||
PlayerOperationResult::with_changes(
|
||||
ptc_enter_scene,
|
||||
PlayerInfo {
|
||||
dungeon_collection: Some(DungeonCollection {
|
||||
scenes: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![
|
||||
(hollow_scene_uid, hollow_scene),
|
||||
(scene_uid, fight_scene.clone()),
|
||||
],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
scene_uid: Some(scene_uid),
|
||||
prev_scene_uid: Some(hollow_scene_uid),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn set_cur_hollow_battle(&self, scene_uid: u64, hollow_scene_uid: u64) -> SceneInfo {
|
||||
let mut player = self.player.borrow_mut();
|
||||
let hollow_scene = player
|
||||
.dungeon_collection
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scenes
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_mut(&hollow_scene_uid)
|
||||
.unwrap();
|
||||
let SceneInfo::Hollow {
|
||||
on_battle_success,
|
||||
battle_scene_uid,
|
||||
..
|
||||
} = hollow_scene
|
||||
else {
|
||||
panic!("Unexpected scene type")
|
||||
};
|
||||
|
||||
*battle_scene_uid = scene_uid;
|
||||
*on_battle_success = String::from("OnEnd");
|
||||
|
||||
hollow_scene.clone()
|
||||
}
|
||||
|
||||
pub fn create_fight(&self, id: i32, hollow_scene_uid: u64) -> PlayerOperationResult<u64> {
|
||||
let mut player = self.player.borrow_mut();
|
||||
let dungeon_collection = player.dungeon_collection.as_mut().unwrap();
|
||||
let scenes = dungeon_collection.scenes.as_mut().unwrap();
|
||||
let hollow_scene = scenes.get_mut(&hollow_scene_uid).unwrap();
|
||||
|
||||
let fight_scene_uid = self.uid_mgr.borrow().next();
|
||||
let fight_scene = SceneInfo::Fight {
|
||||
uid: fight_scene_uid,
|
||||
id,
|
||||
dungeon_uid: hollow_scene.get_dungeon_uid(),
|
||||
end_timestamp: 0,
|
||||
back_scene_uid: hollow_scene_uid,
|
||||
entered_times: 1,
|
||||
section_id: 1,
|
||||
open_ui: UIType::Default,
|
||||
to_be_destroyed: false,
|
||||
camera_x: 0xFFFFFFFF,
|
||||
camera_y: 0xFFFFFFFF,
|
||||
perform_show_progress: phashmap![],
|
||||
end_hollow: false,
|
||||
random_seed: 2281337,
|
||||
};
|
||||
|
||||
scenes.insert(fight_scene_uid, fight_scene.clone());
|
||||
PlayerOperationResult::with_changes(
|
||||
fight_scene_uid,
|
||||
PlayerInfo {
|
||||
dungeon_collection: Some(DungeonCollection {
|
||||
scenes: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(fight_scene_uid, fight_scene)],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_in_tutorial(&self) -> bool {
|
||||
let cur_scene_uid = self.get_cur_scene_uid();
|
||||
|
||||
let player = self.player.borrow();
|
||||
let cur_scene = player
|
||||
.dungeon_collection
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.scenes
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(&cur_scene_uid)
|
||||
.unwrap();
|
||||
|
||||
matches!(cur_scene, SceneInfo::Fresh { .. })
|
||||
}
|
||||
|
||||
pub fn create_hollow(
|
||||
&self,
|
||||
id: i32,
|
||||
world_quest_id: i32,
|
||||
avatar_uids: &[u64],
|
||||
) -> PlayerOperationResult<(u64, u64)> {
|
||||
let back_scene_uid = self.get_default_scene_uid();
|
||||
|
||||
let mut dungeon = self.create_base_dungeon(id, back_scene_uid, world_quest_id);
|
||||
dungeon.hollow_event_version = 526;
|
||||
|
||||
let scene_uid = self.uid_mgr.borrow().next();
|
||||
dungeon.default_scene_uid = scene_uid;
|
||||
dungeon.scene_properties_uid = scene_uid;
|
||||
|
||||
self.add_default_hollow_properties(scene_uid);
|
||||
|
||||
for (index, avatar_uid) in avatar_uids.iter().enumerate() {
|
||||
dungeon.avatar_map.insert(
|
||||
index.try_into().unwrap(),
|
||||
AvatarUnitInfo {
|
||||
uid: *avatar_uid,
|
||||
properties_uid: self.uid_mgr.borrow().next(),
|
||||
is_banned: false,
|
||||
modified_property: pdkhashmap![],
|
||||
hp_add_hollow: 0,
|
||||
hp_lost_hollow: 0,
|
||||
layer_property_change: phashmap![],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let scene = SceneInfo::Hollow {
|
||||
uid: scene_uid,
|
||||
id,
|
||||
dungeon_uid: dungeon.uid,
|
||||
end_timestamp: 0,
|
||||
back_scene_uid,
|
||||
entered_times: 1,
|
||||
section_id: 1,
|
||||
open_ui: UIType::Default,
|
||||
to_be_destroyed: false,
|
||||
camera_x: 0xFFFFFFFF,
|
||||
camera_y: 0xFFFFFFFF,
|
||||
event_variables: phashmap![],
|
||||
buddy: BuddyUnitInfo {
|
||||
uid: 0,
|
||||
properties: 0,
|
||||
},
|
||||
stress_punish_ability_random_pool: vec![String::from(
|
||||
"Stress_Punish_RandomDebuff_Normal",
|
||||
)],
|
||||
finished: false,
|
||||
event_weight_factor: phashmap![],
|
||||
shop_modification: HollowShopModification {
|
||||
ability_modified_num: pdkhashmap![],
|
||||
action_modified_num: phashmap![],
|
||||
overwrite_price: phashmap![],
|
||||
},
|
||||
last_challenge_stat: phashmap![],
|
||||
cur_challenge: phashset![],
|
||||
hollow_system_switch: phashmap![],
|
||||
sections_info: phashmap![(
|
||||
1,
|
||||
PlayerHollowSectionInfo {
|
||||
prev_grid_index: 0,
|
||||
cur_grid_index: 22,
|
||||
entered_times: 0,
|
||||
global_event: 0,
|
||||
perform_event_graph: 3405096459205834,
|
||||
pos_before_move: 0,
|
||||
}
|
||||
)],
|
||||
executing_event: true,
|
||||
event_id: 1000,
|
||||
hollow_event_graph_uid: 22,
|
||||
on_battle_success: String::new(),
|
||||
on_battle_failure: String::new(),
|
||||
battle_finished: false,
|
||||
battle_success: false,
|
||||
battle_scene_uid: 0,
|
||||
scene_global_events: phashmap![],
|
||||
prepare_section: PrepareSection {
|
||||
section_id: 0,
|
||||
initial_pos: 0,
|
||||
show_other: false,
|
||||
battle_end_goto_next_hollow: false,
|
||||
},
|
||||
abilities_info: AbilitiesInfo {
|
||||
abilities: phashmap![],
|
||||
sequence_no: 0,
|
||||
},
|
||||
blackout: false,
|
||||
hollow_system_ui_state: phashmap![],
|
||||
};
|
||||
|
||||
{
|
||||
let mut player = self.player.borrow_mut();
|
||||
player
|
||||
.scene_properties
|
||||
.replace(self.scene_properties.borrow().clone());
|
||||
|
||||
let dungeon_collection = player.dungeon_collection.as_mut().unwrap();
|
||||
|
||||
dungeon_collection
|
||||
.dungeons
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(dungeon.uid, dungeon.clone());
|
||||
dungeon_collection
|
||||
.scenes
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(scene_uid, scene.clone());
|
||||
}
|
||||
let mut player = self.player.borrow_mut();
|
||||
let items = player.items.as_mut().unwrap();
|
||||
|
||||
let mut updated_items = Vec::new();
|
||||
for avatar_uid in avatar_uids {
|
||||
let item = items.get_mut(avatar_uid).unwrap();
|
||||
let ItemInfo::Avatar { robot_id, .. } = item else {
|
||||
continue;
|
||||
};
|
||||
*robot_id = 101000101;
|
||||
updated_items.push((*avatar_uid, item.clone()));
|
||||
}
|
||||
|
||||
let mut prop_changes = Vec::new();
|
||||
for (key, sub_key, value) in &*self.scene_properties.borrow_mut() {
|
||||
prop_changes.push((*key, *sub_key, *value));
|
||||
}
|
||||
|
||||
PlayerOperationResult::with_changes(
|
||||
(dungeon.uid, scene_uid),
|
||||
PlayerInfo {
|
||||
items: Some(PropertyHashMap::Modify {
|
||||
to_add: updated_items,
|
||||
to_remove: vec![],
|
||||
}),
|
||||
dungeon_collection: Some(DungeonCollection {
|
||||
dungeons: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(dungeon.uid, dungeon)],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
scenes: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(scene_uid, scene)],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
scene_properties: Some(PropertyDoubleKeyHashMap::Modify {
|
||||
to_add: prop_changes,
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_hall(&self, id: i32) -> PlayerOperationResult<u64> {
|
||||
let mut dungeon = self.create_base_dungeon(id, 0, 0);
|
||||
let dungeon_uid = dungeon.uid;
|
||||
|
||||
let scene_uid = self.uid_mgr.borrow().next();
|
||||
let hall_scene_info = SceneInfo::Hall {
|
||||
uid: scene_uid,
|
||||
id,
|
||||
dungeon_uid,
|
||||
end_timestamp: 0,
|
||||
back_scene_uid: 0,
|
||||
entered_times: 1,
|
||||
section_id: 1,
|
||||
open_ui: UIType::Default,
|
||||
to_be_destroyed: true,
|
||||
camera_x: 0xFFFFFFFF,
|
||||
camera_y: 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
dungeon.default_scene_uid = scene_uid;
|
||||
|
||||
let mut player = self.player.borrow_mut();
|
||||
let dungeon_collection = player.dungeon_collection.as_mut().unwrap();
|
||||
|
||||
dungeon_collection
|
||||
.dungeons
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(dungeon_uid, dungeon.clone());
|
||||
|
||||
dungeon_collection
|
||||
.scenes
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(scene_uid, hall_scene_info.clone());
|
||||
|
||||
dungeon_collection.default_scene_uid.replace(scene_uid);
|
||||
PlayerOperationResult::with_changes(
|
||||
scene_uid,
|
||||
PlayerInfo {
|
||||
dungeon_collection: Some(DungeonCollection {
|
||||
dungeons: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(dungeon_uid, dungeon)],
|
||||
to_remove: vec![],
|
||||
}),
|
||||
scenes: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(scene_uid, hall_scene_info)],
|
||||
to_remove: vec![],
|
||||
}),
|
||||
default_scene_uid: Some(scene_uid),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_fresh(&self) -> PlayerOperationResult<u64> {
|
||||
let mut dungeon = self.create_base_dungeon(2, 0, 0);
|
||||
let dungeon_uid = dungeon.uid;
|
||||
|
||||
let scene_uid = self.uid_mgr.borrow().next();
|
||||
let fresh_scene_info = SceneInfo::Fresh {
|
||||
uid: scene_uid,
|
||||
id: 2,
|
||||
dungeon_uid,
|
||||
end_timestamp: 0,
|
||||
back_scene_uid: 0,
|
||||
entered_times: 1,
|
||||
section_id: 1,
|
||||
open_ui: UIType::Default,
|
||||
to_be_destroyed: true,
|
||||
camera_x: 0xFFFFFFFF,
|
||||
camera_y: 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
dungeon.default_scene_uid = scene_uid;
|
||||
|
||||
let mut player = self.player.borrow_mut();
|
||||
let dungeon_collection = player.dungeon_collection.as_mut().unwrap();
|
||||
|
||||
dungeon_collection
|
||||
.dungeons
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(dungeon_uid, dungeon.clone());
|
||||
|
||||
dungeon_collection
|
||||
.scenes
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(scene_uid, fresh_scene_info.clone());
|
||||
|
||||
PlayerOperationResult::with_changes(
|
||||
scene_uid,
|
||||
PlayerInfo {
|
||||
dungeon_collection: Some(DungeonCollection {
|
||||
dungeons: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(dungeon_uid, dungeon)],
|
||||
to_remove: vec![],
|
||||
}),
|
||||
scenes: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(scene_uid, fresh_scene_info)],
|
||||
to_remove: vec![],
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn create_base_dungeon(
|
||||
&self,
|
||||
id: i32,
|
||||
back_scene_uid: u64,
|
||||
world_quest_id: i32,
|
||||
) -> DungeonInfo {
|
||||
let player = self.player.borrow();
|
||||
let uid = self.uid_mgr.borrow().next();
|
||||
|
||||
DungeonInfo {
|
||||
uid,
|
||||
id,
|
||||
default_scene_uid: 0,
|
||||
start_timestamp: util::cur_timestamp_ms(),
|
||||
to_be_destroyed: false,
|
||||
back_scene_uid,
|
||||
quest_collection_uid: uid,
|
||||
avatars: phashmap![],
|
||||
buddy: BuddyUnitInfo {
|
||||
uid: 0,
|
||||
properties: 0,
|
||||
},
|
||||
world_quest_id,
|
||||
scene_properties_uid: 0,
|
||||
drop_poll_chg_infos: phashmap![],
|
||||
is_in_dungeon: false,
|
||||
initiative_item: 0,
|
||||
initiative_item_used_times: 0,
|
||||
avatar_map: phashmap![],
|
||||
battle_report: Vec::new(),
|
||||
dungeon_group_uid: player.uid.unwrap(),
|
||||
entered_times: 0,
|
||||
is_preset_avatar: false,
|
||||
hollow_event_version: 0,
|
||||
}
|
||||
}
|
||||
}
|
809
gameserver/src/game/manager/hollow_grid.rs
Normal file
809
gameserver/src/game/manager/hollow_grid.rs
Normal file
|
@ -0,0 +1,809 @@
|
|||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::game::data;
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use protocol::*;
|
||||
use qwer::{phashmap, phashset, PropertyHashMap, PropertyHashSet};
|
||||
|
||||
pub struct HollowGridManager {
|
||||
player: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
map: AtomicRefCell<Option<HollowGridMapProtocolInfo>>,
|
||||
events: AtomicRefCell<HashMap<u64, EventInfo>>,
|
||||
}
|
||||
|
||||
impl HollowGridManager {
|
||||
pub fn new(player: Arc<AtomicRefCell<PlayerInfo>>) -> Self {
|
||||
Self {
|
||||
player,
|
||||
map: AtomicRefCell::new(None),
|
||||
events: AtomicRefCell::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cur_position_in_hollow(&self) -> u16 {
|
||||
self.map.borrow().as_ref().unwrap().start_grid
|
||||
}
|
||||
|
||||
pub fn move_to(
|
||||
&self,
|
||||
destination_grid: u16,
|
||||
scene_uid: u64,
|
||||
) -> (PtcHollowGridArg, Option<PtcSyncHollowEventInfoArg>) {
|
||||
let mut map = self.map.borrow_mut();
|
||||
let map = map.as_mut().unwrap();
|
||||
|
||||
map.start_grid = destination_grid;
|
||||
let grid = map.grids.get_mut(&destination_grid).unwrap();
|
||||
|
||||
self.update_position_to_scene(scene_uid, destination_grid);
|
||||
|
||||
let mut events = self.events.borrow_mut();
|
||||
let sync_event_info =
|
||||
if let Entry::Vacant(entry) = events.entry(u64::from(destination_grid)) {
|
||||
let event_info = EventInfo {
|
||||
id: 1000,
|
||||
cur_action_id: 1001,
|
||||
action_move_path: vec![1001],
|
||||
state: EventState::WaitingClient,
|
||||
prev_state: EventState::Running,
|
||||
cur_action_info: ActionInfo::None {},
|
||||
cur_action_state: ActionState::Init,
|
||||
predicated_failed_actions: phashset![],
|
||||
stack_frames: Vec::new(),
|
||||
};
|
||||
|
||||
entry.insert(event_info.clone());
|
||||
|
||||
Some(PtcSyncHollowEventInfoArg {
|
||||
event_graph_uid: u64::from(destination_grid),
|
||||
hollow_event_template_id: grid.grid.event_graph_info.hollow_event_template_id,
|
||||
event_graph_id: grid.grid.event_graph_info.hollow_event_template_id,
|
||||
updated_event: event_info,
|
||||
specials: phashmap![],
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if !grid.grid.event_graph_info.finished {
|
||||
grid.grid.flag |= HollowGridFlag::Travelled as i32;
|
||||
grid.grid.flag |= HollowGridFlag::ShowEventID as i32;
|
||||
grid.grid.flag &= !(HollowGridFlag::Guide as i32);
|
||||
grid.grid.flag &= !(HollowGridFlag::CanTriggerEvent as i32);
|
||||
grid.grid.flag &= !(HollowGridFlag::ShowEventType as i32);
|
||||
grid.grid.event_graph_info.finished = true;
|
||||
|
||||
grid.grid.event_graph_info.fired_count = 2;
|
||||
}
|
||||
|
||||
(
|
||||
PtcHollowGridArg {
|
||||
player_uid: self.player.borrow().uid.unwrap(),
|
||||
is_partial: true,
|
||||
scene_uid,
|
||||
hollow_level: 1,
|
||||
grids: HashMap::from([(destination_grid, grid.clone())]),
|
||||
},
|
||||
sync_event_info,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn battle_finished(&self) -> PtcSyncHollowEventInfoArg {
|
||||
let map = self.map.borrow();
|
||||
let map = map.as_ref().unwrap();
|
||||
let cur_grid = map.grids.get(&map.start_grid).unwrap();
|
||||
|
||||
PtcSyncHollowEventInfoArg {
|
||||
event_graph_uid: u64::from(map.start_grid),
|
||||
hollow_event_template_id: cur_grid.grid.event_graph_info.hollow_event_template_id,
|
||||
event_graph_id: cur_grid.grid.event_graph_info.hollow_event_template_id,
|
||||
updated_event: EventInfo {
|
||||
id: 1000,
|
||||
cur_action_id: 2001,
|
||||
action_move_path: vec![1001, 1002, 2001],
|
||||
state: EventState::WaitingClient,
|
||||
prev_state: EventState::Running,
|
||||
cur_action_info: ActionInfo::None {},
|
||||
cur_action_state: ActionState::Init,
|
||||
predicated_failed_actions: phashset![],
|
||||
stack_frames: Vec::new(),
|
||||
},
|
||||
specials: phashmap![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cur_event_template_id(&self) -> i32 {
|
||||
let map = self.map.borrow();
|
||||
let map = map.as_ref().unwrap();
|
||||
let cur_grid = map.grids.get(&map.start_grid).unwrap();
|
||||
|
||||
cur_grid.grid.event_graph_info.hollow_event_template_id
|
||||
}
|
||||
|
||||
fn update_position_to_scene(&self, scene_uid: u64, pos: u16) {
|
||||
let mut player = self.player.borrow_mut();
|
||||
let scene = player
|
||||
.dungeon_collection
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.scenes
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_mut(&scene_uid)
|
||||
.unwrap();
|
||||
|
||||
if let SceneInfo::Hollow {
|
||||
sections_info,
|
||||
hollow_event_graph_uid,
|
||||
..
|
||||
} = scene
|
||||
{
|
||||
let section = sections_info.get_mut(&1).unwrap();
|
||||
section.prev_grid_index = section.cur_grid_index;
|
||||
section.pos_before_move = section.cur_grid_index;
|
||||
section.cur_grid_index = pos;
|
||||
*hollow_event_graph_uid = u64::from(pos);
|
||||
} else {
|
||||
panic!("Unexpected scene type")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_event_graph(
|
||||
&self,
|
||||
event_graph_uid: u64,
|
||||
_event_id: i32,
|
||||
move_path: Vec<i32>,
|
||||
) -> (PtcSyncHollowEventInfoArg, PtcHollowGridArg, Option<i32>) {
|
||||
let (player_uid, scene_uid) = {
|
||||
let player = self.player.borrow();
|
||||
|
||||
(player.uid.unwrap(), player.scene_uid.unwrap())
|
||||
};
|
||||
|
||||
let mut map = self.map.borrow_mut();
|
||||
let map = map.as_mut().unwrap();
|
||||
|
||||
let mut trigger_battle_id = None;
|
||||
let mut grid_update = PtcHollowGridArg {
|
||||
player_uid,
|
||||
is_partial: true,
|
||||
scene_uid,
|
||||
hollow_level: 1,
|
||||
grids: HashMap::new(),
|
||||
};
|
||||
|
||||
let sync_hollow_event = {
|
||||
let info = map.grids.get(&(event_graph_uid as u16)).unwrap().clone();
|
||||
let event_config =
|
||||
data::get_event_config_json(info.grid.event_graph_info.hollow_event_template_id);
|
||||
|
||||
let mut last_exec_type = "";
|
||||
for id in &move_path {
|
||||
let index = (id % 1000) - 1;
|
||||
let actions = if id / 1000 == 1 {
|
||||
event_config["Events"]["OnStart"]["Actions"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
} else {
|
||||
event_config["Events"]["OnEnd"]["Actions"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
};
|
||||
if let Some(action) = actions.get(index as usize) {
|
||||
last_exec_type = action["$type"].as_str().unwrap();
|
||||
|
||||
match action["$type"].as_str().unwrap() {
|
||||
"Share.CConfigSetMapState" => {
|
||||
let x = action["X"].as_i64().unwrap() as u16;
|
||||
let y = action["Y"].as_i64().unwrap() as u16;
|
||||
|
||||
let uid = (y * 11) + x;
|
||||
if let Some(info) = map.grids.get_mut(&uid) {
|
||||
info.grid.flag |= HollowGridFlag::Visible as i32
|
||||
| HollowGridFlag::CanMove as i32
|
||||
| HollowGridFlag::ShowEventType as i32;
|
||||
|
||||
grid_update.grids.insert(uid, info.clone());
|
||||
}
|
||||
}
|
||||
"Share.CConfigTriggerBattle" => {
|
||||
trigger_battle_id =
|
||||
Some(match info.grid.event_graph_info.hollow_event_template_id {
|
||||
1000107 => 10101002,
|
||||
_ => 10101001,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let mut action_move_path = move_path;
|
||||
|
||||
let last_client_action = *action_move_path.iter().last().unwrap();
|
||||
let actions = if last_client_action / 1000 == 1 {
|
||||
event_config["Events"]["OnStart"]["Actions"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
} else {
|
||||
event_config["Events"]["OnEnd"]["Actions"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
};
|
||||
let state = if last_client_action == -1 {
|
||||
EventState::Finished
|
||||
} else if last_client_action % 1000 >= actions.len() as i32 {
|
||||
action_move_path.push(-1);
|
||||
EventState::Finished
|
||||
} else {
|
||||
if last_exec_type != "Share.CConfigEmpty" {
|
||||
action_move_path.push(last_client_action + 1);
|
||||
}
|
||||
|
||||
EventState::WaitingClient
|
||||
};
|
||||
|
||||
let finish_event = if last_exec_type != "Share.CConfigTriggerBattle" {
|
||||
PtcSyncHollowEventInfoArg {
|
||||
event_graph_uid,
|
||||
hollow_event_template_id: info.grid.event_graph_info.hollow_event_template_id,
|
||||
event_graph_id: info.grid.event_graph_info.hollow_event_template_id,
|
||||
updated_event: EventInfo {
|
||||
id: 1000,
|
||||
cur_action_id: *action_move_path.iter().last().unwrap(),
|
||||
action_move_path,
|
||||
state,
|
||||
prev_state: EventState::Running,
|
||||
cur_action_info: ActionInfo::None {},
|
||||
cur_action_state: ActionState::Init,
|
||||
predicated_failed_actions: phashset![],
|
||||
stack_frames: Vec::new(),
|
||||
},
|
||||
specials: phashmap![],
|
||||
}
|
||||
} else {
|
||||
PtcSyncHollowEventInfoArg {
|
||||
event_graph_uid,
|
||||
hollow_event_template_id: info.grid.event_graph_info.hollow_event_template_id,
|
||||
event_graph_id: info.grid.event_graph_info.hollow_event_template_id,
|
||||
updated_event: EventInfo {
|
||||
id: 0,
|
||||
cur_action_id: 0,
|
||||
action_move_path: vec![],
|
||||
state: EventState::Initing,
|
||||
prev_state: EventState::Initing,
|
||||
cur_action_info: ActionInfo::None {},
|
||||
cur_action_state: ActionState::Init,
|
||||
predicated_failed_actions: phashset![],
|
||||
stack_frames: Vec::new(),
|
||||
},
|
||||
specials: phashmap![],
|
||||
}
|
||||
};
|
||||
|
||||
//tracing::info!("sending evt info: {:#?}", &finish_event);
|
||||
finish_event
|
||||
};
|
||||
|
||||
(sync_hollow_event, grid_update, trigger_battle_id)
|
||||
}
|
||||
|
||||
pub fn sync_hollow_maps(&self, player_uid: u64, scene_uid: u64) -> PtcSyncHollowGridMapsArg {
|
||||
PtcSyncHollowGridMapsArg {
|
||||
player_uid,
|
||||
scene_uid,
|
||||
hollow_level: 1,
|
||||
main_map: self.map.borrow().clone().unwrap(),
|
||||
time_period: TimePeriodType::Random,
|
||||
weather: WeatherType::Random,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_default_map(&self) {
|
||||
*self.map.borrow_mut() = Some(HollowGridMapProtocolInfo {
|
||||
row: 5,
|
||||
col: 11,
|
||||
start_grid: 22,
|
||||
grids: phashmap![
|
||||
(
|
||||
48,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 12,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
7,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2872,
|
||||
link_to: 10,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1017,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
24,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2658,
|
||||
link_to: 12,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
36,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 3,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
29,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 5,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
49,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2872,
|
||||
link_to: 9,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1018,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
27,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 3,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000104,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::Dialog,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
6,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 12,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
16,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 3,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000105,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::Dialog,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
22,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2686,
|
||||
link_to: 4,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000101,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::Begin,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
30,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 12,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
18,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 3,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
38,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 3,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
32,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2872,
|
||||
link_to: 8,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000107,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::ChangeLevelInteract,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
47,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2872,
|
||||
link_to: 5,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000103,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::BattleNormal,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
25,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2682,
|
||||
link_to: 10,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000102,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::Dialog,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
5,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 6,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::DialogPositive,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
31,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 2848,
|
||||
link_to: 12,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000106,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::Dialog,
|
||||
use_perform: false,
|
||||
}
|
||||
),
|
||||
(
|
||||
23,
|
||||
HollowGridProtocolInfo {
|
||||
grid: HollowGridInfo {
|
||||
flag: 35434,
|
||||
link_to: 12,
|
||||
event_graph_info: HollowEventGraphInfo {
|
||||
config_id: 0,
|
||||
events_info: phashmap![],
|
||||
specials: phashmap![],
|
||||
is_new: false,
|
||||
finished: false,
|
||||
list_specials: phashmap![],
|
||||
fired_count: 0,
|
||||
hollow_event_template_id: 1000109,
|
||||
uid: 0,
|
||||
is_create_by_gm: false,
|
||||
},
|
||||
travelled_count: 0,
|
||||
node_state: NodeState::All,
|
||||
node_visible: NodeVisible::All,
|
||||
},
|
||||
event_type: HollowEventType::Dialog,
|
||||
use_perform: false,
|
||||
}
|
||||
)
|
||||
],
|
||||
chessboard_id: 1000101,
|
||||
});
|
||||
}
|
||||
}
|
172
gameserver/src/game/manager/item.rs
Normal file
172
gameserver/src/game/manager/item.rs
Normal file
|
@ -0,0 +1,172 @@
|
|||
use atomic_refcell::AtomicRefCell;
|
||||
use protocol::{ItemInfo, PlayerInfo};
|
||||
use qwer::{phashmap, PropertyHashMap};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::game::{util, PlayerOperationResult};
|
||||
|
||||
use super::UniqueIDManager;
|
||||
|
||||
pub struct ItemManager {
|
||||
uid_mgr: Arc<AtomicRefCell<UniqueIDManager>>,
|
||||
player_info: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
}
|
||||
|
||||
impl ItemManager {
|
||||
pub fn new(
|
||||
uid_mgr: Arc<AtomicRefCell<UniqueIDManager>>,
|
||||
player_info: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
uid_mgr,
|
||||
player_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_resource(&self, currency_id: i32, amount: i32) -> PlayerOperationResult<i32> {
|
||||
let mut player_info = self.player_info.borrow_mut();
|
||||
|
||||
for (uid, item) in player_info.items.as_mut().unwrap() {
|
||||
if let ItemInfo::Resource { id, count, .. } = item {
|
||||
if currency_id == *id {
|
||||
*count += amount;
|
||||
|
||||
return PlayerOperationResult::with_changes(
|
||||
*count,
|
||||
PlayerInfo {
|
||||
items: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(*uid, item.clone())],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let uid = self.uid_mgr.borrow().next();
|
||||
let item = ItemInfo::Resource {
|
||||
uid,
|
||||
id: currency_id,
|
||||
count: amount,
|
||||
package: 3,
|
||||
first_get_time: util::cur_timestamp_ms(),
|
||||
};
|
||||
|
||||
let items = player_info.items.as_mut().unwrap();
|
||||
items.insert(uid, item.clone());
|
||||
|
||||
PlayerOperationResult::with_changes(
|
||||
amount,
|
||||
PlayerInfo {
|
||||
items: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(uid, item)],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn unlock_avatar(&self, id: i32) -> PlayerOperationResult<u64> {
|
||||
let uid = self.uid_mgr.borrow().next();
|
||||
|
||||
let avatar = ItemInfo::Avatar {
|
||||
uid,
|
||||
id,
|
||||
count: 1,
|
||||
package: 3,
|
||||
first_get_time: util::cur_timestamp_ms(),
|
||||
star: 1,
|
||||
exp: 0,
|
||||
level: 1,
|
||||
rank: 1,
|
||||
unlocked_talent_num: 0,
|
||||
skills: phashmap![(2, 1), (1, 1), (0, 1), (3, 1), (4, 1)],
|
||||
is_custom_by_dungeon: true,
|
||||
robot_id: 0,
|
||||
};
|
||||
|
||||
// Unlock & equip default weapon
|
||||
let weapon_uid = *self.unlock_weapon(10012).unwrap();
|
||||
self.equip_weapon(weapon_uid, uid);
|
||||
|
||||
let mut player_info = self.player_info.borrow_mut();
|
||||
let items = player_info.items.as_mut().unwrap();
|
||||
items.insert(uid, avatar.clone());
|
||||
|
||||
PlayerOperationResult::with_changes(
|
||||
uid,
|
||||
PlayerInfo {
|
||||
items: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![
|
||||
(uid, avatar),
|
||||
(weapon_uid, items.get(&weapon_uid).unwrap().clone()),
|
||||
],
|
||||
to_remove: vec![],
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn unlock_weapon(&self, id: i32) -> PlayerOperationResult<u64> {
|
||||
let mut player_info = self.player_info.borrow_mut();
|
||||
let items = player_info.items.as_mut().unwrap();
|
||||
|
||||
let uid = self.uid_mgr.borrow().next();
|
||||
|
||||
let weapon = ItemInfo::Weapon {
|
||||
uid,
|
||||
id,
|
||||
count: 1,
|
||||
package: 3,
|
||||
first_get_time: util::cur_timestamp_ms(),
|
||||
avatar_uid: 0,
|
||||
star: 0,
|
||||
exp: 0,
|
||||
level: 1,
|
||||
lock: 0,
|
||||
refine_level: 1,
|
||||
};
|
||||
|
||||
items.insert(uid, weapon.clone());
|
||||
PlayerOperationResult::with_changes(
|
||||
uid,
|
||||
PlayerInfo {
|
||||
items: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(uid, weapon)],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn equip_weapon(
|
||||
&self,
|
||||
weapon_uid: u64,
|
||||
equip_avatar_uid: u64,
|
||||
) -> PlayerOperationResult<bool> {
|
||||
let mut player_info = self.player_info.borrow_mut();
|
||||
let items = player_info.items.as_mut().unwrap();
|
||||
|
||||
let Some(ItemInfo::Weapon { avatar_uid, .. }) = items.get_mut(&weapon_uid) else {
|
||||
return PlayerOperationResult::ret(false);
|
||||
};
|
||||
|
||||
*avatar_uid = equip_avatar_uid;
|
||||
|
||||
PlayerOperationResult::with_changes(
|
||||
true,
|
||||
PlayerInfo {
|
||||
items: Some(PropertyHashMap::Modify {
|
||||
to_add: vec![(weapon_uid, items.get(&weapon_uid).unwrap().clone())],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
18
gameserver/src/game/manager/mod.rs
Normal file
18
gameserver/src/game/manager/mod.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
mod dungeon;
|
||||
mod hollow_grid;
|
||||
mod item;
|
||||
pub mod net_stream;
|
||||
mod quest;
|
||||
mod scene_unit;
|
||||
mod unique_id;
|
||||
mod unlock;
|
||||
mod yorozuya_quest;
|
||||
|
||||
pub use dungeon::DungeonManager;
|
||||
pub use hollow_grid::HollowGridManager;
|
||||
pub use item::ItemManager;
|
||||
pub use quest::QuestManager;
|
||||
pub use scene_unit::SceneUnitManager;
|
||||
pub use unique_id::UniqueIDManager;
|
||||
pub use unlock::UnlockManager;
|
||||
pub use yorozuya_quest::YorozuyaQuestManager;
|
30
gameserver/src/game/manager/net_stream.rs
Normal file
30
gameserver/src/game/manager/net_stream.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use atomic_refcell::AtomicRefCell;
|
||||
use protocol::{AccountInfo, PlayerInfo, PropertyBlob};
|
||||
use qwer::OctData;
|
||||
use std::sync::Arc;
|
||||
|
||||
const CLIENT_PROP_FLAG: u16 = 1;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PropertyManager {
|
||||
pub account_info: Arc<AtomicRefCell<AccountInfo>>,
|
||||
pub player_info: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
}
|
||||
|
||||
impl PropertyManager {
|
||||
pub fn serialize_account_info(&self) -> PropertyBlob {
|
||||
Self::serialize_property(&*self.account_info.borrow()).unwrap()
|
||||
}
|
||||
|
||||
pub fn serialize_player_info(&self) -> PropertyBlob {
|
||||
Self::serialize_property(&*self.player_info.borrow()).unwrap()
|
||||
}
|
||||
|
||||
pub fn serialize_property(prop: &impl OctData) -> Result<PropertyBlob, std::io::Error> {
|
||||
let mut stream = Vec::new();
|
||||
let mut cursor = std::io::Cursor::new(&mut stream);
|
||||
|
||||
prop.marshal_to(&mut cursor, CLIENT_PROP_FLAG)?;
|
||||
Ok(PropertyBlob { stream })
|
||||
}
|
||||
}
|
77
gameserver/src/game/manager/quest.rs
Normal file
77
gameserver/src/game/manager/quest.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use qwer::PropertyDoubleKeyHashMap;
|
||||
|
||||
use crate::game::PlayerOperationResult;
|
||||
|
||||
use super::UniqueIDManager;
|
||||
use protocol::*;
|
||||
|
||||
pub struct QuestManager {
|
||||
uid_mgr: Arc<AtomicRefCell<UniqueIDManager>>,
|
||||
player: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
}
|
||||
|
||||
impl QuestManager {
|
||||
pub fn new(
|
||||
uid_mgr: Arc<AtomicRefCell<UniqueIDManager>>,
|
||||
player: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
) -> Self {
|
||||
Self { uid_mgr, player }
|
||||
}
|
||||
|
||||
pub fn add_world_quest(&self, quest: QuestInfo) -> PlayerOperationResult<u64> {
|
||||
let mut world_quest_collection_uid = self
|
||||
.player
|
||||
.borrow()
|
||||
.quest_data
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.world_quest_collection_uid
|
||||
.unwrap();
|
||||
|
||||
if world_quest_collection_uid == 0 {
|
||||
world_quest_collection_uid = self.uid_mgr.borrow().next();
|
||||
self.player
|
||||
.borrow_mut()
|
||||
.quest_data
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.world_quest_collection_uid
|
||||
.replace(world_quest_collection_uid);
|
||||
}
|
||||
|
||||
self.add_quest_to_collection(world_quest_collection_uid, quest)
|
||||
}
|
||||
|
||||
pub fn add_quest_to_collection(
|
||||
&self,
|
||||
collection_uid: u64,
|
||||
mut quest: QuestInfo,
|
||||
) -> PlayerOperationResult<u64> {
|
||||
let mut player = self.player.borrow_mut();
|
||||
let quest_data = player.quest_data.as_mut().unwrap();
|
||||
quest.set_collection_uid(collection_uid);
|
||||
|
||||
quest_data
|
||||
.quests
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(collection_uid, quest.get_id(), quest.clone());
|
||||
|
||||
PlayerOperationResult::with_changes(
|
||||
collection_uid,
|
||||
PlayerInfo {
|
||||
quest_data: Some(QuestData {
|
||||
quests: Some(PropertyDoubleKeyHashMap::Modify {
|
||||
to_add: vec![(collection_uid, quest.get_id(), quest)],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
231
gameserver/src/game/manager/scene_unit.rs
Normal file
231
gameserver/src/game/manager/scene_unit.rs
Normal file
|
@ -0,0 +1,231 @@
|
|||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use protocol::*;
|
||||
use qwer::{phashmap, PropertyHashMap};
|
||||
|
||||
use super::UniqueIDManager;
|
||||
|
||||
pub struct SceneUnitManager {
|
||||
uid_mgr: Arc<AtomicRefCell<UniqueIDManager>>,
|
||||
units: AtomicRefCell<HashMap<u64, SceneUnitProtocolInfo>>,
|
||||
}
|
||||
|
||||
impl SceneUnitManager {
|
||||
pub fn new(uid_mgr: Arc<AtomicRefCell<UniqueIDManager>>) -> Self {
|
||||
Self {
|
||||
uid_mgr,
|
||||
units: AtomicRefCell::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_npc(
|
||||
&self,
|
||||
id: i32,
|
||||
tag: i32,
|
||||
quest_id: i32,
|
||||
interacts_info: PropertyHashMap<i32, InteractInfo>,
|
||||
) -> u64 {
|
||||
let uid = self.uid_mgr.borrow().next();
|
||||
|
||||
self.units.borrow_mut().insert(
|
||||
uid,
|
||||
SceneUnitProtocolInfo::NpcProtocolInfo {
|
||||
uid,
|
||||
tag,
|
||||
id,
|
||||
quest_id,
|
||||
interacts_info,
|
||||
},
|
||||
);
|
||||
|
||||
uid
|
||||
}
|
||||
|
||||
pub fn sync(&self, scene_uid: u64, section_id: i32) -> PtcSyncSceneUnitArg {
|
||||
PtcSyncSceneUnitArg {
|
||||
scene_uid,
|
||||
section_id,
|
||||
is_partial: false,
|
||||
removed_scene_units: Vec::new(),
|
||||
scene_units: self
|
||||
.units
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|(_uid, unit)| unit.clone())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: partial_sync for newly added/removed units
|
||||
|
||||
// currently hardcoded for Main City section 2
|
||||
pub fn add_default_units(&self) {
|
||||
self.create_npc(
|
||||
100171011,
|
||||
3,
|
||||
0,
|
||||
phashmap![(
|
||||
19900006,
|
||||
create_interact(
|
||||
0,
|
||||
1,
|
||||
2.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
"",
|
||||
phashmap![(0, String::new())]
|
||||
)
|
||||
)],
|
||||
);
|
||||
|
||||
self.create_npc(
|
||||
100171011,
|
||||
4,
|
||||
0,
|
||||
phashmap![(
|
||||
19900006,
|
||||
create_interact(
|
||||
0,
|
||||
1,
|
||||
2.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
"",
|
||||
phashmap![(0, String::new())]
|
||||
)
|
||||
)],
|
||||
);
|
||||
|
||||
self.create_npc(
|
||||
100171011,
|
||||
1002,
|
||||
0,
|
||||
phashmap![(
|
||||
19900062,
|
||||
create_interact(
|
||||
0,
|
||||
1,
|
||||
2.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
"",
|
||||
phashmap![(0, String::new())]
|
||||
)
|
||||
)],
|
||||
);
|
||||
|
||||
self.create_npc(
|
||||
100171011,
|
||||
1001,
|
||||
0,
|
||||
phashmap![(
|
||||
10000010,
|
||||
create_interact(
|
||||
10000010,
|
||||
1,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
"A",
|
||||
phashmap![(1001, String::from("A"))]
|
||||
)
|
||||
)],
|
||||
);
|
||||
|
||||
self.create_npc(
|
||||
100171011,
|
||||
1005,
|
||||
0,
|
||||
phashmap![(
|
||||
10000014,
|
||||
create_interact(
|
||||
10000014,
|
||||
1,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
"A",
|
||||
phashmap![(1005, String::from("A"))]
|
||||
)
|
||||
)],
|
||||
);
|
||||
|
||||
self.create_npc(
|
||||
100173001,
|
||||
2028,
|
||||
0,
|
||||
phashmap![(
|
||||
19900052,
|
||||
create_interact(
|
||||
19900052,
|
||||
2,
|
||||
9.0,
|
||||
2.0,
|
||||
2.0,
|
||||
90.0,
|
||||
10.0,
|
||||
"A",
|
||||
phashmap![(2028, String::from("A"))]
|
||||
)
|
||||
)],
|
||||
);
|
||||
|
||||
self.create_npc(
|
||||
100172011,
|
||||
2000,
|
||||
0,
|
||||
phashmap![(
|
||||
19900030,
|
||||
create_interact(
|
||||
0,
|
||||
1,
|
||||
2.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
"",
|
||||
phashmap![(2000, String::from("A")), (2052, String::from("B"))]
|
||||
)
|
||||
)],
|
||||
);
|
||||
|
||||
self.create_npc(100172081, 2052, 0, phashmap![]);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_interact(
|
||||
interact_id: i32,
|
||||
interact_shape: u16,
|
||||
scale_x: f64,
|
||||
scale_y: f64,
|
||||
scale_z: f64,
|
||||
scale_w: f64,
|
||||
scale_r: f64,
|
||||
name: &str,
|
||||
participators: PropertyHashMap<i32, String>,
|
||||
) -> InteractInfo {
|
||||
InteractInfo {
|
||||
interact_id,
|
||||
interact_shape,
|
||||
scale_x,
|
||||
scale_y,
|
||||
scale_z,
|
||||
scale_w,
|
||||
scale_r,
|
||||
name: name.to_string(),
|
||||
participators,
|
||||
}
|
||||
}
|
22
gameserver/src/game/manager/unique_id.rs
Normal file
22
gameserver/src/game/manager/unique_id.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
const BASE_UID: u64 = 1000000;
|
||||
|
||||
pub struct UniqueIDManager {
|
||||
uid_counter: AtomicU64,
|
||||
}
|
||||
|
||||
impl UniqueIDManager {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
uid_counter: AtomicU64::new(BASE_UID),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(&self) -> u64 {
|
||||
let uid = self.uid_counter.load(Ordering::SeqCst) + 1;
|
||||
self.uid_counter.store(uid, Ordering::SeqCst);
|
||||
|
||||
uid
|
||||
}
|
||||
}
|
42
gameserver/src/game/manager/unlock.rs
Normal file
42
gameserver/src/game/manager/unlock.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use protocol::*;
|
||||
use qwer::PropertyHashSet;
|
||||
|
||||
use crate::game::PlayerOperationResult;
|
||||
|
||||
pub struct UnlockManager {
|
||||
player: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
}
|
||||
|
||||
impl UnlockManager {
|
||||
pub fn new(player: Arc<AtomicRefCell<PlayerInfo>>) -> Self {
|
||||
Self { player }
|
||||
}
|
||||
|
||||
pub fn unlock(&self, unlock_id: i32) -> PlayerOperationResult<PtcUnlockArg> {
|
||||
let mut player = self.player.borrow_mut();
|
||||
let unlock_info = player.unlock_info.as_mut().unwrap();
|
||||
|
||||
unlock_info
|
||||
.unlocked_list
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.insert(unlock_id);
|
||||
|
||||
PlayerOperationResult::with_changes(
|
||||
PtcUnlockArg { unlock_id },
|
||||
PlayerInfo {
|
||||
unlock_info: Some(UnlockInfo {
|
||||
unlocked_list: Some(PropertyHashSet::Modify {
|
||||
to_add: vec![unlock_id],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
condition_progress: None,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
68
gameserver/src/game/manager/yorozuya_quest.rs
Normal file
68
gameserver/src/game/manager/yorozuya_quest.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use qwer::{PropertyDoubleKeyHashMap, PropertyHashSet};
|
||||
|
||||
use crate::game::PlayerOperationResult;
|
||||
|
||||
use protocol::*;
|
||||
|
||||
pub struct YorozuyaQuestManager {
|
||||
player: Arc<AtomicRefCell<PlayerInfo>>,
|
||||
}
|
||||
|
||||
impl YorozuyaQuestManager {
|
||||
pub fn new(player: Arc<AtomicRefCell<PlayerInfo>>) -> Self {
|
||||
Self { player }
|
||||
}
|
||||
|
||||
pub fn add_hollow_quest(
|
||||
&self,
|
||||
yorozuya_collection_id: i32,
|
||||
hollow_quest_type: HollowQuestType,
|
||||
id: i32,
|
||||
) -> PlayerOperationResult<i32> {
|
||||
let mut player = self.player.borrow_mut();
|
||||
let yorozuya = player.yorozuya_info.as_mut().unwrap();
|
||||
let hollow_quests = yorozuya.hollow_quests.as_mut().unwrap();
|
||||
|
||||
let updated_set = {
|
||||
if let Some(quests) = hollow_quests.get_mut(&yorozuya_collection_id, &hollow_quest_type)
|
||||
{
|
||||
quests.insert(id);
|
||||
let PropertyHashSet::Base(set) = quests else {
|
||||
return PlayerOperationResult::ret(yorozuya_collection_id);
|
||||
};
|
||||
set.clone()
|
||||
} else {
|
||||
let set = HashSet::from([id]);
|
||||
hollow_quests.insert(
|
||||
yorozuya_collection_id,
|
||||
hollow_quest_type,
|
||||
PropertyHashSet::Base(set.clone()),
|
||||
);
|
||||
|
||||
set
|
||||
}
|
||||
};
|
||||
|
||||
PlayerOperationResult::with_changes(
|
||||
yorozuya_collection_id,
|
||||
PlayerInfo {
|
||||
yorozuya_info: Some(YorozuyaInfo {
|
||||
hollow_quests: Some(PropertyDoubleKeyHashMap::Modify {
|
||||
to_add: vec![(
|
||||
yorozuya_collection_id,
|
||||
hollow_quest_type,
|
||||
PropertyHashSet::Base(updated_set),
|
||||
)],
|
||||
to_remove: Vec::new(),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
7
gameserver/src/game/mod.rs
Normal file
7
gameserver/src/game/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
mod context;
|
||||
pub mod data;
|
||||
pub mod globals;
|
||||
pub mod manager;
|
||||
pub mod util;
|
||||
|
||||
pub use context::{GameContext, PlayerOperationResult};
|
293
gameserver/src/game/util.rs
Normal file
293
gameserver/src/game/util.rs
Normal file
|
@ -0,0 +1,293 @@
|
|||
use protocol::*;
|
||||
use qwer::{
|
||||
pdkhashmap, phashmap, phashset, PropertyDoubleKeyHashMap, PropertyHashMap, PropertyHashSet,
|
||||
};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
pub fn cur_timestamp_ms() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as u64
|
||||
}
|
||||
|
||||
pub fn create_default_account(id: u64) -> AccountInfo {
|
||||
AccountInfo {
|
||||
account_name: Some(format!("1_{id}")),
|
||||
players: Some(vec![id]),
|
||||
gm_level: Some(10),
|
||||
account_type: Some(1),
|
||||
register_cps: Some(String::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_default_player(id: u64) -> PlayerInfo {
|
||||
PlayerInfo {
|
||||
uid: Some(id),
|
||||
account_name: Some(format!("1_{id}")),
|
||||
items: Some(qwer::phashmap![(
|
||||
3405096459205780,
|
||||
ItemInfo::Buddy {
|
||||
uid: 3405096459205780,
|
||||
id: 50012,
|
||||
count: 1,
|
||||
package: 3,
|
||||
first_get_time: 0,
|
||||
}
|
||||
)]),
|
||||
dungeon_collection: Some(DungeonCollection {
|
||||
dungeons: Some(qwer::phashmap![]),
|
||||
scenes: Some(qwer::phashmap![]),
|
||||
default_scene_uid: Some(0),
|
||||
transform: Some(Transform::default()),
|
||||
used_story_mode: Some(true),
|
||||
used_manual_qte_mode: Some(true),
|
||||
}),
|
||||
properties: Some(pdkhashmap![]),
|
||||
scene_properties: Some(pdkhashmap![]),
|
||||
quest_data: Some(QuestData {
|
||||
quests: Some(pdkhashmap![]),
|
||||
is_afk: Some(false),
|
||||
unlock_condition_progress: Some(pdkhashmap![]),
|
||||
world_quest_collection_uid: Some(0),
|
||||
world_quest_for_cur_dungeon: Some(0),
|
||||
world_quest_for_cur_dungeon_afk: Some(0),
|
||||
}),
|
||||
joined_chat_rooms: Some(Vec::new()),
|
||||
last_enter_world_timestamp: Some(0),
|
||||
scene_uid: Some(0),
|
||||
archive_info: Some(ArchiveInfo {
|
||||
videotapes_info: Some(phashmap![]),
|
||||
}),
|
||||
auto_recovery_info: Some(phashmap![(
|
||||
501,
|
||||
AutoRecoveryInfo {
|
||||
buy_times: 0,
|
||||
last_recovery_timestamp: 0,
|
||||
}
|
||||
)]),
|
||||
unlock_info: Some(UnlockInfo {
|
||||
condition_progress: Some(pdkhashmap![]),
|
||||
unlocked_list: Some(phashset![]),
|
||||
}),
|
||||
yorozuya_info: Some(YorozuyaInfo {
|
||||
yorozuya_level: Some(1),
|
||||
yorozuya_rank: Some(1),
|
||||
gm_enabled: Some(true),
|
||||
gm_quests: Some(phashmap![]),
|
||||
finished_hollow_quest_count: Some(0),
|
||||
finished_hollow_quest_count_of_type: Some(phashmap![]),
|
||||
hollow_quests: Some(pdkhashmap![]),
|
||||
urgent_quests_queue: Some(phashmap![]),
|
||||
unlock_hollow_id: Some(vec![102]),
|
||||
unlock_hollow_id_progress: Some(pdkhashmap![]),
|
||||
last_refresh_timestamp_common: Some(0),
|
||||
last_refresh_timestamp_urgent: Some(0),
|
||||
next_refresh_timestamp_urgent: Some(0),
|
||||
}),
|
||||
equip_gacha_info: Some(EquipGachaInfo {
|
||||
avatar_level_advance_times: Some(0),
|
||||
equip_star_up_times: Some(0),
|
||||
security_num_by_lv: Some(phashmap![]),
|
||||
smithy_level: Some(0),
|
||||
total_gacha_times: Some(0),
|
||||
}),
|
||||
beginner_procedure_info: Some(BeginnerProcedureInfo {
|
||||
procedure_info: Some(0),
|
||||
}),
|
||||
pos_in_main_city: Some(PlayerPosInMainCity {
|
||||
position: Some(Vector3f {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
}),
|
||||
rotation: Some(Vector3f {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
}),
|
||||
initial_pos_id: Some(0),
|
||||
}),
|
||||
fairy_info: Some(FairyInfo {
|
||||
condition_progress: Some(pdkhashmap![]),
|
||||
fairy_groups: Some(phashmap![]),
|
||||
}),
|
||||
popup_window_info: Some(PopupWindowInfo {
|
||||
condition_progress: Some(pdkhashmap![]),
|
||||
popup_window_list: Some(Vec::new()),
|
||||
}),
|
||||
tips_info: Some(TipsInfo {
|
||||
tips_list: Some(Vec::new()),
|
||||
tips_group: Some(Vec::new()),
|
||||
tips_condition_progress: Some(pdkhashmap![]),
|
||||
tips_group_condition_progress: Some(pdkhashmap![]),
|
||||
}),
|
||||
main_city_quest_data: Some(MainCityQuestData {
|
||||
in_progress_quests: Some(Vec::new()),
|
||||
exicing_finish_script_group: Some(vec![10020001]),
|
||||
}),
|
||||
embattles: Some(Embattles {
|
||||
last_embattles: Some(phashmap![]),
|
||||
}),
|
||||
day_change_info: Some(DayChangeInfo {
|
||||
last_daily_refresh_timing: Some(0),
|
||||
}),
|
||||
npcs_info: Some(PlayerNPCsInfo {
|
||||
npcs_info: Some(phashmap![]),
|
||||
destroy_npc_when_leave_section: Some(phashset![]),
|
||||
}),
|
||||
scripts_to_execute: Some(pdkhashmap![]),
|
||||
scripts_to_remove: Some(phashmap![]),
|
||||
last_leave_world_timestamp: Some(0),
|
||||
muip_data: Some(MUIPData {
|
||||
alread_cmd_uids: Some(phashset![]),
|
||||
ban_end_time: Some(String::new()),
|
||||
tag_value: Some(0),
|
||||
scene_pass_times: Some(phashmap![]),
|
||||
scene_enter_times: Some(phashmap![]),
|
||||
dungeon_pass_times: Some(phashmap![]),
|
||||
dungeon_enter_times: Some(phashmap![]),
|
||||
ban_begin_time: Some(String::new()),
|
||||
game_total_time: Some(0),
|
||||
language_type: Some(0),
|
||||
}),
|
||||
nick_name: Some(String::new()),
|
||||
ramen_data: Some(RamenData {
|
||||
unlock_ramen: Some(phashset![20301, 20401, 20501, 20601, 20201]),
|
||||
cur_ramen: Some(0),
|
||||
used_times: Some(0),
|
||||
unlock_initiative_item: Some(phashset![]),
|
||||
unlock_ramen_condition_progress: Some(pdkhashmap![]),
|
||||
unlock_item_condition_progress: Some(pdkhashmap![]),
|
||||
has_mystical_spice: Some(true),
|
||||
unlock_has_mystical_spice_condition_progress: Some(phashmap![]),
|
||||
cur_mystical_spice: Some(0),
|
||||
unlock_mystical_spice: Some(phashset![
|
||||
30101, 30601, 30201, 30501, 30301, 30801, 31201, 30401, 31401, 31001
|
||||
]),
|
||||
unlock_mystical_spice_condition_progress: Some(pdkhashmap![]),
|
||||
unlock_initiative_item_group: Some(phashset![]),
|
||||
hollow_item_history: Some(phashmap![]),
|
||||
initial_item_ability: Some(0),
|
||||
new_unlock_ramen: Some(Vec::new()),
|
||||
eat_ramen_times: Some(0),
|
||||
make_hollow_item_times: Some(0),
|
||||
new_unlock_initiative_item: Some(phashset![]),
|
||||
}),
|
||||
|
||||
shop: Some(ShopsInfo {
|
||||
shops: Some(phashmap![]),
|
||||
shop_buy_times: Some(0),
|
||||
vip_level: Some(0),
|
||||
}),
|
||||
vhs_store_data: Some(VHSStoreData {
|
||||
store_level: Some(0),
|
||||
unreceived_reward: Some(0),
|
||||
hollow_enter_times: Some(0),
|
||||
last_receive_time: Some(0),
|
||||
vhs_collection_slot: Some(Vec::new()),
|
||||
unlock_vhs_collection: Some(phashset![]),
|
||||
already_trending: Some(phashset![]),
|
||||
unlock_trending_condition_progress: Some(pdkhashmap![]),
|
||||
is_need_refresh: Some(true),
|
||||
scripts_id: Some(phashset![]),
|
||||
store_exp: Some(0),
|
||||
is_level_chg_tips: Some(true),
|
||||
vhs_hollow: Some(Vec::new()),
|
||||
is_receive_trending_reward: Some(false),
|
||||
is_need_first_trending: Some(false),
|
||||
last_basic_script: Some(0),
|
||||
is_complete_first_trending: Some(false),
|
||||
last_basic_npc: Some(0),
|
||||
can_random_trending: Some(phashset![]),
|
||||
vhs_trending_info: Some(Vec::new()),
|
||||
unlock_vhs_trending_info: Some(phashmap![]),
|
||||
vhs_flow: Some(0),
|
||||
received_reward: Some(0),
|
||||
last_reward: Some(0),
|
||||
last_exp: Some(0),
|
||||
last_flow: Some(0),
|
||||
last_vhs_trending_info: Some(Vec::new()),
|
||||
new_know_trend: Some(Vec::new()),
|
||||
quest_finish_script: Some(pdkhashmap![]),
|
||||
quest_finish_scripts_id: Some(phashset![]),
|
||||
total_received_reward: Some(phashmap![]),
|
||||
last_vhs_npc_info: Some(Vec::new()),
|
||||
vhs_npc_info: Some(Vec::new()),
|
||||
npc_info: Some(phashset![]),
|
||||
total_received_reward_times: Some(0),
|
||||
}),
|
||||
operation_mail_receive_info: Some(OperationMailReceiveInfo {
|
||||
receive_list: Some(phashset![]),
|
||||
condition_progress: Some(pdkhashmap![]),
|
||||
}),
|
||||
second_last_enter_world_timestamp: Some(0),
|
||||
login_times: Some(1),
|
||||
create_timestamp: Some(cur_timestamp_ms()),
|
||||
gender: Some(0),
|
||||
avatar_id: Some(0),
|
||||
prev_scene_uid: Some(2),
|
||||
register_cps: Some(String::new()),
|
||||
register_platform: Some(3),
|
||||
pay_info: Some(PayInfo {
|
||||
month_total_pay: Some(0),
|
||||
}),
|
||||
private_npcs: Some(phashmap![]),
|
||||
battle_event_info: Some(BattleEventInfo {
|
||||
unlock_battle: Some(phashset![]),
|
||||
unlock_battle_condition_progress: Some(pdkhashmap![]),
|
||||
alread_rand_battle: Some(pdkhashmap![]),
|
||||
alread_battle_stage: Some(Vec::new()),
|
||||
rand_battle_type: Some(phashmap![]),
|
||||
}),
|
||||
gm_data: Some(GMData {
|
||||
register_conditions: Some(phashset![]),
|
||||
condition_proress: Some(pdkhashmap![]),
|
||||
completed_conditions: Some(phashset![]),
|
||||
}),
|
||||
player_mail_ext_infos: Some(PlayerMailExtInfos {
|
||||
player_mail_ext_info: Some(phashmap![]),
|
||||
}),
|
||||
single_dungeon_group: Some(SingleDungeonGroup {
|
||||
dungeons: Some(phashmap![]),
|
||||
scenes: Some(pdkhashmap![]),
|
||||
npcs: Some(pdkhashmap![]),
|
||||
section: Some(pdkhashmap![]),
|
||||
}),
|
||||
newbie_info: Some(NewbieInfo {
|
||||
unlocked_id: Some(phashset![3]),
|
||||
condition_progress: Some(pdkhashmap![]),
|
||||
}),
|
||||
loading_page_tips_info: Some(LoadingPageTipsInfo {
|
||||
unlocked_id: Some(phashset![1, 2, 3]),
|
||||
condition_progress: Some(pdkhashmap![]),
|
||||
}),
|
||||
switch_of_story_mode: Some(true),
|
||||
switch_of_qte: Some(true),
|
||||
collect_map: Some(CollectMap {
|
||||
card_map: Some(phashset![]),
|
||||
curse_map: Some(phashset![]),
|
||||
unlock_cards: Some(phashset![]),
|
||||
unlock_curses: Some(phashset![]),
|
||||
event_icon_map: Some(phashset![]),
|
||||
unlock_events: Some(phashset![]),
|
||||
new_card_map: Some(phashset![]),
|
||||
new_curse_map: Some(phashset![]),
|
||||
new_event_icon_map: Some(phashset![]),
|
||||
unlock_event_icon_condition_progress: Some(pdkhashmap![]),
|
||||
unlock_card_condition_progress: Some(pdkhashmap![]),
|
||||
unlock_curse_condition_progress: Some(pdkhashmap![]),
|
||||
unlock_event_condition_progress: Some(pdkhashmap![]),
|
||||
unlock_event_icons: Some(phashset![]),
|
||||
}),
|
||||
areas_info: Some(AreasInfo {
|
||||
area_owners_info: Some(pdkhashmap![]),
|
||||
sequence: Some(0),
|
||||
}),
|
||||
bgm_info: Some(BGMInfo { bgm_id: Some(1) }),
|
||||
main_city_objects_state: Some(phashmap![]),
|
||||
hollow_info: Some(HollowInfo {
|
||||
banned_hollow_event: Some(phashset![]),
|
||||
}),
|
||||
}
|
||||
}
|
58
gameserver/src/logging.rs
Normal file
58
gameserver/src/logging.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use tracing::Instrument;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log_error {
|
||||
($e:expr) => {
|
||||
if let Err(e) = $e {
|
||||
tracing::error!(error.message = %format!("{}", &e), "{:?}", e);
|
||||
}
|
||||
};
|
||||
($context:expr, $e:expr $(,)?) => {
|
||||
if let Err(e) = $e {
|
||||
let e = format!("{:?}", ::anyhow::anyhow!(e).context($context));
|
||||
tracing::error!(error.message = %format!("{}", &e), "{:?}", e);
|
||||
}
|
||||
};
|
||||
($ok_context:expr, $err_context:expr, $e:expr $(,)?) => {
|
||||
if let Err(e) = $e {
|
||||
let e = format!("{:?}", ::anyhow::anyhow!(e).context($err_context));
|
||||
tracing::error!(error.message = %format!("{}", &e), "{:?}", e);
|
||||
} else {
|
||||
tracing::info!($ok_context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn init_tracing() {
|
||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||
}
|
||||
|
||||
pub async fn init_system_logging() {
|
||||
use std::time::Duration;
|
||||
use sysinfo::System;
|
||||
|
||||
tokio::spawn(
|
||||
async {
|
||||
let mut s = System::new_all();
|
||||
s.refresh_all();
|
||||
let num_cpus = s.cpus().len();
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_millis(20000)).await;
|
||||
s.refresh_all();
|
||||
let process = s.process(sysinfo::get_current_pid().unwrap()).unwrap();
|
||||
tracing::info!(
|
||||
cpu_usage = %format!("{:.2}%", process.cpu_usage() / num_cpus as f32)
|
||||
);
|
||||
let memory = process.memory();
|
||||
let formatted = match memory {
|
||||
m if m < 1024 => format!("{m} B"),
|
||||
m if m < 1024 * 1024 => format!("{:.2} KB", m as f32 / 1024.0),
|
||||
m if m < 1024 * 1024 * 1024 => format!("{:.2} MB", m as f32 / 1024.0 / 1024.0),
|
||||
m => format!("{:.2} GB", m as f32 / 1024.0 / 1024.0 / 1024.0),
|
||||
};
|
||||
tracing::info!(total_memory = %format!("{formatted}"));
|
||||
}
|
||||
}
|
||||
.instrument(tracing::info_span!("system-usage")),
|
||||
);
|
||||
}
|
53
gameserver/src/main.rs
Normal file
53
gameserver/src/main.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use tracing::Level;
|
||||
|
||||
mod game;
|
||||
mod logging;
|
||||
mod net;
|
||||
|
||||
use logging::{init_system_logging, init_tracing};
|
||||
|
||||
const GATE_HOST: &str = "0.0.0.0";
|
||||
const GATE_PORT: u16 = 10301;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
#[cfg(target_os = "windows")]
|
||||
ansi_term::enable_ansi_support().unwrap();
|
||||
|
||||
init_config()?;
|
||||
init_tracing();
|
||||
|
||||
let span = tracing::span!(Level::DEBUG, "main");
|
||||
let _enter = span.enter();
|
||||
|
||||
init_system_logging().await;
|
||||
|
||||
net::gateway::listen(GATE_HOST, GATE_PORT).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_config() -> Result<()> {
|
||||
let local_dotenv = Path::new(".env");
|
||||
if local_dotenv.exists() {
|
||||
dotenv::dotenv()?;
|
||||
} else {
|
||||
let config = dirs::config_dir()
|
||||
.ok_or_else(|| anyhow::anyhow!("No config directory found"))?
|
||||
.join("nap-gameserver");
|
||||
|
||||
std::fs::create_dir_all(&config)?;
|
||||
|
||||
let env = config.join(".env");
|
||||
|
||||
if !env.exists() {
|
||||
std::fs::write(&env, "SKIP_TUTORIAL=0")?;
|
||||
}
|
||||
|
||||
dotenv::from_path(&env)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
32
gameserver/src/net/gateway.rs
Normal file
32
gameserver/src/net/gateway.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use anyhow::Result;
|
||||
use tokio::net::TcpListener;
|
||||
use tracing::Instrument;
|
||||
|
||||
use crate::log_error;
|
||||
|
||||
use super::NetworkSession;
|
||||
|
||||
pub async fn listen(host: &str, port: u16) -> Result<()> {
|
||||
let listener = TcpListener::bind(format!("{host}:{port}")).await?;
|
||||
tracing::info!("Listening at {host}:{port}");
|
||||
|
||||
loop {
|
||||
let Ok((client_socket, client_addr)) = listener.accept().await else {
|
||||
continue;
|
||||
};
|
||||
|
||||
tracing::info!("New session from {client_addr}");
|
||||
|
||||
let mut session = NetworkSession::new(client_socket, client_addr);
|
||||
tokio::spawn(
|
||||
async move {
|
||||
log_error!(
|
||||
"Session from {client_addr} disconnected",
|
||||
format!("An error occurred while processing session ({client_addr})"),
|
||||
Box::pin(session.run()).await
|
||||
);
|
||||
}
|
||||
.instrument(tracing::info_span!("session", addr = %client_addr)),
|
||||
);
|
||||
}
|
||||
}
|
16
gameserver/src/net/handlers/battle.rs
Normal file
16
gameserver/src/net/handlers/battle.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use super::*;
|
||||
|
||||
pub async fn on_rpc_battle_report_arg(
|
||||
session: &NetworkSession,
|
||||
arg: &RpcBattleReportArg,
|
||||
) -> Result<()> {
|
||||
let need_index = arg
|
||||
.battle_reports
|
||||
.iter()
|
||||
.last()
|
||||
.map_or(0, |report| report.index + 1);
|
||||
|
||||
session
|
||||
.send_rpc_ret(RpcBattleReportRet::new(need_index))
|
||||
.await
|
||||
}
|
51
gameserver/src/net/handlers/beginner_procedure.rs
Normal file
51
gameserver/src/net/handlers/beginner_procedure.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use super::*;
|
||||
|
||||
const START_PROC_ID: i32 = 1;
|
||||
|
||||
pub async fn on_rpc_advance_beginner_procedure_arg(
|
||||
session: &NetworkSession,
|
||||
arg: &RpcAdvanceBeginnerProcedureArg,
|
||||
) -> Result<()> {
|
||||
let next_procedure_id = if arg.procedure_id == 0 {
|
||||
START_PROC_ID
|
||||
} else {
|
||||
arg.procedure_id + 1
|
||||
};
|
||||
|
||||
tracing::info!("{arg:?}");
|
||||
if arg.procedure_id == 6 {
|
||||
Box::pin(world::enter_main_city(session)).await?;
|
||||
}
|
||||
|
||||
session
|
||||
.send_rpc_ret(RpcAdvanceBeginnerProcedureRet::new(next_procedure_id))
|
||||
.await
|
||||
}
|
||||
|
||||
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
|
||||
)))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn on_rpc_beginnerbattle_end_arg(
|
||||
session: &NetworkSession,
|
||||
arg: &RpcBeginnerbattleEndArg,
|
||||
) -> Result<()> {
|
||||
tracing::info!("Battle statistics: {:?}", arg.battle_statistics);
|
||||
|
||||
session.send_rpc_ret(RpcBeginnerbattleEndRet::new()).await
|
||||
}
|
256
gameserver/src/net/handlers/hollow.rs
Normal file
256
gameserver/src/net/handlers/hollow.rs
Normal file
|
@ -0,0 +1,256 @@
|
|||
use qwer::{phashmap, phashset, PropertyHashMap, PropertyHashSet};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub async fn on_rpc_hollow_move_arg(
|
||||
session: &mut NetworkSession,
|
||||
arg: &RpcHollowMoveArg,
|
||||
) -> Result<()> {
|
||||
tracing::info!("Hollow movement {:?}", &arg);
|
||||
|
||||
let destination_pos = *arg.positions.iter().last().unwrap();
|
||||
let scene_uid = session.get_player().scene_uid.unwrap();
|
||||
|
||||
let hollow_grid_manager = session.context.hollow_grid_manager.borrow();
|
||||
let (ptc_hollow_grid, ptc_sync_hollow_event) =
|
||||
hollow_grid_manager.move_to(destination_pos, scene_uid);
|
||||
|
||||
session.send_rpc_arg(114, &ptc_hollow_grid).await?;
|
||||
if let Some(ptc_sync_hollow_event) = ptc_sync_hollow_event {
|
||||
session.send_rpc_arg(210, &ptc_sync_hollow_event).await?;
|
||||
}
|
||||
|
||||
let pos = PtcPositionInHollowChangedArg {
|
||||
player_uid: 1337,
|
||||
hollow_level: arg.hollow_level,
|
||||
position: destination_pos,
|
||||
};
|
||||
|
||||
session.send_rpc_arg(141, &pos).await?;
|
||||
|
||||
session
|
||||
.send_rpc_ret(RpcHollowMoveRet::new(
|
||||
arg.hollow_level,
|
||||
*arg.positions.iter().last().unwrap(),
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn on_rpc_end_battle_arg(session: &NetworkSession, arg: &RpcEndBattleArg) -> Result<()> {
|
||||
tracing::info!("RpcEndBattle: {:?}", &arg);
|
||||
|
||||
let player_uid = session.get_player_uid();
|
||||
let hollow_grid_manager = session.context.hollow_grid_manager.borrow();
|
||||
|
||||
session
|
||||
.send_rpc_arg(210, &hollow_grid_manager.battle_finished())
|
||||
.await?;
|
||||
|
||||
let dungeon_manager = session.context.dungeon_manager.borrow();
|
||||
let ptc_enter_scene = dungeon_manager
|
||||
.leave_battle()
|
||||
.unwrap()
|
||||
.send_changes(session)
|
||||
.await?
|
||||
.clone();
|
||||
|
||||
session
|
||||
.send_rpc_arg(
|
||||
124,
|
||||
&hollow_grid_manager.sync_hollow_maps(player_uid, dungeon_manager.get_cur_scene_uid()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let ptc_position_in_hollow_changed = PtcPositionInHollowChangedArg {
|
||||
player_uid,
|
||||
hollow_level: 1,
|
||||
position: hollow_grid_manager.get_cur_position_in_hollow(),
|
||||
};
|
||||
|
||||
session
|
||||
.send_rpc_arg(141, &ptc_position_in_hollow_changed)
|
||||
.await?;
|
||||
|
||||
session.send_rpc_arg(118, &ptc_enter_scene).await?;
|
||||
|
||||
session
|
||||
.send_rpc_ret(RpcEndBattleRet::new(
|
||||
hollow_grid_manager.get_cur_event_template_id(),
|
||||
HashMap::new(),
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn on_rpc_run_hollow_event_graph_arg(
|
||||
session: &mut NetworkSession,
|
||||
arg: &RpcRunHollowEventGraphArg,
|
||||
) -> Result<()> {
|
||||
tracing::info!("Run hollow event graph {:?}", arg);
|
||||
|
||||
let scene_uid = session.get_player().scene_uid.unwrap();
|
||||
|
||||
if arg.event_graph_uid == 3405096459205834 {
|
||||
// Perform (cutscene)
|
||||
let finish_perform = PtcSyncHollowEventInfoArg {
|
||||
event_graph_uid: 3405096459205834,
|
||||
hollow_event_template_id: 1000108,
|
||||
event_graph_id: 1000108,
|
||||
updated_event: EventInfo {
|
||||
id: 1000,
|
||||
cur_action_id: -1,
|
||||
action_move_path: vec![1001, 1002, -1],
|
||||
state: EventState::Finished,
|
||||
prev_state: EventState::Running,
|
||||
cur_action_info: ActionInfo::None {},
|
||||
cur_action_state: ActionState::Init,
|
||||
predicated_failed_actions: phashset![],
|
||||
stack_frames: Vec::new(),
|
||||
},
|
||||
specials: phashmap![],
|
||||
};
|
||||
session.send_rpc_arg(210, &finish_perform).await?;
|
||||
|
||||
let hollow_grid_manager = session.context.hollow_grid_manager.borrow();
|
||||
let (ptc_hollow_grid, ptc_sync_hollow_event) = hollow_grid_manager.move_to(22, scene_uid);
|
||||
|
||||
session.send_rpc_arg(114, &ptc_hollow_grid).await?;
|
||||
if let Some(ptc_sync_hollow_event) = ptc_sync_hollow_event {
|
||||
session.send_rpc_arg(210, &ptc_sync_hollow_event).await?;
|
||||
}
|
||||
} else {
|
||||
let hollow_grid_manager = session.context.hollow_grid_manager.borrow();
|
||||
let (sync_hollow_event, hollow_grid, trigger_battle_id) = hollow_grid_manager
|
||||
.run_event_graph(arg.event_graph_uid, arg.event_id, arg.move_path.clone());
|
||||
|
||||
session.send_rpc_arg(210, &sync_hollow_event).await?;
|
||||
session.send_rpc_arg(114, &hollow_grid).await?;
|
||||
|
||||
if let Some(trigger_battle_id) = trigger_battle_id {
|
||||
let dungeon_manager = session.context.dungeon_manager.borrow();
|
||||
let hollow_uid = *session.get_player().scene_uid.as_ref().unwrap();
|
||||
let battle_scene_uid = *dungeon_manager
|
||||
.create_fight(trigger_battle_id, hollow_uid)
|
||||
.send_changes(session)
|
||||
.await?;
|
||||
|
||||
let ptc_position_in_hollow_changed = PtcPositionInHollowChangedArg {
|
||||
player_uid: 1337,
|
||||
hollow_level: 1,
|
||||
position: hollow_grid_manager.get_cur_position_in_hollow(),
|
||||
};
|
||||
|
||||
session
|
||||
.send_rpc_arg(141, &ptc_position_in_hollow_changed)
|
||||
.await?;
|
||||
|
||||
session
|
||||
.send_rpc_arg(
|
||||
118,
|
||||
dungeon_manager
|
||||
.enter_battle(battle_scene_uid)
|
||||
.send_changes(session)
|
||||
.await?,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
session.send_rpc_ret(RpcRunHollowEventGraphRet::new()).await
|
||||
}
|
||||
|
||||
pub async fn on_rpc_start_hollow_quest_arg(
|
||||
session: &NetworkSession,
|
||||
arg: &RpcStartHollowQuestArg,
|
||||
) -> Result<()> {
|
||||
tracing::info!("start hollow quest: {arg:?}");
|
||||
|
||||
for (_idx, avatar_uid) in &arg.avatar_map {
|
||||
// Set character HP
|
||||
let update_properties = PtcPropertyChangedArg {
|
||||
scene_unit_uid: *avatar_uid,
|
||||
is_partial: true,
|
||||
changed_properties: phashmap![(1, 500), (111, 500)],
|
||||
};
|
||||
|
||||
session.send_rpc_arg(129, &update_properties).await?;
|
||||
}
|
||||
|
||||
let dungeon_manager = session.context.dungeon_manager.borrow();
|
||||
|
||||
let avatars: Vec<u64> = arg.avatar_map.iter().map(|(_idx, uid)| *uid).collect();
|
||||
let (dungeon_uid, scene_uid) = *dungeon_manager
|
||||
.create_hollow(10001, 10010001, &avatars)
|
||||
.send_changes(session)
|
||||
.await?;
|
||||
|
||||
let quest_manager = session.context.quest_manager.borrow();
|
||||
quest_manager
|
||||
.add_quest_to_collection(
|
||||
dungeon_uid,
|
||||
QuestInfo::DungeonInner {
|
||||
id: 1001000101,
|
||||
finished_count: 0,
|
||||
collection_uid: 0,
|
||||
progress: 0,
|
||||
parent_quest_id: 10010001,
|
||||
state: QuestState::InProgress,
|
||||
finish_condition_progress: phashmap![],
|
||||
progress_time: 2111605,
|
||||
sort_id: 2000,
|
||||
},
|
||||
)
|
||||
.send_changes(session)
|
||||
.await?;
|
||||
|
||||
let ptc_enter_scene = dungeon_manager
|
||||
.enter_scene(scene_uid)?
|
||||
.send_changes(session)
|
||||
.await?
|
||||
.clone();
|
||||
|
||||
let hollow_grid_manager = session.context.hollow_grid_manager.borrow();
|
||||
hollow_grid_manager.init_default_map();
|
||||
|
||||
session
|
||||
.send_rpc_arg(
|
||||
124,
|
||||
&hollow_grid_manager.sync_hollow_maps(session.get_player_uid(), scene_uid),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let ptc_position_in_hollow_changed = PtcPositionInHollowChangedArg {
|
||||
player_uid: 1337,
|
||||
hollow_level: 1,
|
||||
position: hollow_grid_manager.get_cur_position_in_hollow(),
|
||||
};
|
||||
|
||||
session
|
||||
.send_rpc_arg(141, &ptc_position_in_hollow_changed)
|
||||
.await?;
|
||||
|
||||
let ptc_sync_hollow_event_info = PtcSyncHollowEventInfoArg {
|
||||
event_graph_uid: 3405096459205834,
|
||||
hollow_event_template_id: 1000108,
|
||||
event_graph_id: 1000108,
|
||||
updated_event: EventInfo {
|
||||
id: 1000,
|
||||
cur_action_id: 1001,
|
||||
action_move_path: vec![1001],
|
||||
state: EventState::WaitingClient,
|
||||
prev_state: EventState::Running,
|
||||
cur_action_info: ActionInfo::None {},
|
||||
cur_action_state: ActionState::Init,
|
||||
predicated_failed_actions: phashset![],
|
||||
stack_frames: Vec::new(),
|
||||
},
|
||||
specials: phashmap![],
|
||||
};
|
||||
|
||||
session
|
||||
.send_rpc_arg(210, &ptc_sync_hollow_event_info)
|
||||
.await?;
|
||||
|
||||
session.send_rpc_arg(118, &ptc_enter_scene).await?;
|
||||
session.send_rpc_ret(RpcStartHollowQuestRet::new()).await
|
||||
}
|
36
gameserver/src/net/handlers/login.rs
Normal file
36
gameserver/src/net/handlers/login.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use super::*;
|
||||
use crate::game::util;
|
||||
|
||||
const DEFAULT_ACCOUNT_ID: u64 = 1337;
|
||||
|
||||
pub async fn on_rpc_login_arg(session: &NetworkSession, arg: &RpcLoginArg) -> Result<()> {
|
||||
tracing::info!("Received rpc login arg: {}", arg.account_name);
|
||||
*session.get_account_mut() = util::create_default_account(DEFAULT_ACCOUNT_ID);
|
||||
|
||||
session
|
||||
.send_rpc_ret(RpcLoginRet::new(
|
||||
session.ns_prop_mgr.serialize_account_info(),
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn on_ptc_get_server_timestamp_arg(
|
||||
session: &NetworkSession,
|
||||
_arg: &PtcGetServerTimestampArg,
|
||||
) -> Result<()> {
|
||||
session
|
||||
.send_rpc_ret(PtcGetServerTimestampRet::new(
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as u64,
|
||||
0,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn on_rpc_keep_alive_arg(session: &NetworkSession, _arg: &RpcKeepAliveArg) -> Result<()> {
|
||||
session.send_rpc_ret(RpcKeepAliveRet::new()).await
|
||||
}
|
8
gameserver/src/net/handlers/mail.rs
Normal file
8
gameserver/src/net/handlers/mail.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use super::*;
|
||||
|
||||
pub async fn on_rpc_get_player_mails_arg(
|
||||
session: &NetworkSession,
|
||||
_arg: &RpcGetPlayerMailsArg,
|
||||
) -> Result<()> {
|
||||
session.send_rpc_ret(RpcGetPlayerMailsRet::new(0)).await
|
||||
}
|
25
gameserver/src/net/handlers/mod.rs
Normal file
25
gameserver/src/net/handlers/mod.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
mod battle;
|
||||
mod beginner_procedure;
|
||||
mod hollow;
|
||||
mod login;
|
||||
mod mail;
|
||||
mod plot_play;
|
||||
mod progression;
|
||||
mod role;
|
||||
mod world;
|
||||
mod yorozuya;
|
||||
|
||||
use super::NetworkSession;
|
||||
use anyhow::Result;
|
||||
use protocol::*;
|
||||
|
||||
pub use battle::*;
|
||||
pub use beginner_procedure::*;
|
||||
pub use hollow::*;
|
||||
pub use login::*;
|
||||
pub use mail::*;
|
||||
pub use plot_play::*;
|
||||
pub use progression::*;
|
||||
pub use role::*;
|
||||
pub use world::*;
|
||||
pub use yorozuya::*;
|
36
gameserver/src/net/handlers/plot_play.rs
Normal file
36
gameserver/src/net/handlers/plot_play.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use super::*;
|
||||
|
||||
pub async fn on_rpc_perform_trigger_arg(
|
||||
session: &NetworkSession,
|
||||
arg: &RpcPerformTriggerArg,
|
||||
) -> Result<()> {
|
||||
session
|
||||
.send_rpc_ret(RpcPerformTriggerRet::new(format!(
|
||||
"{}-{}",
|
||||
arg.perform_id, arg.perform_type
|
||||
)))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn on_rpc_perform_end_arg(
|
||||
session: &NetworkSession,
|
||||
_arg: &RpcPerformEndArg,
|
||||
) -> Result<()> {
|
||||
session.send_rpc_ret(RpcPerformEndRet::new()).await
|
||||
}
|
||||
|
||||
pub async fn on_rpc_finish_a_c_t_perform_show_arg(
|
||||
session: &NetworkSession,
|
||||
_arg: &RpcFinishACTPerformShowArg,
|
||||
) -> Result<()> {
|
||||
session
|
||||
.send_rpc_ret(RpcFinishACTPerformShowRet::new())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn on_rpc_perform_jump_arg(
|
||||
session: &NetworkSession,
|
||||
_arg: &RpcPerformJumpArg,
|
||||
) -> Result<()> {
|
||||
session.send_rpc_ret(RpcPerformJumpRet::new()).await
|
||||
}
|
8
gameserver/src/net/handlers/progression.rs
Normal file
8
gameserver/src/net/handlers/progression.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use super::*;
|
||||
|
||||
pub async fn on_rpc_close_level_chg_tips_arg(
|
||||
session: &NetworkSession,
|
||||
_arg: &RpcCloseLevelChgTipsArg,
|
||||
) -> Result<()> {
|
||||
session.send_rpc_ret(RpcCloseLevelChgTipsRet::new()).await
|
||||
}
|
25
gameserver/src/net/handlers/role.rs
Normal file
25
gameserver/src/net/handlers/role.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use super::*;
|
||||
|
||||
#[tracing::instrument(skip(session))]
|
||||
pub async fn on_rpc_mod_nick_name_arg(
|
||||
session: &NetworkSession,
|
||||
arg: &RpcModNickNameArg,
|
||||
) -> Result<()> {
|
||||
tracing::info!("creating character");
|
||||
|
||||
let mut player = session.get_player_mut();
|
||||
player.nick_name.replace(arg.nick_name.clone());
|
||||
player.avatar_id.replace(arg.avatar_id);
|
||||
|
||||
let player_info_changed = PtcPlayerInfoChangedArg {
|
||||
player_uid: player.uid.unwrap(),
|
||||
player_info: PlayerInfo {
|
||||
nick_name: Some(arg.nick_name.clone()),
|
||||
avatar_id: Some(arg.avatar_id),
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
|
||||
session.send_rpc_arg(101, &player_info_changed).await?;
|
||||
session.send_rpc_ret(RpcModNickNameRet::new()).await
|
||||
}
|
252
gameserver/src/net/handlers/world.rs
Normal file
252
gameserver/src/net/handlers/world.rs
Normal file
|
@ -0,0 +1,252 @@
|
|||
use qwer::{
|
||||
pdkhashmap, phashmap, phashset, PropertyDoubleKeyHashMap, PropertyHashMap, PropertyHashSet,
|
||||
};
|
||||
|
||||
use crate::game::{globals, util};
|
||||
|
||||
use super::*;
|
||||
|
||||
static UNLOCK_AVATARS: [i32; 12] = [
|
||||
1011, 1021, 1031, 1041, 1061, 1081, 1091, 1101, 1111, 1121, 1131, 1141,
|
||||
];
|
||||
|
||||
static UNLOCK_FEATURES: [i32; 35] = [
|
||||
1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1013, 1014, 1015, 1016, 1017,
|
||||
1018, 1019, 10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10012, 10013,
|
||||
10014, 10015, 10017, 10018, 10019,
|
||||
];
|
||||
|
||||
#[tracing::instrument(skip(session))]
|
||||
pub async fn on_rpc_run_event_graph_arg(
|
||||
session: &NetworkSession,
|
||||
arg: &RpcRunEventGraphArg,
|
||||
) -> Result<()> {
|
||||
tracing::info!("RunEventGraph requested");
|
||||
|
||||
let mut ptc_sync_event_info = PtcSyncEventInfoArg {
|
||||
owner_type: EventGraphOwnerType::SceneUnit,
|
||||
owner_uid: arg.owner_uid,
|
||||
updated_events: pdkhashmap![],
|
||||
};
|
||||
|
||||
ptc_sync_event_info.updated_events.insert(
|
||||
10000009,
|
||||
100,
|
||||
EventInfo {
|
||||
id: 100,
|
||||
cur_action_id: 101,
|
||||
action_move_path: Vec::from([101, 102, 101]),
|
||||
state: EventState::Initing,
|
||||
prev_state: EventState::WaitingClient,
|
||||
cur_action_info: ActionInfo::None {},
|
||||
cur_action_state: ActionState::Init,
|
||||
predicated_failed_actions: phashset![],
|
||||
stack_frames: Vec::new(),
|
||||
},
|
||||
);
|
||||
|
||||
session.send_rpc_arg(177, &ptc_sync_event_info).await?;
|
||||
session.send_rpc_ret(RpcRunEventGraphRet::new()).await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(session))]
|
||||
pub async fn on_rpc_interact_with_unit_arg(
|
||||
session: &NetworkSession,
|
||||
arg: &RpcInteractWithUnitArg,
|
||||
) -> Result<()> {
|
||||
tracing::info!("InteractWithUnit");
|
||||
|
||||
if arg.event_graph_id == 19900062 {
|
||||
let mut ptc_sync_event_info = PtcSyncEventInfoArg {
|
||||
owner_type: EventGraphOwnerType::SceneUnit,
|
||||
owner_uid: arg.unit_uid,
|
||||
updated_events: pdkhashmap![],
|
||||
};
|
||||
|
||||
ptc_sync_event_info.updated_events.insert(
|
||||
10000009,
|
||||
100,
|
||||
EventInfo {
|
||||
id: 100,
|
||||
cur_action_id: 101,
|
||||
action_move_path: Vec::from([101]),
|
||||
state: EventState::WaitingClient,
|
||||
prev_state: EventState::Running,
|
||||
cur_action_info: ActionInfo::None {},
|
||||
cur_action_state: ActionState::Init,
|
||||
predicated_failed_actions: phashset![],
|
||||
stack_frames: Vec::new(),
|
||||
},
|
||||
);
|
||||
|
||||
session.send_rpc_arg(177, &ptc_sync_event_info).await?;
|
||||
}
|
||||
|
||||
session.send_rpc_ret(RpcInteractWithUnitRet::new()).await
|
||||
}
|
||||
|
||||
pub async fn on_rpc_leave_cur_dungeon_arg(
|
||||
session: &NetworkSession,
|
||||
_arg: &RpcLeaveCurDungeonArg,
|
||||
) -> Result<()> {
|
||||
let dungeon_manager = session.context.dungeon_manager.borrow();
|
||||
if dungeon_manager.is_in_tutorial() {
|
||||
Box::pin(enter_main_city(session)).await?;
|
||||
}
|
||||
|
||||
// TODO: enter scene by back_scene_uid from cur DungeonInfo.
|
||||
|
||||
session.send_rpc_ret(RpcLeaveCurDungeonRet::new()).await
|
||||
}
|
||||
|
||||
pub async fn on_ptc_player_operation_arg(
|
||||
session: &NetworkSession,
|
||||
_arg: &PtcPlayerOperationArg,
|
||||
) -> Result<()> {
|
||||
session.send_rpc_ret(PtcPlayerOperationRet::new()).await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(session))]
|
||||
pub async fn on_rpc_save_pos_in_main_city_arg(
|
||||
session: &NetworkSession,
|
||||
arg: &RpcSavePosInMainCityArg,
|
||||
) -> Result<()> {
|
||||
tracing::info!("MainCity pos updated");
|
||||
|
||||
session.send_rpc_ret(RpcSavePosInMainCityRet::new()).await
|
||||
}
|
||||
|
||||
fn create_player(id: u64) -> PlayerInfo {
|
||||
let mut player = util::create_default_player(id);
|
||||
|
||||
let pos_in_main_city = player.pos_in_main_city.as_mut().unwrap();
|
||||
pos_in_main_city.initial_pos_id.replace(2);
|
||||
pos_in_main_city.position.replace(Vector3f {
|
||||
x: 30.31,
|
||||
y: 0.58002,
|
||||
z: 11.18,
|
||||
});
|
||||
|
||||
if globals::should_skip_tutorial() {
|
||||
let beginner_procedure = player.beginner_procedure_info.as_mut().unwrap();
|
||||
beginner_procedure.procedure_info.replace(6);
|
||||
player.nick_name.replace(String::from("xeondev"));
|
||||
player.avatar_id.replace(2021);
|
||||
}
|
||||
|
||||
player
|
||||
}
|
||||
|
||||
pub async fn enter_main_city(session: &NetworkSession) -> Result<()> {
|
||||
let dungeon_manager = session.context.dungeon_manager.borrow();
|
||||
let scene_unit_mgr = session.context.scene_unit_manager.borrow();
|
||||
|
||||
let hall_scene_uid = dungeon_manager.get_default_scene_uid();
|
||||
|
||||
session
|
||||
.send_rpc_arg(
|
||||
243,
|
||||
dungeon_manager
|
||||
.enter_scene_section(hall_scene_uid, 2)
|
||||
.unwrap(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
session
|
||||
.send_rpc_arg(180, &scene_unit_mgr.sync(hall_scene_uid, 2))
|
||||
.await?;
|
||||
|
||||
session
|
||||
.send_rpc_arg(
|
||||
118,
|
||||
dungeon_manager
|
||||
.enter_main_city()?
|
||||
.send_changes(session)
|
||||
.await?,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn on_rpc_enter_world_arg(
|
||||
session: &NetworkSession,
|
||||
_arg: &RpcEnterWorldArg,
|
||||
) -> Result<()> {
|
||||
let account = session.get_account();
|
||||
|
||||
let id = *account.players.as_ref().unwrap().iter().next().unwrap(); // get first id from list
|
||||
*session.get_player_mut() = create_player(id);
|
||||
|
||||
let item_manager = session.context.item_manager.borrow();
|
||||
|
||||
item_manager.add_resource(501, 120);
|
||||
item_manager.add_resource(10, 228);
|
||||
item_manager.add_resource(100, 1337);
|
||||
|
||||
for avatar_id in UNLOCK_AVATARS {
|
||||
item_manager.unlock_avatar(avatar_id);
|
||||
}
|
||||
|
||||
let unlock_manager = session.context.unlock_manager.borrow();
|
||||
for unlock_id in UNLOCK_FEATURES {
|
||||
unlock_manager.unlock(unlock_id);
|
||||
}
|
||||
|
||||
let dungeon_manager = session.context.dungeon_manager.borrow();
|
||||
dungeon_manager.create_hall(1);
|
||||
let scene_unit_mgr = session.context.scene_unit_manager.borrow();
|
||||
scene_unit_mgr.add_default_units();
|
||||
|
||||
let quest_manager = session.context.quest_manager.borrow();
|
||||
quest_manager.add_world_quest(QuestInfo::MainCity {
|
||||
id: 10020001,
|
||||
finished_count: 0,
|
||||
collection_uid: 0,
|
||||
progress: 0,
|
||||
parent_quest_id: 0,
|
||||
state: QuestState::InProgress,
|
||||
finish_condition_progress: phashmap![],
|
||||
progress_time: 2111012,
|
||||
sort_id: 1000,
|
||||
bound_npc_and_interact: phashmap![],
|
||||
});
|
||||
|
||||
quest_manager.add_world_quest(QuestInfo::Hollow {
|
||||
id: 10010002,
|
||||
finished_count: 0,
|
||||
collection_uid: 3405096459205774,
|
||||
progress: 0,
|
||||
parent_quest_id: 0,
|
||||
state: QuestState::Ready,
|
||||
sort_id: 1001,
|
||||
statistics: phashmap![],
|
||||
statistics_ext: pdkhashmap![],
|
||||
acquired_hollow_challenge_reward: 0,
|
||||
progress_time: 0,
|
||||
finish_condition_progress: phashmap![],
|
||||
dungeon_uid: 0,
|
||||
});
|
||||
|
||||
let yorozuya_quest_manager = session.context.yorozuya_quest_manager.borrow();
|
||||
yorozuya_quest_manager.add_hollow_quest(102, HollowQuestType::SideQuest, 10010002);
|
||||
|
||||
if globals::should_skip_tutorial() {
|
||||
Box::pin(enter_main_city(session)).await?;
|
||||
} else {
|
||||
let fresh_scene_uid = *dungeon_manager.create_fresh().unwrap();
|
||||
session
|
||||
.send_rpc_arg(
|
||||
118,
|
||||
dungeon_manager
|
||||
.enter_scene(fresh_scene_uid)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
session
|
||||
.send_rpc_ret(RpcEnterWorldRet::new(
|
||||
session.ns_prop_mgr.serialize_player_info(),
|
||||
))
|
||||
.await
|
||||
}
|
10
gameserver/src/net/handlers/yorozuya.rs
Normal file
10
gameserver/src/net/handlers/yorozuya.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use super::*;
|
||||
|
||||
pub async fn on_rpc_check_yorozuya_info_refresh_arg(
|
||||
session: &NetworkSession,
|
||||
_arg: &RpcCheckYorozuyaInfoRefreshArg,
|
||||
) -> Result<()> {
|
||||
session
|
||||
.send_rpc_ret(RpcCheckYorozuyaInfoRefreshRet::new())
|
||||
.await
|
||||
}
|
9
gameserver/src/net/mod.rs
Normal file
9
gameserver/src/net/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
pub mod gateway;
|
||||
mod handlers;
|
||||
mod packet;
|
||||
mod session;
|
||||
|
||||
pub use packet::Packet;
|
||||
pub use packet::RequestBody;
|
||||
pub use packet::ResponseBody;
|
||||
pub use session::NetworkSession;
|
270
gameserver/src/net/packet.rs
Normal file
270
gameserver/src/net/packet.rs
Normal file
|
@ -0,0 +1,270 @@
|
|||
use anyhow::Result;
|
||||
use paste::paste;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::net::TcpStream;
|
||||
use tracing::Instrument;
|
||||
|
||||
use protocol::*;
|
||||
use qwer::ProtocolHeader;
|
||||
|
||||
use super::handlers::*;
|
||||
use super::NetworkSession;
|
||||
|
||||
pub struct Packet {
|
||||
pub to_channel: u16,
|
||||
pub header: ProtocolHeader,
|
||||
pub body: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct RequestBody {
|
||||
pub protocol_id: u16,
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct ResponseBody {
|
||||
pub middleware_id: u16,
|
||||
pub middleware_error_code: u16,
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Packet {
|
||||
pub async fn read(stream: &mut TcpStream) -> std::io::Result<Self> {
|
||||
let to_channel = stream.read_u16_le().await?;
|
||||
let body_size = stream.read_u32_le().await? as usize;
|
||||
let header_size = stream.read_u16_le().await? as usize;
|
||||
|
||||
let mut header = vec![0; header_size];
|
||||
stream.read_exact(&mut header).await?;
|
||||
|
||||
let mut body = vec![0; body_size];
|
||||
stream.read_exact(&mut body).await?;
|
||||
|
||||
Ok(Self {
|
||||
to_channel,
|
||||
header: header.into(),
|
||||
body,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for RequestBody {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
let protocol_id = u16::from_le_bytes(value[0..2].try_into().unwrap());
|
||||
let payload_length = u32::from_be_bytes(value[2..6].try_into().unwrap()) as usize;
|
||||
let payload = value[6..payload_length + 6].to_vec();
|
||||
|
||||
Self {
|
||||
protocol_id,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RequestBody> for Vec<u8> {
|
||||
fn from(value: RequestBody) -> Self {
|
||||
let mut out = Self::new();
|
||||
|
||||
out.extend(value.protocol_id.to_le_bytes());
|
||||
out.extend((value.payload.len() as u32).to_be_bytes());
|
||||
out.extend(value.payload);
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ResponseBody> for Vec<u8> {
|
||||
fn from(value: ResponseBody) -> Self {
|
||||
let mut out = Self::with_capacity(4 + value.payload.len());
|
||||
out.extend(value.middleware_id.to_le_bytes());
|
||||
out.extend(value.middleware_error_code.to_le_bytes());
|
||||
out.extend(value.payload);
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! trait_handler {
|
||||
($($name:ident $protocol_id:expr;)*) => {
|
||||
#[allow(dead_code)]
|
||||
#[allow(unused_variables)]
|
||||
pub trait PacketHandler {
|
||||
$(
|
||||
paste! {
|
||||
async fn [<on_$name:snake>](session: &mut NetworkSession, arg: &$name) -> Result<()> {
|
||||
[<on_$name:snake>](session, arg).await
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
async fn on_message(session: &mut NetworkSession, protocol_id: u16, payload: Vec<u8>) -> Result<()> {
|
||||
use ::qwer::OctData;
|
||||
match protocol_id {
|
||||
$(
|
||||
$protocol_id => {
|
||||
let arg = $name::unmarshal_from(&mut &payload[..], 0)?;
|
||||
paste! {
|
||||
Self::[<on_$name:snake>](session, &arg)
|
||||
.instrument(tracing::info_span!(stringify!([<on_$name:snake>]), protocol_id = protocol_id))
|
||||
.await
|
||||
}
|
||||
}
|
||||
)*
|
||||
_ => {
|
||||
tracing::warn!("Unknown protocol id: {protocol_id}");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
trait_handler! {
|
||||
RpcLoginArg 100;
|
||||
// PtcAbilityPopText 239;
|
||||
// PtcAccountInfoChanged 102;
|
||||
// PtcAvatarMapChanged 246;
|
||||
// PtcBeforeGoToHollowLevel 145;
|
||||
// PtcCardDisable 217;
|
||||
// PtcChallengeQuestFinished 193;
|
||||
// PtcClientCommon 110;
|
||||
// PtcConfigUpdated 277;
|
||||
// PtcDungeonQuestFinished 148;
|
||||
// PtcDungeonQuestPrepareToFinish 136;
|
||||
// PtcEnterScene 118;
|
||||
// PtcEnterSceneBegin 200;
|
||||
// PtcEnterSceneEnd 224;
|
||||
// PtcEnterSection 243;
|
||||
// PtcFairyInfoChanged 134;
|
||||
// PtcFunctionSwitchMask 279;
|
||||
PtcGetServerTimestampArg 204;
|
||||
// PtcGoToHollowLevel 154;
|
||||
// PtcHollowBlackout 268;
|
||||
// PtcHollowGlobalEvent 138;
|
||||
// PtcHollowGrid 114;
|
||||
// PtcHollowPushBack 284;
|
||||
// PtcHollowQuestUnlockedByMainCityQuest 201;
|
||||
// PtcHpOrStressChanged 226;
|
||||
// PtcItemChanged 117;
|
||||
// PtcKickPlayer 184;
|
||||
// PtcPauseMainCityTime 116;
|
||||
// PtcPlayerInfoChangedArg 101;
|
||||
// PtcPlayerMailsReceived 222;
|
||||
// PtcPlayerMailsRemoved 225;
|
||||
PtcPlayerOperationArg 203;
|
||||
// PtcPopupWindow 206;
|
||||
// PtcPosition 176;
|
||||
// PtcPositionInHollowChanged 141;
|
||||
// PtcPrepareSection 115;
|
||||
// PtcPreventAddiction 270;
|
||||
// PtcPropertyChanged 129;
|
||||
// PtcQuestUnlocked 158;
|
||||
// PtcReceivedChatMessage_Player2Client 165;
|
||||
// PtcScenePropertyChanged 128;
|
||||
// PtcShowCardGenreTips 276;
|
||||
// PtcShowTips 207;
|
||||
// PtcShowUnlockIDTips 278;
|
||||
// PtcStaminaOverLevelPunish 147;
|
||||
// PtcSyncEventInfo 177;
|
||||
// PtcSyncHollowEventInfo 210;
|
||||
// PtcSyncHollowGridMaps 124;
|
||||
// PtcSyncSceneTime 249;
|
||||
// PtcSyncSceneUnit 180;
|
||||
// PtcTransformToHollowGrid 144;
|
||||
// PtcUnlock 196;
|
||||
// RpcAFKHollowQuest 241;
|
||||
RpcAdvanceBeginnerProcedureArg 171;
|
||||
// RpcAvatarAdvance 111;
|
||||
// RpcAvatarLevelUp 107;
|
||||
// RpcAvatarSkillLevelUp 197;
|
||||
// RpcAvatarStarUp 108;
|
||||
// RpcAvatarUnlockTalent 199;
|
||||
// RpcAwardAllPlayerMail 257;
|
||||
// RpcAwardPlayerMail 256;
|
||||
// RpcBattleRebegin 286;
|
||||
RpcBattleReportArg 125;
|
||||
// RpcBeginArchiveBattleQuest 137;
|
||||
RpcBeginnerbattleBeginArg 258;
|
||||
RpcBeginnerbattleEndArg 285;
|
||||
// RpcBeginnerbattleRebegin 250;
|
||||
// RpcBuyAutoRecoveryItem 167;
|
||||
// RpcBuyVHSCollection 269;
|
||||
RpcCheckYorozuyaInfoRefreshArg 245;
|
||||
// RpcClickHollowSystem 282;
|
||||
RpcCloseLevelChgTipsArg 244;
|
||||
// RpcCreatePlayer 104;
|
||||
// RpcDebugPay 216;
|
||||
// RpcDelNewMap 287;
|
||||
// RpcDelNewRamen 228;
|
||||
// RpcDressEquipment 112;
|
||||
// RpcEatRamen 283;
|
||||
RpcEndBattleArg 251;
|
||||
// RpcEndSlotMachine 186;
|
||||
// RpcEnterSection 175;
|
||||
RpcEnterWorldArg 105;
|
||||
// RpcEquipDecompose 170;
|
||||
// RpcEquipGacha 169;
|
||||
// RpcEquipLock 172;
|
||||
// RpcEquipmentLevelUp 130;
|
||||
// RpcEquipmentStarUp 131;
|
||||
RpcFinishACTPerformShowArg 185;
|
||||
// RpcFinishBlackout 267;
|
||||
// RpcFinishEventGraphPerformShow 187;
|
||||
// RpcFinishGraphInClient 146;
|
||||
// RpcGMCommand 113;
|
||||
// RpcGacha 173;
|
||||
// RpcGetArchiveReward 166;
|
||||
// RpcGetAuthKey 280;
|
||||
// RpcGetChatHistory_Client2Player 159;
|
||||
RpcGetPlayerMailsArg 221;
|
||||
// RpcGetShopInfo 122;
|
||||
// RpcGetYorozuyaInfo 182;
|
||||
// RpcGiveUpDungeonQuest 142;
|
||||
// RpcHollowChangeAffix 143;
|
||||
RpcHollowMoveArg 248;
|
||||
// RpcHollowShopping 213;
|
||||
RpcInteractWithUnitArg 181;
|
||||
// RpcItemConvert 281;
|
||||
RpcKeepAliveArg 149;
|
||||
RpcLeaveCurDungeonArg 140;
|
||||
// RpcLeaveWorld 190;
|
||||
// RpcLogin 100;
|
||||
// RpcLogout 103;
|
||||
// RpcMakeChoiceOfEvent 214;
|
||||
// RpcMakeInitiativeItem 234;
|
||||
RpcModNickNameArg 215;
|
||||
// RpcOpenVHSStore 135;
|
||||
RpcPerformEndArg 255;
|
||||
RpcPerformJumpArg 254;
|
||||
RpcPerformTriggerArg 253;
|
||||
// RpcPrepareNextHollowEnd 252;
|
||||
// RpcReadPlayerMail 263;
|
||||
// RpcReceiveVHSStoreReward 227;
|
||||
// RpcReenterWorld 150;
|
||||
// RpcRefreshShop 237;
|
||||
// RpcRefreshVHSTrending 235;
|
||||
// RpcRemoveHollowCurse 229;
|
||||
// RpcRemovePlayerMailsFromClient 264;
|
||||
RpcRunEventGraphArg 179;
|
||||
RpcRunHollowEventGraphArg 211;
|
||||
RpcSavePosInMainCityArg 202;
|
||||
// RpcSelectChallenge 236;
|
||||
// RpcSelectVHSCollection 219;
|
||||
// RpcSendChatMessage_Client2Player 163;
|
||||
// RpcSetBGM 273;
|
||||
// RpcSetMainCityObjectState 274;
|
||||
// RpcSetPlayerMailOld 265;
|
||||
// RpcShopping 230;
|
||||
RpcStartHollowQuestArg 183;
|
||||
// RpcSwitchHollowRank 198;
|
||||
// RpcUndressEquipment 109;
|
||||
// RpcUseInitiativeItem 220;
|
||||
// RpcWeaponDecompose 120;
|
||||
// RpcWeaponDress 132;
|
||||
// RpcWeaponLevelUp 126;
|
||||
// RpcWeaponLock 119;
|
||||
// RpcWeaponRefine 232;
|
||||
// RpcWeaponStarUp 127;
|
||||
// RpcWeaponUnDress 139;
|
||||
// RpcYorozuyaManualReceiveReward 168;
|
||||
}
|
148
gameserver/src/net/session.rs
Normal file
148
gameserver/src/net/session.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
use anyhow::Result;
|
||||
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;
|
||||
use tokio::sync::{Mutex, MutexGuard};
|
||||
|
||||
use crate::game::manager::net_stream;
|
||||
use crate::game::GameContext;
|
||||
|
||||
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 {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_player_uid(&self) -> u64 {
|
||||
self.get_player().uid.unwrap()
|
||||
}
|
||||
|
||||
pub fn get_account(&self) -> AtomicRef<AccountInfo> {
|
||||
self.ns_prop_mgr.account_info.borrow()
|
||||
}
|
||||
|
||||
pub fn get_player(&self) -> AtomicRef<PlayerInfo> {
|
||||
self.ns_prop_mgr.player_info.borrow()
|
||||
}
|
||||
|
||||
pub fn get_account_mut(&self) -> AtomicRefMut<'_, AccountInfo> {
|
||||
self.ns_prop_mgr.account_info.try_borrow_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_player_mut(&self) -> AtomicRefMut<'_, PlayerInfo> {
|
||||
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()),
|
||||
};
|
||||
tracing::info!(
|
||||
"Session ({}) bound to channel {channel_id}",
|
||||
self.client_addr
|
||||
);
|
||||
|
||||
loop {
|
||||
let packet = match Packet::read(&mut *self.client_socket().await).await {
|
||||
Ok(packet) => packet,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(()),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
let request: RequestBody = packet.body.into();
|
||||
|
||||
self.cur_rpc_uid = packet.header.rpc_arg_uid;
|
||||
Box::pin(Self::on_message(self, request.protocol_id, request.payload)).await?;
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_handshake(&mut self) -> Result<u16, std::io::Error> {
|
||||
self.client_socket().await.read_u16_le().await
|
||||
}
|
||||
|
||||
pub async fn send_rpc_ret(&self, data: impl OctData) -> Result<()> {
|
||||
let header = ProtocolHeader {
|
||||
is_rpc_ret: true,
|
||||
rpc_arg_uid: self.cur_rpc_uid,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut payload = Vec::new();
|
||||
let mut cursor = Cursor::new(&mut payload);
|
||||
data.marshal_to(&mut cursor, 0)?;
|
||||
|
||||
let body: Vec<u8> = ResponseBody {
|
||||
middleware_id: 0,
|
||||
middleware_error_code: 0,
|
||||
payload,
|
||||
}
|
||||
.into();
|
||||
|
||||
let header_buf: Vec<u8> = header.into();
|
||||
let mut packet = Vec::new();
|
||||
packet.extend(0_u16.to_le_bytes());
|
||||
packet.extend((body.len() as u32).to_le_bytes());
|
||||
packet.extend((header_buf.len() as u16).to_le_bytes());
|
||||
packet.extend(header_buf);
|
||||
packet.extend(body);
|
||||
|
||||
self.client_socket().await.write_all(&packet).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_rpc_arg(&self, protocol_id: u16, data: &impl OctData) -> Result<()> {
|
||||
let header: Vec<u8> = ProtocolHeader::default().into();
|
||||
|
||||
let mut payload = Vec::new();
|
||||
let mut cursor = Cursor::new(&mut payload);
|
||||
data.marshal_to(&mut cursor, 0)?;
|
||||
|
||||
let body: Vec<u8> = RequestBody {
|
||||
protocol_id,
|
||||
payload,
|
||||
}
|
||||
.into();
|
||||
|
||||
let mut packet = Vec::new();
|
||||
packet.extend(0_u16.to_le_bytes());
|
||||
packet.extend(((body.len() + 2) as u32).to_le_bytes());
|
||||
packet.extend((header.len() as u16).to_le_bytes());
|
||||
packet.extend(header);
|
||||
packet.extend(body);
|
||||
packet.extend(0_u16.to_le_bytes()); // middleware count
|
||||
|
||||
self.client_socket().await.write_all(&packet).await?;
|
||||
tracing::info!("Ptc with protocol id {protocol_id} sent");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Auto implemented
|
||||
impl PacketHandler for NetworkSession {}
|
9
protocol/Cargo.toml
Normal file
9
protocol/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "protocol"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
byteorder.workspace = true
|
||||
hex.workspace = true
|
||||
qwer.workspace = true
|
585
protocol/src/enums.rs
Normal file
585
protocol/src/enums.rs
Normal file
|
@ -0,0 +1,585 @@
|
|||
use super::*;
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(i32)]
|
||||
pub enum ErrorCode {
|
||||
Fail = -1,
|
||||
Success = 0,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i16)]
|
||||
pub enum HollowQuestType {
|
||||
Common = 0,
|
||||
MainQuest = 1,
|
||||
SideQuest = 2,
|
||||
Urgent = 3,
|
||||
UrgentSupplement = 4,
|
||||
Challenge = 5,
|
||||
ChallengeChaos = 6,
|
||||
AvatarSide = 7,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum FairyState {
|
||||
Unlock = 0,
|
||||
Close = 1,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(i16)]
|
||||
pub enum FightRanking {
|
||||
None = 0,
|
||||
D = 1,
|
||||
C = 2,
|
||||
B = 3,
|
||||
A = 4,
|
||||
S = 5,
|
||||
}
|
||||
|
||||
#[derive(OctData, Hash, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(i16)]
|
||||
pub enum BattleRewardType {
|
||||
Client = 1,
|
||||
BattleEvt = 2,
|
||||
Ext = 3,
|
||||
Fight = 4,
|
||||
Challenge = 5,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(i16)]
|
||||
pub enum MailState {
|
||||
New = 0,
|
||||
Old = 1,
|
||||
Read = 2,
|
||||
Awarded = 3,
|
||||
Removed = 4,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(i16)]
|
||||
pub enum HollowBattleEventType {
|
||||
Default = 0,
|
||||
Normal = 1,
|
||||
Elite = 2,
|
||||
Boss = 3,
|
||||
LevelEnd = 4,
|
||||
LevelFin = 5,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i16)]
|
||||
pub enum QuestType {
|
||||
ArchiveFile = 1,
|
||||
DungeonInner = 2,
|
||||
Hollow = 3,
|
||||
Manual = 4,
|
||||
MainCity = 5,
|
||||
HollowChallenge = 6,
|
||||
ArchiveBattle = 7,
|
||||
Knowledge = 8,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(i16)]
|
||||
pub enum EventState {
|
||||
Initing = 0,
|
||||
Running = 1,
|
||||
Pause = 2,
|
||||
WaitingMsg = 3,
|
||||
WaitingClient = 4,
|
||||
Finished = 5,
|
||||
Error = 6,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(i16)]
|
||||
pub enum ActionState {
|
||||
Init = 0,
|
||||
Running = 1,
|
||||
Finished = 2,
|
||||
Error = 3,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(u8)]
|
||||
pub enum DungeonContentDropPoolType {
|
||||
Card = 0,
|
||||
BaneCard = 1,
|
||||
Arcana = 2,
|
||||
Blessing = 3,
|
||||
Curse = 4,
|
||||
Reward = 5,
|
||||
HollowItem = 6,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(i16)]
|
||||
pub enum ReportType {
|
||||
Fairy = 0,
|
||||
Dialog = 1,
|
||||
Task = 2,
|
||||
DialogInFairy = 3,
|
||||
}
|
||||
|
||||
#[derive(OctData, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u16)]
|
||||
pub enum UIType {
|
||||
Default = 0,
|
||||
None = 1,
|
||||
HollowQuest = 2,
|
||||
Archive = 3,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i16)]
|
||||
pub enum ACTPerformShowMoment {
|
||||
Begin = 0,
|
||||
End = 1,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i16)]
|
||||
pub enum HollowSystemType {
|
||||
Card = 1,
|
||||
Menu = 2,
|
||||
Curse = 3,
|
||||
Bag = 4,
|
||||
HollowItem = 5,
|
||||
HollowResultPage = 6,
|
||||
CurseInfo = 7,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(i16)]
|
||||
pub enum HollowSystemUIState {
|
||||
Normal = 0,
|
||||
Close = 1,
|
||||
Brighten = 2,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i16)]
|
||||
pub enum HollowShopType {
|
||||
All = 0,
|
||||
Item = 1,
|
||||
Card = 2,
|
||||
Curse = 3,
|
||||
HollowItem = 4,
|
||||
Discount = 5,
|
||||
Gachashop = 6,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i16)]
|
||||
pub enum TimePeriodType {
|
||||
Random = 0,
|
||||
Morning = 1,
|
||||
Evening = 2,
|
||||
Night = 3,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i16)]
|
||||
pub enum WeatherType {
|
||||
None = -1,
|
||||
Random = 0,
|
||||
SunShine = 1,
|
||||
Fog = 2,
|
||||
Cloudy = 3,
|
||||
Rain = 4,
|
||||
Thunder = 5,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i16)]
|
||||
pub enum PropertyType {
|
||||
Hp = 1,
|
||||
Armor = 2,
|
||||
Shield = 3,
|
||||
Stun = 4,
|
||||
Sp = 5,
|
||||
Usp = 6,
|
||||
Dead = 99,
|
||||
HpMax = 111,
|
||||
ArmorMax = 112,
|
||||
ShieldMax = 113,
|
||||
StunMax = 114,
|
||||
SpMax = 115,
|
||||
UspMax = 116,
|
||||
Atk = 121,
|
||||
BreakStun = 122,
|
||||
Def = 131,
|
||||
Crit = 201,
|
||||
CritRes = 202,
|
||||
CritDmg = 211,
|
||||
CritDmgRes = 212,
|
||||
Pen = 231,
|
||||
PenValue = 232,
|
||||
Endurance = 301,
|
||||
SpRecover = 305,
|
||||
HpHealRatio = 306,
|
||||
AddedDamageRatio = 307,
|
||||
HpMaxBattle = 1111,
|
||||
ArmorMaxBattle = 1112,
|
||||
ShieldMaxBattle = 1113,
|
||||
StunMaxBattle = 1114,
|
||||
SpBattle = 1115,
|
||||
// UspBattle = 1115,
|
||||
AtkBattle = 1121,
|
||||
BreakStunBattle = 1122,
|
||||
DefBattle = 1131,
|
||||
CritBattle = 1201,
|
||||
CritResBattle = 1202,
|
||||
CritDmgBattle = 1211,
|
||||
CritDmgResBattle = 1212,
|
||||
PenRatioBattle = 1231,
|
||||
PenDeltaBattle = 1232,
|
||||
EnduranceBattle = 1301,
|
||||
SpRecoverBattle = 1305,
|
||||
HpHealRatioBattle = 1306,
|
||||
AddedDamageRatioBattle = 1307,
|
||||
HpMaxBase = 11101,
|
||||
ArmorMaxBase = 11201,
|
||||
ShieldMaxBase = 11301,
|
||||
AtkBase = 12101,
|
||||
DefBase = 13101,
|
||||
CritBase = 20101,
|
||||
CritResBase = 20201,
|
||||
CritDmgBase = 21101,
|
||||
CritDmgResBase = 21201,
|
||||
PenBase = 23101,
|
||||
PenValueBase = 23201,
|
||||
BreakStunBase = 12201,
|
||||
StunMaxBase = 11401,
|
||||
SpMaxBase = 11501,
|
||||
EnduranceBase = 30101,
|
||||
UspMaxBase = 11601,
|
||||
SpRecoverBase = 30501,
|
||||
HpHealRatio1 = 30601,
|
||||
AddedDamageRatio1 = 30701,
|
||||
HpMaxRatio = 11102,
|
||||
ArmorMaxRatio = 11202,
|
||||
ShieldMaxRatio = 11302,
|
||||
AtkRatio = 12102,
|
||||
DefRatio = 13102,
|
||||
BreakStunRatio = 12202,
|
||||
StunMaxRatio = 11402,
|
||||
EnduranceRatio = 30102,
|
||||
SpRecoverRatio = 30502,
|
||||
HpMaxDelta = 11103,
|
||||
ArmorMaxDelta = 11203,
|
||||
ShieldMaxDelta = 11303,
|
||||
AtkDelta = 12103,
|
||||
DefDelta = 13103,
|
||||
BreakStunDelta = 12203,
|
||||
StunMaxDelta = 11403,
|
||||
SpMaxDelta = 11503,
|
||||
CritDelta = 20103,
|
||||
CritResDelta = 20203,
|
||||
CritDmgDelta = 21103,
|
||||
CritDmgResDelta = 21203,
|
||||
UspMaxDelta = 11603,
|
||||
PenDelta = 23103,
|
||||
PenValueDelta = 23203,
|
||||
EnduranceDelta = 30103,
|
||||
SpRecoverDelta = 30503,
|
||||
HpHealRatio3 = 30603,
|
||||
AddedDamageRatio3 = 30703,
|
||||
HpMaxRatioRL = 11104,
|
||||
ArmorMaxRatioRL = 11204,
|
||||
ShieldMaxRatioRL = 11304,
|
||||
AtkRatioRL = 12104,
|
||||
DefRatioRL = 13104,
|
||||
HpMaxDeltaRL = 11105,
|
||||
ArmorMaxDeltaRL = 11205,
|
||||
ShieldMaxDeltaRL = 11305,
|
||||
AtkDeltaRL = 12105,
|
||||
DefDeltaRL = 13105,
|
||||
CritRL = 20105,
|
||||
CritResRL = 20205,
|
||||
CritDmgRL = 21105,
|
||||
CritDmgResRL = 21205,
|
||||
PenRatioRL = 23105,
|
||||
PenDeltaRL = 23205,
|
||||
BreakStunRatioRL = 12204,
|
||||
BreakStunDeltaRL = 12205,
|
||||
StunMaxRatioRL = 11404,
|
||||
// StunMaxDeltaRL = 11404,
|
||||
SpMaxDeltaRL = 11505,
|
||||
UspMaxDeltaRL = 11605,
|
||||
EnduranceRatioRL = 30104,
|
||||
EnduranceDeltaRL = 30105,
|
||||
SpRecoverRatioRL = 30504,
|
||||
SpRecoverDeltaRL = 30505,
|
||||
HpHealRatioRL = 30605,
|
||||
AddedDamageRatioRL = 30705,
|
||||
MapHpreserveMaxhp = 10320,
|
||||
MapHpreserveCurhp = 10330,
|
||||
MapHpreserveAbsolute = 10340,
|
||||
ActorMaxCurHP = 10350,
|
||||
EnumCount = 10351,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(i16)]
|
||||
pub enum ScenePropertyType {
|
||||
Stamina = 1001,
|
||||
StaminaMax = 1002,
|
||||
StaminaRatio = 1003,
|
||||
StaminaDelta = 1004,
|
||||
GoldRatio = 1005,
|
||||
GoldDelta = 1006,
|
||||
CardRWeight = 1007,
|
||||
CardRWeightRatio = 1008,
|
||||
CardSRWeight = 1009,
|
||||
CardSRWeightRatio = 1010,
|
||||
CardSSRWeight = 1011,
|
||||
CardSSRWeightRatio = 1012,
|
||||
Mobility = 1013,
|
||||
BuffTurn = 1014,
|
||||
ForbiddenStamina = 1015,
|
||||
ForbiddenGold = 1016,
|
||||
OptionNum = 1017,
|
||||
ShopPrice = 1018,
|
||||
StaminaIncrease = 1019,
|
||||
StaminaOverLevel = 1020,
|
||||
DropRate = 1021,
|
||||
BanCharacter1 = 1022,
|
||||
BanCharacter2 = 1023,
|
||||
BanCharacter3 = 1024,
|
||||
PlayerView = 1025,
|
||||
ActorAddedDamageRatio = 1030,
|
||||
ActorDamageTakeRatio = 1031,
|
||||
MapHpreserveMaxhp = 1032,
|
||||
MapHpreserveCurhp = 1033,
|
||||
MapHpreserveAbsolute = 1034,
|
||||
ActorMaxCurHP = 1035,
|
||||
ShopPriceDelta = 1036,
|
||||
ShopPriceOverwriteCard = 1037,
|
||||
ShopPriceOverwriteItem = 1038,
|
||||
CardOptionHideNum = 1039,
|
||||
CardOptionForbidNum = 1040,
|
||||
HealingRatio = 1041,
|
||||
DinyRatio = 1042,
|
||||
Weather = 1043,
|
||||
TimePeriod = 1044,
|
||||
ShopPriceOverwriteCurse = 1045,
|
||||
ShopPriceOverwriteHollowItem = 1046,
|
||||
ShopPriceOverwriteDiscount = 1047,
|
||||
ShopPriceOverwriteGachashop = 1048,
|
||||
}
|
||||
|
||||
macro_rules! flag {
|
||||
($repr:ty, $(#[$attr:meta])* $name:ident { $($flag:ident = $value:expr,)* }) => {
|
||||
$(#[$attr])*
|
||||
#[repr($repr)]
|
||||
pub enum $name {
|
||||
$($flag = $value,)*
|
||||
}
|
||||
|
||||
impl From<$name> for $repr {
|
||||
fn from(flag: $name) -> $repr {
|
||||
flag as $repr
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$repr> for $name {
|
||||
fn from(flag: $repr) -> $name {
|
||||
match flag {
|
||||
$($value => $name::$flag,)*
|
||||
_ => panic!("invalid flag value: {}", flag),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
#[must_use]
|
||||
pub fn unpack_flags(val: $repr) -> Vec<$name> {
|
||||
let mut flags = Vec::new();
|
||||
let mut val = val;
|
||||
let mut i = 0 as $repr;
|
||||
while val > 0 {
|
||||
if val & 1 == 1 {
|
||||
flags.push(unsafe { std::mem::transmute(i) });
|
||||
}
|
||||
val >>= 1;
|
||||
i += 1;
|
||||
}
|
||||
flags
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn pack_flags(flags: &[Self]) -> $repr {
|
||||
flags.iter().fold(0, |acc, &flag| acc | (flag as $repr))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
flag! {
|
||||
u32,
|
||||
#[derive(OctData, Clone, Debug, Copy)]
|
||||
HollowGridFlag {
|
||||
Core = 1,
|
||||
CanMove = 2,
|
||||
Travelled = 4,
|
||||
ShowEventType = 8,
|
||||
ShowEventID = 16,
|
||||
CanTriggerEvent = 32,
|
||||
Visible = 64,
|
||||
VisibleAtGridAround = 128,
|
||||
VisibleByTriggerEvent = 256,
|
||||
SyncToClient = 512,
|
||||
Door = 1024,
|
||||
CanTriggerMultiTimes = 2048,
|
||||
TemporaryVisibleAtAround = 4096,
|
||||
Unlocked = 8192,
|
||||
Brighten = 16384,
|
||||
Guide = 32768,
|
||||
Target = 65536,
|
||||
BrightenOnlyVisible = 131072,
|
||||
Unstable = 262144,
|
||||
}
|
||||
}
|
||||
|
||||
flag! {
|
||||
u8,
|
||||
#[derive(OctData, Clone, Debug, Copy)]
|
||||
HollowGridLink {
|
||||
None = 0,
|
||||
Up = 1,
|
||||
Down = 2,
|
||||
Right = 4,
|
||||
Left = 8,
|
||||
All = 15,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(i16)]
|
||||
pub enum NodeState {
|
||||
All = 0,
|
||||
Locked = 1,
|
||||
Unlocked = 2,
|
||||
Finished = 3,
|
||||
ShowEvent = 4,
|
||||
Door = 5,
|
||||
Brighten = 6,
|
||||
Guide = 7,
|
||||
Target = 8,
|
||||
BrightenOnlyVisible = 9,
|
||||
Unstable = 10,
|
||||
EnumCount = 11,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(i16)]
|
||||
pub enum NodeVisible {
|
||||
All = 0,
|
||||
Visible = 1,
|
||||
VisibleAtGridAround = 2,
|
||||
VisibleByTriggerEvent = 3,
|
||||
TemporaryVisibleAtAround = 4,
|
||||
EnumCount = 5,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(i16)]
|
||||
pub enum HollowEventType {
|
||||
None = 0,
|
||||
All = 1,
|
||||
Begin = 10,
|
||||
End = 20,
|
||||
InteractEnd = 21,
|
||||
BattleEnd = 22,
|
||||
ChangeLevelInteract = 23,
|
||||
ChangeLevelFight = 24,
|
||||
Battle = 30,
|
||||
BattleNormal = 31,
|
||||
BattleElite = 32,
|
||||
BattleBoss = 33,
|
||||
Dialog = 40,
|
||||
DialogPositive = 41,
|
||||
DialogNegative = 42,
|
||||
DialogSpecial = 43,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(i16)]
|
||||
pub enum HollowShopCurrency {
|
||||
Coin = 1,
|
||||
Curse = 2,
|
||||
Random = 3,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(i16)]
|
||||
pub enum QuestState {
|
||||
Unlocked = 0,
|
||||
Ready = 10,
|
||||
InProgress = 1,
|
||||
ToFinish = 2,
|
||||
Finished = 3,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(u8)]
|
||||
pub enum QuestStatisticsType {
|
||||
ArrivedLevel = 1,
|
||||
EventCount = 2,
|
||||
CostTime = 3,
|
||||
KilledEnemyCount = 4,
|
||||
ArcanaCount = 5,
|
||||
TarotCardCount = 6,
|
||||
StaminaOverLevelTimes = 7,
|
||||
RebornTimes = 8,
|
||||
FinishedEventTypeCount = 9,
|
||||
FinishedEventIDCount = 10,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(i16)]
|
||||
pub enum System {
|
||||
HollowQuestUI = 0,
|
||||
VHSUI = 1,
|
||||
RoleUI = 2,
|
||||
SmithyUI = 3,
|
||||
PackageUI = 4,
|
||||
TeleportUI = 5,
|
||||
YorozuyaManualUI = 6,
|
||||
VHSStoreUI = 7,
|
||||
RamenUI = 8,
|
||||
WorkbenchUI = 9,
|
||||
GroceryUI = 10,
|
||||
VideoshopUI = 11,
|
||||
SwitchOfStoryMode = 12,
|
||||
SwitchOfQTE = 13,
|
||||
LineupSelect = 14,
|
||||
UseStoryMode = 15,
|
||||
UseManualQTEMode = 16,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(i16)]
|
||||
pub enum InteractTarget {
|
||||
NPC = 0,
|
||||
TriggerBox = 1,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(i16)]
|
||||
pub enum EventGraphOwnerType {
|
||||
Scene = 0,
|
||||
Section = 1,
|
||||
SceneUnit = 2,
|
||||
Hollow = 3,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(i16)]
|
||||
pub enum Operator {
|
||||
Enter = 0,
|
||||
}
|
13
protocol/src/lib.rs
Normal file
13
protocol/src/lib.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use qwer::{OctData, PropertyDoubleKeyHashMap, PropertyHashMap, PropertyHashSet};
|
||||
|
||||
mod enums;
|
||||
mod polymorphic;
|
||||
mod rpc_ptc;
|
||||
mod structs;
|
||||
|
||||
pub use enums::*;
|
||||
pub use polymorphic::*;
|
||||
pub use rpc_ptc::*;
|
||||
pub use structs::*;
|
905
protocol/src/polymorphic.rs
Normal file
905
protocol/src/polymorphic.rs
Normal file
|
@ -0,0 +1,905 @@
|
|||
use super::*;
|
||||
|
||||
macro_rules! polymorphic_scene_unit_protocol_info {
|
||||
(enum $name:ident { $($variant:ident { $($field:ident: $ty:ty),* $(,)? } = $tag:expr,)* }) => {
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(u16)]
|
||||
#[base = 2]
|
||||
pub enum $name {
|
||||
$(
|
||||
$variant {
|
||||
uid: u64,
|
||||
tag: i32,
|
||||
$($field: $ty),*
|
||||
} = $tag,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
#[must_use]
|
||||
pub const fn get_uid(&self) -> u64 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { uid, .. } => *uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_uid(&mut self, uid: u64) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { uid: ref mut u, .. } => *u = uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_tag(&self) -> i32 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { tag, .. } => *tag,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_tag(&mut self, tag: i32) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { tag: ref mut t, .. } => *t = tag,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! polymorphic_scene_info {
|
||||
(enum $name:ident { $($variant:ident { $($field:ident: $ty:ty),* $(,)? } = $tag:expr,)* }) => {
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(u16)]
|
||||
#[base = 11]
|
||||
pub enum $name {
|
||||
$(
|
||||
$variant {
|
||||
uid: u64,
|
||||
id: i32,
|
||||
dungeon_uid: u64,
|
||||
end_timestamp: u64,
|
||||
back_scene_uid: u64,
|
||||
entered_times: u16,
|
||||
section_id: i32,
|
||||
open_ui: UIType,
|
||||
to_be_destroyed: bool,
|
||||
camera_x: u32,
|
||||
camera_y: u32,
|
||||
$($field: $ty),*
|
||||
} = $tag,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
#[must_use]
|
||||
pub const fn get_uid(&self) -> u64 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { uid, .. } => *uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_uid(&mut self, uid: u64) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { uid: ref mut u, .. } => *u = uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_id(&self) -> i32 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { id, .. } => *id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_id(&mut self, id: i32) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { id: ref mut i, .. } => *i = id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_dungeon_uid(&self) -> u64 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { dungeon_uid, .. } => *dungeon_uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_dungeon_uid(&mut self, dungeon_uid: u64) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { dungeon_uid: ref mut d, .. } => *d = dungeon_uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_end_timestamp(&self) -> u64 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { end_timestamp, .. } => *end_timestamp,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_end_timestamp(&mut self, end_timestamp: u64) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { end_timestamp: ref mut e, .. } => *e = end_timestamp,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_back_scene_uid(&self) -> u64 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { back_scene_uid, .. } => *back_scene_uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_back_scene_uid(&mut self, back_scene_uid: u64) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { back_scene_uid: ref mut b, .. } => *b = back_scene_uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_entered_times(&self) -> u16 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { entered_times, .. } => *entered_times,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_entered_times(&mut self, entered_times: u16) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { entered_times: ref mut e, .. } => *e = entered_times,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_section_id(&self) -> i32 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { section_id, .. } => *section_id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_section_id(&mut self, section_id: i32) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { section_id: ref mut s, .. } => *s = section_id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_open_ui(&self) -> UIType {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { open_ui, .. } => *open_ui,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_open_ui(&mut self, open_ui: UIType) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { open_ui: ref mut o, .. } => *o = open_ui,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_to_be_destroyed(&self) -> bool {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { to_be_destroyed, .. } => *to_be_destroyed,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_to_be_destroyed(&mut self, to_be_destroyed: bool) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { to_be_destroyed: ref mut t, .. } => *t = to_be_destroyed,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_camera_x(&self) -> u32 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { camera_x, .. } => *camera_x,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_camera_x(&mut self, camera_x: u32) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { camera_x: ref mut c, .. } => *c = camera_x,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_camera_y(&self) -> u32 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { camera_y, .. } => *camera_y,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_camera_y(&mut self, camera_y: u32) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { camera_y: ref mut c, .. } => *c = camera_y,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! polymorphic_item_info {
|
||||
(enum $name:ident { $($variant:ident { $($field:ident: $ty:ty),* $(,)? } = $tag:expr,)* }) => {
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(u16)]
|
||||
#[base = 5]
|
||||
pub enum $name {
|
||||
$(
|
||||
$variant {
|
||||
uid: u64,
|
||||
id: i32,
|
||||
count: i32,
|
||||
package: u16,
|
||||
first_get_time: u64,
|
||||
$($field: $ty),*
|
||||
} = $tag,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
#[must_use]
|
||||
pub const fn get_uid(&self) -> u64 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { uid, .. } => *uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_uid(&mut self, uid: u64) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { uid: ref mut u, .. } => *u = uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_id(&self) -> i32 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { id, .. } => *id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_id(&mut self, id: i32) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { id: ref mut i, .. } => *i = id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_count(&self) -> i32 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { count, .. } => *count,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_count(&mut self, count: i32) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { count: ref mut c, .. } => *c = count,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_package(&self) -> u16 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { package, .. } => *package,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_package(&mut self, package: u16) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { package: ref mut p, .. } => *p = package,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_first_get_time(&self) -> u64 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { first_get_time, .. } => *first_get_time,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_first_get_time(&mut self, first_get_time: u64) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { first_get_time: ref mut f, .. } => *f = first_get_time,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! polymorphic_dungeon_table_ext {
|
||||
(enum $name:ident { $($variant:ident { $($field:ident: $ty:ty),* $(,)? } = $tag:expr,)* }) => {
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(u16)]
|
||||
#[base = 0]
|
||||
pub enum $name {
|
||||
$(
|
||||
$variant {
|
||||
$($field: $ty),*
|
||||
} = $tag,
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! polymorphic_scene_table_ext {
|
||||
(enum $name:ident { $($variant:ident { $($field:ident: $ty:ty),* $(,)? } = $tag:expr,)* }) => {
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(u16)]
|
||||
#[base = 1]
|
||||
pub enum $name {
|
||||
$(
|
||||
$variant {
|
||||
event_graphs_info: EventGraphsInfo,
|
||||
$($field: $ty),*
|
||||
} = $tag,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
#[must_use]
|
||||
pub const fn get_event_graphs_info(&self) -> &EventGraphsInfo {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { event_graphs_info, .. } => event_graphs_info,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_event_graphs_info(&mut self, event_graphs_info: EventGraphsInfo) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { event_graphs_info: ref mut e, .. } => *e = event_graphs_info,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! polymorphic_section_info_ext {
|
||||
(enum $name:ident { $($variant:ident { $($field:ident: $ty:ty),* $(,)? } = $tag:expr,)* }) => {
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(u16)]
|
||||
#[base = 1]
|
||||
pub enum $name {
|
||||
$(
|
||||
$variant {
|
||||
destroy_npc_when_no_player: PropertyHashSet<u64>,
|
||||
$($field: $ty),*
|
||||
} = $tag,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
#[must_use]
|
||||
pub const fn get_destroy_npc_when_no_player(&self) -> &PropertyHashSet<u64> {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { destroy_npc_when_no_player, .. } => destroy_npc_when_no_player,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_destroy_npc_when_no_player(&mut self, destroy_npc_when_no_player: PropertyHashSet<u64>) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { destroy_npc_when_no_player: ref mut d, .. } => *d = destroy_npc_when_no_player,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! polymorphic_action_info {
|
||||
(enum $name:ident { $($variant:ident { $($field:ident: $ty:ty),* $(,)? } = $tag:expr,)* }) => {
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(u16)]
|
||||
#[base = 0]
|
||||
pub enum $name {
|
||||
$(
|
||||
$variant {
|
||||
$($field: $ty),*
|
||||
} = $tag,
|
||||
)*
|
||||
#[polymorphic_none]
|
||||
None {} = 0xFFFF, // weird detail, polymorphism can be empty.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! polymorphic_event_graph_info {
|
||||
(enum $name:ident { $($variant:ident { $($field:ident: $ty:ty),* $(,)? } = $tag:expr,)* }) => {
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(u16)]
|
||||
#[base = 6]
|
||||
pub enum $name {
|
||||
$(
|
||||
$variant {
|
||||
config_id: i32,
|
||||
events_info: PropertyHashMap<i32, EventInfo>,
|
||||
specials: PropertyHashMap<String, u64>,
|
||||
is_new: bool,
|
||||
finished: bool,
|
||||
list_specials: PropertyHashMap<String, Vec<u64>>,
|
||||
$($field: $ty),*
|
||||
} = $tag,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
#[must_use]
|
||||
pub const fn get_config_id(&self) -> i32 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { config_id, .. } => *config_id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_config_id(&mut self, config_id: i32) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { config_id: ref mut c, .. } => *c = config_id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_events_info(&self) -> &PropertyHashMap<i32, EventInfo> {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { events_info, .. } => events_info,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_events_info(&mut self, events_info: PropertyHashMap<i32, EventInfo>) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { events_info: ref mut e, .. } => *e = events_info,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_specials(&self) -> &PropertyHashMap<String, u64> {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { specials, .. } => specials,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_specials(&mut self, specials: PropertyHashMap<String, u64>) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { specials: ref mut s, .. } => *s = specials,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_is_new(&self) -> bool {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { is_new, .. } => *is_new,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_is_new(&mut self, is_new: bool) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { is_new: ref mut i, .. } => *i = is_new,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_finished(&self) -> bool {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { finished, .. } => *finished,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_finished(&mut self, finished: bool) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { finished: ref mut f, .. } => *f = finished,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_list_specials(&self) -> &PropertyHashMap<String, Vec<u64>> {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { list_specials, .. } => list_specials,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_list_specials(&mut self, list_specials: PropertyHashMap<String, Vec<u64>>) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { list_specials: ref mut l, .. } => *l = list_specials,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! polymorphic_quest_info {
|
||||
(enum $name:ident { $($variant:ident { $($field:ident: $ty:ty),* $(,)? } = $tag:expr,)* }) => {
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
#[repr(u16)]
|
||||
#[base = 9]
|
||||
pub enum $name {
|
||||
$(
|
||||
$variant {
|
||||
id: i32,
|
||||
finished_count: i32,
|
||||
collection_uid: u64,
|
||||
progress: u16,
|
||||
parent_quest_id: i32,
|
||||
state: QuestState,
|
||||
finish_condition_progress: PropertyHashMap<i32, i32>,
|
||||
progress_time: u32,
|
||||
sort_id: u64,
|
||||
$($field: $ty),*
|
||||
} = $tag,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
#[must_use]
|
||||
pub const fn get_id(&self) -> i32 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { id, .. } => *id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_id(&mut self, id: i32) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { id: ref mut c, .. } => *c = id,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_collection_uid(&self) -> u64 {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { collection_uid, .. } => *collection_uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_collection_uid(&mut self, collection_uid: u64) {
|
||||
match self {
|
||||
$(
|
||||
$name::$variant { collection_uid: ref mut c, .. } => *c = collection_uid,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
polymorphic_scene_unit_protocol_info! {
|
||||
enum SceneUnitProtocolInfo {
|
||||
NpcProtocolInfo {
|
||||
id: i32,
|
||||
quest_id: i32,
|
||||
interacts_info: PropertyHashMap<i32, InteractInfo>,
|
||||
} = 0,
|
||||
}
|
||||
}
|
||||
|
||||
polymorphic_scene_info! {
|
||||
enum SceneInfo {
|
||||
Fight {
|
||||
perform_show_progress: PropertyHashMap<ACTPerformShowMoment, u8>,
|
||||
end_hollow: bool,
|
||||
random_seed: i32,
|
||||
} = 3,
|
||||
Fresh {} = 4,
|
||||
Hall {
|
||||
// main_city_time_info: MainCityTimeInfo,
|
||||
} = 1,
|
||||
Hollow {
|
||||
event_variables: PropertyHashMap<String, i32>,
|
||||
buddy: BuddyUnitInfo,
|
||||
stress_punish_ability_random_pool: Vec<String>,
|
||||
finished: bool,
|
||||
event_weight_factor: PropertyHashMap<i32, i32>,
|
||||
shop_modification: HollowShopModification,
|
||||
last_challenge_stat: PropertyHashMap<i32, u8>,
|
||||
cur_challenge: PropertyHashSet<i32>,
|
||||
hollow_system_switch: PropertyHashMap<HollowSystemType, bool>,
|
||||
sections_info: PropertyHashMap<i32, PlayerHollowSectionInfo>,
|
||||
executing_event: bool,
|
||||
event_id: i32,
|
||||
hollow_event_graph_uid: u64,
|
||||
on_battle_success: String,
|
||||
on_battle_failure: String,
|
||||
battle_finished: bool,
|
||||
battle_success: bool,
|
||||
battle_scene_uid: u64,
|
||||
//event_graphs_info: PropertyHashMap<u64, HollowEventGraphInfo>,
|
||||
scene_global_events: PropertyHashMap<i32, u64>,
|
||||
prepare_section: PrepareSection,
|
||||
abilities_info: AbilitiesInfo,
|
||||
blackout: bool,
|
||||
hollow_system_ui_state: PropertyHashMap<HollowSystemType, HollowSystemUIState>,
|
||||
} = 2,
|
||||
}
|
||||
}
|
||||
|
||||
polymorphic_item_info! {
|
||||
enum ItemInfo {
|
||||
Arcana {
|
||||
affix_list: Vec<i32>,
|
||||
dress_index: u8,
|
||||
} = 33,
|
||||
Avatar {
|
||||
star: u8,
|
||||
exp: u32,
|
||||
level: u8,
|
||||
rank: u8,
|
||||
unlocked_talent_num: u8,
|
||||
skills: PropertyHashMap<u8, u8>,
|
||||
is_custom_by_dungeon: bool,
|
||||
robot_id: i32,
|
||||
} = 3,
|
||||
AvatarLevelUpMaterial { } = 12,
|
||||
AvatarPiece { } = 4,
|
||||
Bless {
|
||||
remain_time: i32,
|
||||
get_time: u64,
|
||||
ban_character: Vec<i32>,
|
||||
specials: PropertyHashMap<String, i32>,
|
||||
slot: u8,
|
||||
is_super_curse: bool,
|
||||
} = 32,
|
||||
Buddy { } = 8,
|
||||
Consumable { } = 10,
|
||||
Currency { } = 1,
|
||||
Equip {
|
||||
avatar_uid: u64,
|
||||
avatar_dressed_index: u8,
|
||||
rand_properties: Vec<PropertyKeyValue>,
|
||||
star: u8,
|
||||
exp: u32,
|
||||
leve: u8,
|
||||
lock: u8,
|
||||
base_rand_properties: Vec<PropertyKeyValue>,
|
||||
rand_properties_lv: Vec<i32>,
|
||||
} = 7,
|
||||
EquipLevelUpMaterial { } = 14,
|
||||
Gift { } = 51,
|
||||
HollowItem { } = 15,
|
||||
OptionalGift { } = 52,
|
||||
Resource { } = 2,
|
||||
TarotCard {
|
||||
is_mute: bool,
|
||||
specials: PropertyHashMap<String, i32>,
|
||||
} = 31,
|
||||
Useable { } = 11,
|
||||
Weapon {
|
||||
avatar_uid: u64,
|
||||
star: u8,
|
||||
exp: u32,
|
||||
level: u8,
|
||||
lock: u8,
|
||||
refine_level: u8,
|
||||
} = 5,
|
||||
WeaponLevelUpMaterial { } = 13,
|
||||
}
|
||||
}
|
||||
|
||||
polymorphic_dungeon_table_ext! {
|
||||
enum DungeonTableExt {
|
||||
Hall {} = 1,
|
||||
Hollow {
|
||||
avatars: PropertyHashSet<HollowDungeonAvatarInfo>,
|
||||
scene_properties_uid: u64,
|
||||
buddy: HollowDungeonBuddyInfo,
|
||||
} = 2,
|
||||
}
|
||||
}
|
||||
|
||||
polymorphic_section_info_ext! {
|
||||
enum SectionInfoExt {
|
||||
Hall {} = 1,
|
||||
Hollow {
|
||||
hollow_level_info: HollowLevelInfo,
|
||||
hollow_grid_map_info: HollowGridMapInfo,
|
||||
} = 0,
|
||||
}
|
||||
}
|
||||
|
||||
polymorphic_scene_table_ext! {
|
||||
enum SceneTableExt {
|
||||
Fight {} = 3,
|
||||
Fresh {} = 4,
|
||||
Hall {} = 1,
|
||||
Hollow {
|
||||
grid_random_seed: i32,
|
||||
alter_section_id: i32,
|
||||
} = 2,
|
||||
}
|
||||
}
|
||||
|
||||
polymorphic_action_info! {
|
||||
enum ActionInfo {
|
||||
ServerChoices {
|
||||
choices: Vec<ChoiceInfo>,
|
||||
finished: bool,
|
||||
} = 52,
|
||||
DropHollowItem {
|
||||
drop_item: i32,
|
||||
} = 162,
|
||||
FinishBlackout {
|
||||
finished: bool,
|
||||
show_tips: bool,
|
||||
} = 133,
|
||||
Loop {
|
||||
loop_times: u16,
|
||||
} = 141,
|
||||
Perform {
|
||||
step: u8,
|
||||
r#return: PropertyHashMap<String, i32>,
|
||||
} = 23,
|
||||
PrepareNextHollow {
|
||||
section_id: i32,
|
||||
finished: bool,
|
||||
show_other: bool,
|
||||
main_map: HollowGridMapProtocolInfo,
|
||||
} = 130,
|
||||
ActionRandomChallenge {
|
||||
choices: Vec<i32>,
|
||||
choice_result: i32,
|
||||
finished: bool,
|
||||
} = 109,
|
||||
RemoveCurse {
|
||||
curse_can_remove: Vec<u64>,
|
||||
to_remove_num: u8,
|
||||
choosed: bool,
|
||||
} = 105,
|
||||
SetHollowSystemState {
|
||||
finished: bool,
|
||||
} = 134,
|
||||
Shop {
|
||||
shop_info: PropertyHashMap<HollowShopType, ConfigShopInfo>,
|
||||
finished: bool,
|
||||
} = 62,
|
||||
SlotMachine {
|
||||
indexes: Vec<i32>,
|
||||
index: i32,
|
||||
finished: bool,
|
||||
} = 131,
|
||||
TriggerBattle {
|
||||
next_action_id: i32,
|
||||
finished: bool,
|
||||
} = 56,
|
||||
}
|
||||
}
|
||||
|
||||
polymorphic_event_graph_info! {
|
||||
enum EventGraphInfo {
|
||||
Hollow {
|
||||
fired_count: u8,
|
||||
hollow_event_template_id: i32,
|
||||
uid: u64,
|
||||
is_created_by_gm: bool,
|
||||
} = 3,
|
||||
NPC {
|
||||
sequence_of_group: u16,
|
||||
section_list_events: PropertyHashMap<String, EventListenerInfo>,
|
||||
interact_info: InteractInfo,
|
||||
hide: bool,
|
||||
} = 2,
|
||||
Section { } = 1,
|
||||
}
|
||||
}
|
||||
|
||||
polymorphic_quest_info! {
|
||||
enum QuestInfo {
|
||||
ArchiveBattle {
|
||||
statistics: PropertyHashMap<QuestStatisticsType, u64>,
|
||||
dungeon_uid: u64,
|
||||
star: u8,
|
||||
} = 7,
|
||||
ArchiveFile { } = 1,
|
||||
Challenge { } = 6,
|
||||
DungeonInner { } = 2,
|
||||
Hollow {
|
||||
statistics: PropertyHashMap<QuestStatisticsType, u64>,
|
||||
dungeon_uid: u64,
|
||||
statistics_ext: PropertyDoubleKeyHashMap<QuestStatisticsType, i32, i32>,
|
||||
acquired_hollow_challenge_reward: i32,
|
||||
} = 3,
|
||||
Knowledge { } = 8,
|
||||
MainCity {
|
||||
bound_npc_and_interact: PropertyHashMap<u64, BoundNPCAndInteractInfo>,
|
||||
} = 5,
|
||||
Manual { } = 4,
|
||||
}
|
||||
}
|
403
protocol/src/rpc_ptc.rs
Normal file
403
protocol/src/rpc_ptc.rs
Normal file
|
@ -0,0 +1,403 @@
|
|||
use super::*;
|
||||
|
||||
// :skull:
|
||||
macro_rules! ret {
|
||||
(struct $name:ident $(< $lt:lifetime >)? {
|
||||
$(
|
||||
$(#[$attr:meta])*
|
||||
$field:ident: $ty:ty,
|
||||
)*
|
||||
}) => {
|
||||
#[derive(OctData)]
|
||||
pub struct $name $(< $lt >)? {
|
||||
pub error_code: ErrorCode,
|
||||
pub error_code_params: Vec<String>,
|
||||
$(
|
||||
$(#[$attr])*
|
||||
pub $field: $ty,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $(< $lt >)? $name $(< $lt >)? {
|
||||
#[must_use]
|
||||
pub const fn new($($field: $ty,)*) -> Self {
|
||||
Self {
|
||||
error_code: ErrorCode::Success,
|
||||
error_code_params: Vec::new(),
|
||||
$($field,)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn error(error_code: ErrorCode, error_code_params: Vec<String>) -> Self {
|
||||
Self {
|
||||
error_code,
|
||||
error_code_params,
|
||||
$($field: Default::default(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $(< $lt >)? Default for $name $(< $lt >)? {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
error_code: ErrorCode::Success,
|
||||
error_code_params: Vec::new(),
|
||||
$($field: Default::default(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($(struct $name:ident $(< $lt:lifetime >)? { $($field:tt)* })+) => {
|
||||
$(ret!(struct $name $(< $lt >)? { $($field)* });)+
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(OctData)]
|
||||
pub struct RpcLoginArg {
|
||||
pub account_name: String,
|
||||
pub token: String,
|
||||
pub client_protocol_sign: String,
|
||||
pub config_sign: String,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct PtcEnterSceneArg {
|
||||
pub player_uid: u64,
|
||||
pub scene_uid: u64,
|
||||
pub ext: SceneTableExt,
|
||||
pub entered_times: u16,
|
||||
pub section_id: i32,
|
||||
pub transform: Transform,
|
||||
pub open_ui: UIType,
|
||||
pub condition_config_ids: Vec<i32>,
|
||||
pub timestamp: u64,
|
||||
pub camera_x: u32,
|
||||
pub camera_y: u32,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcEnterWorldArg {}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcGetPlayerMailsArg {}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct PtcUnlockArg {
|
||||
pub unlock_id: i32,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct PtcGetServerTimestampArg {}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcAdvanceBeginnerProcedureArg {
|
||||
pub player_uid: u64,
|
||||
pub procedure_id: i32,
|
||||
pub params: i32,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcPerformTriggerArg {
|
||||
pub perform_id: i32,
|
||||
pub perform_type: i32,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcPerformEndArg {
|
||||
pub perform_id: i32,
|
||||
pub perform_type: i32,
|
||||
pub perform_uid: String,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcModNickNameArg {
|
||||
pub nick_name: String,
|
||||
pub avatar_id: i32,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcFinishACTPerformShowArg {
|
||||
pub moment: ACTPerformShowMoment,
|
||||
pub step: u8,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcKeepAliveArg {}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcPerformJumpArg {
|
||||
pub perform_id: i32,
|
||||
pub perform_type: i32,
|
||||
pub perform_uid: String,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcBeginnerbattleBeginArg {
|
||||
pub battle_id: i32,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcBattleReportArg {
|
||||
pub battle_reports: Vec<BattleReport>,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcBeginnerbattleEndArg {
|
||||
pub battle_id: i32,
|
||||
pub battle_uid: String,
|
||||
pub battle_statistics: LogBattleStatistics,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcLeaveCurDungeonArg {
|
||||
pub player_uid: u64,
|
||||
pub dungeon_uid: u64,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcSavePosInMainCityArg {
|
||||
pub position: Vector3f,
|
||||
pub rotation: Vector3f,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct RpcCloseLevelChgTipsArg {}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct PtcPlayerInfoChangedArg {
|
||||
pub player_uid: u64,
|
||||
#[property_blob]
|
||||
pub player_info: PlayerInfo,
|
||||
}
|
||||
|
||||
#[derive(OctData, Clone, Debug)]
|
||||
pub struct PtcPlayerOperationArg {
|
||||
pub system: System,
|
||||
pub operator: Operator,
|
||||
pub param: i32,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct PtcScenePropertyChangedArg {
|
||||
pub player_uid: u64,
|
||||
pub is_partial: bool,
|
||||
pub changed_properties: PropertyHashMap<u16, i32>,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct PtcPropertyChangedArg {
|
||||
pub scene_unit_uid: u64,
|
||||
pub is_partial: bool,
|
||||
pub changed_properties: PropertyHashMap<u16, i32>,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct PtcSyncSceneUnitArg {
|
||||
pub scene_uid: u64,
|
||||
pub section_id: i32,
|
||||
pub is_partial: bool,
|
||||
pub removed_scene_units: Vec<u64>,
|
||||
pub scene_units: Vec<SceneUnitProtocolInfo>,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct PtcEnterSectionArg {
|
||||
pub section_id: i32,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct RpcRunEventGraphArg {
|
||||
pub owner_type: EventGraphOwnerType,
|
||||
pub owner_uid: u64,
|
||||
pub event_graph_id: i32,
|
||||
pub event_id: i32,
|
||||
pub move_path: Vec<i32>,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct RpcInteractWithUnitArg {
|
||||
pub unit_uid: u64,
|
||||
pub unit_type: InteractTarget,
|
||||
pub event_graph_id: i32,
|
||||
pub interaction: u16,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct PtcSyncEventInfoArg {
|
||||
pub owner_type: EventGraphOwnerType,
|
||||
pub owner_uid: u64,
|
||||
pub updated_events: PropertyDoubleKeyHashMap<i32, i32, EventInfo>,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct RpcCheckYorozuyaInfoRefreshArg {}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct PtcHollowQuestUnlockedByMainCityQuest {
|
||||
pub quest_id: i32,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct RpcStartHollowQuestArg {
|
||||
pub hollow_quest_id: i32,
|
||||
pub buddy: u64,
|
||||
pub initiative_item: i32,
|
||||
pub avatar_map: PropertyHashMap<i8, u64>,
|
||||
pub is_story: bool,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct PtcSyncHollowGridMapsArg {
|
||||
pub player_uid: u64,
|
||||
pub scene_uid: u64,
|
||||
pub hollow_level: i32,
|
||||
pub main_map: HollowGridMapProtocolInfo,
|
||||
pub time_period: TimePeriodType,
|
||||
pub weather: WeatherType,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct PtcPositionInHollowChangedArg {
|
||||
pub player_uid: u64,
|
||||
pub hollow_level: i32,
|
||||
pub position: u16,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct PtcSyncHollowEventInfoArg {
|
||||
pub event_graph_uid: u64,
|
||||
pub hollow_event_template_id: i32,
|
||||
pub event_graph_id: i32,
|
||||
pub updated_event: EventInfo,
|
||||
pub specials: PropertyHashMap<String, i32>,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct RpcRunHollowEventGraphArg {
|
||||
pub event_graph_uid: u64,
|
||||
pub event_id: i32,
|
||||
pub move_path: Vec<i32>,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct PtcHollowGridArg {
|
||||
pub player_uid: u64,
|
||||
pub is_partial: bool,
|
||||
pub scene_uid: u64,
|
||||
pub hollow_level: i32,
|
||||
pub grids: HashMap<u16, HollowGridProtocolInfo>,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct RpcHollowMoveArg {
|
||||
pub player_uid: u64,
|
||||
pub scene_uid: u64,
|
||||
pub hollow_level: i32,
|
||||
pub positions: Vec<u16>,
|
||||
}
|
||||
|
||||
#[derive(OctData, Debug)]
|
||||
pub struct RpcEndBattleArg {
|
||||
pub player_uid: u64,
|
||||
pub fight_ranking: FightRanking,
|
||||
pub success: bool,
|
||||
pub avatar_properties: PropertyHashMap<u64, HashMap<u16, i32>>,
|
||||
pub killed_enemy_count: u16,
|
||||
pub condition_statistics: HashMap<i32, i32>,
|
||||
pub star: u8,
|
||||
pub challenge_stat: HashMap<i32, u8>,
|
||||
pub fight_drop_infos: Vec<FightDropInfo>,
|
||||
pub challenge_result_info: PropertyHashMap<i32, ChallengeResultInfo>,
|
||||
pub battle_statistics: LogBattleStatistics,
|
||||
}
|
||||
|
||||
ret! {
|
||||
struct RpcLoginRet {
|
||||
account_info: PropertyBlob,
|
||||
}
|
||||
|
||||
struct RpcEnterWorldRet {
|
||||
player_info: PropertyBlob,
|
||||
}
|
||||
|
||||
struct RpcGetPlayerMailsRet {
|
||||
mail_count: u32, // Actually List<CPlayerMailInfo>, TODO!
|
||||
}
|
||||
|
||||
struct PtcGetServerTimestampRet {
|
||||
timestamp: u64,
|
||||
base_utc_offset_milliseconds: i64,
|
||||
}
|
||||
|
||||
struct RpcAdvanceBeginnerProcedureRet {
|
||||
next_procedure_id: i32,
|
||||
}
|
||||
|
||||
struct RpcPerformTriggerRet {
|
||||
perform_uid: String,
|
||||
}
|
||||
|
||||
struct RpcPerformEndRet {
|
||||
}
|
||||
|
||||
struct RpcModNickNameRet {
|
||||
}
|
||||
|
||||
struct RpcFinishACTPerformShowRet {
|
||||
}
|
||||
|
||||
struct RpcKeepAliveRet {
|
||||
}
|
||||
|
||||
struct RpcPerformJumpRet {
|
||||
}
|
||||
|
||||
struct RpcBeginnerbattleBeginRet {
|
||||
battle_uid: String,
|
||||
}
|
||||
|
||||
struct RpcBattleReportRet {
|
||||
need_index: i32,
|
||||
}
|
||||
|
||||
struct RpcBeginnerbattleEndRet {
|
||||
}
|
||||
|
||||
struct RpcLeaveCurDungeonRet {
|
||||
}
|
||||
|
||||
struct RpcSavePosInMainCityRet {
|
||||
}
|
||||
|
||||
struct RpcCloseLevelChgTipsRet {
|
||||
}
|
||||
|
||||
struct PtcPlayerOperationRet {
|
||||
}
|
||||
|
||||
struct RpcRunEventGraphRet {
|
||||
}
|
||||
|
||||
struct RpcInteractWithUnitRet {
|
||||
}
|
||||
|
||||
struct RpcCheckYorozuyaInfoRefreshRet {
|
||||
}
|
||||
|
||||
struct RpcStartHollowQuestRet {
|
||||
}
|
||||
|
||||
struct RpcRunHollowEventGraphRet {
|
||||
}
|
||||
|
||||
struct RpcHollowMoveRet {
|
||||
hollow_level: i32,
|
||||
position: u16,
|
||||
}
|
||||
|
||||
struct RpcEndBattleRet {
|
||||
hollow_event_id: i32,
|
||||
reward_items_classify: HashMap<BattleRewardType, HashMap<u64, ItemIDCount>>,
|
||||
}
|
||||
}
|
1339
protocol/src/structs.rs
Normal file
1339
protocol/src/structs.rs
Normal file
File diff suppressed because it is too large
Load diff
15
qwer/Cargo.toml
Normal file
15
qwer/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "qwer"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["collection", "fastoct"]
|
||||
full = ["default", "protocol"]
|
||||
collection = []
|
||||
fastoct = []
|
||||
protocol = []
|
||||
|
||||
[dependencies]
|
||||
byteorder.workspace = true
|
||||
qwer-derive.workspace = true
|
16
qwer/qwer-derive/Cargo.toml
Normal file
16
qwer/qwer-derive/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "qwer-derive"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
description = "Codegen for Zenless Zone Zero's Qwer Protocol"
|
||||
license = "MIT"
|
||||
publish = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
heck.workspace = true
|
||||
proc-macro2 = "1.0.78"
|
||||
quote = "1.0.35"
|
||||
syn = "2.0.52"
|
39
qwer/qwer-derive/src/lib.rs
Normal file
39
qwer/qwer-derive/src/lib.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
mod oct_data;
|
||||
|
||||
/// Generate `OctData` implementation for structs and enums that have all fields
|
||||
/// implement `OctData`.
|
||||
///
|
||||
/// For structs, fields are written one by one in order.
|
||||
/// If the struct must pad a byte due to it being a `CPropertyObject`, the `#[property_object]`
|
||||
/// attribute must be present, and must contain the value to pad with.
|
||||
///
|
||||
/// e.g. `#[property_object(u16, 0x01)]` will pad with 0x01 as a u16.
|
||||
///
|
||||
/// In the presence of these property objects, all fields must be Optional, e.g. `Option<T>`,
|
||||
/// and must also have a tag attribute attached to them for marshalling and unmarshalling, of the
|
||||
/// form `#[tag = <number>]`.
|
||||
///
|
||||
/// For enums, the structure starts with a discriminant with the type specified in the `#[repr]` of
|
||||
/// the enum, followed by the fields of the enum one by one.
|
||||
#[proc_macro_derive(
|
||||
OctData,
|
||||
attributes(
|
||||
property_object,
|
||||
property_blob,
|
||||
skip_property,
|
||||
tag,
|
||||
root,
|
||||
base,
|
||||
polymorphic_none
|
||||
)
|
||||
)]
|
||||
pub fn derive_message(item: TokenStream) -> TokenStream {
|
||||
let parsed = parse_macro_input!(item as DeriveInput);
|
||||
match oct_data::imp(&parsed) {
|
||||
Ok(item) => item.into(),
|
||||
Err(err) => err.to_compile_error().into(),
|
||||
}
|
||||
}
|
1033
qwer/qwer-derive/src/oct_data.rs
Normal file
1033
qwer/qwer-derive/src/oct_data.rs
Normal file
File diff suppressed because it is too large
Load diff
782
qwer/src/collection.rs
Normal file
782
qwer/src/collection.rs
Normal file
|
@ -0,0 +1,782 @@
|
|||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
io::Result,
|
||||
};
|
||||
|
||||
use crate::OctData;
|
||||
|
||||
pub type DoubleKeyHashMap<K1, K2, V> = HashMap<K1, HashMap<K2, V>>;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! phashmap {
|
||||
($(($key:expr, $value:expr)),*) => {
|
||||
PropertyHashMap::Base(
|
||||
{
|
||||
let mut map = std::collections::HashMap::new();
|
||||
$(
|
||||
map.insert($key, $value);
|
||||
)*
|
||||
map
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! phashset {
|
||||
($($value:expr),*) => {
|
||||
PropertyHashSet::Base(
|
||||
{
|
||||
let mut set = std::collections::HashSet::new();
|
||||
$(
|
||||
set.insert($value);
|
||||
)*
|
||||
set
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pdkhashmap {
|
||||
($(($key1:expr, $key2:expr, $value:expr)),*) => {
|
||||
PropertyDoubleKeyHashMap::Base(
|
||||
{
|
||||
let mut map = qwer::DoubleKeyHashMap::new();
|
||||
$(
|
||||
map.entry($key1).or_insert(HashMap::new()).insert($key2, $value);
|
||||
)*
|
||||
map
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PropertyHashMap<K, V>
|
||||
where
|
||||
K: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
Base(HashMap<K, V>),
|
||||
Modify {
|
||||
to_add: Vec<(K, V)>,
|
||||
to_remove: Vec<K>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<K, V> PropertyHashMap<K, V>
|
||||
where
|
||||
K: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
pub fn insert(&mut self, key: K, value: V) {
|
||||
match self {
|
||||
Self::Base(base) => {
|
||||
base.insert(key, value);
|
||||
}
|
||||
Self::Modify { to_add, .. } => {
|
||||
to_add.push((key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: K) -> Option<V> {
|
||||
match self {
|
||||
Self::Base(base) => base.remove(&key),
|
||||
Self::Modify { to_remove, .. } => {
|
||||
to_remove.push(key);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &K) -> Option<&V> {
|
||||
match self {
|
||||
Self::Base(base) => base.get(key),
|
||||
Self::Modify { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
|
||||
match self {
|
||||
Self::Base(base) => base.get_mut(key),
|
||||
Self::Modify { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
Self::Base(base) => base.len(),
|
||||
Self::Modify { to_add, to_remove } => to_add.len() + to_remove.len(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
Self::Base(base) => base.is_empty(),
|
||||
Self::Modify { to_add, to_remove } => to_add.is_empty() && to_remove.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn iter(&self) -> std::collections::hash_map::Iter<K, V> {
|
||||
match self {
|
||||
Self::Base(base) => base.iter(),
|
||||
Self::Modify { .. } => unreachable!("PropertyHashMap::Modify::iter()"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> std::collections::hash_map::IterMut<K, V> {
|
||||
match self {
|
||||
Self::Base(base) => base.iter_mut(),
|
||||
Self::Modify { .. } => unreachable!("PropertyHashMap::Modify::iter_mut()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> IntoIterator for PropertyHashMap<K, V>
|
||||
where
|
||||
K: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
type IntoIter = std::collections::hash_map::IntoIter<K, V>;
|
||||
type Item = (K, V);
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
Self::Base(base) => base.into_iter(),
|
||||
Self::Modify { .. } => unreachable!("PropertyHashMap::Modify::into_iter()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> IntoIterator for &'a PropertyHashMap<K, V>
|
||||
where
|
||||
K: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
type IntoIter = std::collections::hash_map::Iter<'a, K, V>;
|
||||
type Item = (&'a K, &'a V);
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
PropertyHashMap::Base(base) => base.iter(),
|
||||
PropertyHashMap::Modify { .. } => unreachable!("PropertyHashMap::Modify::into_iter()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> IntoIterator for &'a mut PropertyHashMap<K, V>
|
||||
where
|
||||
K: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
type IntoIter = std::collections::hash_map::IterMut<'a, K, V>;
|
||||
type Item = (&'a K, &'a mut V);
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
PropertyHashMap::Base(base) => base.iter_mut(),
|
||||
PropertyHashMap::Modify { .. } => unreachable!("PropertyHashMap::Modify::into_iter()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hashmap_iter() {
|
||||
let mut map = phashmap![(1, 2), (3, 4)];
|
||||
let mut expecting = vec![(1, 2), (3, 4)];
|
||||
let iter = map.iter_mut();
|
||||
for (key, value) in iter {
|
||||
assert!(expecting.contains(&(*key, *value)));
|
||||
expecting.retain(|x| x != &(*key, *value));
|
||||
*value += 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PropertyHashSet<T>
|
||||
where
|
||||
T: OctData + Eq + std::hash::Hash,
|
||||
{
|
||||
pub fn insert(&mut self, value: T) {
|
||||
match self {
|
||||
Self::Base(base) => {
|
||||
base.insert(value);
|
||||
}
|
||||
Self::Modify { to_add, .. } => {
|
||||
to_add.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, value: T) {
|
||||
match self {
|
||||
Self::Base(base) => {
|
||||
base.remove(&value);
|
||||
}
|
||||
Self::Modify { to_remove, .. } => {
|
||||
to_remove.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
Self::Base(base) => base.len(),
|
||||
Self::Modify { to_add, to_remove } => to_add.len() + to_remove.len(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
Self::Base(base) => base.is_empty(),
|
||||
Self::Modify { to_add, to_remove } => to_add.is_empty() && to_remove.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn iter(&self) -> std::collections::hash_set::Iter<T> {
|
||||
match self {
|
||||
Self::Base(base) => base.iter(),
|
||||
Self::Modify { .. } => unreachable!("PropertyHashSet::Modify::iter()"),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn iter_mut(&mut self) -> std::collections::hash_set::Iter<T> {
|
||||
self.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for PropertyHashSet<T>
|
||||
where
|
||||
T: OctData + Eq + std::hash::Hash,
|
||||
{
|
||||
type IntoIter = std::collections::hash_set::IntoIter<T>;
|
||||
type Item = T;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
Self::Base(base) => base.into_iter(),
|
||||
Self::Modify { .. } => unreachable!("PropertyHashSet::Modify::into_iter()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a PropertyHashSet<T>
|
||||
where
|
||||
T: OctData + Eq + std::hash::Hash,
|
||||
{
|
||||
type IntoIter = std::collections::hash_set::Iter<'a, T>;
|
||||
type Item = &'a T;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
PropertyHashSet::Base(base) => base.iter(),
|
||||
PropertyHashSet::Modify { .. } => unreachable!("PropertyHashSet::Modify::into_iter()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a mut PropertyHashSet<T>
|
||||
where
|
||||
T: OctData + Eq + std::hash::Hash,
|
||||
{
|
||||
type IntoIter = std::collections::hash_set::Iter<'a, T>;
|
||||
type Item = &'a T;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
PropertyHashSet::Base(base) => base.iter(),
|
||||
PropertyHashSet::Modify { .. } => unreachable!("PropertyHashSet::Modify::into_iter()"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_phashmap_macro() {
|
||||
let map = phashmap![(1, 2), (3, 4)];
|
||||
assert_eq!(
|
||||
map,
|
||||
PropertyHashMap::Base([(1, 2), (3, 4)].into_iter().collect())
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PropertyHashSet<T>
|
||||
where
|
||||
T: OctData + Eq + std::hash::Hash,
|
||||
{
|
||||
Base(HashSet<T>),
|
||||
Modify { to_add: Vec<T>, to_remove: Vec<T> },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PropertyDoubleKeyHashMap<K1, K2, V>
|
||||
where
|
||||
K1: OctData + Eq + std::hash::Hash,
|
||||
K2: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
Base(DoubleKeyHashMap<K1, K2, V>),
|
||||
Modify {
|
||||
to_add: Vec<(K1, K2, V)>,
|
||||
to_remove: Vec<(K1, K2)>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct PropertyDoubleKeyHashMapIterMut<'a, K1, K2, V> {
|
||||
outer_iter: std::collections::hash_map::IterMut<'a, K1, HashMap<K2, V>>,
|
||||
inner_iter: Option<std::collections::hash_map::IterMut<'a, K2, V>>,
|
||||
current_outer_key: Option<&'a K1>,
|
||||
}
|
||||
|
||||
pub struct PropertyDoubleKeyHashMapIter<'a, K1, K2, V> {
|
||||
outer_iter: std::collections::hash_map::Iter<'a, K1, HashMap<K2, V>>,
|
||||
inner_iter: Option<std::collections::hash_map::Iter<'a, K2, V>>,
|
||||
current_outer_key: Option<&'a K1>,
|
||||
}
|
||||
|
||||
pub struct PropertyDoubleKeyHashMapIntoIter<K1, K2, V> {
|
||||
outer_iter: std::collections::hash_map::IntoIter<K1, HashMap<K2, V>>,
|
||||
inner_iter: Option<std::collections::hash_map::IntoIter<K2, V>>,
|
||||
current_outer_key: Option<K1>,
|
||||
}
|
||||
|
||||
impl<'a, K1, K2, V> Iterator for PropertyDoubleKeyHashMapIterMut<'a, K1, K2, V>
|
||||
where
|
||||
K1: OctData + Eq + std::hash::Hash,
|
||||
K2: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
type Item = (&'a K1, &'a K2, &'a mut V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if self.inner_iter.is_none() {
|
||||
if let Some((key1, sub_map)) = self.outer_iter.next() {
|
||||
self.current_outer_key = Some(key1);
|
||||
self.inner_iter = Some(sub_map.iter_mut());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(inner_iter) = &mut self.inner_iter {
|
||||
if let Some((key2, value)) = inner_iter.next() {
|
||||
return Some((self.current_outer_key.unwrap(), key2, value));
|
||||
}
|
||||
self.inner_iter = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K1, K2, V> Iterator for PropertyDoubleKeyHashMapIter<'a, K1, K2, V>
|
||||
where
|
||||
K1: OctData + Eq + std::hash::Hash,
|
||||
K2: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
type Item = (&'a K1, &'a K2, &'a V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if self.inner_iter.is_none() {
|
||||
if let Some((key1, sub_map)) = self.outer_iter.next() {
|
||||
self.current_outer_key = Some(key1);
|
||||
self.inner_iter = Some(sub_map.iter());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(inner_iter) = &mut self.inner_iter {
|
||||
if let Some((key2, value)) = inner_iter.next() {
|
||||
return Some((self.current_outer_key.unwrap(), key2, value));
|
||||
}
|
||||
self.inner_iter = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K1, K2, V> Iterator for PropertyDoubleKeyHashMapIntoIter<K1, K2, V>
|
||||
where
|
||||
K1: OctData + Eq + std::hash::Hash + Copy,
|
||||
K2: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
type Item = (K1, K2, V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if self.inner_iter.is_none() {
|
||||
if let Some((key1, sub_map)) = self.outer_iter.next() {
|
||||
self.current_outer_key = Some(key1);
|
||||
self.inner_iter = Some(sub_map.into_iter());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(inner_iter) = &mut self.inner_iter {
|
||||
if let Some((key2, value)) = inner_iter.next() {
|
||||
return Some((self.current_outer_key.unwrap(), key2, value));
|
||||
}
|
||||
self.inner_iter = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K1, K2, V> IntoIterator for PropertyDoubleKeyHashMap<K1, K2, V>
|
||||
where
|
||||
K1: OctData + Eq + std::hash::Hash + Copy,
|
||||
K2: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
type IntoIter = PropertyDoubleKeyHashMapIntoIter<K1, K2, V>;
|
||||
type Item = (K1, K2, V);
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
Self::Base(base) => PropertyDoubleKeyHashMapIntoIter {
|
||||
outer_iter: base.into_iter(),
|
||||
inner_iter: None,
|
||||
current_outer_key: None,
|
||||
},
|
||||
Self::Modify { .. } => {
|
||||
unreachable!("PropertyDoubleKeyHashMap::Modify::into_iter() is not implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K1, K2, V> IntoIterator for &'a PropertyDoubleKeyHashMap<K1, K2, V>
|
||||
where
|
||||
K1: OctData + Eq + std::hash::Hash,
|
||||
K2: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
type IntoIter = PropertyDoubleKeyHashMapIter<'a, K1, K2, V>;
|
||||
type Item = (&'a K1, &'a K2, &'a V);
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
PropertyDoubleKeyHashMap::Base(base) => PropertyDoubleKeyHashMapIter {
|
||||
outer_iter: base.iter(),
|
||||
inner_iter: None,
|
||||
current_outer_key: None,
|
||||
},
|
||||
PropertyDoubleKeyHashMap::Modify { .. } => {
|
||||
unreachable!("PropertyDoubleKeyHashMap::Modify::into_iter() is not implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K1, K2, V> IntoIterator for &'a mut PropertyDoubleKeyHashMap<K1, K2, V>
|
||||
where
|
||||
K1: OctData + Eq + std::hash::Hash,
|
||||
K2: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
type IntoIter = PropertyDoubleKeyHashMapIterMut<'a, K1, K2, V>;
|
||||
type Item = (&'a K1, &'a K2, &'a mut V);
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
PropertyDoubleKeyHashMap::Base(base) => PropertyDoubleKeyHashMapIterMut {
|
||||
outer_iter: base.iter_mut(),
|
||||
inner_iter: None,
|
||||
current_outer_key: None,
|
||||
},
|
||||
PropertyDoubleKeyHashMap::Modify { .. } => {
|
||||
unreachable!("PropertyDoubleKeyHashMap::Modify::into_iter() is not implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K1, K2, V> PropertyDoubleKeyHashMap<K1, K2, V>
|
||||
where
|
||||
K1: OctData + Eq + std::hash::Hash,
|
||||
K2: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
pub fn insert(&mut self, key: K1, sub_key: K2, value: V) {
|
||||
match self {
|
||||
Self::Base(base) => {
|
||||
base.entry(key)
|
||||
.or_insert_with(HashMap::new)
|
||||
.insert(sub_key, value);
|
||||
}
|
||||
Self::Modify { to_add, .. } => {
|
||||
to_add.push((key, sub_key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &K1, sub_key: &K2) -> Option<&V> {
|
||||
match self {
|
||||
Self::Base(base) => base.get(key).and_then(|sub_map| sub_map.get(sub_key)),
|
||||
Self::Modify { .. } => {
|
||||
unreachable!("PropertyDoubleKeyHashMap::Modify::get() is not implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, key: &K1, sub_key: &K2) -> Option<&mut V> {
|
||||
match self {
|
||||
Self::Base(base) => base
|
||||
.get_mut(key)
|
||||
.and_then(|sub_map| sub_map.get_mut(sub_key)),
|
||||
Self::Modify { .. } => {
|
||||
unreachable!("PropertyDoubleKeyHashMap::Modify::get_mut() is not implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: K1, sub_key: K2) -> Option<V> {
|
||||
match self {
|
||||
Self::Base(base) => {
|
||||
let mut removed = None;
|
||||
base.entry(key)
|
||||
.and_modify(|sub_map| removed = sub_map.remove(&sub_key));
|
||||
removed
|
||||
}
|
||||
Self::Modify { to_remove, .. } => {
|
||||
to_remove.push((key, sub_key));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
Self::Base(base) => base.len(),
|
||||
Self::Modify { to_add, to_remove } => to_add.len() + to_remove.len(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
Self::Base(base) => base.is_empty(),
|
||||
Self::Modify { to_add, to_remove } => to_add.is_empty() && to_remove.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn iter(&self) -> PropertyDoubleKeyHashMapIter<K1, K2, V> {
|
||||
match self {
|
||||
Self::Base(base) => PropertyDoubleKeyHashMapIter {
|
||||
outer_iter: base.iter(),
|
||||
inner_iter: None,
|
||||
current_outer_key: None,
|
||||
},
|
||||
Self::Modify { .. } => {
|
||||
unreachable!("PropertyDoubleKeyHashMap::Modify::iter() is not implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> PropertyDoubleKeyHashMapIterMut<K1, K2, V> {
|
||||
match self {
|
||||
Self::Base(base) => PropertyDoubleKeyHashMapIterMut {
|
||||
outer_iter: base.iter_mut(),
|
||||
inner_iter: None,
|
||||
current_outer_key: None,
|
||||
},
|
||||
Self::Modify { .. } => {
|
||||
unreachable!("PropertyDoubleKeyHashMap::Modify::iter_mut() is not implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dkhashmap_iter() {
|
||||
let mut map = PropertyDoubleKeyHashMap::Base(
|
||||
std::iter::once((1, [(2, 3), (4, 5)].into_iter().collect())).collect(),
|
||||
);
|
||||
let mut expecting = vec![(1, 2, 3), (1, 4, 5)];
|
||||
let iter = map.iter_mut();
|
||||
for (key1, key2, value) in iter {
|
||||
assert!(expecting.contains(&(*key1, *key2, *value)));
|
||||
expecting.retain(|x| x != &(*key1, *key2, *value));
|
||||
*value += 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> OctData for PropertyHashMap<K, V>
|
||||
where
|
||||
K: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
fn marshal_to<W: std::io::Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {
|
||||
match self {
|
||||
Self::Base(map) => {
|
||||
map.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
Self::Modify { to_add, to_remove } => {
|
||||
let len = -(to_add.len() as i32 + to_remove.len() as i32);
|
||||
len.marshal_to(w, bt_property_tag)?;
|
||||
for (key, value) in to_add {
|
||||
key.marshal_to(w, bt_property_tag)?;
|
||||
false.marshal_to(w, bt_property_tag)?;
|
||||
value.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
for key in to_remove {
|
||||
key.marshal_to(w, bt_property_tag)?;
|
||||
true.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: std::io::Read>(r: &mut R, bt_property_tag: u16) -> Result<Self> {
|
||||
let len = i32::unmarshal_from(r, bt_property_tag)?;
|
||||
|
||||
if len >= 0 {
|
||||
let mut map = HashMap::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
map.insert(
|
||||
K::unmarshal_from(r, bt_property_tag)?,
|
||||
V::unmarshal_from(r, bt_property_tag)?,
|
||||
);
|
||||
}
|
||||
Ok(Self::Base(map))
|
||||
} else {
|
||||
let mut to_add = Vec::new();
|
||||
let mut to_remove = Vec::new();
|
||||
|
||||
for _ in 0..-len {
|
||||
let key = K::unmarshal_from(r, bt_property_tag)?;
|
||||
if !bool::unmarshal_from(r, bt_property_tag)? {
|
||||
to_add.push((key, V::unmarshal_from(r, bt_property_tag)?));
|
||||
} else {
|
||||
to_remove.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self::Modify { to_add, to_remove })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> OctData for PropertyHashSet<K>
|
||||
where
|
||||
K: OctData + Eq + std::hash::Hash,
|
||||
{
|
||||
fn marshal_to<W: std::io::Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {
|
||||
match self {
|
||||
Self::Base(set) => {
|
||||
set.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
Self::Modify { to_add, to_remove } => {
|
||||
let len = -(to_add.len() as i32 + to_remove.len() as i32);
|
||||
len.marshal_to(w, bt_property_tag)?;
|
||||
for value in to_add {
|
||||
value.marshal_to(w, bt_property_tag)?;
|
||||
false.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
for value in to_remove {
|
||||
value.marshal_to(w, bt_property_tag)?;
|
||||
true.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: std::io::Read>(r: &mut R, bt_property_tag: u16) -> Result<Self> {
|
||||
let len = i32::unmarshal_from(r, bt_property_tag)?;
|
||||
|
||||
if len >= 0 {
|
||||
let mut set = HashSet::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
set.insert(K::unmarshal_from(r, bt_property_tag)?);
|
||||
}
|
||||
Ok(Self::Base(set))
|
||||
} else {
|
||||
let mut to_add = Vec::new();
|
||||
let mut to_remove = Vec::new();
|
||||
|
||||
for _ in 0..-len {
|
||||
let value = K::unmarshal_from(r, bt_property_tag)?;
|
||||
if !bool::unmarshal_from(r, bt_property_tag)? {
|
||||
to_add.push(value);
|
||||
} else {
|
||||
to_remove.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self::Modify { to_add, to_remove })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K1, K2, V> OctData for PropertyDoubleKeyHashMap<K1, K2, V>
|
||||
where
|
||||
K1: OctData + Eq + std::hash::Hash,
|
||||
K2: OctData + Eq + std::hash::Hash,
|
||||
V: OctData,
|
||||
{
|
||||
fn marshal_to<W: std::io::Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {
|
||||
match self {
|
||||
Self::Base(map) => {
|
||||
map.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
Self::Modify { to_add, to_remove } => {
|
||||
let len = -(to_add.len() as i32 + to_remove.len() as i32);
|
||||
len.marshal_to(w, bt_property_tag)?;
|
||||
for (key1, key2, value) in to_add {
|
||||
key1.marshal_to(w, bt_property_tag)?;
|
||||
key2.marshal_to(w, bt_property_tag)?;
|
||||
false.marshal_to(w, bt_property_tag)?;
|
||||
value.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
for (key1, key2) in to_remove {
|
||||
key1.marshal_to(w, bt_property_tag)?;
|
||||
key2.marshal_to(w, bt_property_tag)?;
|
||||
true.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: std::io::Read>(r: &mut R, bt_property_tag: u16) -> Result<Self> {
|
||||
let len = i32::unmarshal_from(r, bt_property_tag)?;
|
||||
if len >= 0 {
|
||||
let mut map = HashMap::new();
|
||||
for _ in 0..len {
|
||||
let key1 = K1::unmarshal_from(r, bt_property_tag)?;
|
||||
let key2 = K2::unmarshal_from(r, bt_property_tag)?;
|
||||
let value = V::unmarshal_from(r, bt_property_tag)?;
|
||||
map.entry(key1)
|
||||
.or_insert_with(HashMap::new)
|
||||
.insert(key2, value);
|
||||
}
|
||||
Ok(Self::Base(map))
|
||||
} else {
|
||||
let mut to_add = Vec::new();
|
||||
let mut to_remove = Vec::new();
|
||||
|
||||
for _ in 0..-len {
|
||||
let key1 = K1::unmarshal_from(r, bt_property_tag)?;
|
||||
let key2 = K2::unmarshal_from(r, bt_property_tag)?;
|
||||
if !bool::unmarshal_from(r, bt_property_tag)? {
|
||||
to_add.push((key1, key2, V::unmarshal_from(r, bt_property_tag)?));
|
||||
} else {
|
||||
to_remove.push((key1, key2));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self::Modify { to_add, to_remove })
|
||||
}
|
||||
}
|
||||
}
|
278
qwer/src/fastoct.rs
Normal file
278
qwer/src/fastoct.rs
Normal file
|
@ -0,0 +1,278 @@
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
collections::{HashMap, HashSet},
|
||||
hash::Hash,
|
||||
io::{Read, Result, Write},
|
||||
};
|
||||
|
||||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||
|
||||
pub use qwer_derive::OctData;
|
||||
|
||||
use crate::DoubleKeyHashMap;
|
||||
|
||||
// LE encoded data
|
||||
pub trait OctData: Sized + Send + Sync {
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()>;
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, bt_property_tag: u16) -> Result<Self>;
|
||||
}
|
||||
|
||||
impl OctData for bool {
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, _: u16) -> Result<()> {
|
||||
w.write_u8(u8::from(*self))
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, _: u16) -> Result<Self> {
|
||||
Ok(r.read_u8()? != 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl OctData for u8 {
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, _: u16) -> Result<()> {
|
||||
w.write_u8(*self)
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, _: u16) -> Result<Self> {
|
||||
r.read_u8()
|
||||
}
|
||||
}
|
||||
|
||||
impl OctData for i8 {
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, _: u16) -> Result<()> {
|
||||
w.write_i8(*self)
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, _: u16) -> Result<Self> {
|
||||
r.read_i8()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_primitive {
|
||||
($($t:ty, $write:ident, $read:ident,)*) => {
|
||||
$(
|
||||
impl OctData for $t {
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, _: u16) -> Result<()> {
|
||||
w.$write::<byteorder::LittleEndian>(*self)
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, _: u16) -> Result<Self> {
|
||||
r.$read::<byteorder::LittleEndian>()
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_primitive! {
|
||||
u16, write_u16, read_u16,
|
||||
i16, write_i16, read_i16,
|
||||
u32, write_u32, read_u32,
|
||||
i32, write_i32, read_i32,
|
||||
u64, write_u64, read_u64,
|
||||
i64, write_i64, read_i64,
|
||||
}
|
||||
|
||||
// floats are a bit special, the bits are casted to an integer and then treated as an integer
|
||||
|
||||
impl OctData for f32 {
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, _: u16) -> Result<()> {
|
||||
w.write_u32::<byteorder::LittleEndian>(self.to_bits())
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, _: u16) -> Result<Self> {
|
||||
Ok(Self::from_bits(r.read_u32::<byteorder::LittleEndian>()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl OctData for f64 {
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, _: u16) -> Result<()> {
|
||||
w.write_u64::<byteorder::LittleEndian>(self.to_bits())
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, _: u16) -> Result<Self> {
|
||||
Ok(Self::from_bits(r.read_u64::<byteorder::LittleEndian>()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OctData for Vec<T>
|
||||
where
|
||||
T: OctData,
|
||||
{
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {
|
||||
if self.is_empty() {
|
||||
(0i32).marshal_to(w, bt_property_tag)?;
|
||||
return Ok(());
|
||||
}
|
||||
(self.len() as i32).marshal_to(w, bt_property_tag)?;
|
||||
for item in self {
|
||||
item.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, bt_property_tag: u16) -> Result<Self> {
|
||||
let len = i32::unmarshal_from(r, bt_property_tag)?;
|
||||
if len < 0 {
|
||||
let real_len = -len;
|
||||
let mut vec = Self::with_capacity(real_len as usize);
|
||||
for _ in 0..real_len {
|
||||
bool::unmarshal_from(r, bt_property_tag)?;
|
||||
vec.push(T::unmarshal_from(r, bt_property_tag)?);
|
||||
}
|
||||
Ok(vec)
|
||||
} else {
|
||||
let mut vec = Self::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
vec.push(T::unmarshal_from(r, bt_property_tag)?);
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> OctData for HashMap<K, V>
|
||||
where
|
||||
K: OctData + Eq + Hash,
|
||||
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 {
|
||||
key.marshal_to(w, bt_property_tag)?;
|
||||
value.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
default fn unmarshal_from<R: Read>(r: &mut R, bt_property_tag: u16) -> Result<Self> {
|
||||
let len = i32::unmarshal_from(r, bt_property_tag)?;
|
||||
if len == -1 {
|
||||
return Ok(Self::new());
|
||||
}
|
||||
let mut map = Self::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
map.insert(
|
||||
K::unmarshal_from(r, bt_property_tag)?,
|
||||
V::unmarshal_from(r, bt_property_tag)?,
|
||||
);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "collection")]
|
||||
impl<K1, K2, V> OctData for DoubleKeyHashMap<K1, K2, V>
|
||||
where
|
||||
K1: OctData + Eq + Hash,
|
||||
K2: OctData + Eq + Hash,
|
||||
V: OctData,
|
||||
{
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {
|
||||
self.iter()
|
||||
.map(|(_, inner_map)| inner_map.len() as i32)
|
||||
.sum::<i32>()
|
||||
.marshal_to(w, bt_property_tag)?;
|
||||
for (key1, inner_map) in self {
|
||||
for (key2, value) in inner_map {
|
||||
key1.marshal_to(w, bt_property_tag)?;
|
||||
key2.marshal_to(w, bt_property_tag)?;
|
||||
value.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, bt_property_tag: u16) -> Result<Self> {
|
||||
let len = i32::unmarshal_from(r, bt_property_tag)?;
|
||||
if len == -1 {
|
||||
return Ok(Self::new());
|
||||
}
|
||||
let mut map = Self::new();
|
||||
for _ in 0..len {
|
||||
let key1 = K1::unmarshal_from(r, bt_property_tag)?;
|
||||
let key2 = K2::unmarshal_from(r, bt_property_tag)?;
|
||||
let value = V::unmarshal_from(r, bt_property_tag)?;
|
||||
map.entry(key1)
|
||||
.or_insert_with(HashMap::new)
|
||||
.insert(key2, value);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OctData for HashSet<T>
|
||||
where
|
||||
T: OctData + Eq + Hash,
|
||||
{
|
||||
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 item in self {
|
||||
item.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, bt_property_tag: u16) -> Result<Self> {
|
||||
let len = i32::unmarshal_from(r, bt_property_tag)?;
|
||||
if len == -1 {
|
||||
return Ok(Self::new());
|
||||
}
|
||||
let mut set = Self::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
set.insert(T::unmarshal_from(r, bt_property_tag)?);
|
||||
}
|
||||
Ok(set)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OctData for Option<T>
|
||||
where
|
||||
T: OctData,
|
||||
{
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {
|
||||
if let Some(item) = self {
|
||||
item.marshal_to(w, bt_property_tag)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, bt_property_tag: u16) -> Result<Self> {
|
||||
Ok(Some(T::unmarshal_from(r, bt_property_tag)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl OctData for String {
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {
|
||||
if self.is_empty() {
|
||||
(-1i32).marshal_to(w, bt_property_tag)?;
|
||||
return Ok(());
|
||||
}
|
||||
(self.len() as i32).marshal_to(w, bt_property_tag)?;
|
||||
w.write_all(self.as_bytes())
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, bt_property_tag: u16) -> Result<Self> {
|
||||
let len = i32::unmarshal_from(r, bt_property_tag)?;
|
||||
if len == -1 {
|
||||
return Ok(Self::new());
|
||||
}
|
||||
let mut buf = vec![0; len as usize];
|
||||
r.read_exact(&mut buf)?;
|
||||
Ok(Self::from_utf8(buf).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> OctData for Cow<'a, T>
|
||||
where
|
||||
T: OctData + Clone,
|
||||
{
|
||||
fn marshal_to<W: Write>(&self, w: &mut W, bt_property_tag: u16) -> Result<()> {
|
||||
self.as_ref().marshal_to(w, bt_property_tag)
|
||||
}
|
||||
|
||||
fn unmarshal_from<R: Read>(r: &mut R, bt_property_tag: u16) -> Result<Self> {
|
||||
Ok(Cow::Owned(T::unmarshal_from(r, bt_property_tag)?))
|
||||
}
|
||||
}
|
16
qwer/src/lib.rs
Normal file
16
qwer/src/lib.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
#![allow(incomplete_features)]
|
||||
#![feature(specialization)]
|
||||
|
||||
#[cfg(feature = "collection")]
|
||||
mod collection;
|
||||
#[cfg(feature = "fastoct")]
|
||||
mod fastoct;
|
||||
#[cfg(feature = "protocol")]
|
||||
mod protocol;
|
||||
|
||||
#[cfg(feature = "collection")]
|
||||
pub use collection::*;
|
||||
#[cfg(feature = "fastoct")]
|
||||
pub use fastoct::*;
|
||||
#[cfg(feature = "protocol")]
|
||||
pub use protocol::*;
|
36
qwer/src/protocol.rs
Normal file
36
qwer/src/protocol.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
#[derive(Debug, Default)]
|
||||
pub struct ProtocolHeader {
|
||||
pub to_channel: u16,
|
||||
pub from_channel: u16,
|
||||
pub is_rpc_ret: bool,
|
||||
pub rpc_arg_uid: u64,
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for ProtocolHeader {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
let to_channel = u16::from_le_bytes(value[0..2].try_into().unwrap());
|
||||
let from_channel = u16::from_le_bytes(value[2..4].try_into().unwrap());
|
||||
let is_rpc_ret = value[4] != 100;
|
||||
let rpc_arg_uid = u64::from_le_bytes(value[5..13].try_into().unwrap());
|
||||
|
||||
Self {
|
||||
to_channel,
|
||||
from_channel,
|
||||
is_rpc_ret,
|
||||
rpc_arg_uid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProtocolHeader> for Vec<u8> {
|
||||
fn from(value: ProtocolHeader) -> Self {
|
||||
let mut out = Self::with_capacity(13);
|
||||
|
||||
out.extend(value.to_channel.to_le_bytes());
|
||||
out.extend(value.from_channel.to_le_bytes());
|
||||
out.push(if value.is_rpc_ret { 1 } else { 100 });
|
||||
out.extend(value.rpc_arg_uid.to_le_bytes());
|
||||
|
||||
out
|
||||
}
|
||||
}
|
23
sdkserver/Cargo.toml
Normal file
23
sdkserver/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "sdkserver"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
axum.workspace = true
|
||||
axum-server.workspace = true
|
||||
encoding.workspace = true
|
||||
leb128.workspace = true
|
||||
anyhow.workspace = true
|
||||
env_logger.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tokio-util.workspace = true
|
||||
ansi_term.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "nap-sdkserver"
|
||||
path = "src/main.rs"
|
23
sdkserver/src/crypto.rs
Normal file
23
sdkserver/src/crypto.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use encoding::all::UTF_16LE;
|
||||
use encoding::{EncoderTrap, Encoding};
|
||||
|
||||
pub fn encrypt_config(content: &str, key: &str) -> Vec<u8> {
|
||||
let mut content_bytes = UTF_16LE.encode(content, EncoderTrap::Strict).unwrap();
|
||||
let key_bytes = UTF_16LE.encode(key, EncoderTrap::Strict).unwrap();
|
||||
|
||||
for i in 0..content_bytes.len() {
|
||||
content_bytes[i] ^= key_bytes[i % key_bytes.len()];
|
||||
}
|
||||
|
||||
let k = key.as_bytes();
|
||||
|
||||
let mut out = Vec::with_capacity(4 + k.len() + 8 + content_bytes.len());
|
||||
|
||||
leb128::write::unsigned(&mut out, u64::try_from(k.len()).unwrap()).unwrap();
|
||||
out.extend_from_slice(k);
|
||||
out.extend(0_u32.to_le_bytes());
|
||||
out.extend(u32::try_from(content_bytes.len()).unwrap().to_le_bytes());
|
||||
out.extend(content_bytes);
|
||||
|
||||
out
|
||||
}
|
70
sdkserver/src/main.rs
Normal file
70
sdkserver/src/main.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use anyhow::Result;
|
||||
use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
|
||||
|
||||
use axum::{
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
|
||||
mod crypto;
|
||||
mod services;
|
||||
|
||||
use services::{auth, config, entry, errors};
|
||||
|
||||
const HOST: &str = "0.0.0.0";
|
||||
const PORT: u16 = 21000;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
init_tracing()?;
|
||||
|
||||
let router = Router::new()
|
||||
.route(auth::RISKY_API_CHECK_ENDPOINT, post(auth::risky_api_check))
|
||||
.route(
|
||||
auth::LOGIN_WITH_PASSWORD_ENDPOINT,
|
||||
post(auth::login_with_password),
|
||||
)
|
||||
.route(
|
||||
auth::LOGIN_WITH_SESSION_TOKEN_ENDPOINT,
|
||||
post(auth::login_with_session_token),
|
||||
)
|
||||
.route(
|
||||
auth::GRANTER_LOGIN_VERIFICATION_ENDPOINT,
|
||||
post(auth::granter_login_verification),
|
||||
)
|
||||
.route(config::APP_CONFIG_ENDPOINT, get(config::application))
|
||||
.route(config::SERVER_LIST_ENDPOINT, get(config::server_list))
|
||||
.route(
|
||||
config::VERSIONS_BUNDLE_ENDPOINT,
|
||||
get(config::versions_bundle),
|
||||
)
|
||||
.route(entry::ACCOUNT_TOKEN_ENDPOINT, post(entry::account_token))
|
||||
.route(entry::ACCOUNT_SERVER_ENDPOINT, post(entry::account_server))
|
||||
.fallback(errors::not_found);
|
||||
|
||||
let bind_url = format!("{HOST}:{PORT}");
|
||||
let http_server = axum_server::bind(bind_url.parse()?);
|
||||
tracing::info!("SDK Server is listening at {bind_url}");
|
||||
|
||||
http_server.serve(router.into_make_service()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_tracing() -> Result<()> {
|
||||
#[cfg(target_os = "windows")]
|
||||
ansi_term::enable_ansi_support().unwrap();
|
||||
|
||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||
|
||||
let log_filter = EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| EnvFilter::try_new("info").unwrap())
|
||||
.add_directive("sdkserver=debug".parse().unwrap());
|
||||
|
||||
tracing::subscriber::set_global_default(
|
||||
Registry::default()
|
||||
.with(log_filter)
|
||||
.with(tracing_subscriber::fmt::Layer::default()),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
84
sdkserver/src/services/auth.rs
Normal file
84
sdkserver/src/services/auth.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use axum::Json;
|
||||
use serde_json::json;
|
||||
|
||||
pub const LOGIN_WITH_PASSWORD_ENDPOINT: &str = "/nap_global/mdk/shield/api/login";
|
||||
pub const LOGIN_WITH_SESSION_TOKEN_ENDPOINT: &str = "/nap_global/mdk/shield/api/verify";
|
||||
pub const GRANTER_LOGIN_VERIFICATION_ENDPOINT: &str = "/nap_global/combo/granter/login/v2/login";
|
||||
pub const RISKY_API_CHECK_ENDPOINT: &str = "/account/risky/api/check";
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn login_with_password() -> Json<serde_json::Value> {
|
||||
tracing::info!("login_with_password");
|
||||
Json(json!({
|
||||
"data": {
|
||||
"account": {
|
||||
"area_code": "**",
|
||||
"email": "ReversedRooms",
|
||||
"country": "RU",
|
||||
"is_email_verify": "1",
|
||||
"token": "mostsecuretokenever",
|
||||
"uid": "1337"
|
||||
},
|
||||
"device_grant_required": false,
|
||||
"reactivate_required": false,
|
||||
"realperson_required": false,
|
||||
"safe_mobile_required": false
|
||||
},
|
||||
"message": "OK",
|
||||
"retcode": 0
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn login_with_session_token() -> Json<serde_json::Value> {
|
||||
tracing::info!("login_with_session_token");
|
||||
Json(json!({
|
||||
"data": {
|
||||
"account": {
|
||||
"area_code": "**",
|
||||
"email": "ReversedRooms",
|
||||
"country": "RU",
|
||||
"is_email_verify": "1",
|
||||
"token": "mostsecuretokenever",
|
||||
"uid": "1337"
|
||||
},
|
||||
"device_grant_required": false,
|
||||
"reactivate_required": false,
|
||||
"realperson_required": false,
|
||||
"safe_mobile_required": false
|
||||
},
|
||||
"message": "OK",
|
||||
"retcode": 0
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn granter_login_verification() -> Json<serde_json::Value> {
|
||||
tracing::info!("granter_login_verification");
|
||||
Json(json!({
|
||||
"data": {
|
||||
"account_type": 1,
|
||||
"combo_id": "1337",
|
||||
"combo_token": "9065ad8507d5a1991cb6fddacac5999b780bbd92",
|
||||
"data": "{\"guest\":false}",
|
||||
"heartbeat": false,
|
||||
"open_id": "1337"
|
||||
},
|
||||
"message": "OK",
|
||||
"retcode": 0
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn risky_api_check() -> Json<serde_json::Value> {
|
||||
tracing::info!("risky_api_check");
|
||||
Json(json!({
|
||||
"data": {
|
||||
"id": "06611ed14c3131a676b19c0d34c0644b",
|
||||
"action": "ACTION_NONE",
|
||||
"geetest": null
|
||||
},
|
||||
"message": "OK",
|
||||
"retcode": 0
|
||||
}))
|
||||
}
|
57
sdkserver/src/services/config.rs
Normal file
57
sdkserver/src/services/config.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use axum::{body::Body, response::IntoResponse};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::crypto;
|
||||
|
||||
pub const APP_CONFIG_ENDPOINT: &str = "/design_data/NAP_Publish_AppStore_0.1.0/oversea/config.bin";
|
||||
pub const SERVER_LIST_ENDPOINT: &str =
|
||||
"/design_data/NAP_Publish_AppStore_0.1.0/oversea/serverlist.bin";
|
||||
pub const VERSIONS_BUNDLE_ENDPOINT: &str = "/game_res/NAP_Publish/output_147608_1361f678bc/client/StandaloneWindows64/oversea/versions.bundle";
|
||||
|
||||
pub async fn application() -> Vec<u8> {
|
||||
crypto::encrypt_config(
|
||||
json!({
|
||||
"InfoGroups": {
|
||||
"StandaloneWindows64": {
|
||||
"VersionInfoGroups": {
|
||||
"0.1.0": {
|
||||
"MinVersion": "0.1.0",
|
||||
"LatestVersion": "0.1.0",
|
||||
"GameResUrl": "http://127.0.0.1:21000/game_res/NAP_Publish/output_147608_1361f678bc/client/",
|
||||
"DesignDataUrl": "http://127.0.0.1:21000/",
|
||||
"ServerListUrl": "http://127.0.0.1:21000/design_data/NAP_Publish_AppStore_0.1.0/oversea/serverlist.bin",
|
||||
"$Type": "Foundation.ConfigurationInfo"
|
||||
},
|
||||
},
|
||||
"$Type": "Foundation.ConfigurationInfoGroup"
|
||||
},
|
||||
},
|
||||
})
|
||||
.to_string()
|
||||
.as_str(),
|
||||
"MostSecureKey",
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn server_list() -> Vec<u8> {
|
||||
crypto::encrypt_config(
|
||||
json!([{
|
||||
"sid": 142,
|
||||
"serverName": "HollowPS",
|
||||
"ip": "127.0.0.1",
|
||||
"port": "21000",
|
||||
"noticeRegion": "nap_glb_cbus01",
|
||||
"protocol": "http",
|
||||
"$Type": "MoleMole.ServerListInfo"
|
||||
}])
|
||||
.to_string()
|
||||
.as_str(),
|
||||
"MostSecureKey",
|
||||
)
|
||||
}
|
||||
|
||||
pub const VERSION_BUNDLE: &[u8] = include_bytes!("../../versions.bundle");
|
||||
|
||||
pub async fn versions_bundle() -> impl IntoResponse {
|
||||
Body::from(VERSION_BUNDLE)
|
||||
}
|
31
sdkserver/src/services/entry.rs
Normal file
31
sdkserver/src/services/entry.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use axum::Json;
|
||||
use serde_json::json;
|
||||
|
||||
pub const ACCOUNT_TOKEN_ENDPOINT: &str = "/account/token";
|
||||
pub const ACCOUNT_SERVER_ENDPOINT: &str = "/account/account_server";
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn account_token() -> Json<serde_json::Value> {
|
||||
tracing::info!("account_token");
|
||||
Json(json!({
|
||||
"ErrorCode": 0,
|
||||
"ErrorMsg": null,
|
||||
"Ext": {
|
||||
"Birthday": "01.01",
|
||||
"Country": "RU",
|
||||
"Token": "MostSecureTokenEver"
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn account_server() -> Json<serde_json::Value> {
|
||||
tracing::info!("account_server");
|
||||
Json(json!({
|
||||
"ErrorCode": 0,
|
||||
"ErrorMsg": null,
|
||||
"Ext": {
|
||||
"Address": "127.0.0.1:10301/0"
|
||||
}
|
||||
}))
|
||||
}
|
7
sdkserver/src/services/errors.rs
Normal file
7
sdkserver/src/services/errors.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
use axum::http::{StatusCode, Uri};
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn not_found(uri: Uri) -> impl IntoResponse {
|
||||
tracing::warn!("unhandled http request: {uri}");
|
||||
StatusCode::NOT_FOUND
|
||||
}
|
4
sdkserver/src/services/mod.rs
Normal file
4
sdkserver/src/services/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub mod auth;
|
||||
pub mod config;
|
||||
pub mod entry;
|
||||
pub mod errors;
|
BIN
sdkserver/versions.bundle
Normal file
BIN
sdkserver/versions.bundle
Normal file
Binary file not shown.
Loading…
Reference in a new issue