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