Skip to content

Commit beac442

Browse files
committed
feat(radar-client): automatically reconnect to the server on connection failure
1 parent ff396b3 commit beac442

File tree

10 files changed

+627
-338
lines changed

10 files changed

+627
-338
lines changed

radar/client-standalone/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ log = { workspace = true }
1313
obfstr.workspace = true
1414
radar-client = { path = "../client" }
1515
radar-shared = { path = "../shared" }
16-
tokio = { version = "1.34.0", features = ["io-util", "rt-multi-thread", "net"] }
16+
tokio = { version = "1.34.0", features = ["io-util", "rt-multi-thread", "net", "signal"] }
1717
url = "2.5.0"
1818
utils-state = { path = "../../utils/state" }
1919

radar/client-standalone/src/main.rs

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::path::PathBuf;
1+
use std::{
2+
path::PathBuf,
3+
time::Duration,
4+
};
25

36
use anyhow::Context;
47
use clap::Parser;
@@ -11,8 +14,11 @@ use cs2::{
1114
use obfstr::obfstr;
1215
use radar_client::{
1316
CS2RadarGenerator,
17+
DummyRadarGenerator,
18+
RadarGenerator,
1419
WebRadarPublisher,
1520
};
21+
use tokio::signal;
1622
use url::Url;
1723
use utils_state::StateRegistry;
1824

@@ -31,6 +37,11 @@ struct Args {
3137
/// instead of resolving them at runtime by the CS2 schema system.
3238
#[arg(short, long)]
3339
schema_file: Option<PathBuf>,
40+
41+
/// Use a dummy generator instead of generating the radar data from CS2.
42+
/// This is usefull when testing the radar client without CS2.
43+
#[arg(long, hide = true)]
44+
dummy_generator: bool,
3445
}
3546

3647
#[tokio::main]
@@ -49,10 +60,20 @@ async fn main() -> anyhow::Result<()> {
4960
Ok(())
5061
}
5162

63+
const RECONNECT_INTERVAL: &[Duration] = &[
64+
Duration::from_secs(1),
65+
Duration::from_secs(5),
66+
Duration::from_secs(10),
67+
Duration::from_secs(30),
68+
Duration::from_secs(60),
69+
];
70+
5271
async fn real_main(args: &Args) -> anyhow::Result<()> {
5372
let url = Url::parse(&args.publish_url).context("invalid target server address")?;
5473

55-
let radar_generator = {
74+
let radar_generator: Box<dyn RadarGenerator> = if args.dummy_generator {
75+
Box::new(DummyRadarGenerator)
76+
} else {
5677
let cs2 = match CS2Handle::create(true) {
5778
Ok(cs2) => cs2,
5879
Err(err) => {
@@ -93,7 +114,16 @@ async fn real_main(args: &Args) -> anyhow::Result<()> {
93114

94115
Box::new(CS2RadarGenerator::new(states)?)
95116
};
96-
let radar_client = WebRadarPublisher::connect(radar_generator, &url).await?;
117+
118+
self::radar_publish_loop(radar_generator, &url).await
119+
}
120+
121+
async fn radar_publish_loop(
122+
radar_generator: Box<dyn RadarGenerator>,
123+
url: &Url,
124+
) -> anyhow::Result<()> {
125+
let mut radar_client = WebRadarPublisher::connect(&url, None).await?;
126+
radar_client.set_generator(radar_generator);
97127

98128
let mut radar_url = url.clone();
99129
radar_url.set_path(&format!("/session/{}", radar_client.session_id));
@@ -105,7 +135,56 @@ async fn real_main(args: &Args) -> anyhow::Result<()> {
105135

106136
log::info!("Radar session {}", radar_client.session_id);
107137
log::info!("Available at {}", radar_url);
138+
log::info!("Press CTRL+C to exit");
139+
140+
loop {
141+
tokio::select! {
142+
result = radar_client.execute() => {
143+
match result {
144+
Ok(_) => break,
145+
Err(error) => {
146+
log::error!("{:#}", error);
147+
}
148+
}
149+
},
150+
_ = signal::ctrl_c() => {
151+
log::info!("Stopping radar...");
152+
break;
153+
}
154+
}
155+
156+
let radar_generator = radar_client.take_generator().context("missing generator")?;
157+
let session_auth_token = radar_client.session_auth_token.clone();
158+
159+
/* ensure to close the connection in order to reconnect */
160+
drop(radar_client);
161+
162+
let mut reconnect_index = 0;
163+
radar_client = loop {
164+
log::info!("Reconnecting...");
165+
match WebRadarPublisher::connect(&url, Some(session_auth_token.clone())).await {
166+
Ok(publisher) => break publisher,
167+
Err(error) => {
168+
log::error!("Reconnect failed: {:#}", error);
169+
if reconnect_index >= RECONNECT_INTERVAL.len() {
170+
anyhow::bail!("reconnect failed");
171+
}
172+
173+
let timeout = RECONNECT_INTERVAL[reconnect_index];
174+
reconnect_index += 1;
175+
176+
log::error!("Try again in {:#?}", timeout);
177+
tokio::time::sleep(timeout).await;
178+
}
179+
}
180+
};
181+
radar_client.set_generator(radar_generator);
182+
log::info!(
183+
"Successfully reconnected (session id = {})",
184+
radar_client.session_id
185+
);
186+
}
108187

109-
radar_client.await.context("radar error")?;
188+
radar_client.close_connection().await;
110189
Ok(())
111190
}

0 commit comments

Comments
 (0)