diff --git a/Cargo.lock b/Cargo.lock
index 9e53221..e7ed0ad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2614,6 +2614,34 @@ dependencies = [
"sea-orm",
]
+[[package]]
+name = "oj_rc_lobby"
+version = "0.2.1"
+dependencies = [
+ "clap",
+ "env_logger",
+ "log",
+ "oj_polariton_auth",
+ "polariton",
+ "polariton_server",
+ "tokio",
+]
+
+[[package]]
+name = "oj_rc_lobby_room"
+version = "0.2.1"
+dependencies = [
+ "async-trait",
+ "clap",
+ "env_logger",
+ "log",
+ "oj_polariton_auth",
+ "oj_rc_core",
+ "polariton",
+ "polariton_server",
+ "tokio",
+]
+
[[package]]
name = "oj_rc_microtransactions"
version = "0.2.1"
diff --git a/Cargo.toml b/Cargo.toml
index 9611ab6..9efb354 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,7 @@ members = [
"rc_social", "rc_social_room",
"rc_chat", "rc_chat_room",
"rc_singleplayer", "rc_singleplayer_room",
+ "rc_lobby", "rc_lobby_room",
"rc_core", "rc_database", "rc_factory",
]
diff --git a/assets/robocraft/servenvmulti.config b/assets/robocraft/servenvmulti.config
index ba7a0d9..44e296d 100644
--- a/assets/robocraft/servenvmulti.config
+++ b/assets/robocraft/servenvmulti.config
@@ -11,6 +11,8 @@
127.0.0.1:4537
127.0.0.1:4538
127.0.0.1:4539
+ 127.0.0.1:4540
+ 127.0.0.1:4541
http://127.0.0.1:8001/
http://127.0.0.1:8010/live/data.json
@@ -23,6 +25,8 @@
:4537
sp.services.rc.ngram.ca:4538
:4539
+ !!!TODO!!!:4540
+ :4541
https://live-auth.rc.ngram.ca/
https://static.rc.ngram.ca/live/data.json
https://pay.rc.ngram.ca/
diff --git a/assets/templates/rc_register.html.hbs b/assets/templates/rc_register.html.hbs
index b254568..e259469 100644
--- a/assets/templates/rc_register.html.hbs
+++ b/assets/templates/rc_register.html.hbs
@@ -1,5 +1,7 @@
-
+
+
+
Registration - Openjam
@@ -87,8 +89,9 @@
border-color: red;
}
input:required:invalid.form-input-box::after {
- content: "*";
+ content: "*required";
color: red;
+ font-size: 12px;
}
input {
width: 100%;
@@ -97,6 +100,11 @@
background-color: lightgrey;
font-size: 20px;
}
+ .input-required-hint::after {
+ color: red;
+ /*content: "*required";*/
+ font-size: 12px;
+ }
.form-disclaimer {
margin: 0 auto;
width: 95%;
@@ -138,6 +146,7 @@
+
@@ -145,6 +154,7 @@
+
@@ -152,6 +162,7 @@
+
diff --git a/rc_services_room/src/data/lobby.rs b/rc_core/src/data/lobby.rs
similarity index 100%
rename from rc_services_room/src/data/lobby.rs
rename to rc_core/src/data/lobby.rs
diff --git a/rc_core/src/data/mod.rs b/rc_core/src/data/mod.rs
index 4e83ee5..ce870c3 100644
--- a/rc_core/src/data/mod.rs
+++ b/rc_core/src/data/mod.rs
@@ -14,6 +14,7 @@ pub mod crf;
pub mod channel;
pub mod sanction;
pub mod robot_data;
+pub mod lobby;
pub mod error_codes;
diff --git a/rc_core/src/persist/settings.rs b/rc_core/src/persist/settings.rs
index 90185f9..8a90660 100644
--- a/rc_core/src/persist/settings.rs
+++ b/rc_core/src/persist/settings.rs
@@ -22,7 +22,7 @@ fn default_gameplay_settings() -> super::GameplaySettings {
shield_hps: 2_000,
request_review_level: 10_000,
critical_ratio: 5.0,
- cross_promo_image: "https://git.ngram.ca/assets/img/logo.png".to_owned(),
+ cross_promo_image: "https://git.ngram.ca/OpenJam/servers/raw/branch/main/assets/robocraft/favicon.jpg".to_owned(),
cross_promo_link: "https://git.ngram.ca/OpenJam/servers".to_owned(),
}
}
diff --git a/rc_lobby/Cargo.toml b/rc_lobby/Cargo.toml
new file mode 100644
index 0000000..522556f
--- /dev/null
+++ b/rc_lobby/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "oj_rc_lobby"
+version.workspace = true
+edition.workspace = true
+readme.workspace = true
+license.workspace = true
+repository.workspace = true
+authors.workspace = true
+
+[dependencies]
+log.workspace = true
+env_logger.workspace = true
+tokio = { version = "1.43", features = [ "net", "macros", "rt-multi-thread", "io-util" ] }
+clap.workspace = true
+polariton.workspace = true
+oj_polariton_auth = { version = "*", path = "../polariton_auth" }
+polariton_server.workspace = true
diff --git a/rc_lobby/run_debug.sh b/rc_lobby/run_debug.sh
new file mode 100755
index 0000000..cc15e3f
--- /dev/null
+++ b/rc_lobby/run_debug.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+RUST_BACKTRACE=1 RUST_LOG=debug cargo run -- -1
diff --git a/rc_lobby/src/cli.rs b/rc_lobby/src/cli.rs
new file mode 100644
index 0000000..0fad86d
--- /dev/null
+++ b/rc_lobby/src/cli.rs
@@ -0,0 +1,31 @@
+use clap::Parser;
+
+#[derive(Parser, Debug)]
+#[command(version, about, long_about = None)]
+pub struct CliArgs {
+ /// TCP port on which to accept connections
+ #[arg(short, long, default_value_t = 4540)]
+ pub port: u16,
+
+ /// IP Address on which to accept connections
+ #[arg(long, default_value_t = {"127.0.0.1".to_string()})]
+ pub ip: String,
+
+ /// Domain and port of the game server to send new connections
+ #[arg(long, default_value_t = {"127.0.0.1:4541".to_string()})]
+ pub redirect: String,
+
+ /// Name of game server to send new connections
+ #[arg(long, default_value_t = {"ngram_is_ngnius".to_string()})]
+ pub room_name: String,
+
+ /// Handle one connection and then exit
+ #[arg(short = '1', long)]
+ pub once: bool,
+}
+
+impl CliArgs {
+ pub fn get() -> Self {
+ Self::parse()
+ }
+}
diff --git a/rc_lobby/src/main.rs b/rc_lobby/src/main.rs
new file mode 100644
index 0000000..d8fc8a7
--- /dev/null
+++ b/rc_lobby/src/main.rs
@@ -0,0 +1,263 @@
+#![forbid(unsafe_code)]
+mod cli;
+
+use oj_polariton_auth::Handshake;
+use tokio::net;
+
+use polariton::packet::{Data, Message, Packet, StandardMessage};
+use polariton::operation::{OperationResponse, Typed};
+
+#[tokio::main]
+async fn main() -> std::io::Result<()> {
+ env_logger::init();
+ let args = cli::CliArgs::get();
+ log::debug!("Got cli args {:?}", args);
+
+ let ip_addr: std::net::IpAddr = args.ip.parse().expect("Invalid IP address");
+
+ // memory leak, but only once (so not a big deal)
+ let redirect_static = Box::leak(Box::new(args.redirect.clone()));
+ let room_name_static = Box::leak(Box::new(args.room_name.clone()));
+
+ let listener = net::TcpListener::bind(std::net::SocketAddr::new(ip_addr, args.port)).await?;
+
+ if args.once {
+ log::warn!("Handling first connection and then exiting");
+ let (socket, address) = listener.accept().await?;
+ process_socket(socket, address, redirect_static, room_name_static).await;
+ Ok(())
+ } else {
+ loop {
+ let (socket, address) = listener.accept().await?;
+ tokio::spawn(process_socket(socket, address, redirect_static, room_name_static));
+ }
+ }
+}
+
+async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, redirect_url: &str, lobby_name: &str) {
+ log::debug!("Accepting connection from address {}", address);
+
+ let enc = match do_connect_handshake(&mut socket, lobby_name, redirect_url).await {
+ Some(x) => x,
+ None => {
+ log::error!("Failed to do connect handshake with {}", address);
+ return;
+ }
+ };
+ let ctx = polariton::packet::SerdesContext::from_boxed(Default::default(), enc);
+ while let Ok(packet) = polariton_server::utils::receive_packet_async(&mut socket, &ctx).await {
+ match packet {
+ Packet::Ping(ping) => {
+ polariton_server::utils::handle_ping_async(ping, &mut socket, &ctx).await.unwrap_or_default();
+ },
+ Packet::Packet(packet) => log::warn!("Not handling packet {:?}", packet),
+ }
+ }
+ log::debug!("Goodbye connection from address {}", address);
+}
+
+const APP_ID: &str = "LobbyServer";
+
+struct AuthImpl;
+
+const TOKEN_KEY: u8 = 216; // token;refresh_token
+//const UNKNOWN_BYTE_KEY: u8 = 217;
+const SERVICE_KEY: u8 = 224;
+const USERNAME_KEY: u8 = 225;
+
+//const CCU_KEY: u8 = 245;
+
+#[derive(Debug)]
+enum AuthError {
+ WrongService { expected: String, actual: String },
+ MissingService,
+ MissingToken,
+ MissingUsername,
+}
+
+impl AuthError {
+ fn log_err(&self) {
+ match self {
+ Self::WrongService { expected, actual } => log::error!("(auth fail) Got unexpected service {}, expected {}", actual, expected),
+ Self::MissingService => log::error!("(auth fail) No service name param ({}) received", SERVICE_KEY),
+ Self::MissingToken => log::error!("(auth fail) No token param ({}) received", TOKEN_KEY),
+ Self::MissingUsername => log::error!("(auth fail) No username param ({}) received", USERNAME_KEY),
+ }
+ }
+}
+
+impl oj_polariton_auth::AuthProvider
for AuthImpl {
+ fn validate(&mut self, params: &std::collections::HashMap) -> Result, AuthError> {
+ if let Some(Typed::Str(token)) = params.get(&TOKEN_KEY) {
+ if let Some(Typed::Str(service)) = params.get(&SERVICE_KEY) {
+ if let Some(Typed::Str(user)) = params.get(&USERNAME_KEY) {
+ if service.string == APP_ID {
+ let params_resp = std::collections::HashMap::::new();
+ //params_resp.insert(CCU_KEY, Typed::Byte(0));
+ log::debug!("Auth success for {} (token: {})", user.string, token.string);
+ Ok(params_resp)
+ } else { Err(AuthError::WrongService { expected: APP_ID.to_owned(), actual: service.string.to_owned() }) }
+ } else { Err(AuthError::MissingUsername) }
+ } else { Err(AuthError::MissingService) }
+ } else { Err(AuthError::MissingToken) }
+ }
+}
+
+async fn do_connect_handshake(
+ socket: &mut net::TcpStream,
+ game_server_name: &str,
+ game_server_url: &str,
+) -> Option> {
+ let handshake = Handshake::new(APP_ID);
+ // connect
+ log::debug!("(connect) Handling first packet");
+ let packet1 = match polariton_server::utils::receive_packet_async(socket, &Default::default()).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read connect packet: {}", e);
+ return None;
+ }
+ };
+ let (handshake, to_send) = match handshake.connect(&packet1) {
+ Ok(x) => (x.handshake, x.extra),
+ Err(e) => {
+ log::error!("Failed to handle connect handshake: {:?}", e.extra);
+ return None;
+ }
+ };
+ match polariton_server::utils::send_packet_async(&to_send, socket, &Default::default()).await {
+ Ok(_) => {},
+ Err(e) => {
+ log::error!("Failed to send connect ack packet: {}", e);
+ return None;
+ }
+ }
+ // encrypt
+ log::debug!("(connect) Handling second packet");
+ let mut packet2 = match polariton_server::utils::receive_packet_async(socket, &Default::default()).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) public key packet: {}", e);
+ return None;
+ }
+ };
+ while let Packet::Ping(ping) = packet2 {
+ polariton_server::utils::handle_ping_async(ping, socket, &Default::default()).await.unwrap_or_default();
+ packet2 = match polariton_server::utils::receive_packet_async(socket, &Default::default()).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) public key packet: {}", e);
+ return None;
+ }
+ };
+ }
+ let (handshake, to_send, crypto) = match handshake.encrypt(&packet2) {
+ Ok(x) => (x.handshake, x.extra.0, x.extra.1),
+ Err(e) => {
+ log::error!("Failed to handle encryption handshake: {:?}", e.extra);
+ return None;
+ }
+ };
+ match polariton_server::utils::send_packet_async(&to_send, socket, &Default::default()).await {
+ Ok(_) => {},
+ Err(e) => {
+ log::error!("Failed to send encryption ack packet: {}", e);
+ return None;
+ }
+ }
+ // pre-auth
+ let handshake = handshake.with_auth(AuthImpl);
+ let op_ctx = polariton::serdes::SerdesContext::default();
+ let ctx = polariton::packet::SerdesContext::new(op_ctx, crypto);
+ // authenticate
+ log::debug!("(connect) Handling third packet");
+ let mut packet3 = match polariton_server::utils::receive_packet_async(socket, &ctx).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) auth packet: {}", e);
+ return None;
+ }
+ };
+ while let Packet::Ping(ping) = packet3 {
+ polariton_server::utils::handle_ping_async(ping, socket, &Default::default()).await.unwrap_or_default();
+ packet3 = match polariton_server::utils::receive_packet_async(socket, &ctx).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) auth packet: {}", e);
+ return None;
+ }
+ };
+ }
+ let to_send = match handshake.authenticate(&packet3, &ctx) {
+ Ok(x) => x,
+ Err(h) => match h.extra {
+ oj_polariton_auth::AuthError::Validation(e) => {
+ e.log_err();
+ return None;
+ },
+ e => {
+ log::error!("Failed to handle auth handshake: {:?}", e);
+ return None;
+ },
+ },
+ };
+ match polariton_server::utils::send_packet_async(&to_send, socket, &ctx).await {
+ Ok(_) => {},
+ Err(e) => {
+ log::error!("Failed to send auth ack packet: {}", e);
+ return None;
+ }
+ }
+
+ // redirect to lobby
+ log::debug!("(connect) Handling fourth packet");
+ let mut packet_j = match polariton_server::utils::receive_packet_async(socket, &ctx).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) join packet: {}", e);
+ return None;
+ }
+ };
+ while let Packet::Ping(ping) = packet_j {
+ polariton_server::utils::handle_ping_async(ping, socket, &ctx).await.unwrap_or_default();
+ packet_j = match polariton_server::utils::receive_packet_async(socket, &ctx).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) join packet: {}", e);
+ return None;
+ }
+ };
+ }
+ log::debug!("(connect) Got fourth packet {:?}", packet_j);
+ if let Packet::Packet(msg) = &packet_j {
+ if let Message::Standard(st) = &msg.message {
+ if let Data::OpReq(req) = &st.data {
+ if req.code == 226 { // join lobby
+ log::debug!("Max players from lobby join request: {:?}", req.params.to_owned().to_dict().get(&255));
+ let mut params = std::collections::HashMap::::new();
+ params.insert(230 /* game server address */, Typed::Str(game_server_url.into()));
+ params.insert(255 /* room name */, Typed::Str(game_server_name.into()));
+ let resp = Packet::from_message(
+ Message::Standard(
+ StandardMessage { flags: 0,
+ data: Data::OpResp(OperationResponse {
+ code: req.code,
+ return_code: 0,
+ message: Typed::Null,
+ params: params.into(),
+ }),
+ }.encrypt(true)), 0, true, &ctx).unwrap();
+ match polariton_server::utils::send_packet_async(&resp, socket, &ctx).await {
+ Ok(_) => {},
+ Err(e) => {
+ log::error!("Failed to send lobby ack packet: {}", e);
+ return None;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Some(ctx.into_crypto())
+}
diff --git a/rc_lobby_room/Cargo.toml b/rc_lobby_room/Cargo.toml
new file mode 100644
index 0000000..f899496
--- /dev/null
+++ b/rc_lobby_room/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "oj_rc_lobby_room"
+version.workspace = true
+edition.workspace = true
+readme.workspace = true
+license.workspace = true
+repository.workspace = true
+authors.workspace = true
+
+[dependencies]
+log.workspace = true
+env_logger.workspace = true
+tokio = { version = "1.43", features = [ "net", "macros", "rt-multi-thread", "io-util", "time"] }
+clap.workspace = true
+polariton.workspace = true
+oj_polariton_auth = { version = "*", path = "../polariton_auth" }
+polariton_server.workspace = true
+oj_rc_core = { version = "*", path = "../rc_core" }
+async-trait.workspace = true
diff --git a/rc_lobby_room/run_debug.sh b/rc_lobby_room/run_debug.sh
new file mode 100755
index 0000000..cc15e3f
--- /dev/null
+++ b/rc_lobby_room/run_debug.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+RUST_BACKTRACE=1 RUST_LOG=debug cargo run -- -1
diff --git a/rc_lobby_room/src/cli.rs b/rc_lobby_room/src/cli.rs
new file mode 100644
index 0000000..a269e71
--- /dev/null
+++ b/rc_lobby_room/src/cli.rs
@@ -0,0 +1,31 @@
+use clap::Parser;
+
+#[derive(Parser, Debug)]
+#[command(version, about, long_about = None)]
+pub struct CliArgs {
+ /// TCP port on which to accept connections
+ #[arg(short, long, default_value_t = 4541)]
+ pub port: u16,
+
+ /// IP Address on which to accept connections
+ #[arg(long, default_value_t = {"127.0.0.1".to_string()})]
+ pub ip: String,
+
+ /// Assets root
+ #[arg(long, default_value_t = {"../assets/robocraft".to_string()})]
+ pub assets: String,
+
+ /// User data root
+ #[arg(long, default_value_t = {"../data/robocraft".to_string()})]
+ pub data: String,
+
+ /// Handle one connection and then exit
+ #[arg(short = '1', long)]
+ pub once: bool,
+}
+
+impl CliArgs {
+ pub fn get() -> Self {
+ Self::parse()
+ }
+}
diff --git a/rc_lobby_room/src/data/mod.rs b/rc_lobby_room/src/data/mod.rs
new file mode 100644
index 0000000..e69de29
diff --git a/rc_lobby_room/src/main.rs b/rc_lobby_room/src/main.rs
new file mode 100644
index 0000000..b6f4a47
--- /dev/null
+++ b/rc_lobby_room/src/main.rs
@@ -0,0 +1,280 @@
+#![forbid(unsafe_code)]
+mod cli;
+
+mod data;
+mod operations;
+
+use oj_polariton_auth::Handshake;
+use tokio::net;
+
+use polariton::packet::{Data, Message, Packet, StandardMessage};
+use polariton::operation::{OperationResponse, Typed};
+
+pub struct InitConfig {
+ pub config: oj_rc_core::persist::config::ConfigImpl,
+ pub users: std::sync::Arc,
+ pub factory: std::sync::Arc,
+ pub parsers: oj_rc_core::cubes::CubeParsers,
+}
+
+pub type UserTy = oj_rc_core::UserState<()>;
+
+#[tokio::main]
+async fn main() -> std::io::Result<()> {
+ env_logger::init();
+ let args = cli::CliArgs::get();
+ log::debug!("Got cli args {:?}", args);
+
+ let config = oj_rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data");
+ let users = std::sync::Arc::new(oj_rc_core::persist::user::UserImpl::load(&args.data, &config).await.expect("Bad user data"));
+ let factory = std::sync::Arc::new(>::factory::<'_, '_>(&config).await.expect("Bad vehicle factory (CRF) config"));
+ let parsers = oj_rc_core::cubes::CubeParsers::new(&config);
+
+ let init_ctx = InitConfig {
+ config,
+ users,
+ factory,
+ parsers,
+ };
+
+ let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(&init_ctx), polariton_server::events::EventsHandler::new()));
+
+ let ip_addr: std::net::IpAddr = args.ip.parse().expect("Invalid IP address");
+
+ let listener = net::TcpListener::bind(std::net::SocketAddr::new(ip_addr, args.port)).await?;
+
+ if args.once {
+ log::warn!("Handling first connection and then exiting");
+ let (socket, address) = listener.accept().await?;
+ process_socket(socket, address, server.clone(), init_ctx.users.clone()).await;
+ } else {
+ loop {
+ let (socket, address) = listener.accept().await?;
+ tokio::spawn(process_socket(socket, address, server.clone(), init_ctx.users.clone()));
+ }
+ }
+ server.join();
+ server.join_async().await;
+ Ok(())
+}
+
+async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, server: std::sync::Arc>, users: std::sync::Arc) {
+ log::debug!("Accepting connection from address {}", address);
+ let enc = match do_connect_handshake(&mut socket).await {
+ Some(x) => x,
+ None => {
+ log::error!("Failed to do connect handshake with {}", address);
+ return;
+ }
+ };
+ let (socket_r, socket_w) = socket.into_split();
+ let (chann_tx, chann_rx) = tokio::sync::mpsc::unbounded_channel();
+ let user_state = oj_rc_core::UserState::<()>::new(users, chann_tx.clone());
+ let ctx = polariton::packet::SerdesContext::from_boxed(Default::default(), enc);
+ server.handle_async_with_channel(socket_r, socket_w, user_state, ctx, chann_tx, chann_rx).await;
+ log::debug!("Goodbye connection from address {}", address);
+}
+
+const APP_ID: &str = "LobbyServer";
+
+struct AuthImpl;
+
+const TOKEN_KEY: u8 = 216; // token;refresh_token
+//const UNKNOWN_BYTE_KEY: u8 = 217;
+const SERVICE_KEY: u8 = 224;
+const USERNAME_KEY: u8 = 225;
+
+//const CCU_KEY: u8 = 245;
+
+#[derive(Debug)]
+enum AuthError {
+ WrongService { expected: String, actual: String },
+ MissingService,
+ MissingToken,
+ MissingUsername,
+}
+
+impl AuthError {
+ fn log_err(&self) {
+ match self {
+ Self::WrongService { expected, actual } => log::error!("(auth fail) Got unexpected service {}, expected {}", actual, expected),
+ Self::MissingService => log::error!("(auth fail) No service name param ({}) received", SERVICE_KEY),
+ Self::MissingToken => log::error!("(auth fail) No token param ({}) received", TOKEN_KEY),
+ Self::MissingUsername => log::error!("(auth fail) No username param ({}) received", USERNAME_KEY),
+ }
+ }
+}
+
+impl oj_polariton_auth::AuthProvider for AuthImpl {
+ fn validate(&mut self, params: &std::collections::HashMap) -> Result, AuthError> {
+ if let Some(Typed::Str(token)) = params.get(&TOKEN_KEY) {
+ if let Some(Typed::Str(service)) = params.get(&SERVICE_KEY) {
+ if let Some(Typed::Str(user)) = params.get(&USERNAME_KEY) {
+ if service.string == APP_ID {
+ let params_resp = std::collections::HashMap::::new();
+ //params_resp.insert(CCU_KEY, Typed::Byte(0));
+ log::debug!("Auth success for {} (token: {})", user.string, token.string);
+ Ok(params_resp)
+ } else { Err(AuthError::WrongService { expected: APP_ID.to_owned(), actual: service.string.to_owned() }) }
+ } else { Err(AuthError::MissingUsername) }
+ } else { Err(AuthError::MissingService) }
+ } else { Err(AuthError::MissingToken) }
+ }
+}
+
+async fn do_connect_handshake(
+ socket: &mut net::TcpStream,
+) -> Option> {
+ let handshake = Handshake::new(APP_ID);
+ // connect
+ log::debug!("(connect) Handling first packet");
+ let packet1 = match polariton_server::utils::receive_packet_async(socket, &Default::default()).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read connect packet: {}", e);
+ return None;
+ }
+ };
+ let (handshake, to_send) = match handshake.connect(&packet1) {
+ Ok(x) => (x.handshake, x.extra),
+ Err(e) => {
+ log::error!("Failed to handle connect handshake: {:?}", e.extra);
+ return None;
+ }
+ };
+ match polariton_server::utils::send_packet_async(&to_send, socket, &Default::default()).await {
+ Ok(_) => {},
+ Err(e) => {
+ log::error!("Failed to send connect ack packet: {}", e);
+ return None;
+ }
+ }
+ // encrypt
+ log::debug!("(connect) Handling second packet");
+ let mut packet2 = match polariton_server::utils::receive_packet_async(socket, &Default::default()).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) public key packet: {}", e);
+ return None;
+ }
+ };
+ while let Packet::Ping(ping) = packet2 {
+ polariton_server::utils::handle_ping_async(ping, socket, &Default::default()).await.unwrap_or_default();
+ packet2 = match polariton_server::utils::receive_packet_async(socket, &Default::default()).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) public key packet: {}", e);
+ return None;
+ }
+ };
+ }
+ let (handshake, to_send, crypto) = match handshake.encrypt(&packet2) {
+ Ok(x) => (x.handshake, x.extra.0, x.extra.1),
+ Err(e) => {
+ log::error!("Failed to handle encryption handshake: {:?}", e.extra);
+ return None;
+ }
+ };
+ match polariton_server::utils::send_packet_async(&to_send, socket, &Default::default()).await {
+ Ok(_) => {},
+ Err(e) => {
+ log::error!("Failed to send encryption ack packet: {}", e);
+ return None;
+ }
+ }
+ // pre-auth
+ let handshake = handshake.with_auth(AuthImpl);
+ let op_ctx = polariton::serdes::SerdesContext::default();
+ let ctx = polariton::packet::SerdesContext::new(op_ctx, crypto);
+ // authenticate
+ log::debug!("(connect) Handling third packet");
+ let mut packet3 = match polariton_server::utils::receive_packet_async(socket, &ctx).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) auth packet: {}", e);
+ return None;
+ }
+ };
+ while let Packet::Ping(ping) = packet3 {
+ polariton_server::utils::handle_ping_async(ping, socket, &Default::default()).await.unwrap_or_default();
+ packet3 = match polariton_server::utils::receive_packet_async(socket, &ctx).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) auth packet: {}", e);
+ return None;
+ }
+ };
+ }
+ let to_send = match handshake.authenticate(&packet3, &ctx) {
+ Ok(x) => x,
+ Err(h) => match h.extra {
+ oj_polariton_auth::AuthError::Validation(e) => {
+ e.log_err();
+ return None;
+ },
+ e => {
+ log::error!("Failed to handle auth handshake: {:?}", e);
+ return None;
+ },
+ },
+ };
+ match polariton_server::utils::send_packet_async(&to_send, socket, &ctx).await {
+ Ok(_) => {},
+ Err(e) => {
+ log::error!("Failed to send auth ack packet: {}", e);
+ return None;
+ }
+ }
+
+ // join lobby
+ log::debug!("(join lobby) Handling fourth packet");
+ let mut packet_j = match polariton_server::utils::receive_packet_async(socket, &ctx).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) join packet: {}", e);
+ return None;
+ }
+ };
+ while let Packet::Ping(ping) = packet_j {
+ polariton_server::utils::handle_ping_async(ping, socket, &Default::default()).await.unwrap_or_default();
+ packet_j = match polariton_server::utils::receive_packet_async(socket, &ctx).await {
+ Ok(x) => x,
+ Err(e) => {
+ log::error!("Failed to read (maybe) join packet: {}", e);
+ return None;
+ }
+ };
+ }
+ if let Packet::Packet(msg) = &packet_j {
+ if let Message::Standard(st) = &msg.message {
+ if let Data::OpReq(req) = &st.data {
+ if req.code == 226 { // join lobby (but for real this time)
+ let mut params = std::collections::HashMap::::new();
+ //params.insert(252 /* actors in game */, Typed::Str(game_server_url.into()));
+ params.insert(254 /* game server address */, Typed::Int(42));
+ params.insert(249 /* actor properties */, Typed::HashMap(Vec::new().into()));
+ params.insert(248 /* game properties */, Typed::HashMap(Vec::new().into()));
+ let resp = Packet::from_message(
+ Message::Standard(
+ StandardMessage { flags: 0,
+ data: Data::OpResp(OperationResponse {
+ code: req.code,
+ return_code: 0,
+ message: Typed::Null,
+ params: params.into(),
+ }),
+ }.encrypt(true)), 0, true, &ctx).unwrap();
+ match polariton_server::utils::send_packet_async(&resp, socket, &ctx).await {
+ Ok(_) => {},
+ Err(e) => {
+ log::error!("Failed to send lobby ack packet: {}", e);
+ return None;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Some(ctx.into_crypto())
+}
diff --git a/rc_lobby_room/src/operations/eac.rs b/rc_lobby_room/src/operations/eac.rs
new file mode 100644
index 0000000..e9bbe6b
--- /dev/null
+++ b/rc_lobby_room/src/operations/eac.rs
@@ -0,0 +1,22 @@
+use polariton_server::operations::{Operation, OperationCode};
+
+pub struct EacChallengeIgnorer;
+
+impl Operation for EacChallengeIgnorer {
+ type User = crate::UserTy;
+
+ fn handle(&self, params: polariton::operation::ParameterTable, _: &Self::User) -> polariton::operation::OperationResponse {
+ polariton::operation::OperationResponse {
+ code: 5, // skip the challenge (hopefully)
+ return_code: 0,
+ message: polariton::operation::Typed::Null,
+ params,
+ }
+ }
+}
+
+impl OperationCode for EacChallengeIgnorer {
+ fn op_code() -> u8 {
+ 4
+ }
+}
diff --git a/rc_lobby_room/src/operations/join_queue.rs b/rc_lobby_room/src/operations/join_queue.rs
new file mode 100644
index 0000000..cdf3887
--- /dev/null
+++ b/rc_lobby_room/src/operations/join_queue.rs
@@ -0,0 +1,47 @@
+use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
+use polariton::operation::{ParameterTable, Typed};
+
+const CODE: u8 = 0;
+
+const GROUP_ID_PARAM_KEY: u8 = 2; // str; in
+const GARAGE_SLOT_PARAM_KEY: u8 = 3; // int; in
+const GROUP_SIZE_PARAM_KEY: u8 = 4; // int; in
+const IS_GROUP_LEADER_PARAM_KEY: u8 = 14; // bool; in
+const LOBBY_TY_PARAM_KEY: u8 = 30; // int; in
+const EVENT_TO_JOIN_PARAM_KEY: u8 = 41; // str; in
+
+const ESTIMATED_QUEUE_TIME_PARAM_KEY: u8 = 13; // int (seconds); out
+const PERSONAL_RANKING_PARAM_KEY: u8 = 17; // double; out
+
+pub(super) struct QueueJoinProvider;
+
+#[async_trait::async_trait]
+impl SimpleOperation for QueueJoinProvider {
+ type User = crate::UserTy;
+ const CODE: u8 = CODE;
+
+ async fn handle(&self, params: ParameterTable, _user: &Self::User) -> Result, SimpleOpError> {
+ let mut params = params.to_dict();
+ if let Some(Typed::Str(group_id)) = params.remove(&GROUP_ID_PARAM_KEY) {
+ if let Some(Typed::Int(slot_id)) = params.remove(&GARAGE_SLOT_PARAM_KEY) {
+ if let Some(Typed::Int(group_size)) = params.remove(&GROUP_SIZE_PARAM_KEY) {
+ if let Some(Typed::Bool(is_leader)) = params.remove(&IS_GROUP_LEADER_PARAM_KEY) {
+ if let Some(Typed::Int(lobby_ty)) = params.remove(&LOBBY_TY_PARAM_KEY) {
+ let lobby_ty = oj_rc_core::data::lobby::LobbyType::from_int(lobby_ty)?;
+ if let Some(Typed::Str(event_to_join)) = params.remove(&EVENT_TO_JOIN_PARAM_KEY) {
+ log::debug!("Got lobby join queue request of platoon {} ({} players, is_leader:{}) slot {} lobby {:?} event {}", group_id.string, group_size, is_leader, slot_id, lobby_ty, event_to_join.string);
+ params.insert(ESTIMATED_QUEUE_TIME_PARAM_KEY, Typed::Int(42));
+ params.insert(PERSONAL_RANKING_PARAM_KEY, Typed::Double(42.0));
+ }
+ }
+ }
+ }
+ }
+ }
+ Ok(params.into())
+ }
+}
+
+pub(super) fn join_queue_provider() -> SimpleOpImpl {
+ SimpleOpImpl::new(QueueJoinProvider)
+}
diff --git a/rc_lobby_room/src/operations/mod.rs b/rc_lobby_room/src/operations/mod.rs
new file mode 100644
index 0000000..ffe10cf
--- /dev/null
+++ b/rc_lobby_room/src/operations/mod.rs
@@ -0,0 +1,16 @@
+mod more_auth;
+mod eac;
+mod no_quit;
+mod join_queue;
+
+use polariton_server::operations::OperationsHandler;
+
+pub fn handler(_init_ctx: &crate::InitConfig) -> OperationsHandler {
+ OperationsHandler::::new()
+ .modify(oj_rc_core::polariton::OpIdCopy)
+ .add(more_auth::MoreLobbyAuth)
+ //.add(eac::EacChallengeIgnorer)
+ //.add(polariton_server::operations::Ack::<2, _>::default())
+ .add(no_quit::quit_blocker_provider())
+ .add(join_queue::join_queue_provider())
+}
diff --git a/rc_lobby_room/src/operations/more_auth.rs b/rc_lobby_room/src/operations/more_auth.rs
new file mode 100644
index 0000000..6b0f69f
--- /dev/null
+++ b/rc_lobby_room/src/operations/more_auth.rs
@@ -0,0 +1,42 @@
+use polariton::operation::Typed;
+use polariton_server::operations::{Operation, OperationCode};
+
+pub struct MoreLobbyAuth;
+
+impl MoreLobbyAuth {
+ const AUTH_PAYLOAD_KEY: u8 = 245;
+}
+
+#[async_trait::async_trait]
+impl Operation for MoreLobbyAuth {
+ type User = crate::UserTy;
+
+ async fn handle_async(&self, params: polariton::operation::ParameterTable, user: &Self::User) -> polariton::operation::OperationResponse {
+ let params_dict = params.to_dict();
+ if let Some(Typed::Str(auth_payload)) = params_dict.get(&Self::AUTH_PAYLOAD_KEY) {
+ //let mut write_lock = user.write().unwrap();
+ if user.update_with_auth(&auth_payload.string).await {
+ let mut resp_params = std::collections::HashMap::new();
+ resp_params.insert(Self::AUTH_PAYLOAD_KEY, polariton::operation::Typed::Byte(0));
+ return polariton::operation::OperationResponse {
+ code: Self::op_code(),
+ return_code: 0,
+ message: polariton::operation::Typed::Null,
+ params: resp_params.into(),
+ }
+ }
+ }
+ polariton::operation::OperationResponse {
+ code: Self::op_code(),
+ return_code: 120,
+ message: polariton::operation::Typed::Null,
+ params: std::collections::HashMap::new().into(),
+ }
+ }
+}
+
+impl OperationCode for MoreLobbyAuth {
+ fn op_code() -> u8 {
+ 230
+ }
+}
diff --git a/rc_lobby_room/src/operations/no_quit.rs b/rc_lobby_room/src/operations/no_quit.rs
new file mode 100644
index 0000000..61ef0b7
--- /dev/null
+++ b/rc_lobby_room/src/operations/no_quit.rs
@@ -0,0 +1,26 @@
+use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
+use polariton::operation::{ParameterTable, Typed};
+
+const CODE: u8 = 3;
+
+const DID_QUIT_LAST_GAME_PARAM_KEY: u8 = 19; // bool
+const BLOCK_TIME_PARAM_KEY: u8 = 15; // int
+
+pub(super) struct QuitterBlockProvider;
+
+#[async_trait::async_trait]
+impl SimpleOperation for QuitterBlockProvider {
+ type User = crate::UserTy;
+ const CODE: u8 = CODE;
+
+ async fn handle(&self, params: ParameterTable, _user: &Self::User) -> Result, SimpleOpError> {
+ let mut params = params.to_dict();
+ params.insert(DID_QUIT_LAST_GAME_PARAM_KEY, Typed::Bool(false));
+ params.insert(BLOCK_TIME_PARAM_KEY, Typed::Int(0));
+ Ok(params.into())
+ }
+}
+
+pub(super) fn quit_blocker_provider() -> SimpleOpImpl {
+ SimpleOpImpl::new(QuitterBlockProvider)
+}
diff --git a/rc_services_room/src/data/mod.rs b/rc_services_room/src/data/mod.rs
index 5f7568d..fc299dc 100644
--- a/rc_services_room/src/data/mod.rs
+++ b/rc_services_room/src/data/mod.rs
@@ -23,8 +23,8 @@ pub mod player_rank;
pub mod quest;
//pub use oj_rc_core::data::auto_regen;
//pub use oj_rc_core::data::voting;
-pub mod lobby;
-pub use oj_rc_core::data::error_codes;
+//pub use oj_rc_core::data::lobby;
+//pub use oj_rc_core::data::error_codes;
//pub use oj_rc_core::data::game_mode;
pub mod score_multipliers;
//pub use oj_rc_core::data::campaign;
diff --git a/rc_services_room/src/operations/validate_machine.rs b/rc_services_room/src/operations/validate_machine.rs
index 25cd588..48319d3 100644
--- a/rc_services_room/src/operations/validate_machine.rs
+++ b/rc_services_room/src/operations/validate_machine.rs
@@ -19,7 +19,7 @@ pub(super) fn validate_robot_provider() -> SimpleFunc<102, crate::UserTy, impl (
SimpleFunc::new(|params, _user: &crate::UserTy| {
let mut params = params.to_dict();
if let Some(Typed::Int(lobby_ty)) = params.get(&LOBBY_PARAM_KEY) {
- log::info!("Got lobby type {} ({:?})", lobby_ty, crate::data::lobby::LobbyType::from_int(*lobby_ty));
+ log::info!("Got lobby type {} ({:?})", lobby_ty, oj_rc_core::data::lobby::LobbyType::from_int(*lobby_ty));
}
// let lock = user.read().unwrap();
// let user_info = lock.user()?;