mirror of
https://github.com/RustySamovar/RustySamovar.git
synced 2024-11-25 11:36:46 +00:00
Refactoring, moving out proto to separate repo, fixed login bug
This commit is contained in:
parent
39f2618916
commit
4d32da1e2e
6
.gitignore
vendored
6
.gitignore
vendored
@ -9,15 +9,9 @@ Cargo.lock
|
|||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
# *.proto files
|
|
||||||
/protobuf/
|
|
||||||
|
|
||||||
# keys
|
# keys
|
||||||
/keys/
|
/keys/
|
||||||
|
|
||||||
# Vim temp files
|
# Vim temp files
|
||||||
.*.swp
|
.*.swp
|
||||||
.*.swo
|
.*.swo
|
||||||
|
|
||||||
# Autogenerated files
|
|
||||||
/gen/
|
|
||||||
|
21
Cargo.toml
21
Cargo.toml
@ -8,11 +8,26 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
kcp = { path = "../kcp" }
|
kcp = { path = "../kcp" }
|
||||||
mhycrypt = { path = "../mhycrypt" }
|
mhycrypt = { path = "../mhycrypt" }
|
||||||
|
proto = { path = "../proto" }
|
||||||
|
packet-processor-macro = { path = "packet-processor-macro" }
|
||||||
|
packet-processor = { path = "packet-processor" }
|
||||||
|
|
||||||
prost = "0.8"
|
prost = "0.8"
|
||||||
bytes = "1.1.0"
|
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-traits = "0.2"
|
||||||
num-derive = "0.3"
|
num-derive = "0.3"
|
||||||
base64 = "0.13.0"
|
|
||||||
|
|
||||||
[build-dependencies]
|
pretty-hex = "0.2"
|
||||||
prost-build = { version = "0.8.0" }
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
openssl = { version = "0.10", features = ["vendored"] }
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
openssl = "0.10"
|
||||||
|
65
build.rs
65
build.rs
@ -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<String> = 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),
|
|
||||||
}
|
|
||||||
}
|
|
27
gen_ids.sh
27
gen_ids.sh
@ -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 "}";
|
|
7
misc/gen_key.sh
Executable file
7
misc/gen_key.sh
Executable file
@ -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
|
5
misc/grab_key.sh
Executable file
5
misc/grab_key.sh
Executable file
@ -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
|
3
misc/ssl_stuff/gen_cert.sh
Executable file
3
misc/ssl_stuff/gen_cert.sh
Executable file
@ -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'
|
41
misc/ssl_stuff/ssl.conf
Normal file
41
misc/ssl_stuff/ssl.conf
Normal file
@ -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
|
9
misc/xor.py
Executable file
9
misc/xor.py
Executable file
@ -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]))
|
14
packet-processor-macro/.gitignore
vendored
Normal file
14
packet-processor-macro/.gitignore
vendored
Normal file
@ -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
|
16
packet-processor-macro/Cargo.toml
Normal file
16
packet-processor-macro/Cargo.toml
Normal file
@ -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"
|
107
packet-processor-macro/src/lib.rs
Normal file
107
packet-processor-macro/src/lib.rs
Normal file
@ -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<proto::PacketId, fn(&mut Self, u32, &proto::PacketHead, Vec<u8>) -> ()>,))]
|
||||||
|
);
|
||||||
|
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<proc_macro2::TokenStream> = request.map(|a| a.parse().unwrap()).collect();
|
||||||
|
let notify: Vec<proc_macro2::TokenStream> = notify.map(|a| a.parse().unwrap()).collect();
|
||||||
|
let response: Vec<proc_macro2::TokenStream> = response.map(|a| a.parse().unwrap()).collect();
|
||||||
|
let req_handler: Vec<proc_macro2::TokenStream> = req_handler.map(|a| a.parse().unwrap()).collect();
|
||||||
|
let notify_handler: Vec<proc_macro2::TokenStream> = 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<proto::PacketId> {
|
||||||
|
return self.packet_callbacks.keys().cloned().collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process(&mut self, user_id: u32, packet_id: proto::PacketId, metadata: Vec<u8>, data: Vec<u8>) {
|
||||||
|
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
|
||||||
|
}
|
14
packet-processor/.gitignore
vendored
Normal file
14
packet-processor/.gitignore
vendored
Normal file
@ -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
|
9
packet-processor/Cargo.toml
Normal file
9
packet-processor/Cargo.toml
Normal file
@ -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" }
|
32
packet-processor/src/lib.rs
Normal file
32
packet-processor/src/lib.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
pub trait PacketProcessor {
|
||||||
|
fn register(&mut self);
|
||||||
|
fn supported(&self) -> Vec<proto::PacketId>;
|
||||||
|
fn process(&mut self, user_id: u32, packet_id: proto::PacketId, metadata: Vec<u8>, data: Vec<u8>);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<u8>| {
|
||||||
|
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<u8>| {
|
||||||
|
let notify = proto::$req::decode(&mut std::io::Cursor::new(data)).unwrap();
|
||||||
|
println!("Received NOTIFY {:?}", notify);
|
||||||
|
|
||||||
|
slef.$handler(user_id, &metadata, ¬ify);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
14
src/main.rs
14
src/main.rs
@ -1,3 +1,5 @@
|
|||||||
|
extern crate pretty_env_logger;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate num_derive;
|
extern crate num_derive;
|
||||||
|
|
||||||
@ -6,19 +8,15 @@ use std::thread;
|
|||||||
mod server;
|
mod server;
|
||||||
mod utils;
|
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::NetworkServer;
|
||||||
use server::DispatchServer;
|
use server::DispatchServer;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
pretty_env_logger::init();
|
||||||
|
|
||||||
thread::spawn(|| {
|
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();
|
ds.run();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,20 +1,67 @@
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use prost::Message;
|
||||||
|
|
||||||
use crate::server::IpcMessage;
|
use crate::server::IpcMessage;
|
||||||
|
|
||||||
|
use packet_processor_macro::*;
|
||||||
|
#[macro_use]
|
||||||
|
use packet_processor::*;
|
||||||
|
|
||||||
|
#[packet_processor(GetPlayerTokenReq)]
|
||||||
pub struct AuthManager {
|
pub struct AuthManager {
|
||||||
|
conv_to_user: HashMap<u32, u32>,
|
||||||
|
user_to_conv: HashMap<u32, u32>,
|
||||||
packets_to_send_tx: mpsc::Sender<IpcMessage>,
|
packets_to_send_tx: mpsc::Sender<IpcMessage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthManager {
|
impl AuthManager {
|
||||||
|
const SPOOFED_PLAYER_UID: u32 = 1337;
|
||||||
|
|
||||||
pub fn new(packets_to_send_tx: mpsc::Sender<IpcMessage>) -> AuthManager {
|
pub fn new(packets_to_send_tx: mpsc::Sender<IpcMessage>) -> 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,
|
packets_to_send_tx: packets_to_send_tx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
am.register();
|
||||||
|
|
||||||
return am;
|
return am;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_packet(&mut self, conv: u32, packet_id: u16, metadata: Vec<u8>, data: Vec<u8>) {
|
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<u32> {
|
||||||
|
match self.conv_to_user.get(&conv) {
|
||||||
|
Some(uid) => return Some(*uid),
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_uid(&self, uid: u32) -> Option<u32> {
|
||||||
|
match self.user_to_conv.get(&uid) {
|
||||||
|
Some(conv) => return Some(*conv),
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,12 @@ impl ClientConnection {
|
|||||||
match self.ikcp.recv(&mut buf) {
|
match self.ikcp.recv(&mut buf) {
|
||||||
Err(_) => break,
|
Err(_) => break,
|
||||||
Ok(size) => {
|
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);
|
mhycrypt::mhy_xor(&mut buf[..size], &self.key);
|
||||||
let data = buf[..size].to_owned();
|
let data = buf[..size].to_owned();
|
||||||
packets.push(data);
|
packets.push(data);
|
||||||
|
@ -3,104 +3,126 @@ use std::net::TcpListener;
|
|||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
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;
|
use prost::Message;
|
||||||
extern crate base64;
|
|
||||||
|
|
||||||
use crate::proto;
|
|
||||||
use mhycrypt;
|
use mhycrypt;
|
||||||
|
|
||||||
type RequestCallback = fn(slef: &DispatchServer, args: Vec<(&str, &str)>) -> (String, String);
|
#[derive(Clone)]
|
||||||
|
|
||||||
pub struct DispatchServer {
|
pub struct DispatchServer {
|
||||||
listener: TcpListener,
|
}
|
||||||
callbacks: HashMap<String, RequestCallback>,
|
|
||||||
ip: String,
|
#[derive(Deserialize,Debug)]
|
||||||
port: u16,
|
struct ClientInfo {
|
||||||
|
version: String,
|
||||||
|
lang: i32,
|
||||||
|
platform: i32,
|
||||||
|
binary: i32,
|
||||||
|
time: i32,
|
||||||
|
channel_id: i32,
|
||||||
|
sub_channel_id: i32,
|
||||||
|
account_type: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize,Debug)]
|
||||||
|
struct TokenToVerify
|
||||||
|
{
|
||||||
|
uid: String,
|
||||||
|
token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize,Debug)]
|
||||||
|
struct ActionToCheck
|
||||||
|
{
|
||||||
|
action_type: String,
|
||||||
|
api_name: String,
|
||||||
|
username: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 {
|
impl DispatchServer {
|
||||||
pub fn new(ip: &str, port: u16) -> DispatchServer {
|
pub fn new() -> DispatchServer {
|
||||||
let mut callbacks = HashMap::new();
|
let ds = DispatchServer { };
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return ds;
|
return ds;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&self) {
|
pub fn run(self) {
|
||||||
for stream in self.listener.incoming() {
|
let mut sys = System::new("http-server");
|
||||||
let stream = stream.unwrap();
|
let slef = Arc::new(self);
|
||||||
|
executor::block_on(slef.run_internal());
|
||||||
self.handle_connection(stream);
|
System::current().stop();
|
||||||
}
|
println!("Finished!");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_connection(&self, mut stream: TcpStream) {
|
async fn run_internal(self: &Arc<Self>) {
|
||||||
let mut buffer = [0; 1024];
|
//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::<Vec<_>>();
|
http_server.stop(true).await;
|
||||||
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::<Vec<_>>();
|
|
||||||
let uri = parts[0];
|
|
||||||
let params = if parts.len() > 1 { parts[1].split('&').collect::<Vec<_>>() } else { Vec::new() };
|
|
||||||
let params = params.into_iter().map(|x| x.split('=').collect::<Vec<_>>()).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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_region_list(&self, args: Vec<(&str, &str)>) -> (String, String) {
|
async fn query_region_list(c: web::Query<ClientInfo>) -> String {
|
||||||
println!("Args: {:?}", args);
|
println!("Client: {:?}", c);
|
||||||
|
|
||||||
let keys = self.load_keys("master");
|
let keys = DispatchServer::load_keys("master");
|
||||||
|
|
||||||
let mut region_info = proto::RegionSimpleInfo::default();
|
let mut region_info = proto::RegionSimpleInfo::default();
|
||||||
region_info.name = "private_server".into();
|
region_info.name = "private_server".into();
|
||||||
region_info.title = "Private Server".into();
|
region_info.title = "Private Server".into();
|
||||||
region_info.r#type = "DEV_PUBLIC".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();
|
let mut region_list = proto::QueryRegionListHttpRsp::default();
|
||||||
region_list.region_list = vec![region_info];
|
region_list.region_list = vec![region_info];
|
||||||
@ -120,13 +142,13 @@ impl DispatchServer {
|
|||||||
|
|
||||||
region_list.encode(&mut region_list_buf).unwrap();
|
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) {
|
async fn query_cur_region(c: web::Query<ClientInfo>) -> String {
|
||||||
println!("Args: {:?}", args);
|
println!("Client: {:?}", c);
|
||||||
|
|
||||||
let keys = self.load_keys("master");
|
let keys = DispatchServer::load_keys("master");
|
||||||
|
|
||||||
let mut region_info = proto::RegionInfo::default();
|
let mut region_info = proto::RegionInfo::default();
|
||||||
region_info.gateserver_ip = "127.0.0.1".to_string();
|
region_info.gateserver_ip = "127.0.0.1".to_string();
|
||||||
@ -138,7 +160,7 @@ impl DispatchServer {
|
|||||||
region_config.client_secret_key = keys.0.clone();
|
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\" }}",
|
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();
|
let mut custom_config = json_config.as_bytes().to_owned();
|
||||||
|
|
||||||
@ -150,38 +172,57 @@ impl DispatchServer {
|
|||||||
|
|
||||||
region_config.encode(&mut region_conf_buf).unwrap();
|
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<ActionToCheck>) -> String {
|
||||||
|
println!("Action: {:?}", a);
|
||||||
|
|
||||||
let email = "ceo@hoyolab.com";
|
let email = "ceo@hoyolab.com";
|
||||||
let name = "Ceo";
|
let name = "Ceo";
|
||||||
let token = "Fake-token-hahaha";
|
let token = "Fake-token-hahaha";
|
||||||
let uid = 0x1234;
|
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) {
|
async fn shield_login(l: web::Json<LoginData>) -> String {
|
||||||
let payload = self.verify_token_v2();
|
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 email = "ceo@hoyolab.com";
|
||||||
let name = "Ceo";
|
let name = "Ceo";
|
||||||
let token = "Fake-token-hahaha";
|
let token = "Fake-token-hahaha";
|
||||||
let uid = 0x1234;
|
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<u8>, Vec<u8>) {
|
async fn granter_login(g: web::Json<GranterData>) -> String {
|
||||||
|
println!("Granter: {:?}", g);
|
||||||
|
|
||||||
|
let payload = DispatchServer::verify_token_v2();
|
||||||
|
|
||||||
|
return DispatchServer::make_answer(0, &payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn shield_verify(t: web::Json<TokenToVerify>) -> 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<u8>, Vec<u8>) {
|
||||||
// Key
|
// Key
|
||||||
let filename = format!("./{}/{}.key", "keys", name);
|
let filename = format!("./{}/{}.key", "keys", name);
|
||||||
let mut f = fs::File::open(&filename).expect(&format!("File '{}' not found", filename));
|
let mut f = fs::File::open(&filename).expect(&format!("File '{}' not found", filename));
|
||||||
@ -197,12 +238,16 @@ impl DispatchServer {
|
|||||||
return (ec2b, key);
|
return (ec2b, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_token_v2(&self) -> String {
|
fn verify_token_v2() -> String {
|
||||||
let account_type = 1;
|
let account_type = 1;
|
||||||
let combo_id = 0x4321;
|
let combo_id = 0x4321;
|
||||||
let combo_token = "Fake-token-hehehe";
|
|
||||||
let open_id = 0x1234;
|
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!("{{
|
return format!("{{
|
||||||
\"account_type\": \"{}\",
|
\"account_type\": \"{}\",
|
||||||
\"combo_id\": \"{}\",
|
\"combo_id\": \"{}\",
|
||||||
@ -213,7 +258,7 @@ impl DispatchServer {
|
|||||||
}}", account_type, combo_id, combo_token, open_id);
|
}}", 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!("{{
|
let payload = format!("{{
|
||||||
\"account\": {{
|
\"account\": {{
|
||||||
\"apple_name\": \"\",
|
\"apple_name\": \"\",
|
||||||
@ -242,7 +287,7 @@ impl DispatchServer {
|
|||||||
return payload.into();
|
return payload.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_answer(&self, code: i32, data: &str) -> String {
|
fn make_answer(code: i32, data: &str) -> String {
|
||||||
let message = match code {
|
let message = match code {
|
||||||
0 => "OK",
|
0 => "OK",
|
||||||
-1 => "not matched",
|
-1 => "not matched",
|
||||||
|
@ -4,6 +4,7 @@ use std::collections::HashMap;
|
|||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
|
|
||||||
use crate::server::GameWorld;
|
use crate::server::GameWorld;
|
||||||
|
use packet_processor::PacketProcessor;
|
||||||
use crate::server::IpcMessage;
|
use crate::server::IpcMessage;
|
||||||
|
|
||||||
pub struct GameServer {
|
pub struct GameServer {
|
||||||
@ -27,15 +28,15 @@ impl GameServer {
|
|||||||
let world_processor = thread::spawn(move || {
|
let world_processor = thread::spawn(move || {
|
||||||
println!("Starting world processor");
|
println!("Starting world processor");
|
||||||
// TODO: Load worlds!
|
// TODO: Load worlds!
|
||||||
loop {
|
//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!
|
// TODO: each user_id will have a distinct world!
|
||||||
let world = match self.worlds.entry(conv) {
|
let world = match self.worlds.entry(user_id) {
|
||||||
Occupied(world) => world.into_mut(),
|
Occupied(world) => world.into_mut(),
|
||||||
Vacant(entry) => {
|
Vacant(entry) => {
|
||||||
let mut world = GameWorld::new(self.packets_to_send_tx.clone());
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,9 @@
|
|||||||
use crate::proto;
|
|
||||||
|
|
||||||
use prost;
|
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
|
|
||||||
pub struct IpcMessage(pub u32, pub proto::PacketId, pub Vec<u8>, pub Vec<u8>);
|
pub struct IpcMessage(pub u32, pub proto::PacketId, pub Vec<u8>, pub Vec<u8>);
|
||||||
|
|
||||||
impl IpcMessage {
|
impl IpcMessage {
|
||||||
pub fn new_from_proto<M: prost::Message>(conv: u32, packet_id: proto::PacketId, metadata: &proto::PacketHead, data: &M) -> IpcMessage {
|
pub fn new_from_proto<M: prost::Message>(user_id: u32, packet_id: proto::PacketId, metadata: &proto::PacketHead, data: &M) -> IpcMessage {
|
||||||
println!("Replying with {:?}", packet_id);
|
println!("Replying with {:?}", packet_id);
|
||||||
println!("Data: {:?}", data);
|
println!("Data: {:?}", data);
|
||||||
|
|
||||||
@ -19,7 +16,7 @@ impl IpcMessage {
|
|||||||
metadata.encode(&mut metabuf).unwrap();
|
metadata.encode(&mut metabuf).unwrap();
|
||||||
|
|
||||||
return IpcMessage(
|
return IpcMessage(
|
||||||
conv,
|
user_id,
|
||||||
packet_id,
|
packet_id,
|
||||||
metabuf,
|
metabuf,
|
||||||
buf
|
buf
|
||||||
|
@ -16,21 +16,25 @@ use crate::utils::HandshakePacket;
|
|||||||
use crate::utils::DataPacket;
|
use crate::utils::DataPacket;
|
||||||
use crate::server::ClientConnection;
|
use crate::server::ClientConnection;
|
||||||
use crate::server::GameServer;
|
use crate::server::GameServer;
|
||||||
|
use crate::server::AuthManager;
|
||||||
|
|
||||||
use crate::server::IpcMessage;
|
use crate::server::IpcMessage;
|
||||||
|
|
||||||
use crate::proto;
|
use proto::PacketHead;
|
||||||
use crate::proto::PacketHead;
|
use proto::GetPlayerTokenRsp;
|
||||||
use crate::proto::GetPlayerTokenRsp;
|
use proto::get_player_token_rsp;
|
||||||
use crate::proto::get_player_token_rsp;
|
|
||||||
|
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
|
|
||||||
|
use packet_processor::PacketProcessor;
|
||||||
|
|
||||||
extern crate kcp;
|
extern crate kcp;
|
||||||
|
|
||||||
pub struct NetworkServer {
|
pub struct NetworkServer {
|
||||||
socket: UdpSocket,
|
socket: UdpSocket,
|
||||||
clients: Arc<Mutex<HashMap<u32,ClientConnection>>>,
|
clients: Arc<Mutex<HashMap<u32,ClientConnection>>>,
|
||||||
packets_to_process_tx: Option<mpsc::Sender<IpcMessage>>,
|
packets_to_process_tx: Option<mpsc::Sender<IpcMessage>>,
|
||||||
|
packets_to_send_tx: Option<mpsc::Sender<IpcMessage>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -59,6 +63,7 @@ impl NetworkServer {
|
|||||||
},
|
},
|
||||||
clients: Arc::new(Mutex::new(HashMap::new())),
|
clients: Arc::new(Mutex::new(HashMap::new())),
|
||||||
packets_to_process_tx: None,
|
packets_to_process_tx: None,
|
||||||
|
packets_to_send_tx: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
print!("Connection established\n");
|
print!("Connection established\n");
|
||||||
@ -76,12 +81,20 @@ impl NetworkServer {
|
|||||||
let (packets_to_send_tx, packets_to_send_rx) = mpsc::channel();
|
let (packets_to_send_tx, packets_to_send_rx) = mpsc::channel();
|
||||||
|
|
||||||
self.packets_to_process_tx = Some(packets_to_process_tx);
|
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 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 || {
|
let packet_relaying_thread = thread::spawn(move || {
|
||||||
loop {
|
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());
|
let data_packet = DataPacket::new(packet_id.clone() as u16, metadata, data.clone());
|
||||||
|
|
||||||
@ -110,7 +123,7 @@ impl NetworkServer {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
match self.socket.recv_from(&mut buffer) {
|
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),
|
Err(e) => panic!("Failed to receive data: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,16 +133,16 @@ impl NetworkServer {
|
|||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_udp_packet(&mut self, source_address: SocketAddr, packet_bytes: &[u8]) {
|
fn process_udp_packet(&mut self, source_address: SocketAddr, packet_bytes: &[u8], auth_manager: &mut Arc<Mutex<AuthManager>>) {
|
||||||
print!("Received packet! Len = {}\n", packet_bytes.len());
|
//print!("Received packet! Len = {}\n", packet_bytes.len());
|
||||||
|
|
||||||
let hs_packet = HandshakePacket::new(packet_bytes);
|
let hs_packet = HandshakePacket::new(packet_bytes);
|
||||||
|
|
||||||
match hs_packet {
|
match hs_packet {
|
||||||
Ok(hs_packet) => {
|
Ok(hs_packet) => {
|
||||||
print!("Received handshake packet: {:#?}\n", hs_packet);
|
//print!("Received handshake packet: {:#?}\n", hs_packet);
|
||||||
if hs_packet.is_connect() {
|
if hs_packet.is_connect() {
|
||||||
print!("Sending reply to CONNECT\n");
|
//print!("Sending reply to CONNECT\n");
|
||||||
// TODO: assign conv and token!
|
// TODO: assign conv and token!
|
||||||
let conv = 0x96969696u32;
|
let conv = 0x96969696u32;
|
||||||
let token = 0x42424242u32;
|
let token = 0x42424242u32;
|
||||||
@ -158,13 +171,13 @@ impl NetworkServer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for packet in packets.iter() {
|
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<Mutex<AuthManager>>) {
|
||||||
let data = match DataPacket::new_from_bytes(packet) {
|
let data = match DataPacket::new_from_bytes(packet) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => panic!("Malformed data packet: {:#?}!", e),
|
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 {
|
let sender = match &self.packets_to_process_tx {
|
||||||
Some(sender) => sender,
|
Some(sender) => sender,
|
||||||
None => panic!("Processing queue wasn't set up!"),
|
None => panic!("Processing queue wasn't set up!"),
|
||||||
};
|
};
|
||||||
|
|
||||||
sender.send( IpcMessage(conv, packet_id, data.metadata, data.data) ).unwrap();
|
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;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Got packet {:?}", packet_id);
|
||||||
|
sender.send( IpcMessage(user_id, packet_id, metadata.to_vec(), data.to_vec()) ).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user