Compare commits

..

11 commits

47 changed files with 4964 additions and 294 deletions

2
.cargo/config.toml Normal file
View file

@ -0,0 +1,2 @@
[alias]
xtask = "run --package xtask --"

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
target/
Cargo.lock
proto/StarRail.proto
/*.json

View file

@ -1,5 +1,5 @@
[workspace]
members = [ "gameserver", "proto", "sdkserver"]
members = ["common", "dispatch", "gameserver", "proto", "sdkserver", "xtask"]
resolver = "2"
[workspace.package]
@ -13,6 +13,10 @@ lazy_static = "1.4.0"
axum = "0.7.4"
axum-server = "0.6.0"
tower = "0.4.13"
tower-http = { version = "0.5.2", features = ["normalize-path"] }
hyper = { version = "1.3.0", features = [ "client" ] }
hyper-util = { version = "0.1.3", features = [ "client-legacy" ] }
dirs = "5.0.1"
dotenv = "0.15.0"
@ -49,6 +53,7 @@ tracing-subscriber = { version = "0.3.18", features = [
] }
tracing-bunyan-formatter = "0.3.9"
common = { path = "common/" }
proto = { path = "proto/" }
[profile.release]

View file

@ -208,7 +208,7 @@ If you develop a new program, and you want it to be of the greatest possible use
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
RobinSR
AcheronSR
Copyright (C) 2024 reversedrooms
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
@ -221,7 +221,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
RobinSR Copyright (C) 2024 reversedrooms
AcheronSR Copyright (C) 2024 reversedrooms
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.

View file

@ -1,7 +1,7 @@
# RobinSR
# AcheronSR
A Server emulator for the game [`Honkai: Star Rail`](https://hsr.hoyoverse.com/en-us/)
![screenshot](https://git.xeondev.com/reversedrooms/RobinSR/raw/branch/master/screenshot.png)
![screenshot](https://git.xeondev.com/reversedrooms/AcheronSR/raw/branch/master/screenshot.png)
## Installation
@ -11,52 +11,56 @@ A Server emulator for the game [`Honkai: Star Rail`](https://hsr.hoyoverse.com/e
- [Rust](https://www.rust-lang.org/tools/install)
**NOTE**: Nightly Rust is required to build the project. To install it, first install
Rust itself, then run the following command:
```sh
rustup toolchain install nightly
rustup default nightly
```
#### Building
##### Manually
```sh
git clone https://git.xeondev.com/reversedrooms/RobinSR.git
cd RobinSR
cargo install --path gameserver
cargo install --path sdkserver
git clone https://git.xeondev.com/reversedrooms/AcheronSR.git
cd AcheronSR
cargo build --bin gameserver
cargo build --bin dispatch
cargo build --bin sdkserver
```
##### Using xtasks (use this if stupid)
```sh
cargo xtask run
```
##### To run it with automatic recompilation when any Rust files are changed
```sh
cargo xtask watch
```
### From Pre-built Binaries
Navigate to the [Releases](https://git.xeondev.com/reversedrooms/RobinSR/releases)
Navigate to the [Releases](https://git.xeondev.com/reversedrooms/AcheronSR/releases)
page and download the latest release for your platform.
## Usage
To begin using the server, you need to run both the SDK server and the game server.
If you installed from source, Rust's installer should have added .cargo/bin to your
path, so simply run the following:
```sh
gameserver
sdkserver
```
If you installed from pre-built binaries, navigate to the directory where you downloaded
the binaries and either a) double-click on the following executable names or b)
run the following in a terminal:
```sh
./gameserver
./dispatch
./sdkserver
```
##### Note: the `assets` folder should be in the same directory with the `gameserver`, otherwise it will panic.
## Connecting
[Get 2.2 beta client](https://bhrpg-prod.oss-accelerate.aliyuncs.com/client/beta/20240322124944_scfGE0xJXlWtoJ1r/StarRail_2.1.51.zip),
replace [mhypbase.dll](https://git.xeondev.com/reversedrooms/RobinSR/raw/branch/master/mhypbase.dll) file in your game folder, it will redirect game traffic (and also disable in-game censorship)
replace [mhypbase.dll](https://git.xeondev.com/reversedrooms/AcheronSR/raw/branch/master/mhypbase.dll)
file in your game folder, it will redirect game traffic (and disable in-game censorship)
## Contributing

File diff suppressed because it is too large Load diff

13
common/Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "common"
edition = "2021"
version.workspace = true
[dependencies]
anyhow.workspace = true
ansi_term.workspace = true
env_logger.workspace = true
tracing.workspace = true
lazy_static.workspace = true
serde.workspace = true
serde_json.workspace = true

20
common/src/data/excels.rs Normal file
View file

@ -0,0 +1,20 @@
use serde::Deserialize;
#[derive(Default, Deserialize)]
#[serde(rename_all = "PascalCase")]
#[serde(default)]
pub struct MapEntranceConfig {
#[serde(rename = "BeginMainMissionIDList")]
pub begin_main_mission_idlist: Vec<u32>,
pub entrance_type: String,
#[serde(rename = "FinishMainMissionIDList")]
pub finish_main_mission_idlist: Vec<u32>,
#[serde(rename = "FinishSubMissionIDList")]
pub finish_sub_mission_idlist: Vec<u32>,
#[serde(rename = "FloorID")]
pub floor_id: u32,
#[serde(rename = "ID")]
pub id: u32,
#[serde(rename = "PlaneID")]
pub plane_id: u32,
}

37
common/src/data/mod.rs Normal file
View file

@ -0,0 +1,37 @@
mod excels;
pub use excels::*;
use lazy_static::lazy_static;
use serde_json::from_str;
pub fn init_assets() {
tracing::info!("Loaded {} excel tables", EXCEL_COLLECTION.table_count());
}
lazy_static! {
pub static ref EXCEL_COLLECTION: ExcelCollection = ExcelCollection::new();
}
pub struct ExcelCollection {
pub map_entrance_configs: Vec<MapEntranceConfig>,
}
impl ExcelCollection {
fn new() -> Self {
Self {
map_entrance_configs: from_str(&load_asset(
"assets/ExcelOutput/MapEntranceConfig.json",
))
.unwrap(),
}
}
#[must_use]
pub const fn table_count(&self) -> usize {
1
}
}
fn load_asset(path: &str) -> String {
std::fs::read_to_string(path).unwrap()
}

3
common/src/lib.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod data;
pub mod logging;
pub mod util;

7
common/src/util.rs Normal file
View file

@ -0,0 +1,7 @@
#[must_use]
pub fn load_or_create_config(path: &str, defaults: &str) -> String {
std::fs::read_to_string(path).unwrap_or_else(|_| {
std::fs::write(path, defaults).unwrap();
defaults.to_string()
})
}

34
dispatch/Cargo.toml Normal file
View file

@ -0,0 +1,34 @@
[package]
name = "dispatch"
edition = "2021"
version.workspace = true
[dependencies]
common.workspace = true
anyhow.workspace = true
env_logger.workspace = true
axum.workspace = true
axum-server.workspace = true
lazy_static.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
ansi_term.workspace = true
prost.workspace = true
rbase64.workspace = true
proto.workspace = true
tower.workspace = true
tower-http.workspace = true

34
dispatch/dispatch.json Normal file
View file

@ -0,0 +1,34 @@
{
"http_port": 21041,
"game_servers": {
"acheron_sr": {
"name": "AcheronSR",
"title": "AcheronSR",
"dispatch_url": "http://127.0.0.1:21041/query_gateway/acheron_sr",
"env_type": "2",
"gateserver_ip": "127.0.0.1",
"gateserver_port": 23301,
"gateserver_protocol": "Tcp"
}
},
"versions": {
"CNBETAWin2.1.51": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_6744505_89b2f5dc973e",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_6759713_b4e0e740f0da",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_6755976_3c46d7c46e2c",
"lua_version": "6755976"
},
"CNBETAWin2.1.52": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_6785106_15237df2ef89",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_6787319_5f3f1dae4769",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_6785460_26c4b6c61a8b",
"lua_version": "6785460"
},
"CNBETAWin2.1.53": {
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_6828321_72f2df86102b",
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_6834225_44836493b261",
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_6828764_f749b48347fd",
"lua_version": "6828764"
}
}
}

51
dispatch/src/config.rs Normal file
View file

@ -0,0 +1,51 @@
use std::collections::HashMap;
use common::util::load_or_create_config;
use lazy_static::lazy_static;
use serde::Deserialize;
use serde_json::from_str;
const DEFAULT_CONFIG: &str = include_str!("../dispatch.json");
pub fn init_config() {
let _configuration = &*CONFIGURATION;
}
#[derive(Deserialize)]
pub struct DispatchServerConfiguration {
pub http_port: u16,
pub game_servers: HashMap<String, GameServerConfig>,
pub versions: HashMap<String, VersionConfig>,
}
#[derive(Deserialize)]
pub struct VersionConfig {
pub asset_bundle_url: String,
pub ex_resource_url: String,
pub lua_url: String,
pub lua_version: String,
}
#[derive(Deserialize)]
pub struct GameServerConfig {
pub name: String,
pub title: String,
pub dispatch_url: String,
pub env_type: String,
pub gateserver_ip: String,
pub gateserver_port: u16,
pub gateserver_protocol: GatewayProtocolType,
}
#[derive(Deserialize, Eq, PartialEq)]
pub enum GatewayProtocolType {
Tcp,
Kcp,
}
lazy_static! {
pub static ref CONFIGURATION: DispatchServerConfiguration = {
let data = load_or_create_config("dispatch.json", DEFAULT_CONFIG);
from_str(&data).unwrap()
};
}

85
dispatch/src/handlers.rs Normal file
View file

@ -0,0 +1,85 @@
use crate::config::*;
use axum::extract::{Path, Query};
use prost::Message;
use proto::{Dispatch, Gateserver, RegionInfo};
use serde::Deserialize;
pub const QUERY_DISPATCH_PATH: &str = "/query_dispatch";
pub const QUERY_GATEWAY_PATH: &str = "/query_gateway/:region_name";
#[tracing::instrument]
pub async fn query_dispatch() -> String {
let rsp = Dispatch {
retcode: 0,
region_list: CONFIGURATION
.game_servers
.values()
.map(|c| RegionInfo {
name: c.name.clone(),
title: c.title.clone(),
env_type: c.env_type.clone(),
dispatch_url: c.dispatch_url.clone(),
..Default::default()
})
.collect(),
..Default::default()
};
let mut buff = Vec::new();
rsp.encode(&mut buff).unwrap();
rbase64::encode(&buff)
}
#[derive(Deserialize, Debug)]
pub struct QueryGatewayParameters {
pub version: String,
}
#[tracing::instrument]
pub async fn query_gateway(
Path(region_name): Path<String>,
parameters: Query<QueryGatewayParameters>,
) -> String {
let rsp = if let Some(server_config) = CONFIGURATION.game_servers.get(&region_name) {
if let Some(version_config) = CONFIGURATION.versions.get(&parameters.version) {
Gateserver {
retcode: 0,
ip: server_config.gateserver_ip.clone(),
port: u32::from(server_config.gateserver_port),
asset_bundle_url: version_config.asset_bundle_url.clone(),
ex_resource_url: version_config.ex_resource_url.clone(),
lua_url: version_config.lua_url.clone(),
lua_version: version_config.lua_version.clone(),
ifix_version: String::from("0"),
jblkncaoiao: true,
hjdjakjkdbi: true,
ldknmcpffim: true,
feehapamfci: true,
eebfeohfpph: true,
dfmjjcfhfea: true,
najikcgjgan: true,
giddjofkndm: true,
use_tcp: server_config.gateserver_protocol == GatewayProtocolType::Tcp,
..Default::default()
}
} else {
Gateserver {
retcode: 9,
msg: format!("forbidden version: {} or invalid bind", parameters.version),
..Default::default()
}
}
} else {
Gateserver {
retcode: 9,
msg: format!("server config for {region_name} not found"),
..Default::default()
}
};
let mut buff = Vec::new();
rsp.encode(&mut buff).unwrap();
rbase64::encode(&buff)
}

35
dispatch/src/main.rs Normal file
View file

@ -0,0 +1,35 @@
use anyhow::Result;
use axum::{extract::Request, routing::get, Router, ServiceExt};
use common::logging::init_tracing;
use tokio::net::TcpListener;
use tower::Layer;
use tower_http::normalize_path::NormalizePathLayer;
use tracing::Level;
mod config;
mod handlers;
use config::{init_config, CONFIGURATION};
#[tokio::main]
async fn main() -> Result<()> {
init_tracing();
init_config();
let span = tracing::span!(Level::DEBUG, "main");
let _ = span.enter();
let app = Router::new()
.route(handlers::QUERY_DISPATCH_PATH, get(handlers::query_dispatch))
.route(handlers::QUERY_GATEWAY_PATH, get(handlers::query_gateway));
let app = NormalizePathLayer::trim_trailing_slash().layer(app);
let addr = format!("0.0.0.0:{}", CONFIGURATION.http_port);
let server = TcpListener::bind(&addr).await?;
tracing::info!("dispatch is listening at {addr}");
axum::serve(server, ServiceExt::<Request>::into_make_service(app)).await?;
Ok(())
}

View file

@ -4,6 +4,8 @@ edition = "2021"
version.workspace = true
[dependencies]
common.workspace = true
ansi_term.workspace = true
anyhow.workspace = true
atomic_refcell.workspace = true

View file

@ -1,5 +1,5 @@
{
"lineup": [1309, 1308, 1307, 1315],
"lineup": [1308, 1309, 1307, 1315],
"monster_wave_list":
[
[3013010, 3013010],

View file

@ -1,3 +1,4 @@
use common::util::load_or_create_config;
use lazy_static::lazy_static;
use serde::Deserialize;
use serde_json::from_str;
@ -6,25 +7,7 @@ const DEFAULT_GLOBALS: &str = include_str!("../../globals.json");
lazy_static! {
pub static ref INSTANCE: Globals = {
let local_config = std::path::Path::new("globals.json");
let data = if local_config.exists() {
std::fs::read_to_string("globals.json").unwrap()
} else {
let config = dirs::config_dir()
.expect("No config directory found")
.join("hkrpg-gameserver");
std::fs::create_dir_all(&config).unwrap();
let env = config.join("globals.json");
if !env.exists() {
std::fs::write(&env, DEFAULT_GLOBALS).unwrap();
}
std::fs::read_to_string(&env).unwrap()
};
let data = load_or_create_config("globals.json", DEFAULT_GLOBALS);
from_str(&data).unwrap()
};
}

View file

@ -3,3 +3,7 @@ mod player_info;
pub use global_config::INSTANCE as globals;
pub use player_info::PlayerInfo;
pub fn init_config() {
let _globals = &*globals; // this will initialize the static (and create default config)
}

View file

@ -5,12 +5,14 @@ use anyhow::Result;
use proto::*;
pub struct PlayerInfo {
pub uid: u32,
pub lineup: LineupInfo,
}
impl PlayerInfo {
pub fn new() -> Self {
Self {
uid: 1337,
lineup: default_lineup(),
}
}

View file

@ -1,15 +1,19 @@
use anyhow::Result;
mod game;
mod logging;
mod net;
mod util;
use logging::init_tracing;
use common::data::init_assets;
use common::logging::init_tracing;
use game::init_config;
#[tokio::main]
async fn main() -> Result<()> {
init_tracing();
init_config();
init_assets();
net::gateway::listen("0.0.0.0", 23301).await?;
Ok(())

View file

@ -1,8 +1,9 @@
use anyhow::Result;
use common::log_error;
use tokio::net::TcpListener;
use tracing::{info_span, Instrument};
use crate::{log_error, net::PlayerSession};
use crate::net::PlayerSession;
pub async fn listen(host: &str, port: u16) -> Result<()> {
let listener = TcpListener::bind(format!("{host}:{port}")).await?;

View file

@ -13,7 +13,7 @@ pub async fn on_player_get_token_cs_req(
PlayerGetTokenScRsp {
retcode: 0,
msg: String::from("OK"),
uid: 1337,
uid: session.player_uid(),
..Default::default()
},
)
@ -32,7 +32,7 @@ pub async fn on_player_login_cs_req(
server_timestamp_ms: util::cur_timestamp_ms(),
stamina: 240,
basic_info: Some(PlayerBasicInfo {
nickname: String::from("xeondev"),
nickname: String::from("AcheronSR"),
level: 5,
stamina: 240,
..Default::default()

View file

@ -137,7 +137,8 @@ pub async fn on_quit_lineup_cs_req(session: &PlayerSession, body: &QuitLineupCsR
.await
}
fn lineup_avatar(id: u32, slot: u32) -> LineupAvatar {
#[must_use]
const fn lineup_avatar(id: u32, slot: u32) -> LineupAvatar {
LineupAvatar {
id,
slot,

View file

@ -0,0 +1,27 @@
use super::*;
pub async fn on_get_scene_map_info_cs_req(
session: &PlayerSession,
body: &GetSceneMapInfoCsReq,
) -> Result<()> {
session
.send(
CMD_GET_SCENE_MAP_INFO_SC_RSP,
GetSceneMapInfoScRsp {
entry_id: body.entry_id,
cur_map_entry_id: body.entry_id,
scene_map_info: body
.entry_id_list
.iter()
.map(|id| SceneMapInfo {
cur_map_entry_id: body.entry_id,
entry_id: *id,
..Default::default()
})
.collect(),
retcode: 0,
..Default::default()
},
)
.await
}

View file

@ -2,6 +2,7 @@ mod authentication;
mod avatar;
mod battle;
mod lineup;
mod map;
mod mission;
mod player;
mod scene;
@ -19,6 +20,7 @@ pub use authentication::*;
pub use avatar::*;
pub use battle::*;
pub use lineup::*;
pub use map::*;
pub use mission::*;
pub use player::*;
pub use scene::*;

View file

@ -53,7 +53,7 @@ pub async fn on_player_heart_beat_cs_req(
download_data: Some(ClientDownloadData {
version: 51,
time: util::cur_timestamp_ms() as i64,
data: rbase64::decode("G0x1YVMBGZMNChoKBAQICHhWAAAAAAAAAAAAAAAod0ABKEBDOlxVc2Vyc1x4ZW9uZGV2XERvd25sb2Fkc1xyYWJzdHZvLmx1YQAAAAAAAAAAAAEEEAAAACQAQAApQEAAKYBAACnAQABWAAEALIAAAR1AQQCkgEEA5ABAAOnAwQHpAMIB6UDCAawAAAEsgAAAH8BChRkAgAAMAAAABANDUwQMVW5pdHlFbmdpbmUEC0dhbWVPYmplY3QEBUZpbmQEKVVJUm9vdC9BYm92ZURpYWxvZy9CZXRhSGludERpYWxvZyhDbG9uZSkEF0dldENvbXBvbmVudEluQ2hpbGRyZW4EB3R5cGVvZgQEUlBHBAdDbGllbnQEDkxvY2FsaXplZFRleHQEBXRleHQURVJvYmluU1IgaXMgYSBmcmVlIGFuZCBvcGVuIHNvdXJjZSBzb2Z0d2FyZS4gZGlzY29yZC5nZy9yZXZlcnNlZHJvb21zAQAAAAEAAAAAABAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAAAAAAEAAAAFX0VOVg==").unwrap()
data: rbase64::decode("G0x1YVMBGZMNChoKBAQICHhWAAAAAAAAAAAAAAAod0ABKEBDOlxVc2Vyc1x4ZW9uZGV2XERvd25sb2Fkc1xhY2hlcm9uLmx1YQAAAAAAAAAAAAEEHwAAACQAQAApQEAAKYBAACnAQABWAAEALIAAAR1AQQCkgEEA5ABAAOnAwQHpAMIB6UDCAawAAAEsgAAAH8BChSQAQAApQEAAKYBAACnAQABWAAMALIAAAR1AQQCkgEEA5ABAAOnAwQHpAMIB6UDCAawAAAEsgAAAH0BDhRkAgAAOAAAABANDUwQMVW5pdHlFbmdpbmUEC0dhbWVPYmplY3QEBUZpbmQEKVVJUm9vdC9BYm92ZURpYWxvZy9CZXRhSGludERpYWxvZyhDbG9uZSkEF0dldENvbXBvbmVudEluQ2hpbGRyZW4EB3R5cGVvZgQEUlBHBAdDbGllbnQEDkxvY2FsaXplZFRleHQEBXRleHQULUFjaGVyb25TUiBpcyBhIGZyZWUgYW5kIG9wZW4gc291cmNlIHNvZnR3YXJlBAxWZXJzaW9uVGV4dBQuVmlzaXQgZGlzY29yZC5nZy9yZXZlcnNlZHJvb21zIGZvciBtb3JlIGluZm8hAQAAAAEAAAAAAB8AAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAAAAAAEAAAAFX0VOVg==").unwrap()
}),
},
)

View file

@ -1,5 +1,36 @@
use common::data::EXCEL_COLLECTION;
use super::*;
pub async fn on_enter_scene_cs_req(session: &PlayerSession, body: &EnterSceneCsReq) -> Result<()> {
session
.send(CMD_ENTER_SCENE_SC_RSP, EnterSceneScRsp::default())
.await?;
let entrance_config = EXCEL_COLLECTION
.map_entrance_configs
.iter()
.find(|c| c.id == body.entry_id)
.unwrap();
let player = session.player_info();
let enter_scene_by_server = EnterSceneByServerScNotify {
reason: EnterSceneReason::None.into(),
lineup: Some(player.lineup.clone()),
scene: Some(SceneInfo {
plane_id: entrance_config.plane_id,
floor_id: entrance_config.floor_id,
entry_id: entrance_config.id,
game_mode_type: 2, // TODO: EntranceType -> enum repr(u32)
..Default::default()
}),
};
session
.send(CMD_ENTER_SCENE_BY_SERVER_SC_NOTIFY, enter_scene_by_server)
.await
}
pub async fn on_get_cur_scene_info_cs_req(
session: &PlayerSession,
_body: &GetCurSceneInfoCsReq,
@ -26,7 +57,7 @@ pub async fn on_get_cur_scene_info_cs_req(
avatar_type: 3,
base_avatar_id: 1309,
map_layer: 2,
uid: 1337,
uid: session.player_uid(),
}),
motion: Some(MotionInfo {
aomilajjmii: Some(Vector {

View file

@ -1241,7 +1241,7 @@ trait_handler! {
// SetCurInteractEntityScRsp 1497;
// SceneCastSkillCsReq 1402;
StartCocoonStageCsReq 1408;
// GetSceneMapInfoCsReq 1470;
GetSceneMapInfoCsReq 1470;
// SceneEntityMoveScRsp 1448;
// DeactivateFarmElementScRsp 1467;
// SetCurInteractEntityCsReq 1491;
@ -1263,7 +1263,7 @@ trait_handler! {
// GetSpringRecoverDataCsReq 1466;
// SceneEntityTeleportScRsp 1415;
// SetClientPausedScRsp 1465;
// EnterSceneCsReq 1472;
EnterSceneCsReq 1472;
// GetAllServerPrefsDataScRsp 6148;
// GetServerPrefsDataCsReq 6162;
// UpdateServerPrefsDataScRsp 6109;

View file

@ -51,6 +51,10 @@ impl PlayerSession {
self.client_socket.lock().await
}
pub fn player_uid(&self) -> u32 {
self.player_info().uid
}
pub fn player_info(&self) -> AtomicRef<PlayerInfo> {
self.player_info.borrow()
}

View file

@ -1,6 +1,8 @@
use std::path::Path;
pub fn main() {
let proto_file = "StarRail.proto";
if std::path::Path::new(proto_file).exists() {
if Path::new(proto_file).exists() {
println!("cargo:rerun-if-changed={proto_file}");
prost_build::Config::new()

View file

@ -207,7 +207,7 @@ pub struct Cldjmhdelhn {
#[prost(uint32, tag = "2")]
pub monster_id: u32,
#[prost(uint32, tag = "3")]
pub ifjocipnpgd: u32,
pub config_id: u32,
#[prost(uint32, tag = "4")]
pub world_level: u32,
#[prost(int64, tag = "5")]
@ -4549,7 +4549,7 @@ pub struct Acageokchob {
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Hgnpppadonl {
#[prost(enumeration = "Pdgndfeojif", tag = "2")]
pub bpodijpdnnk: i32,
pub reason: i32,
#[prost(enumeration = "Leieahhhhmi", tag = "3")]
pub jdgielljojd: i32,
}
@ -4748,7 +4748,7 @@ pub struct Codlhohjmag {
#[prost(enumeration = "Lcmlaclkndi", tag = "6")]
pub hfbfdnemjed: i32,
#[prost(enumeration = "Jgjcjhmakka", tag = "11")]
pub bpodijpdnnk: i32,
pub reason: i32,
#[prost(uint32, tag = "15")]
pub jageglcnmjj: u32,
#[prost(message, repeated, tag = "3")]
@ -10955,7 +10955,7 @@ pub struct Cnmoppanlgc {
#[prost(uint32, tag = "1")]
pub iabdegkmaom: u32,
#[prost(uint32, tag = "8")]
pub ifjocipnpgd: u32,
pub config_id: u32,
#[prost(uint32, tag = "5")]
pub ippleljgojk: u32,
#[prost(message, optional, tag = "14")]
@ -12593,7 +12593,7 @@ pub struct AnnounceData {
#[prost(uint32, tag = "3")]
pub dfbogdogcpp: u32,
#[prost(uint32, tag = "12")]
pub ifjocipnpgd: u32,
pub config_id: u32,
#[prost(string, tag = "6")]
pub chjojjlobei: ::prost::alloc::string::String,
#[prost(int64, tag = "14")]
@ -13530,7 +13530,7 @@ pub struct Ingnmgnigfo {
#[prost(message, optional, tag = "11")]
pub mdnlmmamejd: ::core::option::Option<PunkLordBattleRecord>,
#[prost(enumeration = "PunkLordMonsterInfoNotifyReason", tag = "13")]
pub bpodijpdnnk: i32,
pub reason: i32,
#[prost(message, optional, tag = "15")]
pub basic_info: ::core::option::Option<Cldjmhdelhn>,
}
@ -13590,7 +13590,7 @@ pub struct Kjeaaledkib {
#[prost(int64, tag = "8")]
pub phhhfhobhmk: i64,
#[prost(uint32, tag = "5")]
pub ifjocipnpgd: u32,
pub config_id: u32,
#[prost(uint32, tag = "3")]
pub blipenmcnbg: u32,
#[prost(bool, tag = "4")]
@ -14035,7 +14035,7 @@ pub struct Mipgopjanij {
#[prost(message, optional, tag = "3")]
pub lineup: ::core::option::Option<LineupInfo>,
#[prost(enumeration = "Mldgocoemih", tag = "1")]
pub bpodijpdnnk: i32,
pub reason: i32,
#[prost(message, optional, tag = "9")]
pub scene: ::core::option::Option<SceneInfo>,
}
@ -17196,7 +17196,7 @@ pub struct SceneInfo {
#[prost(message, repeated, tag = "497")]
pub chhmmbdhjpg: ::prost::alloc::vec::Vec<Dhkacjhaoid>,
#[prost(uint32, repeated, tag = "9")]
pub phicefeaigb: ::prost::alloc::vec::Vec<u32>,
pub lighten_section_list: ::prost::alloc::vec::Vec<u32>,
#[prost(message, repeated, tag = "7")]
pub env_buff_list: ::prost::alloc::vec::Vec<BuffInfo>,
#[prost(uint32, tag = "8")]
@ -17813,7 +17813,7 @@ pub struct Eogjjcbdadn {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Lckgkdehclb {
pub struct EnterSceneCsReq {
#[prost(bool, tag = "4")]
pub bfpgcodlocf: bool,
#[prost(uint32, tag = "2")]
@ -17826,7 +17826,7 @@ pub struct Lckgkdehclb {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Ehhfgomdfkd {
pub struct EnterSceneScRsp {
#[prost(bool, tag = "9")]
pub bfpgcodlocf: bool,
#[prost(bool, tag = "15")]
@ -17837,11 +17837,11 @@ pub struct Ehhfgomdfkd {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Jdokmmikidp {
pub struct EnterSceneByServerScNotify {
#[prost(message, optional, tag = "11")]
pub lineup: ::core::option::Option<LineupInfo>,
#[prost(enumeration = "Ffnhcbelgpd", tag = "8")]
pub bpodijpdnnk: i32,
#[prost(enumeration = "EnterSceneReason", tag = "8")]
pub reason: i32,
#[prost(message, optional, tag = "15")]
pub scene: ::core::option::Option<SceneInfo>,
}
@ -17859,9 +17859,9 @@ pub struct Bkpebkeapjh {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Fkjoeabiioe {
pub struct GetSceneMapInfoCsReq {
#[prost(uint32, repeated, tag = "1")]
pub dmkkkfnkofh: ::prost::alloc::vec::Vec<u32>,
pub entry_id_list: ::prost::alloc::vec::Vec<u32>,
#[prost(uint32, tag = "11")]
pub entry_id: u32,
#[prost(bool, tag = "6")]
@ -17872,20 +17872,20 @@ pub struct Fkjoeabiioe {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Kangcibfhee {
pub struct MazePropState {
#[prost(uint32, tag = "15")]
pub group_id: u32,
#[prost(uint32, tag = "1")]
pub ifjocipnpgd: u32,
pub config_id: u32,
#[prost(uint32, tag = "12")]
pub state: u32,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Gecjjlmabhp {
pub struct MazeGroup {
#[prost(int64, tag = "14")]
pub opmnklhfdch: i64,
pub modify_time: i64,
#[prost(uint32, tag = "8")]
pub group_id: u32,
#[prost(bool, tag = "5")]
@ -17896,7 +17896,7 @@ pub struct Gecjjlmabhp {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Gbiimoglajl {
pub struct ChestInfo {
#[prost(enumeration = "Kihbdaniehp", tag = "3")]
pub gommoeicmjg: i32,
#[prost(uint32, tag = "11")]
@ -17918,48 +17918,48 @@ pub struct Kbbeoemcdhi {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Fjniajephmj {
pub struct SceneMapInfo {
#[prost(uint32, repeated, tag = "13")]
pub ojlnmnehgai: ::prost::alloc::vec::Vec<u32>,
pub unlock_teleport_list: ::prost::alloc::vec::Vec<u32>,
#[prost(message, repeated, tag = "11")]
pub aechnhklpkp: ::prost::alloc::vec::Vec<Kbbeoemcdhi>,
#[prost(message, repeated, tag = "15")]
pub cgkfbhoadpc: ::prost::alloc::vec::Vec<Kangcibfhee>,
pub maze_prop_list: ::prost::alloc::vec::Vec<MazePropState>,
#[prost(message, repeated, tag = "6")]
pub dcbdhkkkpgd: ::prost::alloc::vec::Vec<Gbiimoglajl>,
pub dcbdhkkkpgd: ::prost::alloc::vec::Vec<ChestInfo>,
#[prost(uint32, tag = "4")]
pub retcode: u32,
#[prost(message, repeated, tag = "2")]
pub pmolfbcbfpe: ::prost::alloc::vec::Vec<Gecjjlmabhp>,
pub pmolfbcbfpe: ::prost::alloc::vec::Vec<MazeGroup>,
#[prost(uint32, tag = "12")]
pub entry_id: u32,
#[prost(uint32, tag = "14")]
pub kjlbpaefaff: u32,
pub cur_map_entry_id: u32,
#[prost(uint32, tag = "7")]
pub cngakkcmonh: u32,
#[prost(uint32, repeated, tag = "10")]
pub phicefeaigb: ::prost::alloc::vec::Vec<u32>,
pub lighten_section_list: ::prost::alloc::vec::Vec<u32>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Cegeebldbke {
pub struct GetSceneMapInfoScRsp {
#[prost(uint32, tag = "6")]
pub entry_id: u32,
#[prost(uint32, repeated, tag = "3")]
pub ojlnmnehgai: ::prost::alloc::vec::Vec<u32>,
pub unlock_teleport_list: ::prost::alloc::vec::Vec<u32>,
#[prost(message, repeated, tag = "8")]
pub cgkfbhoadpc: ::prost::alloc::vec::Vec<Kangcibfhee>,
pub maze_prop_list: ::prost::alloc::vec::Vec<MazePropState>,
#[prost(uint32, tag = "10")]
pub kjlbpaefaff: u32,
pub cur_map_entry_id: u32,
#[prost(message, repeated, tag = "14")]
pub dcbdhkkkpgd: ::prost::alloc::vec::Vec<Gbiimoglajl>,
pub dcbdhkkkpgd: ::prost::alloc::vec::Vec<ChestInfo>,
#[prost(uint32, repeated, tag = "9")]
pub phicefeaigb: ::prost::alloc::vec::Vec<u32>,
pub lighten_section_list: ::prost::alloc::vec::Vec<u32>,
#[prost(message, repeated, tag = "13")]
pub pmolfbcbfpe: ::prost::alloc::vec::Vec<Gecjjlmabhp>,
pub pmolfbcbfpe: ::prost::alloc::vec::Vec<MazeGroup>,
#[prost(message, repeated, tag = "2")]
pub mhefdgcamjl: ::prost::alloc::vec::Vec<Fjniajephmj>,
pub scene_map_info: ::prost::alloc::vec::Vec<SceneMapInfo>,
#[prost(bool, tag = "5")]
pub dhdoicckifl: bool,
#[prost(uint32, tag = "4")]
@ -17970,11 +17970,11 @@ pub struct Cegeebldbke {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Glknjjgjgjn {}
pub struct SyncServerSceneChangeNotify {}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Onfclaedboj {
pub struct GameplayCounterCountDownCsReq {
#[prost(uint32, tag = "7")]
pub baokagnfnab: u32,
#[prost(uint32, tag = "11")]
@ -17983,16 +17983,16 @@ pub struct Onfclaedboj {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Ppjlpfbfbhd {
pub struct GameplayCounterCountDownScRsp {
#[prost(uint32, tag = "13")]
pub retcode: u32,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Dceecobdeko {
pub struct GameplayCounterUpdateScNotify {
#[prost(enumeration = "Oigihagkoib", tag = "14")]
pub bpodijpdnnk: i32,
pub reason: i32,
#[prost(uint32, tag = "1")]
pub baokagnfnab: u32,
#[prost(uint32, tag = "15")]
@ -18001,7 +18001,7 @@ pub struct Dceecobdeko {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Lmkkjhdeaph {
pub struct GameplayCounterRecoverCsReq {
#[prost(uint32, tag = "1")]
pub ifmmefaoeoa: u32,
#[prost(uint32, tag = "6")]
@ -18010,32 +18010,35 @@ pub struct Lmkkjhdeaph {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Ijpkjflgcgn {
pub struct GameplayCounterRecoverScRsp {
#[prost(uint32, tag = "15")]
pub retcode: u32,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Fhjnibeddad {
pub struct UpdateFloorSavedValueNotify {
#[prost(map = "string, int32", tag = "6")]
pub mbpmoihjnfi: ::std::collections::HashMap<::prost::alloc::string::String, i32>,
pub saved_value_map: ::std::collections::HashMap<
::prost::alloc::string::String,
i32,
>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Nmbdnhoimim {
pub struct GetUnlockTeleportCsReq {
#[prost(uint32, repeated, tag = "1")]
pub dmkkkfnkofh: ::prost::alloc::vec::Vec<u32>,
pub entry_id_list: ::prost::alloc::vec::Vec<u32>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Pokegknhbga {
pub struct GetUnlockTeleportScRsp {
#[prost(uint32, tag = "12")]
pub retcode: u32,
#[prost(uint32, repeated, tag = "8")]
pub ojlnmnehgai: ::prost::alloc::vec::Vec<u32>,
pub unlock_teleport_list: ::prost::alloc::vec::Vec<u32>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
@ -18051,22 +18054,22 @@ pub struct Gffbkjofnad {
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Jnofbbanolk {
pub struct GroupRefreshInfo {
#[prost(uint32, tag = "4")]
pub state: u32,
#[prost(uint32, tag = "1")]
pub group_id: u32,
#[prost(message, repeated, tag = "14")]
pub fiiciciambe: ::prost::alloc::vec::Vec<Gffbkjofnad>,
#[prost(enumeration = "Njdmhcchfdj", tag = "7")]
pub kppckepfpce: i32,
#[prost(enumeration = "SceneGroupRefreshType", tag = "7")]
pub group_refresh_type: i32,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Ljihfeagpcl {
pub struct SceneGroupRefreshScNotify {
#[prost(message, repeated, tag = "14")]
pub kpfomkdmoce: ::prost::alloc::vec::Vec<Jnofbbanolk>,
pub group_refresh_list: ::prost::alloc::vec::Vec<GroupRefreshInfo>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
@ -18191,7 +18194,7 @@ pub struct Diplgalkehc {
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Kkbapmgmmcb {
#[prost(uint32, repeated, tag = "8")]
pub dmkkkfnkofh: ::prost::alloc::vec::Vec<u32>,
pub entry_id_list: ::prost::alloc::vec::Vec<u32>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::derive_partial_eq_without_eq)]
@ -20228,7 +20231,7 @@ pub struct Loldclbcgpd {
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Nihdpcfoidb {
#[prost(uint32, tag = "6")]
pub ifjocipnpgd: u32,
pub config_id: u32,
#[prost(uint32, tag = "15")]
pub group_id: u32,
}
@ -35473,44 +35476,32 @@ impl Aggoobcfjlh {
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum Ffnhcbelgpd {
EnterSceneReasonNone = 0,
EnterSceneReasonChallengeTimeout = 1,
EnterSceneReasonRogueTimeout = 2,
EnterSceneReasonChangeStoryline = 3,
pub enum EnterSceneReason {
None = 0,
ChallengeTimeout = 1,
RogueTimeout = 2,
ChangeStoryline = 3,
}
impl Ffnhcbelgpd {
impl EnterSceneReason {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Ffnhcbelgpd::EnterSceneReasonNone => "ENTER_SCENE_REASON_NONE",
Ffnhcbelgpd::EnterSceneReasonChallengeTimeout => {
"ENTER_SCENE_REASON_CHALLENGE_TIMEOUT"
}
Ffnhcbelgpd::EnterSceneReasonRogueTimeout => {
"ENTER_SCENE_REASON_ROGUE_TIMEOUT"
}
Ffnhcbelgpd::EnterSceneReasonChangeStoryline => {
"ENTER_SCENE_REASON_CHANGE_STORYLINE"
}
EnterSceneReason::None => "ENTER_SCENE_REASON_NONE",
EnterSceneReason::ChallengeTimeout => "ENTER_SCENE_REASON_CHALLENGE_TIMEOUT",
EnterSceneReason::RogueTimeout => "ENTER_SCENE_REASON_ROGUE_TIMEOUT",
EnterSceneReason::ChangeStoryline => "ENTER_SCENE_REASON_CHANGE_STORYLINE",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"ENTER_SCENE_REASON_NONE" => Some(Self::EnterSceneReasonNone),
"ENTER_SCENE_REASON_CHALLENGE_TIMEOUT" => {
Some(Self::EnterSceneReasonChallengeTimeout)
}
"ENTER_SCENE_REASON_ROGUE_TIMEOUT" => {
Some(Self::EnterSceneReasonRogueTimeout)
}
"ENTER_SCENE_REASON_CHANGE_STORYLINE" => {
Some(Self::EnterSceneReasonChangeStoryline)
}
"ENTER_SCENE_REASON_NONE" => Some(Self::None),
"ENTER_SCENE_REASON_CHALLENGE_TIMEOUT" => Some(Self::ChallengeTimeout),
"ENTER_SCENE_REASON_ROGUE_TIMEOUT" => Some(Self::RogueTimeout),
"ENTER_SCENE_REASON_CHANGE_STORYLINE" => Some(Self::ChangeStoryline),
_ => None,
}
}
@ -35600,29 +35591,29 @@ impl Oigihagkoib {
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum Njdmhcchfdj {
SceneGroupRefreshTypeNone = 0,
SceneGroupRefreshTypeLoaded = 1,
SceneGroupRefreshTypeUnload = 2,
pub enum SceneGroupRefreshType {
None = 0,
Loaded = 1,
Unload = 2,
}
impl Njdmhcchfdj {
impl SceneGroupRefreshType {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Njdmhcchfdj::SceneGroupRefreshTypeNone => "SCENE_GROUP_REFRESH_TYPE_NONE",
Njdmhcchfdj::SceneGroupRefreshTypeLoaded => "SCENE_GROUP_REFRESH_TYPE_LOADED",
Njdmhcchfdj::SceneGroupRefreshTypeUnload => "SCENE_GROUP_REFRESH_TYPE_UNLOAD",
SceneGroupRefreshType::None => "SCENE_GROUP_REFRESH_TYPE_NONE",
SceneGroupRefreshType::Loaded => "SCENE_GROUP_REFRESH_TYPE_LOADED",
SceneGroupRefreshType::Unload => "SCENE_GROUP_REFRESH_TYPE_UNLOAD",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"SCENE_GROUP_REFRESH_TYPE_NONE" => Some(Self::SceneGroupRefreshTypeNone),
"SCENE_GROUP_REFRESH_TYPE_LOADED" => Some(Self::SceneGroupRefreshTypeLoaded),
"SCENE_GROUP_REFRESH_TYPE_UNLOAD" => Some(Self::SceneGroupRefreshTypeUnload),
"SCENE_GROUP_REFRESH_TYPE_NONE" => Some(Self::None),
"SCENE_GROUP_REFRESH_TYPE_LOADED" => Some(Self::Loaded),
"SCENE_GROUP_REFRESH_TYPE_UNLOAD" => Some(Self::Unload),
_ => None,
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 MiB

After

Width:  |  Height:  |  Size: 2.7 MiB

View file

@ -1,4 +0,0 @@
ASSET_BUNDLE_URL=https://autopatchcn.bhsr.com/asb/BetaLive/output_6744505_89b2f5dc973e
EX_RESOURCE_URL=https://autopatchcn.bhsr.com/design_data/BetaLive/output_6759713_b4e0e740f0da
LUA_URL=https://autopatchcn.bhsr.com/lua/BetaLive/output_6755976_3c46d7c46e2c
LUA_VERSION=6755976

View file

@ -4,15 +4,21 @@ version = "0.1.0"
edition = "2021"
[dependencies]
common.workspace = true
anyhow.workspace = true
env_logger.workspace = true
axum.workspace = true
axum-server.workspace = true
hyper.workspace = true
hyper-util.workspace = true
dirs.workspace = true
dotenv.workspace = true
lazy_static.workspace = true
serde.workspace = true
serde_json.workspace = true
@ -26,6 +32,6 @@ tracing-subscriber.workspace = true
tracing-bunyan-formatter.workspace = true
ansi_term.workspace = true
prost.workspace = true
rbase64.workspace = true
proto.workspace = true
tower.workspace = true
tower-http.workspace = true

4
sdkserver/sdkserver.json Normal file
View file

@ -0,0 +1,4 @@
{
"http_port": 21000,
"dispatch_endpoint": "http://127.0.0.1:21041"
}

23
sdkserver/src/config.rs Normal file
View file

@ -0,0 +1,23 @@
use common::util::load_or_create_config;
use lazy_static::lazy_static;
use serde::Deserialize;
use serde_json::from_str;
const DEFAULT_CONFIG: &str = include_str!("../sdkserver.json");
pub fn init_config() {
let _configuration = &*CONFIGURATION;
}
#[derive(Deserialize)]
pub struct SDKServerConfiguration {
pub http_port: u16,
pub dispatch_endpoint: String,
}
lazy_static! {
pub static ref CONFIGURATION: SDKServerConfiguration = {
let data = load_or_create_config("sdkserver.json", DEFAULT_CONFIG);
from_str(&data).unwrap()
};
}

View file

@ -1,29 +0,0 @@
#[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() {
#[cfg(target_os = "windows")]
ansi_term::enable_ansi_support().unwrap();
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
}

View file

@ -1,33 +1,43 @@
use anyhow::Result;
use axum::body::Body;
use axum::extract::Request;
use axum::routing::{get, post};
use axum::Router;
use logging::init_tracing;
use services::{auth, dispatch, errors};
use std::path::Path;
use axum::{Router, ServiceExt};
use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
use services::{auth, errors};
use tokio::net::TcpListener;
use tower::Layer;
use tower_http::normalize_path::NormalizePathLayer;
use tracing::Level;
mod logging;
type Client = hyper_util::client::legacy::Client<HttpConnector, Body>;
mod config;
mod services;
const PORT: u16 = 21000;
const DEFAULT_DOTENV: &str = include_str!("../.env");
use common::logging::init_tracing;
use config::{init_config, CONFIGURATION};
use services::reverse_proxy;
#[tokio::main]
async fn main() -> Result<()> {
init_tracing();
init_config()?;
init_config();
let span = tracing::span!(Level::DEBUG, "main");
let _ = span.enter();
let router = Router::new()
// For dispatch reverse proxy
let client: Client =
hyper_util::client::legacy::Client::<(), ()>::builder(TokioExecutor::new())
.build(HttpConnector::new());
let app = Router::new()
.route("/query_dispatch", get(reverse_proxy::forward_to_dispatch))
.route(
dispatch::QUERY_DISPATCH_ENDPOINT,
get(dispatch::query_dispatch),
)
.route(
dispatch::QUERY_GATEWAY_ENDPOINT,
get(dispatch::query_gateway),
"/query_gateway/:region_name",
get(reverse_proxy::forward_to_dispatch),
)
.route(auth::RISKY_API_CHECK_ENDPOINT, post(auth::risky_api_check))
.route(
@ -42,36 +52,16 @@ async fn main() -> Result<()> {
auth::GRANTER_LOGIN_VERIFICATION_ENDPOINT,
post(auth::granter_login_verification),
)
.fallback(errors::not_found);
.fallback(errors::not_found)
.with_state(client);
let addr = format!("0.0.0.0:{PORT}");
let server = axum_server::bind(addr.parse()?);
let app = NormalizePathLayer::trim_trailing_slash().layer(app);
let addr = format!("0.0.0.0:{}", CONFIGURATION.http_port);
let server = TcpListener::bind(&addr).await?;
tracing::info!("sdkserver is listening at {addr}");
server.serve(router.into_make_service()).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("hkrpg-sdkserver");
std::fs::create_dir_all(&config)?;
let env = config.join(".env");
if !env.exists() {
std::fs::write(&env, DEFAULT_DOTENV)?;
}
dotenv::from_path(&env)?;
}
axum::serve(server, ServiceExt::<Request>::into_make_service(app)).await?;
Ok(())
}

View file

@ -1,7 +1,7 @@
use axum::Json;
use serde_json::json;
pub const LOGIN_WITH_PASSWORD_ENDPOINT: &str = "/:product_name/mdk/shield/api/login/";
pub const LOGIN_WITH_PASSWORD_ENDPOINT: &str = "/:product_name/mdk/shield/api/login";
pub const LOGIN_WITH_SESSION_TOKEN_ENDPOINT: &str = "/:product_name/mdk/shield/api/verify";
pub const GRANTER_LOGIN_VERIFICATION_ENDPOINT: &str = "/:product_name/combo/granter/login/v2/login";
pub const RISKY_API_CHECK_ENDPOINT: &str = "/account/risky/api/check";
@ -12,7 +12,7 @@ pub async fn login_with_password() -> Json<serde_json::Value> {
"data": {
"account": {
"area_code": "**",
"email": "ReversedRooms",
"email": "AcheronSR",
"country": "RU",
"is_email_verify": "1",
"token": "mostsecuretokenever",
@ -34,7 +34,7 @@ pub async fn login_with_session_token() -> Json<serde_json::Value> {
"data": {
"account": {
"area_code": "**",
"email": "ReversedRooms",
"email": "AcheronSR",
"country": "RU",
"is_email_verify": "1",
"token": "mostsecuretokenever",

View file

@ -1,61 +0,0 @@
use std::env;
use prost::Message;
use proto::{Dispatch, Gateserver, RegionInfo};
pub const QUERY_DISPATCH_ENDPOINT: &str = "/query_dispatch";
pub const QUERY_GATEWAY_ENDPOINT: &str = "/query_gateway";
#[tracing::instrument]
pub async fn query_dispatch() -> String {
let rsp = Dispatch {
retcode: 0,
region_list: vec![RegionInfo {
name: String::from("RobinSR"),
title: String::from("RobinSR"),
env_type: String::from("9"),
dispatch_url: String::from("http://127.0.0.1:21000/query_gateway"),
..Default::default()
}],
..Default::default()
};
let mut buff = Vec::new();
rsp.encode(&mut buff).unwrap();
rbase64::encode(&buff)
}
#[tracing::instrument]
pub async fn query_gateway() -> String {
let rsp = Gateserver {
retcode: 0,
ip: String::from("127.0.0.1"),
port: 23301,
asset_bundle_url: env::var("ASSET_BUNDLE_URL").unwrap(),
ex_resource_url: env::var("EX_RESOURCE_URL").unwrap(),
lua_url: env::var("LUA_URL").unwrap(),
lua_version: env::var("LUA_VERSION").unwrap(),
ifix_version: String::from("0"),
jblkncaoiao: true,
hjdjakjkdbi: true,
ldknmcpffim: true,
feehapamfci: true,
eebfeohfpph: true,
dfmjjcfhfea: true,
najikcgjgan: true,
giddjofkndm: true,
fbnbbembcgn: false,
dedgfjhbnok: false,
use_tcp: true,
linlaijbboh: false,
ahmbfbkhmgh: false,
nmdccehcdcc: false,
..Default::default()
};
let mut buff = Vec::new();
rsp.encode(&mut buff).unwrap();
rbase64::encode(&buff)
}

View file

@ -1,3 +1,3 @@
pub mod auth;
pub mod dispatch;
pub mod errors;
pub mod reverse_proxy;

View file

@ -0,0 +1,33 @@
use axum::{
body::Body,
extract::{Request, State},
http::uri::{PathAndQuery, Uri},
response::{IntoResponse, Response},
};
use hyper::StatusCode;
use hyper_util::client::legacy::connect::HttpConnector;
use crate::config::CONFIGURATION;
type Client = hyper_util::client::legacy::Client<HttpConnector, Body>;
pub async fn forward_to_dispatch(
State(client): State<Client>,
mut req: Request,
) -> Result<Response, StatusCode> {
let path = req.uri().path();
let path_query = req
.uri()
.path_and_query()
.map_or(path, PathAndQuery::as_str);
let uri = format!("{}{}", CONFIGURATION.dispatch_endpoint, path_query);
*req.uri_mut() = Uri::try_from(uri).unwrap();
Ok(client
.request(req)
.await
.map_err(|_| StatusCode::BAD_REQUEST)?
.into_response())
}

7
xtask/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "xtask"
edition = "2021"
version.workspace = true
[dependencies]
notify = "6.1.1"

112
xtask/src/main.rs Normal file
View file

@ -0,0 +1,112 @@
use std::{io, process::Command, sync::mpsc, thread};
fn print_help() {
println!(
"
xtask must specify a task to run.
Usage: `cargo xtask <task>`
Tasks:
run
Run the gameserver and sdkserver.
watch
Watch for changes in the project and restart the servers if any file changes.
"
);
}
// run gameserver and sdkserver, wait till any of them exit
fn spawn_servers(release: bool) -> Result<(), Box<dyn std::error::Error>> {
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();
let handle1 = thread::spawn(move || {
let mut gameserver = Command::new("cargo")
.arg("run")
.arg("--bin")
.arg("gameserver")
.args(if release { vec!["--release"] } else { vec![] })
.spawn()
.expect("failed to start gameserver");
gameserver.wait()?;
tx1.send(()).expect("failed to send completion signal");
Ok::<(), io::Error>(())
});
let tx2 = tx.clone();
let handle2 = thread::spawn(move || {
let mut sdkserver = Command::new("cargo")
.arg("run")
.arg("--bin")
.arg("sdkserver")
.args(if release { vec!["--release"] } else { vec![] })
.spawn()
.expect("failed to start sdkserver");
let _ = sdkserver.wait()?;
tx2.send(()).expect("failed to send completion signal");
Ok::<(), io::Error>(())
});
let handle3 = thread::spawn(move || {
let mut dispatch = Command::new("cargo")
.arg("run")
.arg("--bin")
.arg("dispatch")
.args(if release { vec!["--release"] } else { vec![] })
.spawn()
.expect("failed to start dispatch");
let _ = dispatch.wait()?;
tx.send(()).expect("failed to send completion signal");
Ok::<(), io::Error>(())
});
rx.recv().expect("failed to receive from channel");
handle1.join().expect("failed to join gameserver thread")?;
handle2.join().expect("failed to join sdkserver thread")?;
handle3.join().expect("failed to join dispatch thread")?;
Ok(())
}
// watch for changes in the project and restart the servers if any file changes
fn watch(release: bool) -> Result<(), Box<dyn std::error::Error>> {
let mut cmd = std::process::Command::new("cargo");
cmd.arg("watch").arg("-x").arg(format!(
"xtask run {}",
if release { "--release" } else { "" }
));
let mut child = cmd.spawn()?;
child.wait()?;
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let Some(task) = std::env::args().nth(1) else {
print_help();
std::process::exit(0);
};
let release = std::env::args().any(|arg| arg == "--release");
match task.as_str() {
"run" => spawn_servers(release)?,
"watch" => watch(release)?,
_ => {
println!("invalid task: `{task}`, run `cargo xtask` for a list of tasks");
std::process::exit(1);
}
}
Ok(())
}