mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Implement non-event part of singleplayer for #7
This commit is contained in:
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -1787,6 +1787,32 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rc_singleplayer"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
"polariton",
|
||||||
|
"polariton_auth",
|
||||||
|
"polariton_server",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rc_singleplayer_room"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
"polariton",
|
||||||
|
"polariton_auth",
|
||||||
|
"polariton_server",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rc_social"
|
name = "rc_social"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ members = [
|
|||||||
"rc_services", "rc_services_room",
|
"rc_services", "rc_services_room",
|
||||||
"rc_static_data", "rc_microtransactions",
|
"rc_static_data", "rc_microtransactions",
|
||||||
"rc_social", "rc_social_room",
|
"rc_social", "rc_social_room",
|
||||||
"rc_chat", "rc_chat_room"
|
"rc_chat", "rc_chat_room",
|
||||||
|
"rc_singleplayer", "rc_singleplayer_room",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
|||||||
@@ -6,7 +6,11 @@
|
|||||||
<setting name="WebServicesServerAddress">127.0.0.1:4532</setting>
|
<setting name="WebServicesServerAddress">127.0.0.1:4532</setting>
|
||||||
<setting name="WebServicesServerAddress__note__forwardedTo">127.0.0.1:4533</setting>
|
<setting name="WebServicesServerAddress__note__forwardedTo">127.0.0.1:4533</setting>
|
||||||
<setting name="SocialServerAddress">127.0.0.1:4534</setting>
|
<setting name="SocialServerAddress">127.0.0.1:4534</setting>
|
||||||
<setting name="ChatServerAddress">127.0.0.1:4535</setting>
|
<setting name="SocialServerAddress__note__forwardedTo">127.0.0.1:4535</setting>
|
||||||
|
<setting name="ChatServerAddress">127.0.0.1:4536</setting>
|
||||||
|
<setting name="ChatServerAddress__note__forwardedTo">127.0.0.1:4537</setting>
|
||||||
|
<setting name="SinglePlayerServerAddress">127.0.0.1:4538</setting>
|
||||||
|
<setting name="SinglePlayerServerAddress__note__forwardedTo">127.0.0.1:4539</setting>
|
||||||
<setting name="authUrl">http://127.0.0.1:8001/</setting>
|
<setting name="authUrl">http://127.0.0.1:8001/</setting>
|
||||||
<setting name="S3URL">http://127.0.0.1:8010/live/data.json</setting>
|
<setting name="S3URL">http://127.0.0.1:8010/live/data.json</setting>
|
||||||
</dev>
|
</dev>
|
||||||
|
|||||||
41
rc_services_room/src/data/game_mode.rs
Normal file
41
rc_services_room/src/data/game_mode.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use polariton::{operation::Typed, serdes::TypePrefix};
|
||||||
|
|
||||||
|
pub struct GameModeConfig {
|
||||||
|
pub respawn_heal_duration: f32,
|
||||||
|
pub respawn_full_heal_duration: f32,
|
||||||
|
pub kill_limit: i32,
|
||||||
|
pub game_time_minutes: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameModeConfig {
|
||||||
|
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||||
|
Typed::HashMap(vec![
|
||||||
|
(Typed::Str("respawnHealDuration".into()), Typed::Float(self.respawn_heal_duration)),
|
||||||
|
(Typed::Str("respawnFullHealDuration".into()), Typed::Float(self.respawn_full_heal_duration)),
|
||||||
|
(Typed::Str("killLimit".into()), Typed::Int(self.kill_limit)),
|
||||||
|
(Typed::Str("gameTimeMinutes".into()), Typed::Int(self.game_time_minutes)),
|
||||||
|
].into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GameModeConfigs {
|
||||||
|
pub battle_arena: GameModeConfig,
|
||||||
|
pub elimination: GameModeConfig,
|
||||||
|
pub the_pit: GameModeConfig,
|
||||||
|
pub team_deathmatch: GameModeConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameModeConfigs {
|
||||||
|
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||||
|
Typed::Dict(polariton::operation::Dict {
|
||||||
|
key_ty: TypePrefix::Str,
|
||||||
|
val_ty: TypePrefix::HashMap,
|
||||||
|
items: vec![
|
||||||
|
(Typed::Str("BattleArena".into()), self.battle_arena.as_transmissible()),
|
||||||
|
(Typed::Str("Elimination".into()), self.elimination.as_transmissible()),
|
||||||
|
(Typed::Str("ThePit".into()), self.the_pit.as_transmissible()),
|
||||||
|
(Typed::Str("TeamDeathmatch".into()), self.team_deathmatch.as_transmissible()),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,8 +25,11 @@ pub mod auto_regen;
|
|||||||
pub mod voting;
|
pub mod voting;
|
||||||
pub mod lobby;
|
pub mod lobby;
|
||||||
pub mod error_codes;
|
pub mod error_codes;
|
||||||
|
pub mod game_mode;
|
||||||
|
pub mod score_multipliers;
|
||||||
|
|
||||||
pub(self) fn encode_7_bit_i32(mut src: i32) -> Vec<u8> {
|
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);
|
let mut out = Vec::with_capacity(5);
|
||||||
while src != 0 {
|
while src != 0 {
|
||||||
let last_7 = (src & 0x7F) as u8;
|
let last_7 = (src & 0x7F) as u8;
|
||||||
|
|||||||
109
rc_services_room/src/data/score_multipliers.rs
Normal file
109
rc_services_room/src/data/score_multipliers.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
use polariton::operation::Typed;
|
||||||
|
|
||||||
|
pub struct ScoreMultipliersData {
|
||||||
|
pub max_cpu: f32,
|
||||||
|
pub stat_multipliers: std::collections::HashMap<InGameStat, ScoreMultiplier>,
|
||||||
|
pub completed_battle_base_multiplier: f32,
|
||||||
|
pub completed_battle_bonus_multiplier: f32,
|
||||||
|
pub delta_scaler: f32,
|
||||||
|
pub defeat_score: u32,
|
||||||
|
pub victory_score: u32,
|
||||||
|
pub max_score_ratio: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScoreMultipliersData {
|
||||||
|
fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
|
||||||
|
writer.write_all(&self.max_cpu.to_le_bytes())?;
|
||||||
|
for (key, val) in self.stat_multipliers.iter() {
|
||||||
|
writer.write_all(&(*key as u32).to_le_bytes())?;
|
||||||
|
writer.write_all(&val.base.to_le_bytes())?;
|
||||||
|
writer.write_all(&val.bonus.to_le_bytes())?;
|
||||||
|
}
|
||||||
|
writer.write_all(&self.completed_battle_base_multiplier.to_le_bytes())?;
|
||||||
|
writer.write_all(&self.completed_battle_bonus_multiplier.to_le_bytes())?;
|
||||||
|
writer.write_all(&self.delta_scaler.to_le_bytes())?;
|
||||||
|
writer.write_all(&self.defeat_score.to_le_bytes())?;
|
||||||
|
writer.write_all(&self.victory_score.to_le_bytes())?;
|
||||||
|
writer.write_all(&self.max_score_ratio.to_le_bytes())?;
|
||||||
|
Ok(28 + (12 * self.stat_multipliers.len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
self.dump(&mut std::io::Cursor::new(&mut buf)).unwrap();
|
||||||
|
Typed::Bytes(buf.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ScoreMultiplier {
|
||||||
|
pub base: f32,
|
||||||
|
pub bonus: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for ScoreMultiplier {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
base: 0.5,
|
||||||
|
bonus: 0.9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum InGameStat {
|
||||||
|
None = 0,
|
||||||
|
DestroyedCubes = 1,
|
||||||
|
DestroyedCubesInProtection = 2,
|
||||||
|
DestroyedCubesDefendingTheBase = 3,
|
||||||
|
Kill = 4,
|
||||||
|
KillAssist = 5,
|
||||||
|
HealCubes = 6,
|
||||||
|
HealAssist = 7,
|
||||||
|
DestroyedProtoniumCubes = 8,
|
||||||
|
BaseCaptureClassicMode = 9,
|
||||||
|
RobotDestroyed = 10,
|
||||||
|
Score = 11,
|
||||||
|
HealthPercentageBonusClassicMode = 12,
|
||||||
|
Points = 13,
|
||||||
|
CurrentKillStreak = 14,
|
||||||
|
BestKillStreak = 15,
|
||||||
|
CapturePointBattleArenaMode = 16,
|
||||||
|
EqualiserDestroyedBattleArenaMode = 17,
|
||||||
|
BattleArenaObjectives = 18,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for ScoreMultipliersData {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
max_cpu: 1000.0,
|
||||||
|
stat_multipliers: vec![
|
||||||
|
(InGameStat::DestroyedCubes, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::DestroyedCubesInProtection, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::DestroyedCubesDefendingTheBase, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::Kill, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::KillAssist, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::HealCubes, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::HealAssist, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::DestroyedProtoniumCubes, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::BaseCaptureClassicMode, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::RobotDestroyed, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::Score, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::HealthPercentageBonusClassicMode, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::Points, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::CurrentKillStreak, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::BestKillStreak, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::CapturePointBattleArenaMode, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::EqualiserDestroyedBattleArenaMode, ScoreMultiplier::default()),
|
||||||
|
(InGameStat::BattleArenaObjectives, ScoreMultiplier::default()),
|
||||||
|
].into_iter().collect(),
|
||||||
|
completed_battle_base_multiplier: 1.0,
|
||||||
|
completed_battle_bonus_multiplier: 1.2,
|
||||||
|
delta_scaler: 0.5,
|
||||||
|
defeat_score: 500,
|
||||||
|
victory_score: 2_000,
|
||||||
|
max_score_ratio: 2.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im
|
|||||||
items: vec![
|
items: vec![
|
||||||
CustomisationData {
|
CustomisationData {
|
||||||
id: "RC_MothershipSkin_Neptune_01".to_string(),
|
id: "RC_MothershipSkin_Neptune_01".to_string(),
|
||||||
localised_name: "Neptune 01".to_string(),
|
localised_name: "strNeptune".to_string(),
|
||||||
skin_scene_name: "RC_MothershipSkin_Neptune_01".to_string(),
|
skin_scene_name: "RC_MothershipSkin_Neptune_01".to_string(),
|
||||||
simulation_prefab: "TODO_sim_prefab".to_string(),
|
simulation_prefab: "TODO_sim_prefab".to_string(),
|
||||||
preview_image_name: "TODO_preview_img".to_string(),
|
preview_image_name: "TODO_preview_img".to_string(),
|
||||||
@@ -31,12 +31,61 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im
|
|||||||
params.insert(SPAWNS_KEY, Typed::Arr(Arr {
|
params.insert(SPAWNS_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::HashMap, // hashtable
|
ty: TypePrefix::HashMap, // hashtable
|
||||||
items: vec![
|
items: vec![
|
||||||
|
// TODO set these up with the correct values (IDs are correct)
|
||||||
CustomisationData {
|
CustomisationData {
|
||||||
id: "spawn0".to_string(),
|
id: "Spawn".to_string(),
|
||||||
localised_name: "Default".to_string(),
|
localised_name: "strSpawnFXWarpIn".to_string(),
|
||||||
skin_scene_name: "TODO_skin".to_string(),
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
simulation_prefab: "TODO_sim_prefab".to_string(),
|
simulation_prefab: "Respawn_WarpIn".to_string(),
|
||||||
preview_image_name: "TODO_preview_img".to_string(),
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Spawn_BlackHole".to_string(),
|
||||||
|
localised_name: "strSpawnFXWarpIn".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Respawn_WarpIn".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Spawn_Lander".to_string(),
|
||||||
|
localised_name: "strSpawnFXWarpIn".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Respawn_WarpIn".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Spawn_Lootcrate".to_string(),
|
||||||
|
localised_name: "strSpawnFXWarpIn".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Respawn_WarpIn".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Spawn_Warp".to_string(),
|
||||||
|
localised_name: "strSpawnFXWarpIn".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Respawn_WarpIn".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Spawn_Present".to_string(),
|
||||||
|
localised_name: "strSpawnFXWarpIn".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Respawn_WarpIn".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Spawn_EasterEgg".to_string(),
|
||||||
|
localised_name: "strSpawnFXWarpIn".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Respawn_WarpIn".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
is_default: true,
|
is_default: true,
|
||||||
}.as_transmissible(),
|
}.as_transmissible(),
|
||||||
],
|
],
|
||||||
@@ -44,12 +93,61 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im
|
|||||||
params.insert(DEATHS_KEY, Typed::Arr(Arr {
|
params.insert(DEATHS_KEY, Typed::Arr(Arr {
|
||||||
ty: TypePrefix::HashMap, // hashtable
|
ty: TypePrefix::HashMap, // hashtable
|
||||||
items: vec![
|
items: vec![
|
||||||
|
// TODO set these up with the correct values (IDs are correct)
|
||||||
CustomisationData {
|
CustomisationData {
|
||||||
id: "death0".to_string(),
|
id: "Explosion".to_string(),
|
||||||
localised_name: "Default".to_string(),
|
localised_name: "strDeathFXEmergencyWarp".to_string(),
|
||||||
skin_scene_name: "TODO_skin".to_string(),
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
simulation_prefab: "TODO_sim_prefab".to_string(),
|
simulation_prefab: "Death_WarpOut".to_string(),
|
||||||
preview_image_name: "TODO_preview_img".to_string(),
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Explosion_Toon".to_string(),
|
||||||
|
localised_name: "strDeathFXEmergencyWarp".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Death_WarpOut".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Explosion_Feathers_Rainbow".to_string(),
|
||||||
|
localised_name: "strDeathFXEmergencyWarp".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Death_WarpOut".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Explosion_Nuclear".to_string(),
|
||||||
|
localised_name: "strDeathFXEmergencyWarp".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Death_WarpOut".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Explosion_Warp".to_string(),
|
||||||
|
localised_name: "strDeathFXEmergencyWarp".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Death_WarpOut".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Explosion_BlackHole".to_string(),
|
||||||
|
localised_name: "strDeathFXEmergencyWarp".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Death_WarpOut".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
|
is_default: true,
|
||||||
|
}.as_transmissible(),
|
||||||
|
CustomisationData {
|
||||||
|
id: "Explosion_Firework".to_string(),
|
||||||
|
localised_name: "strDeathFXEmergencyWarp".to_string(),
|
||||||
|
skin_scene_name: "Splash_Loading_Screen".to_string(),
|
||||||
|
simulation_prefab: "Death_WarpOut".to_string(),
|
||||||
|
preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(),
|
||||||
is_default: true,
|
is_default: true,
|
||||||
}.as_transmissible(),
|
}.as_transmissible(),
|
||||||
],
|
],
|
||||||
|
|||||||
15
rc_services_room/src/operations/game_mode_config.rs
Normal file
15
rc_services_room/src/operations/game_mode_config.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use polariton_server::operations::SimpleFunc;
|
||||||
|
use polariton::operation::ParameterTable;
|
||||||
|
|
||||||
|
use crate::persist::config::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> {
|
||||||
|
let game_config = conf.game_mode_config();
|
||||||
|
SimpleFunc::new(move |params, _| {
|
||||||
|
let mut params = params.to_dict();
|
||||||
|
params.insert(PARAM_KEY, game_config.clone());
|
||||||
|
Ok(params.into())
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -76,6 +76,9 @@ mod regen_config;
|
|||||||
mod pageantry;
|
mod pageantry;
|
||||||
mod signup_time;
|
mod signup_time;
|
||||||
mod validate_machine;
|
mod validate_machine;
|
||||||
|
mod game_mode_config;
|
||||||
|
mod score_multipliers_config;
|
||||||
|
mod player_robot_rank;
|
||||||
|
|
||||||
use polariton_server::operations::OperationsHandler;
|
use polariton_server::operations::OperationsHandler;
|
||||||
|
|
||||||
@@ -170,4 +173,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
|
|||||||
.without_state(pageantry::after_battle_vote_thresholds_provider(&init_ctx.cubes))
|
.without_state(pageantry::after_battle_vote_thresholds_provider(&init_ctx.cubes))
|
||||||
.without_state(signup_time::user_signup_date_provider())
|
.without_state(signup_time::user_signup_date_provider())
|
||||||
.without_state(validate_machine::validate_robot_provider())
|
.without_state(validate_machine::validate_robot_provider())
|
||||||
|
.without_state(game_mode_config::game_mode_config_provider(&init_ctx.cubes))
|
||||||
|
.without_state(score_multipliers_config::tdm_ai_score_config_provider())
|
||||||
|
.without_state(player_robot_rank::player_robot_rank_provider())
|
||||||
}
|
}
|
||||||
|
|||||||
23
rc_services_room/src/operations/player_robot_rank.rs
Normal file
23
rc_services_room/src/operations/player_robot_rank.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use polariton_server::operations::SimpleFunc;
|
||||||
|
use polariton::operation::{ParameterTable, Typed};
|
||||||
|
|
||||||
|
const USERNAME_PARAM_KEY: u8 = 30; // in; str
|
||||||
|
const RANK_PARAM_KEY: u8 = 84; // out; int
|
||||||
|
const CPU_PARAM_KEY: u8 = 177; // out; int
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
let robot = user.slot_by_id(user.selected_garage_slot() as i32)?;
|
||||||
|
params.insert(RANK_PARAM_KEY, robot.robot_rank);
|
||||||
|
params.insert(CPU_PARAM_KEY, robot.cpu);
|
||||||
|
params.insert(COSMETIC_CPU_PARAM_KEY, robot.cosmetic_cpu);
|
||||||
|
Ok(params.into())
|
||||||
|
})
|
||||||
|
}
|
||||||
15
rc_services_room/src/operations/score_multipliers_config.rs
Normal file
15
rc_services_room/src/operations/score_multipliers_config.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use polariton_server::operations::SimpleFunc;
|
||||||
|
use polariton::operation::ParameterTable;
|
||||||
|
|
||||||
|
use crate::data::score_multipliers::*;
|
||||||
|
|
||||||
|
const PARAM_KEY: u8 = 137;
|
||||||
|
|
||||||
|
pub(super) fn tdm_ai_score_config_provider(/*conf: &crate::persist::config::ConfigImpl*/) -> SimpleFunc<117, 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();
|
||||||
|
params.insert(PARAM_KEY, ScoreMultipliersData::default().as_transmissible());
|
||||||
|
Ok(params.into())
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ use serde::{Serialize, Deserialize};
|
|||||||
pub struct BattleConfig {
|
pub struct BattleConfig {
|
||||||
pub regen: AutoRegenHealth,
|
pub regen: AutoRegenHealth,
|
||||||
pub votes: HashMap<Vote, Vec<VoteThreshold>>,
|
pub votes: HashMap<Vote, Vec<VoteThreshold>>,
|
||||||
|
#[serde(default = "default_game_modes")]
|
||||||
|
pub games: GameModes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
@@ -60,3 +62,70 @@ impl std::convert::Into<crate::data::voting::Vote> for Vote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
|
||||||
|
pub struct GameMode {
|
||||||
|
pub respawn_heal_duration: f32,
|
||||||
|
pub respawn_full_heal_duration: f32,
|
||||||
|
pub kill_limit: i32,
|
||||||
|
pub game_time_m: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::Into<crate::data::game_mode::GameModeConfig> for GameMode {
|
||||||
|
fn into(self) -> crate::data::game_mode::GameModeConfig {
|
||||||
|
crate::data::game_mode::GameModeConfig {
|
||||||
|
respawn_heal_duration: self.respawn_heal_duration,
|
||||||
|
respawn_full_heal_duration: self.respawn_full_heal_duration,
|
||||||
|
kill_limit: self.kill_limit,
|
||||||
|
game_time_minutes: self.game_time_m,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
|
||||||
|
pub struct GameModes {
|
||||||
|
pub battle_arena: GameMode,
|
||||||
|
pub elimination: GameMode,
|
||||||
|
pub pit: GameMode,
|
||||||
|
pub team_deathmatch: GameMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::Into<crate::data::game_mode::GameModeConfigs> for GameModes {
|
||||||
|
fn into(self) -> crate::data::game_mode::GameModeConfigs {
|
||||||
|
crate::data::game_mode::GameModeConfigs {
|
||||||
|
battle_arena: self.battle_arena.into(),
|
||||||
|
elimination: self.elimination.into(),
|
||||||
|
the_pit: self.pit.into(),
|
||||||
|
team_deathmatch: self.team_deathmatch.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_game_modes() -> GameModes {
|
||||||
|
GameModes {
|
||||||
|
battle_arena: GameMode {
|
||||||
|
respawn_heal_duration: 10.0,
|
||||||
|
respawn_full_heal_duration: 10.0,
|
||||||
|
kill_limit: 0,
|
||||||
|
game_time_m: 20,
|
||||||
|
},
|
||||||
|
elimination: GameMode {
|
||||||
|
respawn_heal_duration: 10.0,
|
||||||
|
respawn_full_heal_duration: 10.0,
|
||||||
|
kill_limit: 10,
|
||||||
|
game_time_m: 10,
|
||||||
|
},
|
||||||
|
pit: GameMode {
|
||||||
|
respawn_heal_duration: 20.0,
|
||||||
|
respawn_full_heal_duration: 20.0,
|
||||||
|
kill_limit: 15,
|
||||||
|
game_time_m: 15,
|
||||||
|
},
|
||||||
|
team_deathmatch: GameMode {
|
||||||
|
respawn_heal_duration: 10.0,
|
||||||
|
respawn_full_heal_duration: 10.0,
|
||||||
|
kill_limit: 10,
|
||||||
|
game_time_m: 10,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -177,4 +177,9 @@ impl <C: Clone> super::ConfigProvider<C> for CubeConfig {
|
|||||||
items: vote_data,
|
items: vote_data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn game_mode_config(&self) -> Typed<C> {
|
||||||
|
let game_mode_data: crate::data::game_mode::GameModeConfigs = self.battle.games.into();
|
||||||
|
game_mode_data.as_transmissible()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ pub trait ConfigProvider<C> {
|
|||||||
fn ids(&self) -> Vec<u32>;
|
fn ids(&self) -> Vec<u32>;
|
||||||
fn regen_config(&self) -> Typed<C>;
|
fn regen_config(&self) -> Typed<C>;
|
||||||
fn after_battle_vote_config(&self) -> Typed<C>;
|
fn after_battle_vote_config(&self) -> Typed<C>;
|
||||||
|
fn game_mode_config(&self) -> Typed<C>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,6 +150,9 @@ impl <C: Clone> super::User<C> for UserData {
|
|||||||
control_type: polariton::operation::Typed::Int(control_ty as _),
|
control_type: polariton::operation::Typed::Int(control_ty as _),
|
||||||
control_options: control_options.as_transmissible(),
|
control_options: control_options.as_transmissible(),
|
||||||
mastery_level: polariton::operation::Typed::Int(0), // TODO
|
mastery_level: polariton::operation::Typed::Int(0), // TODO
|
||||||
|
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 _),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ pub struct UserSlotData<C> {
|
|||||||
pub control_type: polariton::operation::Typed<C>,
|
pub control_type: polariton::operation::Typed<C>,
|
||||||
pub control_options: polariton::operation::Typed<C>,
|
pub control_options: polariton::operation::Typed<C>,
|
||||||
pub mastery_level: polariton::operation::Typed<C>,
|
pub mastery_level: polariton::operation::Typed<C>,
|
||||||
|
pub robot_rank: polariton::operation::Typed<C>,
|
||||||
|
pub cpu: polariton::operation::Typed<C>,
|
||||||
|
pub cosmetic_cpu: polariton::operation::Typed<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VehicleData {
|
pub struct VehicleData {
|
||||||
|
|||||||
13
rc_singleplayer/Cargo.toml
Normal file
13
rc_singleplayer/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "rc_singleplayer"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[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
|
||||||
|
polariton_auth = { version = "*", path = "../polariton_auth" }
|
||||||
|
polariton_server.workspace = true
|
||||||
3
rc_singleplayer/build_arm64.sh
Executable file
3
rc_singleplayer/build_arm64.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cargo build --release --target aarch64-unknown-linux-musl
|
||||||
3
rc_singleplayer/run_debug.sh
Executable file
3
rc_singleplayer/run_debug.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
RUST_BACKTRACE=1 RUST_LOG=debug cargo run -- -1
|
||||||
31
rc_singleplayer/src/cli.rs
Normal file
31
rc_singleplayer/src/cli.rs
Normal file
@@ -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 = 4538)]
|
||||||
|
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:4539".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()
|
||||||
|
}
|
||||||
|
}
|
||||||
263
rc_singleplayer/src/main.rs
Normal file
263
rc_singleplayer/src/main.rs
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
mod cli;
|
||||||
|
mod state;
|
||||||
|
|
||||||
|
use 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 sock_state = state::State::new(enc);
|
||||||
|
while let Ok(packet) = polariton_server::utils::receive_packet_async(&mut socket, &sock_state.serdes_ctx()).await {
|
||||||
|
match packet {
|
||||||
|
Packet::Ping(ping) => {
|
||||||
|
polariton_server::utils::handle_ping_async(ping, &mut socket, &sock_state.serdes_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 = "SinglePlayerServer";
|
||||||
|
|
||||||
|
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 polariton_auth::AuthProvider<AuthError> for AuthImpl {
|
||||||
|
fn validate(&mut self, params: &std::collections::HashMap<u8, Typed>) -> Result<std::collections::HashMap<u8, Typed>, 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::<u8, Typed>::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<polariton_auth::CryptoImpl> {
|
||||||
|
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, &crypto) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(h) => match h.extra {
|
||||||
|
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::<u8, Typed>::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(crypto)
|
||||||
|
}
|
||||||
17
rc_singleplayer/src/state.rs
Normal file
17
rc_singleplayer/src/state.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const SERDES_SERDES_CTX_REF: &'static polariton::serdes::SerdesContext<(), polariton::serdes::NoCustomSerdes> = &polariton::serdes::SerdesContext::default_const();
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
pub crypto: polariton_auth::CryptoImpl,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn new(c: polariton_auth::CryptoImpl) -> Self {
|
||||||
|
Self {
|
||||||
|
crypto: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serdes_ctx(&self) -> polariton::packet::SerdesContext<'_, (), polariton::serdes::NoCustomSerdes> {
|
||||||
|
polariton::packet::SerdesContext::new(SERDES_SERDES_CTX_REF, &self.crypto)
|
||||||
|
}
|
||||||
|
}
|
||||||
13
rc_singleplayer_room/Cargo.toml
Normal file
13
rc_singleplayer_room/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "rc_singleplayer_room"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[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
|
||||||
|
polariton_auth = { version = "*", path = "../polariton_auth" }
|
||||||
|
polariton_server.workspace = true
|
||||||
3
rc_singleplayer_room/build_arm64.sh
Executable file
3
rc_singleplayer_room/build_arm64.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cargo build --release --target aarch64-unknown-linux-musl
|
||||||
3
rc_singleplayer_room/run_debug.sh
Executable file
3
rc_singleplayer_room/run_debug.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
RUST_BACKTRACE=1 RUST_LOG=debug cargo run -- -1
|
||||||
23
rc_singleplayer_room/src/cli.rs
Normal file
23
rc_singleplayer_room/src/cli.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
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 = 4539)]
|
||||||
|
pub port: u16,
|
||||||
|
|
||||||
|
/// IP Address on which to accept connections
|
||||||
|
#[arg(long, default_value_t = {"127.0.0.1".to_string()})]
|
||||||
|
pub ip: String,
|
||||||
|
|
||||||
|
/// Handle one connection and then exit
|
||||||
|
#[arg(short = '1', long)]
|
||||||
|
pub once: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CliArgs {
|
||||||
|
pub fn get() -> Self {
|
||||||
|
Self::parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
23
rc_singleplayer_room/src/data/mod.rs
Normal file
23
rc_singleplayer_room/src/data/mod.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
77
rc_singleplayer_room/src/data/player_data.rs
Normal file
77
rc_singleplayer_room/src/data/player_data.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
256
rc_singleplayer_room/src/main.rs
Normal file
256
rc_singleplayer_room/src/main.rs
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
mod cli;
|
||||||
|
mod state;
|
||||||
|
|
||||||
|
mod data;
|
||||||
|
mod operations;
|
||||||
|
|
||||||
|
use polariton_auth::Handshake;
|
||||||
|
use tokio::net;
|
||||||
|
|
||||||
|
use polariton::packet::{Data, Message, Packet, StandardMessage};
|
||||||
|
use polariton::operation::{OperationResponse, Typed};
|
||||||
|
|
||||||
|
pub type UserTy = std::sync::RwLock<state::UserState>;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
env_logger::init();
|
||||||
|
let args = cli::CliArgs::get();
|
||||||
|
log::debug!("Got cli args {:?}", args);
|
||||||
|
|
||||||
|
let server = std::sync::Arc::new(polariton_server::Server::new(operations::handler()));
|
||||||
|
|
||||||
|
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).await;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
loop {
|
||||||
|
let (socket, address) = listener.accept().await?;
|
||||||
|
tokio::spawn(process_socket(socket, address, server.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn process_socket(mut socket: net::TcpStream, address: std::net::SocketAddr, server: std::sync::Arc<polariton_server::Server<crate::UserTy>>) {
|
||||||
|
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 user_state = state::UserState::new();
|
||||||
|
server.handle_async(socket, user_state, enc, Default::default()).await;
|
||||||
|
log::debug!("Goodbye connection from address {}", address);
|
||||||
|
}
|
||||||
|
|
||||||
|
const APP_ID: &str = "SinglePlayerServer";
|
||||||
|
|
||||||
|
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 polariton_auth::AuthProvider<AuthError> for AuthImpl {
|
||||||
|
fn validate(&mut self, params: &std::collections::HashMap<u8, Typed>) -> Result<std::collections::HashMap<u8, Typed>, 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::<u8, Typed>::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<polariton_auth::CryptoImpl> {
|
||||||
|
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, &crypto) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(h) => match h.extra {
|
||||||
|
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::<u8, Typed>::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(crypto)
|
||||||
|
}
|
||||||
23
rc_singleplayer_room/src/operations/eac.rs
Normal file
23
rc_singleplayer_room/src/operations/eac.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use polariton_server::operations::{Operation, OperationCode};
|
||||||
|
|
||||||
|
pub struct EacChallengeIgnorer;
|
||||||
|
|
||||||
|
impl <C> Operation<C> for EacChallengeIgnorer {
|
||||||
|
type State = ();
|
||||||
|
type User = crate::UserTy;
|
||||||
|
|
||||||
|
fn handle(&self, params: polariton::operation::ParameterTable<C>, _: &mut Self::State, _: &Self::User) -> polariton::operation::OperationResponse<C> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
855
rc_singleplayer_room/src/operations/load_ai_robots.rs
Normal file
855
rc_singleplayer_room/src/operations/load_ai_robots.rs
Normal file
@@ -0,0 +1,855 @@
|
|||||||
|
use polariton_server::operations::SimpleFunc;
|
||||||
|
use polariton::operation::ParameterTable;
|
||||||
|
|
||||||
|
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.read().unwrap();
|
||||||
|
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: "1_1".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());
|
||||||
|
Ok(params.into())
|
||||||
|
})
|
||||||
|
}
|
||||||
13
rc_singleplayer_room/src/operations/mod.rs
Normal file
13
rc_singleplayer_room/src/operations/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
mod more_auth;
|
||||||
|
mod eac;
|
||||||
|
mod load_ai_robots;
|
||||||
|
|
||||||
|
use polariton_server::operations::OperationsHandler;
|
||||||
|
|
||||||
|
pub fn handler() -> OperationsHandler<crate::UserTy> {
|
||||||
|
OperationsHandler::<crate::UserTy>::new()
|
||||||
|
.without_state(more_auth::MoreLobbyAuth)
|
||||||
|
.without_state(eac::EacChallengeIgnorer)
|
||||||
|
.without_state(load_ai_robots::tdm_machines_provider())
|
||||||
|
//.without_state(polariton_server::operations::Ack::<33, _>::default()) // get user clan info (this is equivalent to not being in a clan)
|
||||||
|
}
|
||||||
42
rc_singleplayer_room/src/operations/more_auth.rs
Normal file
42
rc_singleplayer_room/src/operations/more_auth.rs
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <C> Operation<C> for MoreLobbyAuth {
|
||||||
|
type State = ();
|
||||||
|
type User = crate::UserTy;
|
||||||
|
|
||||||
|
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();
|
||||||
|
resp_params.insert(Self::AUTH_PAYLOAD_KEY, polariton::operation::Typed::Byte(0));
|
||||||
|
return polariton::operation::OperationResponse {
|
||||||
|
code: 230,
|
||||||
|
return_code: 0,
|
||||||
|
message: polariton::operation::Typed::Null,
|
||||||
|
params: resp_params.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
polariton::operation::OperationResponse {
|
||||||
|
code: 230,
|
||||||
|
return_code: 120,
|
||||||
|
message: polariton::operation::Typed::Null,
|
||||||
|
params: std::collections::HashMap::new().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OperationCode for MoreLobbyAuth {
|
||||||
|
fn op_code() -> u8 {
|
||||||
|
230
|
||||||
|
}
|
||||||
|
}
|
||||||
27
rc_singleplayer_room/src/state.rs
Normal file
27
rc_singleplayer_room/src/state.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
use polariton_server::operations::SimpleFunc;
|
use polariton_server::operations::SimpleFunc;
|
||||||
use polariton::operation::ParameterTable;
|
use polariton::operation::ParameterTable;
|
||||||
|
|
||||||
//const PLATOON_ID_PARAM_KEY: u8 = 16;
|
const PLATOON_ID_PARAM_KEY: u8 = 16;
|
||||||
//const PLATOON_LEADER_PARAM_KEY: u8 = 17;
|
//const PLATOON_LEADER_PARAM_KEY: u8 = 17;
|
||||||
//const USER_LIST_PARAM_KEY: u8 = 7;
|
//const USER_LIST_PARAM_KEY: u8 = 7;
|
||||||
|
|
||||||
pub(super) fn platoon_provider<C: Send + Sync>() -> SimpleFunc<18, crate::UserTy, impl (Fn(ParameterTable<C>, &crate::UserTy) -> Result<ParameterTable<C>, i16>) + Sync + Sync, C> {
|
pub(super) fn platoon_provider<C: Send + Sync>() -> SimpleFunc<18, crate::UserTy, impl (Fn(ParameterTable<C>, &crate::UserTy) -> Result<ParameterTable<C>, i16>) + Sync + Sync, C> {
|
||||||
SimpleFunc::new(|params, _| {
|
SimpleFunc::new(|params, _| {
|
||||||
//let mut params = params.to_dict();
|
let mut params = params.to_dict();
|
||||||
// if platoon ID is not provided, you're not in a platoon
|
// if platoon ID is not provided, you're not in a platoon
|
||||||
//Ok(params.into())
|
params.insert(PLATOON_ID_PARAM_KEY, polariton::operation::Typed::Null);
|
||||||
Ok(params)
|
Ok(params.into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user