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

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,77 +0,0 @@
use polariton::operation::Typed;
pub struct PlayerData {
pub name: String,
pub display_name: String,
pub mastery: i32,
pub tier: i32,
pub robot_name: String,
pub robot_map: Vec<u8>,
// -- unused i32 here --
pub team: i32,
pub has_premium: bool,
pub robot_uuid: String,
pub cpu: i32,
pub weapon_order: Vec<i32>,
pub colour_map: Vec<u8>,
pub is_ai: bool,
pub spawn_effect: String,
pub death_effect: String,
pub player_rank: i32,
pub weapon_rank: std::collections::HashMap<i32, i32>,
}
impl PlayerData {
fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
let mut total_len = super::write_str_for_binreader(&self.name, writer)?;
total_len += super::write_str_for_binreader(&self.display_name, writer)?;
writer.write_all(&self.mastery.to_le_bytes())?;
writer.write_all(&self.tier.to_le_bytes())?;
total_len += super::write_str_for_binreader(&self.robot_name, writer)?;
writer.write_all(&(self.robot_map.len() as i32).to_le_bytes())?;
writer.write_all(&self.robot_map)?;
writer.write_all(&[0xDE, 0xAD, 0xBE, 0xEF])?;
writer.write_all(&self.team.to_le_bytes())?;
writer.write_all(&[self.has_premium as u8])?;
total_len += super::write_str_for_binreader(&self.robot_uuid, writer)?;
writer.write_all(&self.cpu.to_le_bytes())?;
writer.write_all(&(self.weapon_order.len() as i32).to_le_bytes())?;
for weapon_key in self.weapon_order.iter() {
writer.write_all(&weapon_key.to_le_bytes())?;
}
writer.write_all(&(self.colour_map.len() as i32).to_le_bytes())?;
writer.write_all(&self.colour_map)?;
writer.write_all(&[self.is_ai as u8])?;
total_len += super::write_str_for_binreader(&self.spawn_effect, writer)?;
total_len += super::write_str_for_binreader(&self.death_effect, writer)?;
writer.write_all(&self.player_rank.to_le_bytes())?;
writer.write_all(&(self.weapon_rank.len() as i32).to_le_bytes())?;
for (key, val) in self.weapon_rank.iter() {
writer.write_all(&key.to_le_bytes())?;
writer.write_all(&val.to_le_bytes())?;
}
Ok(42 + self.robot_map.len() + (self.weapon_order.len() * 4) + self.colour_map.len() + (self.weapon_rank.len() * 8) + total_len)
}
}
pub struct PlayerDatas {
pub players: Vec<PlayerData>,
}
impl PlayerDatas {
fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
writer.write_all(&(self.players.len() as i32).to_le_bytes())?;
let mut total_len = 4;
for data in self.players.iter() {
total_len += data.dump(writer)?;
}
Ok(total_len)
}
pub fn as_transmissible<C>(&self) -> Typed<C> {
let mut buf = Vec::new();
let write_size = self.dump(&mut std::io::Cursor::new(&mut buf)).unwrap();
log::debug!("PlayerDatas serialized to {} bytes: {:?}", write_size, buf);
Typed::Bytes(buf.into())
}
}

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,
}
}
}