From 4d32da1e2edae59cde017da6415451fa86bdfdb8 Mon Sep 17 00:00:00 2001 From: Nobody Date: Thu, 21 Oct 2021 02:51:01 +0500 Subject: [PATCH] Refactoring, moving out proto to separate repo, fixed login bug --- .gitignore | 6 - Cargo.toml | 21 +- build.rs | 65 --- gen_ids.sh | 27 - misc/gen_key.sh | 7 + misc/grab_key.sh | 5 + misc/ssl_stuff/gen_cert.sh | 3 + misc/ssl_stuff/ssl.conf | 41 ++ misc/xor.py | 9 + packet-processor-macro/.gitignore | 14 + packet-processor-macro/Cargo.toml | 16 + packet-processor-macro/src/lib.rs | 107 ++++ packet-processor/.gitignore | 14 + packet-processor/Cargo.toml | 9 + packet-processor/src/lib.rs | 32 ++ src/main.rs | 14 +- src/server/auth_manager.rs | 51 +- src/server/client_connection.rs | 6 + src/server/dispatch_server.rs | 233 ++++---- src/server/game_server.rs | 13 +- src/server/game_world.rs | 887 +++++++++++++----------------- src/server/ipc_message.rs | 7 +- src/server/network_server.rs | 74 ++- 23 files changed, 921 insertions(+), 740 deletions(-) delete mode 100644 build.rs delete mode 100755 gen_ids.sh create mode 100755 misc/gen_key.sh create mode 100755 misc/grab_key.sh create mode 100755 misc/ssl_stuff/gen_cert.sh create mode 100644 misc/ssl_stuff/ssl.conf create mode 100755 misc/xor.py create mode 100644 packet-processor-macro/.gitignore create mode 100644 packet-processor-macro/Cargo.toml create mode 100644 packet-processor-macro/src/lib.rs create mode 100644 packet-processor/.gitignore create mode 100644 packet-processor/Cargo.toml create mode 100644 packet-processor/src/lib.rs diff --git a/.gitignore b/.gitignore index 9c8929f..d5469e3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,15 +9,9 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk -# *.proto files -/protobuf/ - # keys /keys/ # Vim temp files .*.swp .*.swo - -# Autogenerated files -/gen/ diff --git a/Cargo.toml b/Cargo.toml index 109fd64..51ca599 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,11 +8,26 @@ edition = "2018" [dependencies] kcp = { path = "../kcp" } mhycrypt = { path = "../mhycrypt" } +proto = { path = "../proto" } +packet-processor-macro = { path = "packet-processor-macro" } +packet-processor = { path = "packet-processor" } + prost = "0.8" bytes = "1.1.0" +base64 = "0.13.0" +tokio = { version = "1", features = ["full"] } +actix-web = { version = "3", features = ["openssl"] } +futures = "0.3" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +pretty_env_logger = "0.4" num-traits = "0.2" num-derive = "0.3" -base64 = "0.13.0" -[build-dependencies] -prost-build = { version = "0.8.0" } +pretty-hex = "0.2" + +[target.'cfg(windows)'.dependencies] +openssl = { version = "0.10", features = ["vendored"] } + +[target.'cfg(unix)'.dependencies] +openssl = "0.10" diff --git a/build.rs b/build.rs deleted file mode 100644 index 565fc3f..0000000 --- a/build.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::io::Result; - -fn main() -> Result<()> { - let proto_dir = "protobuf"; - - let protos = vec![ - // Dispatch protocol - "QueryRegionListHttpRsp", - "QueryCurrRegionHttpRsp", - "RegionSimpleInfo", - "RegionInfo", - - // Game protocol - "packet_header", - - "GetPlayerTokenReq", - "GetPlayerTokenRsp", - "PlayerLoginReq", - "OpenStateUpdateNotify", - "StoreWeightLimitNotify", - "PlayerStoreNotify", - "AvatarDataNotify", - "PlayerEnterSceneNotify", - "PlayerLoginRsp", - "GetPlayerSocialDetailReq", - "GetPlayerSocialDetailRsp", - "EnterSceneReadyReq", - "EnterSceneReadyRsp", - "SceneInitFinishReq", - "EnterScenePeerNotify", - "WorldDataNotify", - "WorldPlayerInfoNotify", - "ScenePlayerInfoNotify", - "PlayerEnterSceneInfoNotify", - "PlayerGameTimeNotify", - "SceneTimeNotify", - "SceneDataNotify", - "HostPlayerNotify", - "SceneTeamUpdateNotify", - "SceneInitFinishRsp", - "EnterSceneDoneReq", - "SceneEntityAppearNotify", - "EnterSceneDoneRsp", - "PostEnterSceneReq", - "PostEnterSceneRsp", - - "WorldPlayerRTTNotify", - "PingReq", - "PingRsp", - "PlayerDataNotify", - - "EnterWorldAreaReq", - "EnterWorldAreaRsp", - - ]; - - let protos: Vec = protos.iter().map(|&x| format!("{}/{}.proto", proto_dir, x)).collect(); - - let ret = prost_build::compile_protos(&protos, &[format!("{}/", proto_dir)]); - - match ret { - Ok(_) => return Ok(()), - Err(e) => panic!("{}", e), - } -} diff --git a/gen_ids.sh b/gen_ids.sh deleted file mode 100755 index cd26ea5..0000000 --- a/gen_ids.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -#echo "#[macro_use]"; -#echo "extern crate num_derive;"; - -#echo ""; - -echo "// Autogenerated file, do not edit!" - -echo ""; - -echo "#[repr(u16)]"; -echo "#[derive(FromPrimitive, ToPrimitive)]"; -echo "#[derive(Debug, PartialEq, Eq, Clone, Hash)]"; - -echo "pub enum PacketId {"; - -for f in protobuf/*; do - CMD_ID=`cat $f | grep CMD_ID | cut -d'=' -f2 | tr -d ' ;\015'`; - - if [ "x${CMD_ID}" != "x" ]; then - NAME=`echo $f | cut -d'/' -f2 | cut -d'.' -f1`; - echo " ${NAME} = ${CMD_ID}," - fi -done - -echo "}"; diff --git a/misc/gen_key.sh b/misc/gen_key.sh new file mode 100755 index 0000000..39814aa --- /dev/null +++ b/misc/gen_key.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +echo -ne "\x45\x63\x32\x62" > fake.ec2b +echo -ne "\x10\x00\x00\x00" >> fake.ec2b +dd if=/dev/urandom bs=16 count=1 >> fake.ec2b +echo -ne "\x00\x08\x00\x00" >> fake.ec2b +dd if=/dev/urandom bs=2048 count=1 >> fake.ec2b diff --git a/misc/grab_key.sh b/misc/grab_key.sh new file mode 100755 index 0000000..6260cca --- /dev/null +++ b/misc/grab_key.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +xxd -r text.log > gptr.bin +./xor.py gptr.bin 0x20 +dd if=gptr.bin of=key.bin bs=4096 count=1 skip=1 diff --git a/misc/ssl_stuff/gen_cert.sh b/misc/ssl_stuff/gen_cert.sh new file mode 100755 index 0000000..7a39881 --- /dev/null +++ b/misc/ssl_stuff/gen_cert.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3650 -config ssl.conf -nodes -sha256 -extensions 'req_ext' diff --git a/misc/ssl_stuff/ssl.conf b/misc/ssl_stuff/ssl.conf new file mode 100644 index 0000000..b72d00b --- /dev/null +++ b/misc/ssl_stuff/ssl.conf @@ -0,0 +1,41 @@ +[ req ] +default_bits = 2048 +distinguished_name = req_distinguished_name +req_extensions = req_ext +extensions = req_ext +prompt = no + +[ req_distinguished_name ] +countryName = CN +stateOrProvinceName = Beijing +localityName = Xicheng +organizationName = oYoHim + +[ req_ext ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = api-os-takumi.mihoyo.com +DNS.2 = api-takumi.mihoyo.com +DNS.3 = autopatchhk.yuanshen.com +DNS.4 = autopatchcn.yuanshen.com +DNS.5 = dispatchosglobal.yuanshen.com +DNS.6 = hk4e-api-os-static.mihoyo.com +DNS.7 = hk4e-api-static.mihoyo.com +DNS.8 = hk4e-api-os.mihoyo.com +DNS.9 = hk4e-api.mihoyo.com +DNS.10 = hk4e-sdk-os.mihoyo.com +DNS.11 = hk4e-sdk.mihoyo.com +DNS.12 = log-upload-os.mihoyo.com +DNS.13 = log-upload.mihoyo.com +DNS.14 = osusadispatch.yuanshen.com +DNS.15 = oseurodispatch.yuanshen.com +DNS.16 = osasiadispatch.yuanshen.com +DNS.17 = sdk-os-static.mihoyo.com +DNS.18 = sdk-static.mihoyo.com +DNS.19 = webstatic-sea.mihoyo.com +DNS.20 = webstatic.mihoyo.com +DNS.21 = uploadstatic-sea.mihoyo.com +DNS.22 = uploadstatic.mihoyo.com +DNS.23 = devlog-upload.mihoyo.com +DNS.24 = localhost diff --git a/misc/xor.py b/misc/xor.py new file mode 100755 index 0000000..ae8c4de --- /dev/null +++ b/misc/xor.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +import sys + +filename = sys.argv[1] +key = int(sys.argv[2], 16) + +data = open(filename, "rb").read() +open(filename, "wb").write(bytes([i ^ key for i in data])) diff --git a/packet-processor-macro/.gitignore b/packet-processor-macro/.gitignore new file mode 100644 index 0000000..4685c27 --- /dev/null +++ b/packet-processor-macro/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +/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 + +# Vim temp files +.*.swp +.*.swo diff --git a/packet-processor-macro/Cargo.toml b/packet-processor-macro/Cargo.toml new file mode 100644 index 0000000..cb76420 --- /dev/null +++ b/packet-processor-macro/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "packet-processor-macro" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +regex = "1" +syn = { version = "1.0", features = ["full","derive"] } +quote = "1.0" +proc-macro2 = "1.0" +convert_case = "0.4.0" diff --git a/packet-processor-macro/src/lib.rs b/packet-processor-macro/src/lib.rs new file mode 100644 index 0000000..1bcc775 --- /dev/null +++ b/packet-processor-macro/src/lib.rs @@ -0,0 +1,107 @@ +extern crate regex; +extern crate convert_case; + +use proc_macro::{self, TokenStream}; +use quote::quote; +use syn::{parse_macro_input, AttributeArgs}; +use regex::Regex; +use convert_case::{Case, Casing}; + +fn get_nested_meta_name(nested_meta: &syn::NestedMeta) -> String { + match nested_meta { + syn::NestedMeta::Meta(meta) => match meta { + syn::Meta::Path(ident) => ident.get_ident().cloned().unwrap().to_string(), + _ => panic!("Unsupported macro argument"), + }, + syn::NestedMeta::Lit(_) => panic!("Only identifiers are supported as an arguments to the macro"), + } +} + +#[proc_macro_attribute] +pub fn packet_processor(args: TokenStream, input: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as AttributeArgs); + //let input = parse_macro_input!(input as DeriveInput); + + let mut found_struct = false; + let mut struct_name = None; + + let modified_struct: proc_macro::TokenStream = input.into_iter().map(|r| { + match &r { + &proc_macro::TokenTree::Ident(ref ident) if ident.to_string() == "struct" => { // react on keyword "struct" so we don't randomly modify non-structs + found_struct = true; + r + }, + &proc_macro::TokenTree::Ident(ref ident) if found_struct == true && struct_name == None => { // Next ident right after "struct" is the struct name + struct_name = Some(ident.to_string()); + r + }, + &proc_macro::TokenTree::Group(ref group) if group.delimiter() == proc_macro::Delimiter::Brace && found_struct == true => { // Opening brackets for the struct + let mut stream = proc_macro::TokenStream::new(); + + stream.extend( + vec![proc_macro::TokenStream::from(quote!(packet_callbacks: HashMap) -> ()>,))] + ); + stream.extend(group.stream()); + + proc_macro::TokenTree::Group( + proc_macro::Group::new( + proc_macro::Delimiter::Brace, + stream + ) + ) + } + _ => r + } + }).collect(); + + let args = args.clone().into_iter().map(|a| get_nested_meta_name(&a)); + let re = Regex::new(r"Req$").unwrap(); + + let request = args.clone().into_iter().filter(|a| a.ends_with("Req")); + let notify = args.clone().into_iter().filter(|a| a.ends_with("Notify")); + let response = request.clone().into_iter().map(|a| re.replace_all(&a, "Rsp").to_string()); + let req_handler = request.clone().into_iter().map(|a| format!("process_{}", &a[..a.len()-3].to_case(Case::Snake))); + let notify_handler = notify.clone().into_iter().map(|a| format!("process_{}", &a[..a.len()-6].to_case(Case::Snake))); + + let request: Vec = request.map(|a| a.parse().unwrap()).collect(); + let notify: Vec = notify.map(|a| a.parse().unwrap()).collect(); + let response: Vec = response.map(|a| a.parse().unwrap()).collect(); + let req_handler: Vec = req_handler.map(|a| a.parse().unwrap()).collect(); + let notify_handler: Vec = notify_handler.map(|a| a.parse().unwrap()).collect(); + + let struct_name: proc_macro2::TokenStream = match &struct_name { + None => panic!("Failed to find struct name"), + Some(name) => name.clone().parse().unwrap(), + }; + + let implementation = vec![proc_macro::TokenStream::from(quote!( + impl PacketProcessor for #struct_name { + fn register(&mut self) { + let mut callbacks = &mut self.packet_callbacks; + #(register_callback!(callbacks, #request, #response, #req_handler);)* + #(register_callback!(callbacks, #notify, #notify_handler);)* + } + + fn supported(&self) -> Vec { + return self.packet_callbacks.keys().cloned().collect(); + } + + fn process(&mut self, user_id: u32, packet_id: proto::PacketId, metadata: Vec, data: Vec) { + let callback = self.packet_callbacks.get(&packet_id); + let metadata = proto::PacketHead::decode(&mut std::io::Cursor::new(metadata)).unwrap(); + + match callback { + Some(callback) => callback(self, user_id, &metadata, data), + None => println!("Unhandled packet {:?}", packet_id), + } + } + } + + ))]; + + let mut ret = proc_macro::TokenStream::new(); + ret.extend(modified_struct); + ret.extend(implementation); + //println!("{}", ret); + ret +} diff --git a/packet-processor/.gitignore b/packet-processor/.gitignore new file mode 100644 index 0000000..4685c27 --- /dev/null +++ b/packet-processor/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +/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 + +# Vim temp files +.*.swp +.*.swo diff --git a/packet-processor/Cargo.toml b/packet-processor/Cargo.toml new file mode 100644 index 0000000..9b6c8b7 --- /dev/null +++ b/packet-processor/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "packet-processor" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proto = { path = "../../proto" } diff --git a/packet-processor/src/lib.rs b/packet-processor/src/lib.rs new file mode 100644 index 0000000..9117a47 --- /dev/null +++ b/packet-processor/src/lib.rs @@ -0,0 +1,32 @@ +pub trait PacketProcessor { + fn register(&mut self); + fn supported(&self) -> Vec; + fn process(&mut self, user_id: u32, packet_id: proto::PacketId, metadata: Vec, data: Vec); +} + +#[macro_export] +macro_rules! register_callback { + ($hashmap:ident, $req:ident, $rsp:ident, $handler:ident) => { + $hashmap.insert(proto::PacketId::$req, |slef: &mut Self, user_id: u32, metadata: &proto::PacketHead, data: Vec| { + let req = proto::$req::decode(&mut std::io::Cursor::new(data)).unwrap(); + let mut rsp = proto::$rsp::default(); + + println!("Received REQ {:?}", req); + + slef.$handler(user_id, &metadata, &req, &mut rsp); + + let message = IpcMessage::new_from_proto(user_id, proto::PacketId::$rsp, metadata, &rsp); + slef.packets_to_send_tx.send(message).unwrap(); + }); + }; + + ($hashmap:ident, $notify:ident, $handler:ident) => { + $hashmap.insert(proto::PacketId::$notify, |slef: &mut Self, user_id: u32, metadata: &proto::PacketHead, data: Vec| { + let notify = proto::$req::decode(&mut std::io::Cursor::new(data)).unwrap(); + println!("Received NOTIFY {:?}", notify); + + slef.$handler(user_id, &metadata, ¬ify); + }); + }; +} + diff --git a/src/main.rs b/src/main.rs index 4492613..04ad366 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +extern crate pretty_env_logger; + #[macro_use] extern crate num_derive; @@ -6,19 +8,15 @@ use std::thread; mod server; mod utils; -pub mod proto { - include!(concat!(env!("OUT_DIR"), "/proto.rs")); - include!(concat!("..", "/gen", "/packet_id.rs")); - include!(concat!("..", "/gen", "/player_prop.rs")); - include!(concat!("..", "/gen", "/open_state.rs")); -} - use server::NetworkServer; use server::DispatchServer; fn main() { + pretty_env_logger::init(); + thread::spawn(|| { - let mut ds = DispatchServer::new("127.0.0.1", 9696); + //let mut ds = DispatchServer::new("127.0.0.1", 9696); + let mut ds = DispatchServer::new(); ds.run(); }); diff --git a/src/server/auth_manager.rs b/src/server/auth_manager.rs index b5d9b96..3093f8c 100644 --- a/src/server/auth_manager.rs +++ b/src/server/auth_manager.rs @@ -1,20 +1,67 @@ use std::sync::mpsc; +use std::collections::HashMap; + +use prost::Message; use crate::server::IpcMessage; +use packet_processor_macro::*; +#[macro_use] +use packet_processor::*; + +#[packet_processor(GetPlayerTokenReq)] pub struct AuthManager { + conv_to_user: HashMap, + user_to_conv: HashMap, packets_to_send_tx: mpsc::Sender, } impl AuthManager { + const SPOOFED_PLAYER_UID: u32 = 1337; + pub fn new(packets_to_send_tx: mpsc::Sender) -> AuthManager { - let am = AuthManager { + let mut am = AuthManager { + conv_to_user: HashMap::new(), + user_to_conv: HashMap::new(), + packet_callbacks: HashMap::new(), packets_to_send_tx: packets_to_send_tx, }; + am.register(); + return am; } - pub fn process_packet(&mut self, conv: u32, packet_id: u16, metadata: Vec, data: Vec) { + pub fn process_get_player_token(&mut self, conv: u32, metadata: &proto::PacketHead, req: &proto::GetPlayerTokenReq, rsp: &mut proto::GetPlayerTokenRsp) { + let seed: u64 = 0x123456789ABCDEF0; // TODO: use real value! + let uid = self.get_uid_by_account_id(req.account_uid.parse().unwrap()); + + rsp.account_type = req.account_type; + rsp.account_uid = req.account_uid.clone(); + rsp.token = format!("token-game-{}", req.account_uid); + rsp.secret_key_seed = seed; + rsp.uid = uid; + + self.conv_to_user.insert(conv, uid); + self.user_to_conv.insert(uid, conv); + } + + fn get_uid_by_account_id(&self, account_uid: u32) -> u32 { + // TODO! + return AuthManager::SPOOFED_PLAYER_UID; + } + + pub fn resolve_conv(&self, conv: u32) -> Option { + match self.conv_to_user.get(&conv) { + Some(uid) => return Some(*uid), + None => return None, + }; + } + + pub fn resolve_uid(&self, uid: u32) -> Option { + match self.user_to_conv.get(&uid) { + Some(conv) => return Some(*conv), + None => return None, + }; } } diff --git a/src/server/client_connection.rs b/src/server/client_connection.rs index 8410cd9..1f0f053 100644 --- a/src/server/client_connection.rs +++ b/src/server/client_connection.rs @@ -76,6 +76,12 @@ impl ClientConnection { match self.ikcp.recv(&mut buf) { Err(_) => break, Ok(size) => { + #[cfg(feature = "raw_packet_dump")] + { + use pretty_hex::*; + let cfg = HexConfig {title: true, width: 16, group: 0, ascii: true, ..HexConfig::default() }; + println!("{:?}", buf[..size].to_vec().hex_conf(cfg)); + } mhycrypt::mhy_xor(&mut buf[..size], &self.key); let data = buf[..size].to_owned(); packets.push(data); diff --git a/src/server/dispatch_server.rs b/src/server/dispatch_server.rs index 1215b91..7a7be17 100644 --- a/src/server/dispatch_server.rs +++ b/src/server/dispatch_server.rs @@ -3,104 +3,126 @@ use std::net::TcpListener; use std::net::TcpStream; use std::collections::HashMap; use std::fs; +use std::sync::Arc; + +extern crate futures; +extern crate base64; +extern crate actix_web; +extern crate openssl; + +use serde::{Deserialize,Serialize}; +use futures::executor; + +use actix_web::{rt::System, web, get, App, HttpRequest, HttpResponse, HttpServer, Responder, middleware::Logger}; +use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode, SslOptions, SslMode}; use prost::Message; -extern crate base64; -use crate::proto; use mhycrypt; -type RequestCallback = fn(slef: &DispatchServer, args: Vec<(&str, &str)>) -> (String, String); - +#[derive(Clone)] pub struct DispatchServer { - listener: TcpListener, - callbacks: HashMap, - ip: String, - port: u16, +} + +#[derive(Deserialize,Debug)] +struct ClientInfo { + version: String, + lang: i32, + platform: i32, + binary: i32, + time: i32, + channel_id: i32, + sub_channel_id: i32, + account_type: Option, +} + +#[derive(Deserialize,Debug)] +struct TokenToVerify +{ + uid: String, + token: String, +} + +#[derive(Deserialize,Debug)] +struct ActionToCheck +{ + action_type: String, + api_name: String, + username: Option, +} + +#[derive(Deserialize,Debug)] +struct LoginData { + account: String, + is_crypto: bool, + password: String, +} + +#[derive(Deserialize,Debug)] +struct GranterData { + app_id: String, + channel_id: String, + device: String, + sign: String, + data: String, } impl DispatchServer { - pub fn new(ip: &str, port: u16) -> DispatchServer { - let mut callbacks = HashMap::new(); - - let mut ds = DispatchServer { - listener: TcpListener::bind(format!("{}:{}", ip, port)).unwrap(), - callbacks: callbacks, - ip: ip.to_string(), - port: port, - }; - - ds.callbacks.insert("/query_region_list".to_string(), DispatchServer::query_region_list); - ds.callbacks.insert("/query_cur_region".to_string(), DispatchServer::query_cur_region); - ds.callbacks.insert("/account/risky/api/check".to_string(), DispatchServer::risky_api_check); - ds.callbacks.insert("/hk4e_global/mdk/shield/api/login".to_string(), DispatchServer::risky_api_check); - ds.callbacks.insert("/hk4e_global/combo/granter/login/v2/login".to_string(), DispatchServer::granter_login); - ds.callbacks.insert("/hk4e_global/mdk/shield/api/verify".to_string(), DispatchServer::shield_verify); + pub fn new() -> DispatchServer { + let ds = DispatchServer { }; return ds; } - pub fn run(&self) { - for stream in self.listener.incoming() { - let stream = stream.unwrap(); - - self.handle_connection(stream); - } + pub fn run(self) { + let mut sys = System::new("http-server"); + let slef = Arc::new(self); + executor::block_on(slef.run_internal()); + System::current().stop(); + println!("Finished!"); } - fn handle_connection(&self, mut stream: TcpStream) { - let mut buffer = [0; 1024]; + async fn run_internal(self: &Arc) { + //let (http_port, https_port) = (2880, 2443); + let (http_port, https_port) = (80, 443); - stream.read(&mut buffer).unwrap(); + let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls_server()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + builder.set_min_proto_version(None).unwrap(); + builder.set_cipher_list("DEFAULT").unwrap(); + builder.set_mode(SslMode::NO_AUTO_CHAIN | SslMode::SEND_FALLBACK_SCSV); + builder.set_private_key_file("keys/ssl.key", SslFiletype::PEM).unwrap(); + builder.set_certificate_chain_file("keys/ssl.cer").unwrap(); - let buffer = String::from_utf8_lossy(&buffer); + let http_server = HttpServer::new(move || { + App::new() + .wrap(Logger::default()) + .route("/", web::get().to(|| HttpResponse::Ok())) + .route("/query_region_list", web::get().to(DispatchServer::query_region_list)) + .route("/query_cur_region", web::get().to(DispatchServer::query_cur_region)) + //.route("", web::post().to(DispatchServer::)) + .route("/hk4e_global/mdk/shield/api/verify", web::post().to(DispatchServer::shield_verify)) + .route("/account/risky/api/check", web::post().to(DispatchServer::risky_api_check)) + .route("/hk4e_global/mdk/shield/api/login", web::post().to(DispatchServer::shield_login)) + .route("/hk4e_global/combo/granter/login/v2/login", web::post().to(DispatchServer::granter_login)) + }) + .bind(format!("127.0.0.1:{}", http_port)).expect("Failed to bind HTTP port") + .bind_openssl(format!("127.0.0.1:{}", https_port), builder).expect("Failed to bind HTTPS port") + .run(); - let data = buffer.split_whitespace().collect::>(); - let method = data[0]; - let url = data[1]; - - println!("Access to '{}' using {}", url, method); - - let (status_code, contents) = match method { - "GET" | "POST" => { - let parts = url.split('?').collect::>(); - let uri = parts[0]; - let params = if parts.len() > 1 { parts[1].split('&').collect::>() } else { Vec::new() }; - let params = params.into_iter().map(|x| x.split('=').collect::>()).filter(|x| x.len() > 1).map(|x| (x[0], x[1])).collect(); - - match self.callbacks.get(uri) { - Some(callback) => callback(&self, params), - None => ("404 Not Found".into(), "Nothing here".into()), - } - }, - _ => ("400 Bad Request".into(), "You are stupid".into()), - }; - - //let contents = fs::read_to_string(filename).unwrap(); - - let status_line = format!("HTTP/1.1 {}", status_code); - - let response = format!( - "{}\r\nContent-Length: {}\r\n\r\n{}", - status_line, - contents.len(), - contents - ); - - stream.write(response.as_bytes()).unwrap(); - stream.flush().unwrap(); + http_server.stop(true).await; } - fn query_region_list(&self, args: Vec<(&str, &str)>) -> (String, String) { - println!("Args: {:?}", args); + async fn query_region_list(c: web::Query) -> String { + println!("Client: {:?}", c); - let keys = self.load_keys("master"); + let keys = DispatchServer::load_keys("master"); let mut region_info = proto::RegionSimpleInfo::default(); region_info.name = "private_server".into(); region_info.title = "Private Server".into(); region_info.r#type = "DEV_PUBLIC".into(); - region_info.dispatch_url = format!("http://localhost:{}/query_cur_region", self.port); + region_info.dispatch_url = format!("http://localhost:{}/query_cur_region", 80); let mut region_list = proto::QueryRegionListHttpRsp::default(); region_list.region_list = vec![region_info]; @@ -120,13 +142,13 @@ impl DispatchServer { region_list.encode(&mut region_list_buf).unwrap(); - return ("200 OK".into(), base64::encode(region_list_buf)); + return base64::encode(region_list_buf); } - fn query_cur_region(&self, args: Vec<(&str, &str)>) -> (String, String) { - println!("Args: {:?}", args); + async fn query_cur_region(c: web::Query) -> String { + println!("Client: {:?}", c); - let keys = self.load_keys("master"); + let keys = DispatchServer::load_keys("master"); let mut region_info = proto::RegionInfo::default(); region_info.gateserver_ip = "127.0.0.1".to_string(); @@ -138,7 +160,7 @@ impl DispatchServer { region_config.client_secret_key = keys.0.clone(); let json_config = format!("{{\"coverSwitch\": [\"8\"], \"perf_report_config_url\": \"http://localhost:{}/config/verify\", \"perf_report_record_url\": \"http://localhost:{}/dataUpload\" }}", - self.port, self.port); + 80, 80); let mut custom_config = json_config.as_bytes().to_owned(); @@ -150,38 +172,57 @@ impl DispatchServer { region_config.encode(&mut region_conf_buf).unwrap(); - return ("200 OK".into(), base64::encode(region_conf_buf)); + return base64::encode(region_conf_buf); } - fn risky_api_check(&self, args: Vec<(&str, &str)>) -> (String, String) { + async fn risky_api_check(a: web::Json) -> String { + println!("Action: {:?}", a); + let email = "ceo@hoyolab.com"; let name = "Ceo"; let token = "Fake-token-hahaha"; let uid = 0x1234; - let payload = self.build_account_data(email, name, token, uid); + let payload = DispatchServer::build_account_data(email, name, token, uid); - return ("200 OK".into(), self.make_answer(0, &payload)); + return DispatchServer::make_answer(0, &payload); } - fn granter_login(&self, args: Vec<(&str, &str)>) -> (String, String) { - let payload = self.verify_token_v2(); + async fn shield_login(l: web::Json) -> String { + println!("Login: {:?}", l); - return ("200 OK".into(), self.make_answer(0, &payload)); - } - - fn shield_verify(&self, args: Vec<(&str, &str)>) -> (String, String) { let email = "ceo@hoyolab.com"; let name = "Ceo"; let token = "Fake-token-hahaha"; let uid = 0x1234; - let payload = self.build_account_data(email, name, token, uid); + let payload = DispatchServer::build_account_data(email, name, token, uid); - return ("200 OK".into(), self.make_answer(0, &payload)); + return DispatchServer::make_answer(0, &payload); } - fn load_keys(&self, name: &str) -> (Vec, Vec) { + async fn granter_login(g: web::Json) -> String { + println!("Granter: {:?}", g); + + let payload = DispatchServer::verify_token_v2(); + + return DispatchServer::make_answer(0, &payload); + } + + async fn shield_verify(t: web::Json) -> String { + println!("Token: {:?}", t); + + let email = "ceo@hoyolab.com"; + let name = "Ceo"; + let token = "Fake-token-hahaha"; + let uid = t.uid.parse().unwrap(); + + let payload = DispatchServer::build_account_data(email, name, token, uid); + + return DispatchServer::make_answer(0, &payload); + } + + fn load_keys(name: &str) -> (Vec, Vec) { // Key let filename = format!("./{}/{}.key", "keys", name); let mut f = fs::File::open(&filename).expect(&format!("File '{}' not found", filename)); @@ -197,12 +238,16 @@ impl DispatchServer { return (ec2b, key); } - fn verify_token_v2(&self) -> String { + fn verify_token_v2() -> String { let account_type = 1; let combo_id = 0x4321; - let combo_token = "Fake-token-hehehe"; let open_id = 0x1234; + #[cfg(not(feature = "raw_packet_dump"))] + let combo_token = "Fake-token-hehehe"; + #[cfg(feature = "raw_packet_dump")] + let combo_token = std::str::from_utf8(&[32u8; 4096*3]).unwrap(); + return format!("{{ \"account_type\": \"{}\", \"combo_id\": \"{}\", @@ -213,7 +258,7 @@ impl DispatchServer { }}", account_type, combo_id, combo_token, open_id); } - fn build_account_data(&self, email: &str, name: &str, token: &str, uid: i32) -> String { + fn build_account_data(email: &str, name: &str, token: &str, uid: i32) -> String { let payload = format!("{{ \"account\": {{ \"apple_name\": \"\", @@ -242,7 +287,7 @@ impl DispatchServer { return payload.into(); } - fn make_answer(&self, code: i32, data: &str) -> String { + fn make_answer(code: i32, data: &str) -> String { let message = match code { 0 => "OK", -1 => "not matched", diff --git a/src/server/game_server.rs b/src/server/game_server.rs index 9d67c17..c531845 100644 --- a/src/server/game_server.rs +++ b/src/server/game_server.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; use crate::server::GameWorld; +use packet_processor::PacketProcessor; use crate::server::IpcMessage; pub struct GameServer { @@ -27,15 +28,15 @@ impl GameServer { let world_processor = thread::spawn(move || { println!("Starting world processor"); // TODO: Load worlds! - loop { - } + //loop { + //} }); loop { - let IpcMessage(conv, packet_id, metadata, data) = self.packets_to_process_rx.recv().unwrap(); + let IpcMessage(user_id, packet_id, metadata, data) = self.packets_to_process_rx.recv().unwrap(); - // TODO: each conv will have a distinct world! - let world = match self.worlds.entry(conv) { + // TODO: each user_id will have a distinct world! + let world = match self.worlds.entry(user_id) { Occupied(world) => world.into_mut(), Vacant(entry) => { let mut world = GameWorld::new(self.packets_to_send_tx.clone()); @@ -43,7 +44,7 @@ impl GameServer { }, }; - world.process_packet(conv, packet_id, metadata, data); + world.process(user_id, packet_id, metadata, data); } } } diff --git a/src/server/game_world.rs b/src/server/game_world.rs index f623837..1059ed4 100644 --- a/src/server/game_world.rs +++ b/src/server/game_world.rs @@ -5,33 +5,11 @@ use std::time::SystemTime; use prost::Message; -use crate::proto; - use crate::server::IpcMessage; -pub type PacketCallback = fn(&mut GameWorld, u32, &proto::PacketHead, Vec) -> (); - -macro_rules! register_callback { - ($hashmap:ident, $req:ident, $rsp:ident, $handler:ident) => { - $hashmap.insert(proto::PacketId::$req, |slef: &mut GameWorld, conv: u32, metadata: &proto::PacketHead, data: Vec| { - let req = proto::$req::decode(&mut Cursor::new(data)).unwrap(); - let mut rsp = proto::$rsp::default(); - - slef.$handler(conv, &metadata, &req, &mut rsp); - - let message = IpcMessage::new_from_proto(conv, proto::PacketId::$rsp, metadata, &rsp); - slef.packets_to_send_tx.send(message).unwrap(); - }); - }; - - ($hashmap:ident, $notify:ident, $handler:ident) => { - $hashmap.insert(proto::PacketId::$notify, |slef: &mut GameWorld, conv: u32, metadata: &proto::PacketHead, data: Vec| { - let notify = proto::$req::decode(&mut Cursor::new(data)).unwrap(); - - slef.$handler(conv, &metadata, ¬ify); - }); - }; -} +use packet_processor_macro::*; +#[macro_use] +use packet_processor::*; macro_rules! collection { // map-like @@ -46,14 +24,41 @@ macro_rules! collection { }}; } +macro_rules! build_and_send { + ($id:ident, $self:ident, $user_id: ident, $metadata:ident, { $($i:ident : $e:expr,)* }) => {{ + $self.packets_to_send_tx.send( + IpcMessage::new_from_proto( + $user_id, + proto::PacketId::$id, + $metadata, + &proto::$id { $($i: $e,)* ..proto::$id::default() } + ) + ).unwrap(); + }}; +} + +macro_rules! build { + ($id:ident, { $($i:ident : $e:expr,)* }) => {{ + proto::$id { $($i: $e,)* ..proto::$id::default() } + }}; +} + +#[packet_processor( + PingReq, + PlayerLoginReq, + GetPlayerSocialDetailReq, + EnterSceneReadyReq, + SceneInitFinishReq, + EnterSceneDoneReq, + PostEnterSceneReq, + EnterWorldAreaReq, +)] pub struct GameWorld { packets_to_send_tx: mpsc::Sender, - callbacks: HashMap, } impl GameWorld { const BASE_GUID: u64 = 0x2400000000000000; - const SPOOFED_PLAYER_UID: u32 = 1337; const SPOOFED_AVATAR_EID: u32 = (1<<24) + 146; const SPOOFED_AVATAR_GUID: u64 = GameWorld::BASE_GUID + 1; const SPOOFED_WEAPON_EID: u32 = 0x6000000 + 146; @@ -62,364 +67,338 @@ impl GameWorld { const SPOOFED_SCENE_ID: u32 = 3; pub fn new(packets_to_send_tx: mpsc::Sender) -> GameWorld { - let mut callbacks: HashMap = HashMap::new(); - - register_callback!(callbacks, PingReq, PingRsp, process_ping); - register_callback!(callbacks, GetPlayerTokenReq, GetPlayerTokenRsp, process_get_token); - register_callback!(callbacks, PlayerLoginReq, PlayerLoginRsp, process_login_req); - register_callback!(callbacks, GetPlayerSocialDetailReq, GetPlayerSocialDetailRsp, process_social_details_req); - register_callback!(callbacks, EnterSceneReadyReq, EnterSceneReadyRsp, process_enter_ready); - register_callback!(callbacks, SceneInitFinishReq, SceneInitFinishRsp, process_scene_init_finish); - register_callback!(callbacks, EnterSceneDoneReq, EnterSceneDoneRsp, process_enter_done); - register_callback!(callbacks, PostEnterSceneReq, PostEnterSceneRsp, process_post_enter_scene); - register_callback!(callbacks, EnterWorldAreaReq, EnterWorldAreaRsp, process_enter_world_area); - - let gw = GameWorld { + let mut gw = GameWorld { packets_to_send_tx: packets_to_send_tx, - callbacks: callbacks, + packet_callbacks: HashMap::new(), }; + gw.register(); + return gw; } - pub fn process_packet(&mut self, conv: u32, packet_id: proto::PacketId, metadata: Vec, data: Vec) { - let callback = self.callbacks.get(&packet_id); - let metadata = proto::PacketHead::decode(&mut Cursor::new(metadata)).unwrap(); - - match callback { - Some(callback) => callback(self, conv, &metadata, data), - None => println!("Unhandled packet {:?}", packet_id), - } - } - - fn process_ping(&self, conv: u32, metadata: &proto::PacketHead, req: &proto::PingReq, rsp: &mut proto::PingRsp) { + fn process_ping(&self, user_id: u32, metadata: &proto::PacketHead, req: &proto::PingReq, rsp: &mut proto::PingRsp) { rsp.client_time = req.client_time; } - fn process_get_token(&self, conv: u32, metadata: &proto::PacketHead, req: &proto::GetPlayerTokenReq, rsp: &mut proto::GetPlayerTokenRsp) { - let seed: u64 = 0x123456789ABCDEF0; // TODO: use real value! - rsp.account_type = req.account_type; - rsp.account_uid = req.account_uid.clone(); - rsp.token = format!("token-game-{}", req.account_uid); - rsp.secret_key_seed = seed; - rsp.uid = GameWorld::SPOOFED_PLAYER_UID; // TODO: use real value! + fn process_player_login(&self, user_id: u32, metadata: &proto::PacketHead, req: &proto::PlayerLoginReq, rsp: &mut proto::PlayerLoginRsp) { + build_and_send! ( PlayerDataNotify, self, user_id, metadata, + { nick_name: "Fapper".into(), server_time: self.timestamp(), prop_map: self.spoof_player_props(), } + ); + + build_and_send! ( OpenStateUpdateNotify, self, user_id, metadata, + { open_state_map: self.spoof_world_props(), } + ); + + build_and_send! (StoreWeightLimitNotify, self, user_id, metadata, + { + store_type: proto::StoreType::StorePack as i32, + weight_limit: 30000, + material_count_limit: 2000, + weapon_count_limit: 2000, + reliquary_count_limit: 1000, + furniture_count_limit: 2000, + } + ); + + build_and_send! (PlayerStoreNotify, self, user_id, metadata, + {store_type: proto::StoreType::StorePack as i32, weight_limit: 30000, item_list: self.spoof_inventory(),} + ); + + build_and_send! (AvatarDataNotify, self, user_id, metadata, + { + avatar_list: vec![self.spoof_default_avatar2()], + avatar_team_map: self.spoof_team_map(), + cur_avatar_team_id: 2, + choose_avatar_guid: GameWorld::SPOOFED_AVATAR_GUID, + } + ); + + build_and_send! (PlayerEnterSceneNotify, self, user_id, metadata, + { + scene_id: GameWorld::SPOOFED_SCENE_ID, + r#type: proto::EnterType::EnterSelf as i32, + scene_begin_time: self.timestamp(), + pos: Some(proto::Vector {x: -3400.0, y: 203.0, z: -3427.60}), + target_uid: user_id, + world_level: 8, + enter_scene_token: GameWorld::SPOOFED_SCENE_TOKEN, + //enter_reason: 1, + } + ); } - fn process_login_req(&self, conv: u32, metadata: &proto::PacketHead, req: &proto::PlayerLoginReq, rsp: &mut proto::PlayerLoginRsp) { - let mut data_notify = proto::PlayerDataNotify::default(); - data_notify.nick_name = "Fapper".to_string(); - data_notify.server_time = self.timestamp(); - data_notify.prop_map = self.spoof_player_props(); - //data_notify.region_id = 50; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::PlayerDataNotify, metadata, &data_notify); - self.packets_to_send_tx.send(message).unwrap(); + fn process_get_player_social_detail(&self, user_id: u32, metadata: &proto::PacketHead, req: &proto::GetPlayerSocialDetailReq, rsp: &mut proto::GetPlayerSocialDetailRsp) { + let avatar_info = build!(SocialShowAvatarInfo, + { + avatar_id: 10000007, + level: 80, + } + ); - let mut open_state_notify = proto::OpenStateUpdateNotify::default(); - open_state_notify.open_state_map = self.spoof_world_props(); - let message = IpcMessage::new_from_proto(conv, proto::PacketId::OpenStateUpdateNotify, metadata, &open_state_notify); - self.packets_to_send_tx.send(message).unwrap(); - - let mut store_weight_notify = proto::StoreWeightLimitNotify::default(); - store_weight_notify.store_type = proto::StoreType::StorePack as i32; - store_weight_notify.weight_limit = 30000; - store_weight_notify.material_count_limit = 2000; - store_weight_notify.weapon_count_limit = 2000; - store_weight_notify.reliquary_count_limit = 1000; - //store_weight_notify.furniture_count_limit = 2000; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::StoreWeightLimitNotify, metadata, &store_weight_notify); - self.packets_to_send_tx.send(message).unwrap(); - - let mut player_store_notify = proto::PlayerStoreNotify::default(); - player_store_notify.store_type = proto::StoreType::StorePack as i32; - player_store_notify.weight_limit = 30000; - player_store_notify.item_list = self.spoof_inventory(); - let message = IpcMessage::new_from_proto(conv, proto::PacketId::PlayerStoreNotify, metadata, &player_store_notify); - self.packets_to_send_tx.send(message).unwrap(); - - let mut avatar_data_notify = proto::AvatarDataNotify::default(); - avatar_data_notify.avatar_list = vec![self.spoof_default_avatar2()]; - avatar_data_notify.avatar_team_map = self.spoof_team_map(); - avatar_data_notify.cur_avatar_team_id = 2; - avatar_data_notify.choose_avatar_guid = GameWorld::SPOOFED_AVATAR_GUID; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::AvatarDataNotify, metadata, &avatar_data_notify); - self.packets_to_send_tx.send(message).unwrap(); - - let mut enter_scene_notify = proto::PlayerEnterSceneNotify::default(); - let mut pos = proto::Vector::default(); - pos.x = -3400.0; - pos.y = 203.0; - pos.z = -3427.60; - enter_scene_notify.scene_id = GameWorld::SPOOFED_SCENE_ID; - enter_scene_notify.r#type = proto::EnterType::EnterSelf as i32; - enter_scene_notify.scene_begin_time = self.timestamp(); - enter_scene_notify.pos = Some(pos); - enter_scene_notify.target_uid = GameWorld::SPOOFED_PLAYER_UID; - enter_scene_notify.world_level = 8; - enter_scene_notify.enter_scene_token = GameWorld::SPOOFED_SCENE_TOKEN; - //enter_scene_notify.enter_reason = 1; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::PlayerEnterSceneNotify, metadata, &enter_scene_notify); - self.packets_to_send_tx.send(message).unwrap(); - } - - fn process_social_details_req(&self, conv: u32, metadata: &proto::PacketHead, req: &proto::GetPlayerSocialDetailReq, rsp: &mut proto::GetPlayerSocialDetailRsp) { - let mut details = proto::SocialDetail::default(); - - details.uid = GameWorld::SPOOFED_PLAYER_UID; - details.nickname = "Fukker".to_string(); - details.level = 56; - details.avatar_id = 10000007; - details.signature = "Fuck you".to_string(); - let mut birthday = proto::Birthday::default(); - birthday.month = 2; - birthday.day = 11; - details.birthday = Some(birthday); - details.world_level = 8; - details.online_state = proto::FriendOnlineState::FriendOnline as i32; - details.is_friend = true; - details.is_mp_mode_available = true; - details.name_card_id = 210051; - details.finish_achievement_num = 42; - details.tower_floor_index = 1; - details.tower_level_index = 1; - let mut avatar_info = proto::SocialShowAvatarInfo::default(); - avatar_info.avatar_id = 10000007; - avatar_info.level = 80; - details.show_avatar_info_list = vec![avatar_info]; // TODO - details.show_name_card_id_list = vec![210051]; - // Field 25! + let details = build!(SocialDetail, + { + uid: user_id, + nickname: "Fukker".to_string(), + level: 56, + avatar_id: 10000007, + signature: "Fuck you".to_string(), + birthday: Some(proto::Birthday {month: 2, day: 11}), + world_level: 8, + online_state: proto::FriendOnlineState::FriendOnline as i32, + is_friend: true, + is_mp_mode_available: true, + name_card_id: 210051, + finish_achievement_num: 42, + tower_floor_index: 1, + tower_level_index: 1, + show_avatar_info_list: vec![avatar_info], // TODO + show_name_card_id_list: vec![210051], + // Field 25! + } + ); rsp.detail_data = Some(details); } - fn process_enter_ready(&self, conv: u32, metadata: &proto::PacketHead, req: &proto::EnterSceneReadyReq, rsp: &mut proto::EnterSceneReadyRsp) { + fn process_enter_scene_ready(&self, user_id: u32, metadata: &proto::PacketHead, req: &proto::EnterSceneReadyReq, rsp: &mut proto::EnterSceneReadyRsp) { rsp.enter_scene_token = req.enter_scene_token; - let mut peer_notify = proto::EnterScenePeerNotify::default(); - peer_notify.dest_scene_id = GameWorld::SPOOFED_SCENE_ID; - peer_notify.peer_id = 1; - peer_notify.host_peer_id = 1; - peer_notify.enter_scene_token = GameWorld::SPOOFED_SCENE_TOKEN; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::EnterScenePeerNotify, metadata, &peer_notify); - self.packets_to_send_tx.send(message).unwrap(); + build_and_send!(EnterScenePeerNotify, self, user_id, metadata, + { + dest_scene_id: GameWorld::SPOOFED_SCENE_ID, + peer_id: 1, + host_peer_id: 1, + enter_scene_token: GameWorld::SPOOFED_SCENE_TOKEN, + } + ); } - fn process_scene_init_finish(&self, conv: u32, metadata: &proto::PacketHead, req: &proto::SceneInitFinishReq, rsp: &mut proto::SceneInitFinishRsp) { + fn process_scene_init_finish(&self, user_id: u32, metadata: &proto::PacketHead, req: &proto::SceneInitFinishReq, rsp: &mut proto::SceneInitFinishRsp) { rsp.enter_scene_token = GameWorld::SPOOFED_SCENE_TOKEN; - let mut world_data_notify = proto::WorldDataNotify::default(); - let world_prop_map = collection!{1 => 8, 2 => 0}; - world_data_notify.world_prop_map = self.remap(&world_prop_map); - let message = IpcMessage::new_from_proto(conv, proto::PacketId::WorldDataNotify, metadata, &world_data_notify); - self.packets_to_send_tx.send(message).unwrap(); + build_and_send!(WorldDataNotify, self, user_id, metadata, + { world_prop_map: self.remap(&collection!{1 => 8, 2 => 0}), } + ); - let mut online_player_info = proto::OnlinePlayerInfo::default(); - online_player_info.uid = GameWorld::SPOOFED_PLAYER_UID; - online_player_info.nickname = "Fukker".to_string(); - online_player_info.player_level = 56; - online_player_info.avatar_id = 10000007; - online_player_info.mp_setting_type = proto::MpSettingType::MpSettingEnterAfterApply as i32; - online_player_info.cur_player_num_in_world = 1; - online_player_info.world_level = 8; - online_player_info.name_card_id = 210051; - online_player_info.signature = "Fuck you!".to_string(); - // TODO: Field 12! + let online_player_info = build!(OnlinePlayerInfo, + { + uid: user_id, + nickname: "Fukker".to_string(), + player_level: 56, + avatar_id: 10000007, + mp_setting_type: proto::MpSettingType::MpSettingEnterAfterApply as i32, + cur_player_num_in_world: 1, + world_level: 8, + name_card_id: 210051, + signature: "Fuck you!".to_string(), + // TODO: Field 12! + } + ); - let mut world_player_notify = proto::WorldPlayerInfoNotify::default(); - world_player_notify.player_info_list = vec![online_player_info.clone()]; - world_player_notify.player_uid_list = vec![GameWorld::SPOOFED_PLAYER_UID]; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::WorldPlayerInfoNotify, metadata, &world_player_notify); - self.packets_to_send_tx.send(message).unwrap(); + build_and_send!(WorldPlayerInfoNotify, self, user_id, metadata, + { + player_info_list: vec![online_player_info.clone()], + player_uid_list: vec![user_id], + } + ); - let mut scene_player_info_e = proto::ScenePlayerInfo::default(); - scene_player_info_e.uid = GameWorld::SPOOFED_PLAYER_UID; - scene_player_info_e.peer_id = 1; - scene_player_info_e.name = "Fukker".to_string(); - scene_player_info_e.scene_id = GameWorld::SPOOFED_SCENE_ID; - scene_player_info_e.online_player_info = Some(online_player_info); - let mut scene_player_info = proto::ScenePlayerInfoNotify::default(); - scene_player_info.player_info_list = vec![scene_player_info_e]; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::ScenePlayerInfoNotify, metadata, &scene_player_info); - self.packets_to_send_tx.send(message).unwrap(); + let scene_player_info_e = build!(ScenePlayerInfo, + { + uid: user_id, + peer_id: 1, + name: "Fukker".to_string(), + scene_id: GameWorld::SPOOFED_SCENE_ID, + online_player_info: Some(online_player_info), + } + ); + + build_and_send!(ScenePlayerInfoNotify, self, user_id, metadata, + { + player_info_list: vec![scene_player_info_e], + } + ); - let mut avatar_enter_info = proto::AvatarEnterSceneInfo::default(); - avatar_enter_info.avatar_guid = GameWorld::SPOOFED_AVATAR_GUID; - avatar_enter_info.avatar_entity_id = GameWorld::SPOOFED_AVATAR_EID; - avatar_enter_info.weapon_guid = GameWorld::SPOOFED_WEAPON_GUID; - avatar_enter_info.weapon_entity_id = GameWorld::SPOOFED_WEAPON_EID; - let mut mp_level_info = proto::MpLevelEntityInfo::default(); - mp_level_info.entity_id = 0xb000000 + 146; - mp_level_info.authority_peer_id = 1; - let mut team_enter_info = proto::TeamEnterSceneInfo::default(); - team_enter_info.team_entity_id = 0x9000000 + 1; - let mut player_enter_info_notify = proto::PlayerEnterSceneInfoNotify::default(); - player_enter_info_notify.enter_scene_token = GameWorld::SPOOFED_SCENE_TOKEN; - player_enter_info_notify.avatar_enter_info = vec![avatar_enter_info]; - player_enter_info_notify.cur_avatar_entity_id = GameWorld::SPOOFED_AVATAR_EID; - player_enter_info_notify.mp_level_entity_info = Some(mp_level_info); - player_enter_info_notify.team_enter_info = Some(team_enter_info); - let message = IpcMessage::new_from_proto(conv, proto::PacketId::PlayerEnterSceneInfoNotify, metadata, &player_enter_info_notify); - self.packets_to_send_tx.send(message).unwrap(); + let avatar_enter_info = build!(AvatarEnterSceneInfo, + { + avatar_guid: GameWorld::SPOOFED_AVATAR_GUID, + avatar_entity_id: GameWorld::SPOOFED_AVATAR_EID, + weapon_guid: GameWorld::SPOOFED_WEAPON_GUID, + weapon_entity_id: GameWorld::SPOOFED_WEAPON_EID, + } + ); + let mp_level_info = build!(MpLevelEntityInfo, + { + entity_id: 0xb000000 + 146, + authority_peer_id: 1, + } + ); + let team_enter_info = build!(TeamEnterSceneInfo, { team_entity_id: 0x9000000 + 1, }); - let mut game_time_notify = proto::PlayerGameTimeNotify::default(); - game_time_notify.game_time = 5*60*60; - game_time_notify.uid = GameWorld::SPOOFED_PLAYER_UID; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::PlayerGameTimeNotify, metadata, &game_time_notify); - self.packets_to_send_tx.send(message).unwrap(); + build_and_send!(PlayerEnterSceneInfoNotify, self, user_id, metadata, + { + enter_scene_token: GameWorld::SPOOFED_SCENE_TOKEN, + avatar_enter_info: vec![avatar_enter_info], + cur_avatar_entity_id: GameWorld::SPOOFED_AVATAR_EID, + mp_level_entity_info: Some(mp_level_info), + team_enter_info: Some(team_enter_info), + } + ); - let mut scene_time_notify = proto::SceneTimeNotify::default(); - scene_time_notify.scene_id = GameWorld::SPOOFED_SCENE_ID; - scene_time_notify.scene_time = 9000; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::SceneTimeNotify, metadata, &scene_time_notify); - self.packets_to_send_tx.send(message).unwrap(); + build_and_send!(PlayerGameTimeNotify, self, user_id, metadata, { + game_time: 5*60*60, + uid: user_id, + }); - let mut scene_data_notify = proto::SceneDataNotify::default(); - scene_data_notify.level_config_name_list = vec!["Level_BigWorld".to_string()]; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::SceneDataNotify, metadata, &scene_data_notify); - self.packets_to_send_tx.send(message).unwrap(); + build_and_send!(SceneTimeNotify, self, user_id, metadata, { + scene_id: GameWorld::SPOOFED_SCENE_ID, + scene_time: 9000, + }); + + build_and_send!(SceneDataNotify, self, user_id, metadata, { + level_config_name_list: vec!["Level_BigWorld".to_string()], + }); - let mut host_notify = proto::HostPlayerNotify::default(); - host_notify.host_uid = GameWorld::SPOOFED_PLAYER_UID; - host_notify.host_peer_id = 1; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::HostPlayerNotify, metadata, &host_notify); - self.packets_to_send_tx.send(message).unwrap(); + build_and_send!(HostPlayerNotify, self, user_id, metadata, { + host_uid: user_id, + host_peer_id: 1, + }); - let mut scene_team_avatar = proto::SceneTeamAvatar::default(); - scene_team_avatar.scene_id = GameWorld::SPOOFED_SCENE_ID; - scene_team_avatar.player_uid = GameWorld::SPOOFED_PLAYER_UID; - scene_team_avatar.avatar_guid = GameWorld::SPOOFED_AVATAR_GUID; - scene_team_avatar.entity_id = GameWorld::SPOOFED_AVATAR_EID; - scene_team_avatar.weapon_guid = GameWorld::SPOOFED_WEAPON_GUID; - scene_team_avatar.weapon_entity_id = GameWorld::SPOOFED_WEAPON_EID; - scene_team_avatar.is_player_cur_avatar = true; - scene_team_avatar.scene_entity_info = Some(self.spoof_scene_default_avatar()); - scene_team_avatar.ability_control_block = Some(self.spoof_default_abilities()); - let mut scene_team_update = proto::SceneTeamUpdateNotify::default(); - scene_team_update.scene_team_avatar_list = vec![scene_team_avatar]; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::SceneTeamUpdateNotify, metadata, &scene_team_update); - self.packets_to_send_tx.send(message).unwrap(); + let scene_team_avatar = build!(SceneTeamAvatar, { + scene_id: GameWorld::SPOOFED_SCENE_ID, + player_uid: user_id, + avatar_guid: GameWorld::SPOOFED_AVATAR_GUID, + entity_id: GameWorld::SPOOFED_AVATAR_EID, + weapon_guid: GameWorld::SPOOFED_WEAPON_GUID, + weapon_entity_id: GameWorld::SPOOFED_WEAPON_EID, + is_player_cur_avatar: true, + scene_entity_info: Some(self.spoof_scene_default_avatar(user_id)), + ability_control_block: Some(self.spoof_default_abilities()), + }); + build_and_send!(SceneTeamUpdateNotify, self, user_id, metadata, { + scene_team_avatar_list: vec![scene_team_avatar], + }); } - fn process_enter_done(&self, conv: u32, metadata: &proto::PacketHead, req: &proto::EnterSceneDoneReq, rsp: &mut proto::EnterSceneDoneRsp) { + fn process_enter_scene_done(&self, user_id: u32, metadata: &proto::PacketHead, req: &proto::EnterSceneDoneReq, rsp: &mut proto::EnterSceneDoneRsp) { rsp.enter_scene_token = req.enter_scene_token; - let mut appear_notify = proto::SceneEntityAppearNotify::default(); - appear_notify.entity_list = vec![self.spoof_scene_default_avatar()]; - appear_notify.appear_type = proto::VisionType::VisionBorn as i32; - let message = IpcMessage::new_from_proto(conv, proto::PacketId::SceneEntityAppearNotify, metadata, &appear_notify); - self.packets_to_send_tx.send(message).unwrap(); + build_and_send!(SceneEntityAppearNotify, self, user_id, metadata, { + entity_list: vec![self.spoof_scene_default_avatar(user_id)], + appear_type: proto::VisionType::VisionBorn as i32, + }); } - fn process_post_enter_scene(&self, conv: u32, metadata: &proto::PacketHead, req: &proto::PostEnterSceneReq, rsp: &mut proto::PostEnterSceneRsp) { + fn process_post_enter_scene(&self, user_id: u32, metadata: &proto::PacketHead, req: &proto::PostEnterSceneReq, rsp: &mut proto::PostEnterSceneRsp) { rsp.enter_scene_token = GameWorld::SPOOFED_SCENE_TOKEN; } - fn process_enter_world_area(&self, conv: u32, metadata: &proto::PacketHead, req: &proto::EnterWorldAreaReq, rsp: &mut proto::EnterWorldAreaRsp) { + fn process_enter_world_area(&self, user_id: u32, metadata: &proto::PacketHead, req: &proto::EnterWorldAreaReq, rsp: &mut proto::EnterWorldAreaRsp) { rsp.area_type = req.area_type; rsp.area_id = req.area_id; } - fn spoof_scene_default_avatar(&self) -> proto::SceneEntityInfo { - let mut pos = proto::Vector::default(); - pos.x = -3400.0; - pos.y = 203.0; - pos.z = -3427.60; - let mut motion_info = proto::MotionInfo::default(); - motion_info.pos = Some(pos); - motion_info.rot = Some(proto::Vector::default()); - motion_info.speed = Some(proto::Vector::default()); + fn spoof_scene_default_avatar(&self, user_id: u32) -> proto::SceneEntityInfo { + let motion_info = build!(MotionInfo, { + pos: Some(proto::Vector {x: -3400.0, y: 203.0, z: -3427.0}), + rot: Some(proto::Vector::default()), + speed: Some(proto::Vector::default()), + }); - let mut weapon = proto::SceneWeaponInfo::default(); - weapon.entity_id = GameWorld::SPOOFED_WEAPON_EID; - weapon.gadget_id = 50011406; // TODO! - weapon.item_id = 11406; - weapon.guid = GameWorld::SPOOFED_WEAPON_GUID; - weapon.level = 70; - weapon.promote_level = 4; - weapon.affix_map = collection! { 111406 => 0 }; + let weapon = build!(SceneWeaponInfo, { + entity_id: GameWorld::SPOOFED_WEAPON_EID, + gadget_id: 50011406, // TODO! + item_id: 11406, + guid: GameWorld::SPOOFED_WEAPON_GUID, + level: 70, + promote_level: 4, + affix_map: collection! { 111406 => 0 }, + }); - let mut scene_avatar_info = proto::SceneAvatarInfo::default(); - scene_avatar_info.uid = GameWorld::SPOOFED_PLAYER_UID; - scene_avatar_info.avatar_id = 10000007; - scene_avatar_info.guid = GameWorld::SPOOFED_AVATAR_GUID; - scene_avatar_info.peer_id = 1; - scene_avatar_info.skill_depot_id = 704; - scene_avatar_info.born_time = 1633790000; - scene_avatar_info.talent_id_list = vec![71, 72, 73, 74, 75, 76]; - scene_avatar_info.inherent_proud_skill_list = vec![72101, 72201]; - scene_avatar_info.skill_level_map = collection!{100553 => 3, 10067 => 3, 10068 => 3}; - scene_avatar_info.proud_skill_extra_level_map = collection!{739 => 3, 732 => 3}; - scene_avatar_info.equip_id_list = vec![11406]; - scene_avatar_info.weapon = Some(weapon); + let scene_avatar_info = build!(SceneAvatarInfo, { + uid: user_id, + avatar_id: 10000007, + guid: GameWorld::SPOOFED_AVATAR_GUID, + peer_id: 1, + skill_depot_id: 704, + born_time: 1633790000, + talent_id_list: vec![71, 72, 73, 74, 75, 76], + inherent_proud_skill_list: vec![72101, 72201], + skill_level_map: collection!{100553 => 3, 10067 => 3, 10068 => 3}, + proud_skill_extra_level_map: collection!{739 => 3, 732 => 3}, + equip_id_list: vec![11406], + weapon: Some(weapon), + }); - let mut scene_entity_info = proto::SceneEntityInfo::default(); - scene_entity_info.entity_type = proto::ProtEntityType::ProtEntityAvatar as i32; - scene_entity_info.entity_id = GameWorld::SPOOFED_AVATAR_EID; - scene_entity_info.life_state = 1; - scene_entity_info.entity = Some(proto::scene_entity_info::Entity::Avatar(scene_avatar_info)); - scene_entity_info.prop_list = self.spoof_scene_avatar_props(); - scene_entity_info.fight_prop_list = self.spoof_scene_avatar_fight_props(); - scene_entity_info.motion_info = Some(motion_info); + let scene_ai_info = build!(SceneEntityAiInfo, { + is_ai_open: true, + born_pos: Some(proto::Vector::default()), + }); + let authority_info = build!(EntityAuthorityInfo, { ai_info: Some(scene_ai_info), }); - let mut scene_ai_info = proto::SceneEntityAiInfo::default(); - scene_ai_info.is_ai_open = true; - scene_ai_info.born_pos = Some(proto::Vector::default()); - let mut authority_info = proto::EntityAuthorityInfo::default(); - authority_info.ai_info = Some(scene_ai_info); - scene_entity_info.entity_authority_info = Some(authority_info); + let scene_entity_info = build!(SceneEntityInfo, { + entity_type: proto::ProtEntityType::ProtEntityAvatar as i32, + entity_id: GameWorld::SPOOFED_AVATAR_EID, + life_state: 1, + entity: Some(proto::scene_entity_info::Entity::Avatar(scene_avatar_info)), + prop_list: self.spoof_scene_avatar_props(), + fight_prop_list: self.spoof_scene_avatar_fight_props(), + motion_info: Some(motion_info), + entity_authority_info: Some(authority_info), + }); return scene_entity_info; } fn spoof_default_avatar2(&self) -> proto::AvatarInfo { - let mut avatar_info = proto::AvatarInfo::default(); - avatar_info.avatar_id = 10000007; - avatar_info.avatar_type = 1; - avatar_info.guid = GameWorld::SPOOFED_AVATAR_GUID; - avatar_info.skill_depot_id = 704; - avatar_info.born_time = 1633790000; - avatar_info.talent_id_list = vec![71, 72, 73, 74, 75, 76]; - avatar_info.prop_map = self.spoof_avatar_props(); - avatar_info.fight_prop_map = self.spoof_avatar_fight_props(); - avatar_info.fetter_info = Some(self.spoof_fetter_info()); - avatar_info.equip_guid_list = vec![GameWorld::SPOOFED_WEAPON_GUID]; - avatar_info.inherent_proud_skill_list = vec![72101, 72201]; - avatar_info.skill_level_map = collection!{100553 => 3, 10067 => 3, 10068 => 3}; - avatar_info.proud_skill_extra_level_map = collection!{739 => 3, 732 => 3}; + let mut avatar_info = build!(AvatarInfo, { + avatar_id: 10000007, + avatar_type: 1, + guid: GameWorld::SPOOFED_AVATAR_GUID, + skill_depot_id: 704, + born_time: 1633790000, + talent_id_list: vec![71, 72, 73, 74, 75, 76], + prop_map: self.spoof_avatar_props(), + fight_prop_map: self.spoof_avatar_fight_props(), + fetter_info: Some(self.spoof_fetter_info()), + equip_guid_list: vec![GameWorld::SPOOFED_WEAPON_GUID], + inherent_proud_skill_list: vec![72101, 72201], + skill_level_map: collection!{100553 => 3, 10067 => 3, 10068 => 3}, + proud_skill_extra_level_map: collection!{739 => 3, 732 => 3}, + }); return avatar_info; } fn spoof_team_map(&self) -> HashMap { - let mut at = proto::AvatarTeam::default(); - at.team_name = "Fuck yea".to_string(); - at.avatar_guid_list = vec![GameWorld::SPOOFED_AVATAR_GUID]; + let at = build!(AvatarTeam, { + team_name: "Fuck yea".to_string(), + avatar_guid_list: vec![GameWorld::SPOOFED_AVATAR_GUID], + }); - let mut hm = HashMap::new(); - - hm.insert(1, at.clone()); - hm.insert(2, at.clone()); - hm.insert(3, at.clone()); - hm.insert(4, at.clone()); - - return hm; + return collection! { + 1 => at.clone(), + 2 => at.clone(), + 3 => at.clone(), + 4 => at.clone(), + }; } fn spoof_player_props(&self) -> HashMap { // TODO: fill! let map = collection! { - 10004 => 1, - 10009 => 1, - proto::PlayerProp::GliderUnlocked as u32 => 1, - proto::PlayerProp::TeleportUnlocked as u32 => 1, - proto::PlayerProp::PlayerLevel as u32 => 56, - proto::PlayerProp::PlayerExperience as u32 => 1337, - proto::PlayerProp::CurrencyPrimogems as u32 => 9001, - proto::PlayerProp::CurrencyMora as u32 => 9002, - proto::PlayerProp::WorldLevel as u32 => 8, - proto::PlayerProp::CurrencyResin as u32 => 159, - proto::PlayerProp::CurrencyGenesis as u32 => 9003, - proto::PlayerProp::MaxStamina as u32 => 120, - proto::PlayerProp::CurStamina as u32 => 120, + proto::PropType::PropIsSpringAutoUse as u32 => 1, + proto::PropType::PropIsFlyable as u32 => 1, + proto::PropType::PropIsTransferable as u32 => 1, + proto::PropType::PropPlayerLevel as u32 => 56, + proto::PropType::PropPlayerExp as u32 => 1337, + proto::PropType::PropPlayerHcoin as u32 => 9001, + proto::PropType::PropPlayerScoin as u32 => 9002, + proto::PropType::PropPlayerWorldLevel as u32 => 8, + proto::PropType::PropPlayerResin as u32 => 159, + proto::PropType::PropPlayerMcoin as u32 => 9003, + proto::PropType::PropMaxStamina as u32 => 120, + proto::PropType::PropCurPersistStamina as u32 => 120, }; return self.remap(&map); @@ -428,37 +407,37 @@ impl GameWorld { fn spoof_world_props(&self) -> HashMap { // TODO: fill! let map = collection! { - proto::OpenState::Menu as u32 => 1, + proto::OpenStateType::OpenStatePaimon as u32 => 1, - proto::OpenState::TutorialCharaUpgrade as u32 => 1, + proto::OpenStateType::OpenStatePlayerLvupGuide as u32 => 1, - proto::OpenState::Gacha as u32 => 1, - proto::OpenState::TutorialGacha as u32 => 1, + proto::OpenStateType::OpenStateGacha as u32 => 1, + proto::OpenStateType::OpenStateGuideGacha as u32 => 1, - proto::OpenState::TutorialPartySetup as u32 => 1, + proto::OpenStateType::OpenStateGuideTeam as u32 => 1, - proto::OpenState::TutorialInventory as u32 => 1, + proto::OpenStateType::OpenStateGuideBag as u32 => 1, - proto::OpenState::MapUnlockedUnknown0 as u32 => 1, - proto::OpenState::MapUnlockedUnknown1 as u32 => 1, - proto::OpenState::CoopMode as u32 => 0, + proto::OpenStateType::OpenStateLimitRegionFreshmeat as u32 => 1, + proto::OpenStateType::OpenStateLimitRegionGlobal as u32 => 1, + proto::OpenStateType::OpenStateMultiplayer as u32 => 0, - proto::OpenState::DressingRoom as u32 => 1, + proto::OpenStateType::OpenStateAvatarFashion as u32 => 1, - proto::OpenState::TutorialDressingRoom as u32 => 1, + proto::OpenStateType::OpenStateGuideAppearance as u32 => 1, - proto::OpenState::Shop as u32 => 1, - proto::OpenState::ShopUnknown1 as u32 => 1, - proto::OpenState::ShopUnknown2 as u32 => 1, - proto::OpenState::ShopUnknown3 as u32 => 1, + proto::OpenStateType::OpenStateShopTypeMall as u32 => 1, // 900 + proto::OpenStateType::OpenStateShopTypeRecommanded as u32 => 1, // 901 + proto::OpenStateType::OpenStateShopTypeGenesiscrystal as u32 => 1, // 902 + proto::OpenStateType::OpenStateShopTypeGiftpackage as u32 => 1, // 903 - proto::OpenState::Investigations as u32 => 1, - proto::OpenState::InvestigationsEnemies as u32 => 1, - proto::OpenState::InvestigationsDomains as u32 => 1, + proto::OpenStateType::OpenAdventureManual as u32 => 1, // 1100 + proto::OpenStateType::OpenAdventureManualMonster as u32 => 1, // 1103 + proto::OpenStateType::OpenAdventureManualBossDungeon as u32 => 1, // 1104 - proto::OpenState::MiningDepositsMondstadt as u32 => 1, - proto::OpenState::MiningDepositsLiyue as u32 => 1, - proto::OpenState::Inazuma as u32 => 1, + proto::OpenStateType::OpenStateMengdeInfusedcrystal as u32 => 1, + proto::OpenStateType::OpenStateLiyueInfusedcrystal as u32 => 1, + proto::OpenStateType::OpenStateInazumaMainquestFinished as u32 => 1, }; return map; @@ -467,11 +446,11 @@ impl GameWorld { fn spoof_avatar_props_raw(&self) -> HashMap { // TODO: fill! let map = collection! { - 1001 => 0, - 4001 => 80, - 1002 => 5, - 1003 => 0, - 1004 => 0, + proto::PropType::PropExp as u32 => 0, + proto::PropType::PropLevel as u32 => 80, + proto::PropType::PropBreakLevel as u32 => 5, + proto::PropType::PropSatiationVal as u32 => 0, + proto::PropType::PropSatiationPenaltyTime as u32 => 0, }; return map; @@ -487,45 +466,56 @@ impl GameWorld { fn spoof_avatar_fight_props(&self) -> HashMap { // TODO: fill! let map = collection! { - 2000 => 11575.4, - 2001 => 929.546, - 2002 => 675.656, - 2003 => 0.00000, - 20 => 0.241600, - 21 => 0.00000, - 22 => 0.605700, - 23 => 1.88170, - 26 => 0.00000, - 27 => 0.00000, - 28 => 33.5700, - 29 => 0.00000, - 30 => 0.719204, - 40 => 0.00000, - 41 => 0.00000, - 42 => 0.00000, - 43 => 0.00000, - 1004 => 60.0000, - 44 => 0.00000, - 45 => 0.00000, - 46 => 0.00000, - 1010 => 10320.2, - 50 => 0.00000, - 51 => 0.00000, - 52 => 0.00000, - 53 => 0.00000, - 54 => 0.00000, - 55 => 0.00000, - 56 => 0.00000, - 1 => 9637.80, - 2 => 1218.60, - 3 => 0.0746000, - 4 => 616.929, - 5 => 52.8900, - 6 => 0.421000, - 7 => 604.879, - 8 => 42.5900, - 9 => 0.0466000, - 74 => 60.0000, + proto::FightPropType::FightPropBaseHp as u32 => 9000.0, + proto::FightPropType::FightPropHp as u32 => 3000.0, + proto::FightPropType::FightPropHpPercent as u32 => 0.0746000, + + proto::FightPropType::FightPropBaseAttack as u32 => 600.0, + proto::FightPropType::FightPropAttack as u32 => 50.0, + proto::FightPropType::FightPropAttackPercent as u32 => 0.40, + + proto::FightPropType::FightPropBaseDefense as u32 => 600.0, + proto::FightPropType::FightPropDefense as u32 => 40.0, + proto::FightPropType::FightPropDefensePercent as u32 => 0.04, + + proto::FightPropType::FightPropCritical as u32 => 0.99, + proto::FightPropType::FightPropAntiCritical as u32 => 0.00000, + proto::FightPropType::FightPropCriticalHurt as u32 => 0.99, + proto::FightPropType::FightPropChargeEfficiency as u32 => 1.337, + + proto::FightPropType::FightPropHealAdd as u32 => 0.00000, + proto::FightPropType::FightPropHealedAdd as u32 => 0.00000, + proto::FightPropType::FightPropElementMastery as u32 => 42.0, + + proto::FightPropType::FightPropPhysicalSubHurt as u32 => 0.00000, + proto::FightPropType::FightPropPhysicalAddHurt as u32 => 0.271828, + + proto::FightPropType::FightPropFireAddHurt as u32 => 0.00000, + proto::FightPropType::FightPropElecAddHurt as u32 => 0.00000, + proto::FightPropType::FightPropWaterAddHurt as u32 => 0.00000, + proto::FightPropType::FightPropGrassAddHurt as u32 => 0.00000, + proto::FightPropType::FightPropWindAddHurt as u32 => 0.00000, + proto::FightPropType::FightPropRockAddHurt as u32 => 0.00000, + proto::FightPropType::FightPropIceAddHurt as u32 => 0.00000, + + proto::FightPropType::FightPropFireSubHurt as u32 => 0.00000, + proto::FightPropType::FightPropElecSubHurt as u32 => 0.00000, + proto::FightPropType::FightPropWaterSubHurt as u32 => 0.00000, + proto::FightPropType::FightPropGrassSubHurt as u32 => 0.00000, + proto::FightPropType::FightPropWindSubHurt as u32 => 0.00000, + proto::FightPropType::FightPropRockSubHurt as u32 => 0.00000, + proto::FightPropType::FightPropIceSubHurt as u32 => 0.00000, + + proto::FightPropType::FightPropMaxWindEnergy as u32 => 60.0000, + + proto::FightPropType::FightPropCurWindEnergy as u32 => 60.0000, + + proto::FightPropType::FightPropCurHp as u32 => 10000.0, + + proto::FightPropType::FightPropMaxHp as u32 => 12000.0, + proto::FightPropType::FightPropCurAttack as u32 => 900.0, + proto::FightPropType::FightPropCurDefense as u32 => 700.0, + proto::FightPropType::FightPropCurSpeed as u32 => 10.00000, }; return map; @@ -601,134 +591,11 @@ impl GameWorld { } fn spoof_fetter_info(&self) -> proto::AvatarFetterInfo { + // Fetter info is used for character info and voicelines in "about" section of chara menu let mut afi = proto::AvatarFetterInfo::default(); afi.exp_level = 1; let map: HashMap = collection! { - 2115 => 1, - 2114 => 1, - 2113 => 1, - 2112 => 1, - 2111 => 1, - 2110 => 1, - 2109 => 1, - 2108 => 1, - 2107 => 1, - 2106 => 1, - 2105 => 1, - 2303 => 3, - 2104 => 1, - 2016 => 3, - 2015 => 3, - 2014 => 3, - 2013 => 3, - 2012 => 3, - 2011 => 3, - 2010 => 3, - 2009 => 3, - 2207 => 2, - 2008 => 3, - 2200 => 2, - 2001 => 3, - 2098 => 1, - 105 => 2, - 2095 => 1, - 2096 => 1, - 2201 => 2, - 2002 => 3, - 2099 => 1, - 2401 => 3, - 2202 => 2, - 2003 => 3, - 2100 => 1, - 2402 => 3, - 2203 => 1, - 2004 => 3, - 2101 => 1, - 2403 => 3, - 2204 => 1, - 2005 => 3, - 2301 => 3, - 2102 => 1, - 2205 => 1, - 2006 => 3, - 2302 => 3, - 2103 => 1, - 2206 => 1, - 2007 => 3, - 2020 => 3, - 2021 => 3, - 2022 => 3, - 2023 => 3, - 2024 => 3, - 2025 => 3, - 2038 => 3, - 2039 => 3, - 2040 => 3, - 2041 => 3, - 2032 => 3, - 2042 => 3, - 2078 => 3, - 2031 => 3, - 2090 => 1, - 2043 => 3, - 2029 => 3, - 2076 => 3, - 2077 => 3, - 2030 => 3, - 2037 => 3, - 2036 => 3, - 2035 => 3, - 2034 => 3, - 2033 => 3, - 2075 => 3, - 2028 => 3, - 2027 => 3, - 2026 => 3, - 2017 => 3, - 2018 => 3, - 2019 => 3, - 2044 => 3, - 2045 => 3, - 2046 => 3, - 2047 => 3, - 2048 => 3, - 2049 => 3, - 2050 => 3, - 2051 => 3, - 2052 => 3, - 2053 => 3, - 2054 => 3, - 2055 => 3, - 2056 => 3, - 2057 => 3, - 2058 => 3, - 2059 => 3, - 2060 => 3, - 2061 => 3, - 2062 => 3, - 2063 => 3, - 2064 => 3, - 2065 => 3, - 2066 => 3, - 2067 => 3, - 2068 => 3, - 2069 => 3, - 2070 => 3, - 2071 => 3, - 2072 => 3, - 2073 => 3, - 2074 => 3, - 2084 => 3, - 2085 => 3, - 2086 => 3, - 2087 => 3, - 2088 => 3, - 2089 => 1, - 2091 => 1, - 2092 => 1, - 2093 => 1, - 2097 => 1, }; let mut fl = vec![]; diff --git a/src/server/ipc_message.rs b/src/server/ipc_message.rs index 5c077bd..33f8db0 100644 --- a/src/server/ipc_message.rs +++ b/src/server/ipc_message.rs @@ -1,12 +1,9 @@ -use crate::proto; - -use prost; use prost::Message; pub struct IpcMessage(pub u32, pub proto::PacketId, pub Vec, pub Vec); impl IpcMessage { - pub fn new_from_proto(conv: u32, packet_id: proto::PacketId, metadata: &proto::PacketHead, data: &M) -> IpcMessage { + pub fn new_from_proto(user_id: u32, packet_id: proto::PacketId, metadata: &proto::PacketHead, data: &M) -> IpcMessage { println!("Replying with {:?}", packet_id); println!("Data: {:?}", data); @@ -19,7 +16,7 @@ impl IpcMessage { metadata.encode(&mut metabuf).unwrap(); return IpcMessage( - conv, + user_id, packet_id, metabuf, buf diff --git a/src/server/network_server.rs b/src/server/network_server.rs index 93810f2..0467fac 100644 --- a/src/server/network_server.rs +++ b/src/server/network_server.rs @@ -16,21 +16,25 @@ use crate::utils::HandshakePacket; use crate::utils::DataPacket; use crate::server::ClientConnection; use crate::server::GameServer; +use crate::server::AuthManager; + use crate::server::IpcMessage; -use crate::proto; -use crate::proto::PacketHead; -use crate::proto::GetPlayerTokenRsp; -use crate::proto::get_player_token_rsp; +use proto::PacketHead; +use proto::GetPlayerTokenRsp; +use proto::get_player_token_rsp; use prost::Message; +use packet_processor::PacketProcessor; + extern crate kcp; pub struct NetworkServer { socket: UdpSocket, clients: Arc>>, packets_to_process_tx: Option>, + packets_to_send_tx: Option>, } #[derive(Debug, Clone)] @@ -59,6 +63,7 @@ impl NetworkServer { }, clients: Arc::new(Mutex::new(HashMap::new())), packets_to_process_tx: None, + packets_to_send_tx: None, }; print!("Connection established\n"); @@ -76,12 +81,20 @@ impl NetworkServer { let (packets_to_send_tx, packets_to_send_rx) = mpsc::channel(); self.packets_to_process_tx = Some(packets_to_process_tx); + self.packets_to_send_tx = Some(packets_to_send_tx.clone()); // TODO: hack! let clients = self.clients.clone(); + let mut auth_manager = Arc::new(Mutex::new(AuthManager::new(packets_to_send_tx.clone()))); + let am = auth_manager.clone(); let packet_relaying_thread = thread::spawn(move || { loop { - let IpcMessage(conv, packet_id, metadata, data) = packets_to_send_rx.recv().unwrap(); + let IpcMessage(user_id, packet_id, metadata, data) = packets_to_send_rx.recv().unwrap(); + + let conv = match packet_id { + proto::PacketId::GetPlayerTokenRsp => user_id, // Mapping is not performed on those + _ => am.lock().unwrap().resolve_uid(user_id).unwrap_or_else(|| panic!("Unknown user ID {}!", user_id)), + }; let data_packet = DataPacket::new(packet_id.clone() as u16, metadata, data.clone()); @@ -110,7 +123,7 @@ impl NetworkServer { loop { match self.socket.recv_from(&mut buffer) { - Ok( (bytes_number, source_address) ) => self.process_udp_packet(source_address, &buffer[..bytes_number]), + Ok( (bytes_number, source_address) ) => self.process_udp_packet(source_address, &buffer[..bytes_number], &mut auth_manager), Err(e) => panic!("Failed to receive data: {}", e), } } @@ -120,16 +133,16 @@ impl NetworkServer { return Ok(0); } - fn process_udp_packet(&mut self, source_address: SocketAddr, packet_bytes: &[u8]) { - print!("Received packet! Len = {}\n", packet_bytes.len()); + fn process_udp_packet(&mut self, source_address: SocketAddr, packet_bytes: &[u8], auth_manager: &mut Arc>) { + //print!("Received packet! Len = {}\n", packet_bytes.len()); let hs_packet = HandshakePacket::new(packet_bytes); match hs_packet { Ok(hs_packet) => { - print!("Received handshake packet: {:#?}\n", hs_packet); + //print!("Received handshake packet: {:#?}\n", hs_packet); if hs_packet.is_connect() { - print!("Sending reply to CONNECT\n"); + //print!("Sending reply to CONNECT\n"); // TODO: assign conv and token! let conv = 0x96969696u32; let token = 0x42424242u32; @@ -158,13 +171,13 @@ impl NetworkServer { }; for packet in packets.iter() { - self.process_game_packet(conv, packet); + self.process_game_packet(conv, packet, auth_manager); } }, }; } - fn process_game_packet(&mut self, conv: u32, packet: &[u8]) { + fn process_game_packet(&mut self, conv: u32, packet: &[u8], auth_manager: &mut Arc>) { let data = match DataPacket::new_from_bytes(packet) { Ok(data) => data, Err(e) => panic!("Malformed data packet: {:#?}!", e), @@ -183,13 +196,46 @@ impl NetworkServer { } }; - println!("Got packet {:?}", packet_id); + let user_id = match packet_id { + proto::PacketId::GetPlayerTokenReq => { + auth_manager.lock().unwrap().process(conv, packet_id, data.metadata, data.data); + return; + }, + _ => match auth_manager.lock().unwrap().resolve_conv(conv) { + None => { + println!("Unknown user with conv {}! Skipping", conv); + return; + }, + Some(user_id) => user_id, + }, + }; + if packet_id == proto::PacketId::UnionCmdNotify { + let union = proto::UnionCmdNotify::decode(&mut Cursor::new(&data.data)).unwrap(); + for u_cmd in union.cmd_list.into_iter() { + self.send_packet_to_process(user_id, u_cmd.message_id as u16, &data.metadata, &u_cmd.body); + } + } else { + self.send_packet_to_process(user_id, data.packet_id, &data.metadata, &data.data); + } + } + + fn send_packet_to_process(&mut self, user_id: u32, packet_id: u16, metadata: &[u8], data: &[u8]) + { let sender = match &self.packets_to_process_tx { Some(sender) => sender, None => panic!("Processing queue wasn't set up!"), }; + + let packet_id: proto::PacketId = match FromPrimitive::from_u16(packet_id) { + Some(packet_id) => packet_id, + None => { + println!("Skipping unknown packet ID {}", packet_id); + return; + }, + }; - sender.send( IpcMessage(conv, packet_id, data.metadata, data.data) ).unwrap(); + println!("Got packet {:?}", packet_id); + sender.send( IpcMessage(user_id, packet_id, metadata.to_vec(), data.to_vec()) ).unwrap(); } }