1
0
mirror of https://git.ngram.ca/OpenJam/rc-servers synced 2026-08-23 23:08:52 +00:00

Move common components to shared lib

This commit is contained in:
NGnius (Graham)
2025-03-30 16:22:25 -04:00
parent 610f54e236
commit 0e048a17b1
76 changed files with 284 additions and 1110 deletions

18
Cargo.lock generated
View File

@@ -1743,6 +1743,21 @@ dependencies = [
"polariton",
"polariton_auth",
"polariton_server",
"rc_core",
"tokio",
]
[[package]]
name = "rc_core"
version = "0.1.0"
dependencies = [
"chrono",
"hex",
"log",
"polariton",
"polariton_server",
"serde",
"serde_json",
"tokio",
]
@@ -1782,6 +1797,7 @@ dependencies = [
"polariton",
"polariton_auth",
"polariton_server",
"rc_core",
"serde",
"serde_json",
"tokio",
@@ -1810,6 +1826,7 @@ dependencies = [
"polariton",
"polariton_auth",
"polariton_server",
"rc_core",
"tokio",
]
@@ -1836,6 +1853,7 @@ dependencies = [
"polariton",
"polariton_auth",
"polariton_server",
"rc_core",
"tokio",
]

View File

@@ -11,7 +11,7 @@ members = [
"rc_static_data", "rc_microtransactions",
"rc_social", "rc_social_room",
"rc_chat", "rc_chat_room",
"rc_singleplayer", "rc_singleplayer_room",
"rc_singleplayer", "rc_singleplayer_room", "rc_core",
]
[workspace.dependencies]

View File

@@ -16,7 +16,12 @@ To get Robocraft to use these servers, place [this servenvmulti.config](assets/r
## Privacy
No data is collected or logged, except in dev mode. Some personal identifiers are sent but only exist ephemerally.
The minimum amount of data is (and should be) collected to provide the expected functionality.
In most cases, these means no data is collected or logged except some debug log messages in development versions.
Some personal identifiers are sent but only exist ephemerally.
The exception is Robocraft servers, which store the minimum account info possible on disk.
This includes a unique user identifier, username, vehicle data, and user configuration data.
The current PC's MAC address is also sent to the server (this is a Robocraft client """feature""", it is not recorded by any server).
## Development
@@ -24,8 +29,10 @@ No data is collected or logged, except in dev mode. Some personal identifiers ar
Run all of the servers using their respective `run_debug.sh` scripts and use the `dev` profile in `servenvmulti.config` to point the game to your local dev servers.
It is possible to remove the obfuscation from the Robocraft's `Assembly-CSharp.dll` if you need to figure out how it expects the server to behave.
## Contributing
If you can program, pull requests are appreciated! If you can't and don't like learning, reporting issues is also welcome.
If you can program or are learning Rust, pull requests are appreciated! If you can't and would prefer not to learn, reporting issues is also welcome.
If you'd like to discuss, contact NGnius on Signal `rfc.1149` or email `ngniusness@gmail.com`. I'll make a Signal group chat if there's enough interest.

View File

@@ -11,3 +11,4 @@ clap.workspace = true
polariton.workspace = true
polariton_auth = { version = "*", path = "../polariton_auth" }
polariton_server.workspace = true
rc_core = { version = "*", path = "../rc_core" }

View File

@@ -11,6 +11,14 @@ pub struct CliArgs {
#[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,

View File

@@ -1,5 +1,4 @@
mod cli;
mod state;
mod data;
mod operations;
@@ -10,7 +9,7 @@ use tokio::net;
use polariton::packet::{Data, Message, Packet, StandardMessage};
use polariton::operation::{OperationResponse, Typed};
pub type UserTy = std::sync::RwLock<state::UserState>;
pub type UserTy = rc_core::UserState;
#[tokio::main]
async fn main() -> std::io::Result<()> {
@@ -18,6 +17,9 @@ async fn main() -> std::io::Result<()> {
let args = cli::CliArgs::get();
log::debug!("Got cli args {:?}", args);
let cubes = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data");
let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).expect("Bad user data"));
let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(), polariton_server::events::EventsHandler::new()));
let ip_addr: std::net::IpAddr = args.ip.parse().expect("Invalid IP address");
@@ -27,11 +29,11 @@ async fn main() -> std::io::Result<()> {
if args.once {
log::warn!("Handling first connection and then exiting");
let (socket, address) = listener.accept().await?;
process_socket(socket, address, server.clone()).await;
process_socket(socket, address, server.clone(), users.clone()).await;
} else {
loop {
let (socket, address) = listener.accept().await?;
tokio::spawn(process_socket(socket, address, server.clone()));
tokio::spawn(process_socket(socket, address, server.clone(), users.clone()));
}
}
server.join();
@@ -39,7 +41,7 @@ async fn main() -> std::io::Result<()> {
Ok(())
}
async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, server: std::sync::Arc<polariton_server::Server<crate::UserTy>>) {
async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, server: std::sync::Arc<polariton_server::Server<crate::UserTy>>, users: std::sync::Arc<rc_core::persist::user::UserImpl>) {
log::debug!("Accepting connection from address {}", address);
let enc = match do_connect_handshake(&mut socket).await {
Some(x) => x,
@@ -48,9 +50,10 @@ async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAdd
return;
}
};
let user_state = state::UserState::new();
let (chann_tx, chann_rx) = tokio::sync::mpsc::unbounded_channel();
let user_state = rc_core::UserState::<()>::new(users, chann_tx.clone());
let (socket_r, socket_w) = socket.into_split();
let _packet_chann = server.handle_async(socket_r, socket_w, user_state, polariton::packet::SerdesContext::from_boxed(Default::default(), enc)).await;
server.handle_async_with_channel(socket_r, socket_w, user_state, polariton::packet::SerdesContext::from_boxed(Default::default(), enc), chann_tx, chann_rx).await;
log::debug!("Goodbye connection from address {}", address);
}

View File

@@ -14,8 +14,7 @@ impl <C> Operation<C> for MoreLobbyAuth {
fn handle(&self, params: polariton::operation::ParameterTable<C>, _: &mut Self::State, user: &Self::User) -> polariton::operation::OperationResponse<C> {
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 write_lock.update_with_auth(&auth_payload.string) {
if user.update_with_auth(&auth_payload.string) {
let mut resp_params = std::collections::HashMap::new();
resp_params.insert(Self::AUTH_PAYLOAD_KEY, polariton::operation::Typed::Byte(0));
return polariton::operation::OperationResponse {

View File

@@ -1,27 +0,0 @@
use std::sync::RwLock;
#[derive(Default, Debug)]
pub struct UserState {
pub uuid: String,
pub token: String,
pub refresh_token: String,
}
impl UserState {
pub fn update_with_auth(&mut self, auth_str: &str) -> bool {
let splits: Vec<&str> = auth_str.split(';').collect();
if splits.len() != 3 {
log::warn!("Invalid auth payload: {}", auth_str);
false
} else {
self.uuid = splits[0].to_owned();
self.token = splits[1].to_owned();
self.refresh_token = splits[2].to_owned();
true
}
}
pub fn new() -> crate::UserTy {
RwLock::new(UserState::default())
}
}

14
rc_core/Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[package]
name = "rc_core"
version = "0.1.0"
edition = "2024"
[dependencies]
log.workspace = true
polariton.workspace = true
hex = "0.4"
serde.workspace = true
serde_json.workspace = true
chrono = "0.4"
polariton_server.workspace = true
tokio = { version = "1.43", features = [ "net", "macros", "rt-multi-thread", "io-util", "time"] }

39
rc_core/src/data/mod.rs Normal file
View File

@@ -0,0 +1,39 @@
pub mod auto_regen;
pub mod campaign;
pub mod cube_list;
pub mod game_mode;
pub mod garage_bay;
pub mod movement_list;
pub mod player_data;
pub mod tech_tree;
pub mod voting;
pub mod weapon_list;
pub mod weapon_upgrade;
pub mod error_codes;
pub fn encode_7_bit_i32(mut src: i32) -> Vec<u8> {
if src == 0 { return vec![0] }
let mut out = Vec::with_capacity(5);
while src != 0 {
let last_7 = (src & 0x7F) as u8;
src = src >> 7;
if src != 0 {
out.push(last_7 | 0x80);
} else {
out.push(last_7);
}
}
out
}
pub fn write_str_for_binreader(s: &str, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
let s_bytes = s.as_bytes();
let mut total_len = writer.write(&encode_7_bit_i32(s_bytes.len() as i32))?;
total_len += writer.write(s_bytes)?;
Ok(total_len)
}
pub fn cube_id_to_str(id: u32) -> String {
hex::encode(id.to_be_bytes()).into()
}

8
rc_core/src/lib.rs Normal file
View File

@@ -0,0 +1,8 @@
pub mod data;
mod state;
pub use state::UserState;
pub mod persist;
pub use persist::user::{UserImpl, UserProvider};
pub use persist::config::{ConfigImpl, ConfigProvider};

View File

@@ -153,6 +153,7 @@ impl <C: Clone> super::User<C> for UserData {
robot_rank: polariton::operation::Typed::Int(slot.total_robot_ranking as _),
cpu: polariton::operation::Typed::Int(slot.total_robot_cpu as _),
cosmetic_cpu: polariton::operation::Typed::Int(slot.total_cosmetic_cpu as _),
uuid: polariton::operation::Typed::Str(format!("{}_{}", slot.uuid.0, slot.uuid.1).into()),
})
},
Err(e) => {
@@ -200,6 +201,37 @@ impl <C: Clone> super::User<C> for UserData {
super::since_windows_epoch(0)
}
fn singleplayer_robots(&self) -> Result<polariton::operation::Typed<C>, i16> {
let current_slot = self.load_garage_by_id(self.account.garage.slot).map_err(|e| {
log::error!("Failed to load current vehicle: {}", e);
INVALID_ROBOT_ERR
})?;
let user_uuid = self.token.uuid.clone();
Ok(crate::data::player_data::PlayerDatas {
players: vec![
crate::data::player_data::PlayerData {
name: user_uuid.clone(),
display_name: user_uuid,
mastery: current_slot.mastery_level,
tier: 1, // FIXME
robot_name: current_slot.name,
robot_map: current_slot.robot_data,
team: 0,
has_premium: true, // FIXME
robot_uuid: format!("{}_{}", current_slot.uuid.0, current_slot.uuid.1),
cpu: current_slot.total_robot_cpu as i32,
weapon_order: current_slot.weapon_order.clone(),
colour_map: current_slot.colour_data,
is_ai: false,
spawn_effect: "Spawn_Warp".to_owned(), // FIXME
death_effect: "Explosion_Warp".to_owned(), // FIXME
player_rank: 1, // FIXME
weapon_rank: current_slot.weapon_order.into_iter().map(|x| (x, if x == 0 { 0 } else { 1 })).collect(),
}
],
}.as_transmissible())
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]

View File

@@ -22,6 +22,7 @@ pub trait User<C> {
fn slot_by_id(&self, id: i32) -> Result<UserSlotData<C>, i16>;
fn save_slot(&self, vehicle: VehicleData) -> Result<(), i16>;
fn signup_date(&self) -> i64;
fn singleplayer_robots(&self) -> Result<polariton::operation::Typed<C>, i16>;
}
pub struct UserSlots<C> {
@@ -41,6 +42,7 @@ pub struct UserSlotData<C> {
pub robot_rank: polariton::operation::Typed<C>,
pub cpu: polariton::operation::Typed<C>,
pub cosmetic_cpu: polariton::operation::Typed<C>,
pub uuid: polariton::operation::Typed<C>,
}
pub struct VehicleData {

View File

@@ -1,12 +1,15 @@
use crate::persist::user::UserProvider;
use polariton_server::ToSend;
pub struct UserState<C: Clone> {
state: InitState<C>,
pub struct UserState<C: Clone = ()> {
state: std::sync::RwLock<InitState<C>>,
event_tx: tokio::sync::mpsc::UnboundedSender<ToSend<C>>,
}
impl <C: Clone> UserState<C> {
pub fn update_with_auth(&mut self, auth_str: &str) -> bool {
match &self.state {
pub fn update_with_auth(&self, auth_str: &str) -> bool {
let mut lock = self.state.write().unwrap();
match &*lock {
InitState::Unauthenticated(auth) => {
let splits: Vec<&str> = auth_str.split(';').collect();
if splits.len() != 3 {
@@ -20,7 +23,7 @@ impl <C: Clone> UserState<C> {
};
match auth.authenticate(token) {
Ok(user) => {
self.state = InitState::Authenticated(user);
*lock = InitState::Authenticated(std::sync::Arc::new(user));
true
},
Err(e) => {
@@ -38,21 +41,31 @@ impl <C: Clone> UserState<C> {
}
pub fn new(provider: std::sync::Arc<crate::persist::user::UserImpl>) -> Self {
pub fn new(provider: std::sync::Arc<crate::persist::user::UserImpl>, event_tx: tokio::sync::mpsc::UnboundedSender<ToSend<C>>) -> Self {
Self {
state: InitState::Unauthenticated(provider),
state: std::sync::RwLock::new(InitState::Unauthenticated(provider)),
event_tx,
}
}
pub fn user(&self) -> Result<&dyn crate::persist::user::User<C>, i16> {
match &self.state {
pub fn user(&self) -> Result<std::sync::Arc<Box<dyn crate::persist::user::User<C> + Send + Sync>>, i16> {
let lock = self.state.read().unwrap();
match &*lock {
InitState::Unauthenticated(_) => Err(120),
InitState::Authenticated(user) => Ok(user.as_ref()),
InitState::Authenticated(user) => Ok(user.clone()),
}
}
pub fn event(&self, event_data: ToSend<C>) {
self.event_tx.send(event_data).unwrap()
}
pub fn event_sender(&self) -> tokio::sync::mpsc::UnboundedSender<ToSend<C>> {
self.event_tx.clone()
}
}
enum InitState<C> {
Unauthenticated(std::sync::Arc<crate::persist::user::UserImpl>),
Authenticated(Box<dyn crate::persist::user::User<C> + Send + Sync>),
Authenticated(std::sync::Arc<Box<dyn crate::persist::user::User<C> + Send + Sync>>),
}

View File

@@ -16,3 +16,4 @@ hex = "0.4"
serde.workspace = true
serde_json.workspace = true
chrono = "0.4"
rc_core = { version = "*", path = "../rc_core" }

View File

@@ -30,21 +30,21 @@ impl ItemShopBundle {
fn dump(&self, writer: &mut dyn Write) -> std::io::Result<usize> {
let sku_bytes = self.sku.as_bytes();
let mut total_len = writer.write(&super::encode_7_bit_i32(sku_bytes.len() as i32))?;
let mut total_len = writer.write(&rc_core::data::encode_7_bit_i32(sku_bytes.len() as i32))?;
total_len += writer.write(sku_bytes)?;
let bundle_name_key_bytes = self.bundle_name_key.as_bytes();
total_len += writer.write(&super::encode_7_bit_i32(bundle_name_key_bytes.len() as i32))?;
total_len += writer.write(&rc_core::data::encode_7_bit_i32(bundle_name_key_bytes.len() as i32))?;
total_len += writer.write(bundle_name_key_bytes)?;
let sprite_bytes = self.sprite.as_bytes();
total_len += writer.write(&super::encode_7_bit_i32(sprite_bytes.len() as i32))?;
total_len += writer.write(&rc_core::data::encode_7_bit_i32(sprite_bytes.len() as i32))?;
total_len += writer.write(sprite_bytes)?;
total_len += writer.write(&[self.is_sprite_full_size as u8])?;
let currency_bytes = self.currency.as_str().as_bytes();
total_len += writer.write(&super::encode_7_bit_i32(currency_bytes.len() as i32))?;
total_len += writer.write(&rc_core::data::encode_7_bit_i32(currency_bytes.len() as i32))?;
total_len += writer.write(currency_bytes)?;
total_len += writer.write(&self.price.to_le_bytes())?;

View File

@@ -1,56 +1,30 @@
pub mod cube_list;
pub use rc_core::data::cube_list;
pub mod special_item;
pub mod premium_config;
pub mod palette;
pub mod client_config;
pub mod crf_config;
pub mod weapon_list;
pub mod movement_list;
pub use rc_core::data::weapon_list;
//pub use rc_core::data::movement_list;
pub mod damage_boost;
pub mod battle_arena_config;
pub mod cpu_limits;
pub mod cosmetic_limits;
pub mod taunts_config;
pub mod customisation_info;
pub mod garage_bay;
pub use rc_core::data::garage_bay;
pub mod custom_games;
pub mod tech_tree;
//pub use rc_core::data::tech_tree;
pub mod item_shop_bundle;
pub mod player_robopass_season;
pub mod weapon_upgrade;
//pub use rc_core::data::weapon_upgrade;
pub mod player_rank;
pub mod robot_data;
pub mod quest;
pub mod auto_regen;
pub mod voting;
//pub use rc_core::data::auto_regen;
//pub use rc_core::data::voting;
pub mod lobby;
pub mod error_codes;
pub mod game_mode;
pub use rc_core::data::error_codes;
//pub use rc_core::data::game_mode;
pub mod score_multipliers;
pub mod campaign;
pub(self) fn encode_7_bit_i32(mut src: i32) -> Vec<u8> {
if src == 0 { return vec![0] }
let mut out = Vec::with_capacity(5);
while src != 0 {
let last_7 = (src & 0x7F) as u8;
src = src >> 7;
if src != 0 {
out.push(last_7 | 0x80);
} else {
out.push(last_7);
}
}
out
}
pub(self) fn write_str_for_binreader(s: &str, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
let s_bytes = s.as_bytes();
let mut total_len = writer.write(&encode_7_bit_i32(s_bytes.len() as i32))?;
total_len += writer.write(s_bytes)?;
Ok(total_len)
}
pub(self) fn cube_id_to_str(id: u32) -> String {
hex::encode(id.to_be_bytes()).into()
}
//pub use rc_core::data::campaign;

View File

@@ -43,9 +43,9 @@ pub struct QuestInfo {
impl QuestInfo {
fn dump(&self, writer: &mut dyn Write) -> std::io::Result<usize> {
let mut total_len = super::write_str_for_binreader(&self.id, writer)?;
total_len += super::write_str_for_binreader(&self.name, writer)?;
total_len += super::write_str_for_binreader(&self.description, writer)?;
let mut total_len = rc_core::data::write_str_for_binreader(&self.id, writer)?;
total_len += rc_core::data::write_str_for_binreader(&self.name, writer)?;
total_len += rc_core::data::write_str_for_binreader(&self.description, writer)?;
total_len += writer.write(&self.xp.to_le_bytes())?;
total_len += writer.write(&self.premium_xp.to_le_bytes())?;
total_len += writer.write(&self.robits.to_le_bytes())?;

View File

@@ -1,10 +1,8 @@
mod cli;
mod state;
mod data;
mod events;
mod operations;
mod persist;
use polariton_auth::Handshake;
use tokio::net;
@@ -12,11 +10,11 @@ use tokio::net;
use polariton::packet::{Data, Message, Packet, StandardMessage};
use polariton::operation::{OperationResponse, Typed};
pub type UserTy = std::sync::RwLock<state::UserState<()>>;
pub type UserTy = rc_core::UserState<()>;
pub struct InitConfig {
pub cubes: persist::config::ConfigImpl,
pub users: std::sync::Arc<persist::user::UserImpl>,
pub cubes: rc_core::persist::config::ConfigImpl,
pub users: std::sync::Arc<rc_core::persist::user::UserImpl>,
}
#[tokio::main]
@@ -25,8 +23,8 @@ async fn main() -> std::io::Result<()> {
let args = cli::CliArgs::get();
log::debug!("Got cli args {:?}", args);
let cubes = persist::config::ConfigImpl::load(&args.assets).expect("Bad config data");
let users = std::sync::Arc::new(persist::user::UserImpl::load(&args.data, &cubes).expect("Bad user data"));
let cubes = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data");
let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).expect("Bad user data"));
let init_ctx = std::sync::Arc::new(InitConfig {
cubes,
users,
@@ -63,9 +61,10 @@ async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAdd
}
};
let (socket_r, socket_w) = socket.into_split();
let user_state = std::sync::RwLock::new(state::UserState::<()>::new(init_ctx.users.clone()));
let (chann_tx, chann_rx) = tokio::sync::mpsc::unbounded_channel();
let user_state = rc_core::UserState::<()>::new(init_ctx.users.clone(), chann_tx.clone());
let ctx = polariton::packet::SerdesContext::from_boxed(Default::default(), enc);
server.handle_async(socket_r, socket_w, user_state, ctx).await;
server.handle_async_with_channel(socket_r, socket_w, user_state, ctx, chann_tx, chann_rx).await;
log::debug!("Goodbye connection from address {}", address);
}

View File

@@ -1,11 +1,11 @@
use polariton_server::operations::SimpleFunc;
use polariton::{operation::{Dict, ParameterTable, Typed}, serdes::TypePrefix};
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
const PARAM_KEY: u8 = 16;
pub(super) fn cube_inv_provider(cubes: &crate::persist::config::ConfigImpl) -> SimpleFunc<16, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
let cube_ids = <crate::persist::config::ConfigImpl as ConfigProvider<()>>::ids(cubes);
pub(super) fn cube_inv_provider(cubes: &rc_core::ConfigImpl) -> SimpleFunc<16, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
let cube_ids = <rc_core::ConfigImpl as ConfigProvider<()>>::ids(cubes);
SimpleFunc::new(move |params, _| {
let mut params = params.to_dict();
params.insert(PARAM_KEY, Typed::Dict(Dict {

View File

@@ -1,10 +1,10 @@
use polariton_server::operations::Immediate;
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
const PARAM_KEY: u8 = 1;
//const DEFAULT_CUBE_ID: u32 = 227205318;
pub(super) fn cube_list_provider(cubes: &crate::persist::config::ConfigImpl) -> Immediate<2, crate::UserTy> {
pub(super) fn cube_list_provider(cubes: &rc_core::ConfigImpl) -> Immediate<2, crate::UserTy> {
Immediate::new(|| {
let mut params = std::collections::HashMap::with_capacity(2);
params.insert(PARAM_KEY, cubes.cube_list());

View File

@@ -1,11 +1,11 @@
use polariton_server::operations::SimpleFunc;
use polariton::operation::ParameterTable;
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
const PARAM_KEY: u8 = 1;
pub(super) fn game_mode_config_provider(conf: &crate::persist::config::ConfigImpl) -> SimpleFunc<113, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
pub(super) fn game_mode_config_provider(conf: &rc_core::ConfigImpl) -> SimpleFunc<113, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
let game_config = conf.game_mode_config();
SimpleFunc::new(move |params, _| {
let mut params = params.to_dict();

View File

@@ -5,8 +5,7 @@ const PARAM_KEY: u8 = 54;
pub(super) fn garage_id_provider() -> SimpleFunc<177, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
SimpleFunc::new(|params, user: &crate::UserTy| {
let lock = user.read().unwrap();
let user_info = lock.user()?;
let user_info = user.user()?;
let mut params = params.to_dict();
params.insert(PARAM_KEY, Typed::Str(user_info.selected_garage_uuid().into()));
Ok(params.into())

View File

@@ -7,8 +7,7 @@ const SLOT_ORDER_PARAM_KEY: u8 = 58;
pub(super) fn garage_slot_provider() -> SimpleFunc<40, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
SimpleFunc::new(|params, user: &crate::UserTy| {
let lock = user.read().unwrap();
let user_info = lock.user()?;
let user_info = user.user()?;
let mut params = params.to_dict();
let all_slots = user_info.all_slots_by_id();
params.insert(SLOTS_PARAM_KEY, all_slots.slot_info);

View File

@@ -13,8 +13,7 @@ const MASTERY_LEVEL_PARAM_KEY: u8 = 18; // int
pub(super) fn garage_machine_provider() -> SimpleFunc<43, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
SimpleFunc::new(|params, user: &crate::UserTy| {
let mut params = params.to_dict();
let lock = user.read().unwrap();
let user_info = lock.user()?;
let user_info = user.user()?;
if let Some(Typed::Int(garage_slot)) = params.get(&SLOT_PARAM_KEY) {
log::debug!("Got machine request for slot {:?}", garage_slot);
let machine = user_info.slot_by_id(*garage_slot)?;
@@ -48,9 +47,8 @@ pub(super) fn garage_machine_save_provider() -> SimpleFunc<41, crate::UserTy, im
if let Some(Typed::Bytes(colour_data)) = params.remove(&COMPRESSED_COLOUR_DATA_PARAM_KEY) {
if let Some(Typed::Arr(weapon_order)) = params.remove(&WEAPON_ORDER_PARAM_KEY) {
let weapon_order_filtered: Vec<_> = weapon_order.items.into_iter().filter_map(|ty| if let Typed::Int(i) = ty { Some(i) } else { None }).collect();
let lock = user.read().unwrap();
let user_info = lock.user()?;
let vehicle_data = crate::persist::user::VehicleData {
let user_info = user.user()?;
let vehicle_data = rc_core::persist::user::VehicleData {
id: slot_index,
robot_data: robot_data.vec,
colour_data: colour_data.vec,

View File

@@ -8,8 +8,7 @@ const DATA_PARAM_KEY: u8 = 33; // byte arr
pub(super) fn garage_machine_colour_provider() -> SimpleFunc<33, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
SimpleFunc::new(|params, user: &crate::UserTy| {
let mut params = params.to_dict();
let lock = user.read().unwrap();
let user_info = lock.user()?;
let user_info = user.user()?;
if let Some(Typed::Int(garage_slot)) = params.get(&SLOT_PARAM_KEY) {
log::debug!("Got machine colour request for slot {:?}", garage_slot);
let machine = user_info.slot_by_id(*garage_slot)?;

View File

@@ -14,8 +14,8 @@ impl <C> Operation<C> for MoreLobbyAuth {
fn handle(&self, params: polariton::operation::ParameterTable<C>, _: &mut Self::State, user: &Self::User) -> polariton::operation::OperationResponse<C> {
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 write_lock.update_with_auth(&auth_payload.string) {
//let mut write_lock = user.write().unwrap();
if user.update_with_auth(&auth_payload.string) {
let mut resp_params = std::collections::HashMap::new();
resp_params.insert(Self::AUTH_PAYLOAD_KEY, polariton::operation::Typed::Byte(0));
return polariton::operation::OperationResponse {

View File

@@ -1,10 +1,10 @@
//use polariton::serdes::TypePrefix;
use polariton_server::operations::Immediate;
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
const PARAM_KEY: u8 = 1;
pub(super) fn movement_config_provider(cubes: &crate::persist::config::ConfigImpl) -> Immediate<62, crate::UserTy> {
pub(super) fn movement_config_provider(cubes: &rc_core::ConfigImpl) -> Immediate<62, crate::UserTy> {
Immediate::new(|| {
let mut params = std::collections::HashMap::with_capacity(2);
params.insert(PARAM_KEY, cubes.movement_list());

View File

@@ -1,10 +1,10 @@
use polariton_server::operations::Immediate;
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
const PARAM_KEY: u8 = 203; // bytes
pub(super) fn after_battle_vote_thresholds_provider(conf: &crate::persist::config::ConfigImpl) -> Immediate<169, crate::UserTy> {
pub(super) fn after_battle_vote_thresholds_provider(conf: &rc_core::ConfigImpl) -> Immediate<169, crate::UserTy> {
Immediate::new(|| {
let mut params = std::collections::HashMap::with_capacity(1);
params.insert(PARAM_KEY, conf.after_battle_vote_config());

View File

@@ -8,8 +8,7 @@ const COSMETIC_CPU_PARAM_KEY: u8 = 176; // out; int
pub(super) fn player_robot_rank_provider() -> SimpleFunc<79, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
SimpleFunc::new(|params, user: &crate::UserTy| {
let lock = user.read().unwrap();
let user = lock.user()?;
let user = user.user()?;
let mut params = params.to_dict();
if let Some(Typed::Str(username)) = params.get(&USERNAME_PARAM_KEY) {
log::debug!("Get robot rank for user {}", username.string);

View File

@@ -1,10 +1,10 @@
use polariton_server::operations::Immediate;
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
const PARAM_KEY: u8 = 37; // bytes
pub(super) fn auto_regen_config_provider(conf: &crate::persist::config::ConfigImpl) -> Immediate<35, crate::UserTy> {
pub(super) fn auto_regen_config_provider(conf: &rc_core::ConfigImpl) -> Immediate<35, crate::UserTy> {
Immediate::new(|| {
let mut params = std::collections::HashMap::with_capacity(1);
params.insert(PARAM_KEY, conf.regen_config());

View File

@@ -6,9 +6,8 @@ const PARAM_KEY: u8 = 71; // long
pub(super) fn user_signup_date_provider() -> SimpleFunc<63, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
SimpleFunc::new(|params, user: &crate::UserTy| {
let mut params = params.to_dict();
let lock = user.read().unwrap();
let user_info = lock.user()?;
params.insert(PARAM_KEY, Typed::Long(user_info.signup_date()));
let user = user.user()?;
params.insert(PARAM_KEY, Typed::Long(user.signup_date()));
Ok(params.into())
})
}

View File

@@ -1,13 +1,13 @@
use polariton_server::operations::{Immediate, SimpleFunc};
use polariton::operation::{ParameterTable, Typed};
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
const CAMPAIGNS_BYTES_PARAM_KEY: u8 = 64; // list of bytes (serialised data)
const CAMPAIGNS_WAVES_PARAM_KEY: u8 = 70; // hashtable
const CAMPAIGNS_VERSIONS_PARAM_KEY: u8 = 69; // hashtable
pub(super) fn singleplayer_campaigns_provider(conf: &crate::persist::config::ConfigImpl) -> Immediate<65, crate::UserTy> {
pub(super) fn singleplayer_campaigns_provider(conf: &rc_core::ConfigImpl) -> Immediate<65, crate::UserTy> {
Immediate::new(|| {
let mut params = std::collections::HashMap::new();
params.insert(CAMPAIGNS_BYTES_PARAM_KEY, conf.campaigns_parameters()); // first 4 bytes are i32 for length of the rest
@@ -29,8 +29,8 @@ const CAMPAIGN_ID_PARAM_KEY: u8 = 22; // string; in
const CAMPAIGN_DIFFICULTY_PARAM_KEY: u8 = 23; // i32; in
const CAMPAIGN_WAVES_PARAM_KEY: u8 = 75; // bytes; out
pub(super) fn singleplayer_complete_campaign_provider(conf: &crate::persist::config::ConfigImpl) -> SimpleFunc<64, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
let campaign_details = <crate::persist::config::ConfigImpl as crate::persist::config::ConfigProvider<()>>::campaign_details(conf);
pub(super) fn singleplayer_complete_campaign_provider(conf: &rc_core::ConfigImpl) -> SimpleFunc<64, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
let campaign_details = <rc_core::ConfigImpl as rc_core::ConfigProvider<()>>::campaign_details(conf);
SimpleFunc::new(move |params, _| {
let mut params = params.to_dict();
if let Some(Typed::Str(campaign_id)) = params.get(&CAMPAIGN_ID_PARAM_KEY) {
@@ -47,14 +47,14 @@ pub(super) fn singleplayer_complete_campaign_provider(conf: &crate::persist::con
const CAMPAIGN_WAVE_NUMBER_PARAM_KEYL: u8 = 73;
pub(super) fn singleplayer_save_complete_campaign_provider() -> SimpleFunc<68, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
//let campaign_details = <crate::persist::config::ConfigImpl as crate::persist::config::ConfigProvider<()>>::campaign_details(conf);
//let campaign_details = <rc_core::ConfigImpl as rc_core::ConfigProvider<()>>::campaign_details(conf);
SimpleFunc::new(move |params, user: &crate::UserTy| {
let mut params = params.to_dict();
if let Some(Typed::Str(campaign_id)) = params.get(&CAMPAIGN_ID_PARAM_KEY) {
if let Some(Typed::Int(campaign_difficulty)) = params.get(&CAMPAIGN_DIFFICULTY_PARAM_KEY) {
if let Some(Typed::Int(wave_number)) = params.get(&CAMPAIGN_WAVE_NUMBER_PARAM_KEYL) {
let user_lock = user.read().unwrap();
log::info!("User {} completed campaign {} difficulty {} wave {}", user_lock.user()?.token().uuid, campaign_id.string, campaign_difficulty, wave_number);
let user_info = user.user()?;
log::info!("User {} completed campaign {} difficulty {} wave {}", user_info.token().uuid, campaign_id.string, campaign_difficulty, wave_number);
// TODO save wave as completed
params.clear();
}

View File

@@ -1,9 +1,9 @@
use polariton_server::operations::Immediate;
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
const PARAM_KEY: u8 = 210;
pub(super) fn tech_tree_layout_provider(cubes: &crate::persist::config::ConfigImpl) -> Immediate<183, crate::UserTy> {
pub(super) fn tech_tree_layout_provider(cubes: &rc_core::ConfigImpl) -> Immediate<183, crate::UserTy> {
Immediate::new(|| {
let mut params = std::collections::HashMap::with_capacity(2);
params.insert(PARAM_KEY, cubes.tech_tree_nodes(&vec![

View File

@@ -7,8 +7,7 @@ const ADM_PARAM_KEY: u8 = 12;
pub(super) fn user_rights_provider() -> SimpleFunc<14, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
SimpleFunc::new(|params, user: &crate::UserTy| {
let lock = user.read().unwrap();
let user_info = lock.user()?;
let user_info = user.user()?;
let mut params = params.to_dict();
params.insert(MOD_PARAM_KEY, Typed::Bool(user_info.is_mod()));
params.insert(DEV_PARAM_KEY, Typed::Bool(user_info.is_dev()));

View File

@@ -1,10 +1,10 @@
use polariton_server::operations::Immediate;
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
pub const DEFAULT_WEAPON_ORDER_PARAM_KEY: u8 = 138;
pub(super) fn weapon_order_provider(conf: &crate::persist::config::ConfigImpl) -> Immediate<118, crate::UserTy> {
pub(super) fn weapon_order_provider(conf: &rc_core::ConfigImpl) -> Immediate<118, crate::UserTy> {
let weapon_orders = conf.weapon_keys();
Immediate::new(|| {
let mut params = std::collections::HashMap::with_capacity(1);

View File

@@ -1,9 +1,9 @@
use polariton_server::operations::Immediate;
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
const PARAM_KEY: u8 = 57;
pub(super) fn weapon_config_provider(cubes: &crate::persist::config::ConfigImpl) -> Immediate<47, crate::UserTy> {
pub(super) fn weapon_config_provider(cubes: &rc_core::ConfigImpl) -> Immediate<47, crate::UserTy> {
Immediate::new(|| {
let mut params = std::collections::HashMap::with_capacity(2);
params.insert(PARAM_KEY, cubes.weapon_list());

View File

@@ -1,9 +1,9 @@
use polariton_server::operations::Immediate;
use crate::persist::config::ConfigProvider;
use rc_core::ConfigProvider;
const PARAM_KEY: u8 = 38;
pub(super) fn weapons_upgrade_provider(cubes: &crate::persist::config::ConfigImpl) -> Immediate<82, crate::UserTy> {
pub(super) fn weapons_upgrade_provider(cubes: &rc_core::ConfigImpl) -> Immediate<82, crate::UserTy> {
Immediate::new(|| {
let mut params = std::collections::HashMap::with_capacity(2);
params.insert(PARAM_KEY, cubes.weapon_upgrade_list());

View File

@@ -11,3 +11,4 @@ clap.workspace = true
polariton.workspace = true
polariton_auth = { version = "*", path = "../polariton_auth" }
polariton_server.workspace = true
rc_core = { version = "*", path = "../rc_core" }

View File

@@ -11,6 +11,14 @@ pub struct CliArgs {
#[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,

View File

@@ -1,23 +0,0 @@
pub mod player_data;
pub(self) fn encode_7_bit_i32(mut src: i32) -> Vec<u8> {
if src == 0 { return vec![0] }
let mut out = Vec::with_capacity(5);
while src != 0 {
let last_7 = (src & 0x7F) as u8;
src = src >> 7;
if src != 0 {
out.push(last_7 | 0x80);
} else {
out.push(last_7);
}
}
out
}
pub(self) fn write_str_for_binreader(s: &str, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
let s_bytes = s.as_bytes();
let mut total_len = writer.write(&encode_7_bit_i32(s_bytes.len() as i32))?;
total_len += writer.write(s_bytes)?;
Ok(total_len)
}

View File

@@ -1,5 +1,4 @@
mod cli;
mod state;
mod data;
mod operations;
@@ -10,7 +9,7 @@ use tokio::net;
use polariton::packet::{Data, Message, Packet, StandardMessage};
use polariton::operation::{OperationResponse, Typed};
pub type UserTy = state::UserState;
pub type UserTy = rc_core::UserState<()>;
#[tokio::main]
async fn main() -> std::io::Result<()> {
@@ -18,6 +17,9 @@ async fn main() -> std::io::Result<()> {
let args = cli::CliArgs::get();
log::debug!("Got cli args {:?}", args);
let cubes = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data");
let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).expect("Bad user data"));
let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(), polariton_server::events::EventsHandler::new()));
let ip_addr: std::net::IpAddr = args.ip.parse().expect("Invalid IP address");
@@ -27,11 +29,11 @@ async fn main() -> std::io::Result<()> {
if args.once {
log::warn!("Handling first connection and then exiting");
let (socket, address) = listener.accept().await?;
process_socket(socket, address, server.clone()).await;
process_socket(socket, address, server.clone(), users.clone()).await;
} else {
loop {
let (socket, address) = listener.accept().await?;
tokio::spawn(process_socket(socket, address, server.clone()));
tokio::spawn(process_socket(socket, address, server.clone(), users.clone()));
}
}
server.join();
@@ -39,7 +41,7 @@ async fn main() -> std::io::Result<()> {
Ok(())
}
async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, server: std::sync::Arc<polariton_server::Server<crate::UserTy>>) {
async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, server: std::sync::Arc<polariton_server::Server<crate::UserTy>>, users: std::sync::Arc<rc_core::persist::user::UserImpl>) {
log::debug!("Accepting connection from address {}", address);
let enc = match do_connect_handshake(&mut socket).await {
Some(x) => x,
@@ -50,7 +52,7 @@ async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAdd
};
let (socket_r, socket_w) = socket.into_split();
let (chann_tx, chann_rx) = tokio::sync::mpsc::unbounded_channel();
let user_state = state::UserState::new(chann_tx.clone());
let user_state = 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);

View File

@@ -1,878 +1,37 @@
use polariton_server::operations::SimpleFunc;
use polariton::operation::{ParameterTable, Typed};
use crate::data::player_data::*;
const PARAM_KEY: u8 = 8;
const VALID_ROBOT: &[u8] = &[64,
0,
0,
0,
38,
190,
25,
77,
24,
6,
29,
0,
80,
135,
103,
211,
27,
4,
27,
6,
80,
135,
103,
211,
21,
4,
27,
12,
198,
224,
138,
13,
24,
4,
26,
0,
198,
224,
138,
13,
24,
4,
27,
22,
198,
224,
138,
13,
24,
4,
28,
22,
198,
224,
138,
13,
24,
4,
25,
0,
198,
224,
138,
13,
24,
4,
29,
0,
198,
224,
138,
13,
23,
4,
29,
0,
198,
224,
138,
13,
23,
4,
28,
0,
198,
224,
138,
13,
23,
4,
27,
0,
198,
224,
138,
13,
23,
4,
26,
22,
198,
224,
138,
13,
23,
4,
25,
22,
198,
224,
138,
13,
25,
4,
29,
0,
198,
224,
138,
13,
25,
4,
27,
22,
198,
224,
138,
13,
25,
4,
28,
22,
198,
224,
138,
13,
25,
4,
26,
22,
198,
224,
138,
13,
25,
4,
25,
22,
198,
224,
138,
13,
26,
4,
29,
0,
198,
224,
138,
13,
26,
4,
28,
22,
198,
224,
138,
13,
26,
4,
27,
22,
198,
224,
138,
13,
26,
4,
26,
22,
198,
224,
138,
13,
26,
4,
25,
22,
198,
224,
138,
13,
22,
4,
25,
0,
198,
224,
138,
13,
22,
4,
26,
0,
198,
224,
138,
13,
22,
4,
27,
23,
198,
224,
138,
13,
22,
4,
28,
23,
198,
224,
138,
13,
22,
4,
29,
0,
198,
224,
138,
13,
25,
5,
29,
0,
198,
224,
138,
13,
23,
5,
29,
0,
198,
224,
138,
13,
24,
5,
29,
0,
198,
224,
138,
13,
22,
5,
28,
0,
198,
224,
138,
13,
26,
5,
28,
0,
198,
224,
138,
13,
25,
5,
26,
0,
198,
224,
138,
13,
23,
5,
26,
0,
198,
224,
138,
13,
24,
4,
24,
22,
198,
224,
138,
13,
24,
4,
23,
22,
198,
224,
138,
13,
24,
4,
22,
22,
198,
224,
138,
13,
24,
4,
21,
22,
198,
224,
138,
13,
24,
4,
20,
22,
198,
224,
138,
13,
24,
4,
19,
22,
198,
224,
138,
13,
24,
4,
18,
22,
198,
224,
138,
13,
24,
4,
17,
22,
198,
224,
138,
13,
24,
4,
16,
22,
198,
224,
138,
13,
24,
4,
15,
22,
198,
224,
138,
13,
24,
4,
14,
22,
198,
224,
138,
13,
23,
4,
17,
12,
198,
224,
138,
13,
25,
4,
17,
6,
198,
224,
138,
13,
23,
4,
16,
12,
198,
224,
138,
13,
25,
4,
16,
6,
198,
224,
138,
13,
23,
4,
15,
12,
198,
224,
138,
13,
25,
4,
15,
6,
198,
224,
138,
13,
23,
4,
14,
12,
198,
224,
138,
13,
25,
4,
14,
6,
198,
224,
138,
13,
22,
4,
17,
12,
198,
224,
138,
13,
26,
4,
17,
6,
198,
224,
138,
13,
22,
4,
16,
12,
198,
224,
138,
13,
26,
4,
16,
6,
198,
224,
138,
13,
22,
4,
15,
12,
198,
224,
138,
13,
26,
4,
15,
6,
198,
224,
138,
13,
22,
4,
14,
12,
198,
224,
138,
13,
26,
4,
14,
6,
240,
75,
110,
137,
21,
4,
15,
12,
240,
75,
110,
137,
27,
4,
15,
6];
const VALID_COLOUR: &[u8] = &[64,
0,
0,
0,
0,
24,
6,
29,
0,
27,
4,
27,
0,
21,
4,
27,
0,
24,
4,
26,
0,
24,
4,
27,
0,
24,
4,
28,
0,
24,
4,
25,
0,
24,
4,
29,
0,
23,
4,
29,
0,
23,
4,
28,
0,
23,
4,
27,
0,
23,
4,
26,
0,
23,
4,
25,
0,
25,
4,
29,
0,
25,
4,
27,
0,
25,
4,
28,
0,
25,
4,
26,
0,
25,
4,
25,
0,
26,
4,
29,
0,
26,
4,
28,
0,
26,
4,
27,
0,
26,
4,
26,
0,
26,
4,
25,
0,
22,
4,
25,
0,
22,
4,
26,
0,
22,
4,
27,
0,
22,
4,
28,
0,
22,
4,
29,
1,
25,
5,
29,
1,
23,
5,
29,
1,
24,
5,
29,
1,
22,
5,
28,
1,
26,
5,
28,
1,
25,
5,
26,
1,
23,
5,
26,
0,
24,
4,
24,
0,
24,
4,
23,
0,
24,
4,
22,
0,
24,
4,
21,
0,
24,
4,
20,
0,
24,
4,
19,
0,
24,
4,
18,
0,
24,
4,
17,
0,
24,
4,
16,
0,
24,
4,
15,
0,
24,
4,
14,
0,
23,
4,
17,
0,
25,
4,
17,
0,
23,
4,
16,
0,
25,
4,
16,
0,
23,
4,
15,
0,
25,
4,
15,
0,
23,
4,
14,
0,
25,
4,
14,
0,
22,
4,
17,
0,
26,
4,
17,
0,
22,
4,
16,
0,
26,
4,
16,
0,
22,
4,
15,
0,
26,
4,
15,
0,
22,
4,
14,
0,
26,
4,
14,
0,
21,
4,
15,
0,
27,
4,
15];
pub(super) fn tdm_machines_provider() -> SimpleFunc<1, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
SimpleFunc::new(|params, user: &crate::UserTy| {
let ulock = user.auth.read().unwrap();
let ulock = user.user()?;
let mut params = params.to_dict();
params.insert(PARAM_KEY, PlayerDatas {
players: vec![
PlayerData {
name: ulock.uuid.clone(),
display_name: ulock.uuid.clone(),
mastery: 1,
tier: 1,
robot_name: "RE_machine_name_mine_sp".to_owned(),
robot_map: VALID_ROBOT.iter().map(|x| *x).collect(),
team: 0,
has_premium: false,
robot_uuid: "12345_12345".to_owned(),
cpu: 0,
weapon_order: vec![20000200, 0, 0],
colour_map: VALID_COLOUR.iter().map(|x| *x).collect(),
is_ai: false,
spawn_effect: "Spawn_Warp".to_owned(),
death_effect: "Explosion_Warp".to_owned(),
player_rank: 1,
weapon_rank: vec![(20000200, 1), (0, 0)].into_iter().collect(),
},
// TODO: use SingleplayerEvent 3 (SpawnRobot) to spawn enemy bots instead
// doing it through this op response seems to have a bug/flaw (intentional?) in the code
/*PlayerData {
name: "RE_username0".to_owned(),
display_name: "RE_displayname0".to_owned(),
mastery: 1,
tier: 1,
robot_name: "RE_machine_name0_sp".to_owned(),
robot_map: VALID_ROBOT.iter().map(|x| *x).collect(),
team: 1,
has_premium: false,
robot_uuid: "123_123".to_owned(),
cpu: 0,
weapon_order: vec![20000200, 0, 0],
colour_map: VALID_COLOUR.iter().map(|x| *x).collect(),
is_ai: true,
spawn_effect: "Spawn_Warp".to_owned(),
death_effect: "Explosion_Warp".to_owned(),
player_rank: 1,
weapon_rank: vec![(20000200, 1), (0, 0)].into_iter().collect(),
},
PlayerData {
name: "RE_username1".to_owned(),
display_name: "RE_displayname1".to_owned(),
mastery: 1,
tier: 1,
robot_name: "RE_machine_name1_sp".to_owned(),
robot_map: VALID_ROBOT.iter().map(|x| *x).collect(),
team: 1,
has_premium: false,
robot_uuid: "12_12".to_owned(),
cpu: 0,
weapon_order: vec![20000200, 0, 0],
colour_map: VALID_COLOUR.iter().map(|x| *x).collect(),
is_ai: true,
spawn_effect: "Spawn_Warp".to_owned(),
death_effect: "Explosion_Warp".to_owned(),
player_rank: 1,
weapon_rank: vec![(20000200, 1), (0, 0)].into_iter().collect(),
},*/
]
}.as_transmissible());
let event_tx = user.event_tx.clone();
params.insert(PARAM_KEY, ulock.singleplayer_robots()?);
let event_tx = user.event_sender();
let user_bot_data = ulock.slot_by_id(ulock.selected_garage_slot() as _)?;
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
log::debug!("Sending singleplayer events");
tokio::time::sleep(std::time::Duration::from_secs(20)).await;
log::debug!("Sending singleplayer event");
let mut spawn_params = std::collections::HashMap::with_capacity(4);
spawn_params.insert(2 /* robot GUID */, Typed::Str("12_12".into()));
spawn_params.insert(3 /* machine model */, Typed::Bytes(VALID_ROBOT.iter().map(|x| *x).collect::<Vec<_>>().into()));
spawn_params.insert(4 /* robot name */, Typed::Str("RE_robot_spawn_name0".into()));
spawn_params.insert(7 /* color model */, Typed::Bytes(VALID_COLOUR.iter().map(|x| *x).collect::<Vec<_>>().into()));
spawn_params.insert(2 /* robot GUID */, Typed::Str("1337_1337".into()));
spawn_params.insert(3 /* machine model */, user_bot_data.data);
spawn_params.insert(4 /* robot name */, Typed::Str("RE_robot_spawn_name0".into())); // FIXME
spawn_params.insert(7 /* color model */, user_bot_data.colour_data);
event_tx.send(polariton_server::ToSend::Data {
data: polariton::packet::Data::Event(polariton::operation::Event { code: 3, params: spawn_params.into() }),
encrypt: true,
channel: 0,
reliable: true,
}).unwrap();
let mut update_params = std::collections::HashMap::with_capacity(1);
/*let mut update_params = std::collections::HashMap::with_capacity(1);
update_params.insert(6 /* ??? */, Typed::Int(5));
event_tx.send(polariton_server::ToSend::Data {
data: polariton::packet::Data::Event(polariton::operation::Event { code: 5, params: update_params.into() }),
encrypt: true,
channel: 0,
reliable: true,
}).unwrap();
}).unwrap();*/
});
Ok(params.into())
})

View File

@@ -1,40 +0,0 @@
use std::sync::RwLock;
use tokio::sync::mpsc::UnboundedSender;
use polariton_server::ToSend;
#[derive(Debug, Default)]
pub struct UserAuthInfo {
pub uuid: String,
pub token: String,
pub refresh_token: String,
}
#[derive(Debug)]
pub struct UserState {
pub auth: RwLock<UserAuthInfo>,
pub event_tx: UnboundedSender<ToSend>,
}
impl UserState {
pub fn update_with_auth(&self, auth_str: &str) -> bool {
let splits: Vec<&str> = auth_str.split(';').collect();
if splits.len() != 3 {
log::warn!("Invalid auth payload: {}", auth_str);
false
} else {
let mut lock = self.auth.write().unwrap();
lock.uuid = splits[0].to_owned();
lock.token = splits[1].to_owned();
lock.refresh_token = splits[2].to_owned();
true
}
}
pub fn new(event_tx: UnboundedSender<ToSend>) -> crate::UserTy {
UserState {
auth: RwLock::new(Default::default()),
event_tx,
}
}
}

View File

@@ -11,3 +11,4 @@ clap.workspace = true
polariton.workspace = true
polariton_auth = { version = "*", path = "../polariton_auth" }
polariton_server.workspace = true
rc_core = { version = "*", path = "../rc_core" }

View File

@@ -11,6 +11,14 @@ pub struct CliArgs {
#[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,

View File

@@ -1,5 +1,4 @@
mod cli;
mod state;
mod data;
mod operations;
@@ -10,7 +9,7 @@ use tokio::net;
use polariton::packet::{Data, Message, Packet, StandardMessage};
use polariton::operation::{OperationResponse, Typed};
pub type UserTy = std::sync::RwLock<state::UserState>;
pub type UserTy = rc_core::UserState<crate::data::custom::CustomType>;
#[tokio::main]
async fn main() -> std::io::Result<()> {
@@ -18,6 +17,9 @@ async fn main() -> std::io::Result<()> {
let args = cli::CliArgs::get();
log::debug!("Got cli args {:?}", args);
let cubes = rc_core::persist::config::ConfigImpl::load(&args.assets).expect("Bad config data");
let users = std::sync::Arc::new(rc_core::persist::user::UserImpl::load(&args.data, &cubes).expect("Bad user data"));
let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler(), polariton_server::events::EventsHandler::new()));
let ip_addr: std::net::IpAddr = args.ip.parse().expect("Invalid IP address");
@@ -27,11 +29,11 @@ async fn main() -> std::io::Result<()> {
if args.once {
log::warn!("Handling first connection and then exiting");
let (socket, address) = listener.accept().await?;
process_socket(socket, address, server.clone()).await;
process_socket(socket, address, server.clone(), users.clone()).await;
} else {
loop {
let (socket, address) = listener.accept().await?;
tokio::spawn(process_socket(socket, address, server.clone()));
tokio::spawn(process_socket(socket, address, server.clone(), users.clone()));
}
}
server.join();
@@ -39,7 +41,7 @@ async fn main() -> std::io::Result<()> {
Ok(())
}
async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, server: std::sync::Arc<polariton_server::Server<crate::UserTy, crate::data::custom::CustomType>>) {
async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, server: std::sync::Arc<polariton_server::Server<crate::UserTy, crate::data::custom::CustomType>>, users: std::sync::Arc<rc_core::UserImpl>) {
log::debug!("Accepting connection from address {}", address);
let enc = match do_connect_handshake(&mut socket).await {
Some(x) => x,
@@ -49,10 +51,11 @@ async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAdd
}
};
let (socket_r, socket_w) = socket.into_split();
let user_state = state::UserState::new();
let (chann_tx, chann_rx) = tokio::sync::mpsc::unbounded_channel();
let user_state = rc_core::UserState::<crate::data::custom::CustomType>::new(users, chann_tx.clone());
let op_ctx = polariton::serdes::SerdesContext::<crate::data::custom::CustomType, crate::data::custom::CustomTypeSerdes>::default_const();
let ctx = polariton::packet::SerdesContext::from_boxed(op_ctx, enc);
server.handle_async(socket_r, socket_w, user_state, ctx).await;
server.handle_async_with_channel(socket_r, socket_w, user_state, ctx, chann_tx, chann_rx).await;
log::debug!("Goodbye connection from address {}", address);
}

View File

@@ -14,9 +14,8 @@ impl <C> Operation<C> for MoreLobbyAuth {
fn handle(&self, params: polariton::operation::ParameterTable<C>, _: &mut Self::State, user: &Self::User) -> polariton::operation::OperationResponse<C> {
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 write_lock.update_with_auth(&auth_payload.string) {
let mut resp_params = std::collections::HashMap::new();
if user.update_with_auth(&auth_payload.string) {
let mut resp_params = std::collections::HashMap::with_capacity(1);
resp_params.insert(Self::AUTH_PAYLOAD_KEY, polariton::operation::Typed::Byte(0));
return polariton::operation::OperationResponse {
code: 230,

View File

@@ -1,27 +0,0 @@
use std::sync::RwLock;
#[derive(Default, Debug)]
pub struct UserState {
pub uuid: String,
pub token: String,
pub refresh_token: String,
}
impl UserState {
pub fn update_with_auth(&mut self, auth_str: &str) -> bool {
let splits: Vec<&str> = auth_str.split(';').collect();
if splits.len() != 3 {
log::warn!("Invalid auth payload: {}", auth_str);
false
} else {
self.uuid = splits[0].to_owned();
self.token = splits[1].to_owned();
self.refresh_token = splits[2].to_owned();
true
}
}
pub fn new() -> crate::UserTy {
RwLock::new(UserState::default())
}
}