Skip to content

Commit

Permalink
Implements server side ping
Browse files Browse the repository at this point in the history
  • Loading branch information
klautcomputing committed Jul 23, 2024
1 parent 7269ba6 commit 226132f
Show file tree
Hide file tree
Showing 33 changed files with 312 additions and 180 deletions.
3 changes: 1 addition & 2 deletions apis/src/common/client_message.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use super::game_action::GameAction;
use super::{challenge_action::ChallengeAction, TournamentAction};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use shared_types::{ChatMessageContainer, GameId};

Expand All @@ -10,7 +9,7 @@ pub enum ClientRequest {
Chat(ChatMessageContainer),
Challenge(ChallengeAction),
Game { game_id: GameId, action: GameAction },
Ping(DateTime<Utc>),
Pong(u64),
Tournament(TournamentAction),
// leptos-use idle or window unfocused will send
Away, // Online and Offline are not needed because they will be handled by the WS connection
Expand Down
6 changes: 1 addition & 5 deletions apis/src/common/server_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use super::game_reaction::GameReaction;
use crate::responses::{
ChallengeResponse, GameResponse, HeartbeatResponse, TournamentResponse, UserResponse,
};
use chrono::{DateTime, Utc};
use http::StatusCode;
use serde::{Deserialize, Serialize};
use shared_types::{ChallengeId, ChatMessageContainer};
Expand Down Expand Up @@ -37,10 +36,7 @@ impl fmt::Display for ExternalServerError {

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ServerMessage {
Pong {
ping_sent: DateTime<Utc>,
pong_sent: DateTime<Utc>,
},
Ping { nonce: u64, value: f64 },
ConnectionUpdated(Uuid, String),
Chat(Vec<ChatMessageContainer>),
Game(Box<GameUpdate>),
Expand Down
6 changes: 3 additions & 3 deletions apis/src/components/atoms/status_indicator.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
common::UserStatus,
providers::{
online_users::OnlineUsersSignal, websocket::WebsocketContext, AuthContext, PingSignal,
online_users::OnlineUsersSignal, websocket::WebsocketContext, AuthContext, PingContext,
},
};
use chrono::Utc;
Expand All @@ -12,7 +12,7 @@ use leptos_use::core::ConnectionReadyState;
#[component]
pub fn StatusIndicator(username: String) -> impl IntoView {
let websocket = expect_context::<WebsocketContext>();
let ping = expect_context::<PingSignal>();
let ping = expect_context::<PingContext>();
let auth_context = expect_context::<AuthContext>();
let online_users = expect_context::<OnlineUsersSignal>();
let username = store_value(username);
Expand All @@ -22,7 +22,7 @@ pub fn StatusIndicator(username: String) -> impl IntoView {
};
let user_has_ws = move || {
Utc::now()
.signed_duration_since(ping.signal.get().last_update)
.signed_duration_since(ping.last_updated.get_untracked())
.num_seconds()
< 5
&& matches!(websocket.ready_state.get(), ConnectionReadyState::Open)
Expand Down
20 changes: 8 additions & 12 deletions apis/src/components/layouts/base_layout.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use crate::components::atoms::og::OG;
use crate::components::atoms::title::Title;
use crate::components::atoms::{og::OG, title::Title};
use crate::components::molecules::alert::Alert;
use crate::components::organisms::header::Header;
use crate::providers::game_state::GameStateSignal;
use crate::providers::navigation_controller::NavigationControllerSignal;
use crate::providers::refocus::RefocusSignal;
use crate::providers::websocket::WebsocketContext;
use crate::providers::PingSignal;
use crate::providers::{load_audio_buffer, ApiRequests, AuthContext, ColorScheme, SoundsSignal};
use crate::providers::{
game_state::GameStateSignal, load_audio_buffer,
navigation_controller::NavigationControllerSignal, refocus::RefocusSignal,
websocket::WebsocketContext, AuthContext, ColorScheme, PingContext, SoundsSignal,
};
use cfg_if::cfg_if;
use chrono::Utc;
use hive_lib::GameControl;
Expand Down Expand Up @@ -61,7 +59,7 @@ pub struct OrientationSignal {
pub fn BaseLayout(children: ChildrenFn) -> impl IntoView {
let color_scheme = expect_context::<ColorScheme>();
let sounds_signal = expect_context::<SoundsSignal>();
let ping = expect_context::<PingSignal>();
let ping = expect_context::<PingContext>();
let ws = expect_context::<WebsocketContext>();
let ws_ready = ws.ready_state;
let auth_context = expect_context::<AuthContext>();
Expand Down Expand Up @@ -175,9 +173,7 @@ pub fn BaseLayout(children: ChildrenFn) -> impl IntoView {
move || {
batch({
let ws = ws.clone();
let api = ApiRequests::new();
move || {
api.ping();
counter.update(|c| *c += 1);
match ws_ready() {
ConnectionReadyState::Closed => {
Expand All @@ -195,7 +191,7 @@ pub fn BaseLayout(children: ChildrenFn) -> impl IntoView {
_ => {}
}
if Utc::now()
.signed_duration_since(ping.signal.get_untracked().last_update)
.signed_duration_since(ping.last_updated.get_untracked())
.num_seconds()
>= 5
&& retry_at.get() == counter.get()
Expand Down
2 changes: 1 addition & 1 deletion apis/src/components/molecules/control_buttons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
challenges::ChallengeStateSignal, game_state::GameStateSignal, ApiRequests, AuthContext,
},
};
use hive_lib::{ColorChoice, GameControl, GameStatus};
use hive_lib::{ColorChoice, GameControl};
use leptos::*;
use leptos_router::use_navigate;
use shared_types::{ChallengeDetails, ChallengeVisibility};
Expand Down
1 change: 0 additions & 1 deletion apis/src/components/molecules/live_timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ pub fn LiveTimer(side: Signal<Color>) -> impl IntoView {
});

view! {

<div class=move || {
format!(
"flex resize h-full select-none items-center justify-center text-xl md:text-2xl lg:text-4xl {}",
Expand Down
23 changes: 13 additions & 10 deletions apis/src/components/molecules/ping.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::providers::websocket::WebsocketContext;
use crate::providers::PingSignal;
use crate::providers::PingContext;
use chrono::Utc;
use leptos::*;
use leptos_icons::*;
Expand All @@ -8,24 +8,27 @@ use leptos_use::core::ConnectionReadyState;
#[component]
pub fn Ping() -> impl IntoView {
let websocket = expect_context::<WebsocketContext>();
let ping = expect_context::<PingSignal>();
let ping = expect_context::<PingContext>();

let signal = move || {
if Utc::now()
.signed_duration_since(ping.signal.get_untracked().last_update)
.signed_duration_since(ping.last_updated.get_untracked())
.num_seconds()
>= 5
{
view! { <Icon class="fill-ladybug-red" icon=icondata::BiNoSignalRegular/> }.into_view()
} else {
match websocket.ready_state.get() {
ConnectionReadyState::Open => view! {
<div class="flex items-center">
<Icon class="fill-grasshopper-green" icon=icondata::BiSignal5Regular/>
{move || { format!("{}ms", ping.signal.get().ping_duration.num_milliseconds()) }}
</div>
}.into_view(),
_ => view! { <Icon class="fill-ladybug-red" icon=icondata::BiNoSignalRegular/> }.into_view(),}
ConnectionReadyState::Open => view! {
<div class="flex items-center">
<Icon class="fill-grasshopper-green" icon=icondata::BiSignal5Regular/>
{move || { format!("{:.0}ms", ping.ping.get()) }}
</div>
}
.into_view(),
_ => view! { <Icon class="fill-ladybug-red" icon=icondata::BiNoSignalRegular/> }
.into_view(),
}
}
};

Expand Down
2 changes: 2 additions & 0 deletions apis/src/components/organisms/display_timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ pub fn DisplayTimer(placement: Placement, vertical: bool) -> impl IntoView {
TimeMode::Correspondence | TimeMode::RealTime
)
}

fallback=|| {
view! {
<Icon
Expand All @@ -122,6 +123,7 @@ pub fn DisplayTimer(placement: Placement, vertical: bool) -> impl IntoView {
}
}
>

<LiveTimer side/>
</Show>
</button>
Expand Down
10 changes: 4 additions & 6 deletions apis/src/components/organisms/sound_toggle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,20 @@ pub fn SoundToggle() -> impl IntoView {
} else {
icondata::BiVolumeMuteRegular
};
view! {<Icon icon class="w-4 h-4"/>}
view! { <Icon icon class="w-4 h-4"/> }
};
view! {
<ActionForm
action=sounds_signal.action
class="inline-flex justify-center items-center m-1 rounded">
class="inline-flex justify-center items-center m-1 rounded"
>

<input
type="hidden"
name="prefers_sound"
value=move || (!(sounds_signal.prefers_sound)()).to_string()
/>
<button
type="submit"
class="flex justify-center items-center px-1 py-2 w-full h-full"
>
<button type="submit" class="flex justify-center items-center px-1 py-2 w-full h-full">
{icon}
</button>
</ActionForm>
Expand Down
6 changes: 3 additions & 3 deletions apis/src/jobs/heartbeat.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use crate::websockets::lobby::Lobby;
use crate::websockets::messages::GameHB;
use crate::websockets::ws_server::WsServer;
use actix::Addr;
use actix_web::web::Data;
use std::time::Duration;

pub fn run(lobby: Data<Addr<Lobby>>) {
pub fn run(ws_server: Data<Addr<WsServer>>) {
actix_rt::spawn(async move {
let mut interval = actix_rt::time::interval(Duration::from_secs(3));
loop {
interval.tick().await;
lobby.do_send(GameHB {});
ws_server.do_send(GameHB {});
}
});
}
1 change: 1 addition & 0 deletions apis/src/jobs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod heartbeat;
pub mod ping;
pub mod tournament_start;
15 changes: 15 additions & 0 deletions apis/src/jobs/ping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::websockets::messages::Ping;
use crate::websockets::ws_server::WsServer;
use actix::Addr;
use actix_web::web::Data;
use std::time::Duration;

pub fn run(ws_server: Data<Addr<WsServer>>) {
actix_rt::spawn(async move {
let mut interval = actix_rt::time::interval(Duration::from_secs(1));
loop {
interval.tick().await;
ws_server.do_send(Ping {});
}
});
}
8 changes: 4 additions & 4 deletions apis/src/jobs/tournament_start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ use crate::common::{
};
use crate::responses::{GameResponse, TournamentResponse};
use crate::websockets::internal_server_message::{InternalServerMessage, MessageDestination};
use crate::websockets::lobby::Lobby;
use crate::websockets::messages::ClientActorMessage;
use crate::websockets::ws_server::WsServer;
use actix::Addr;
use actix_web::web::Data;
use db_lib::{get_conn, models::Tournament, DbPool};
use diesel_async::scoped_futures::ScopedFutureExt;
use diesel_async::AsyncConnection;
use std::time::Duration;

pub fn run(pool: DbPool, lobby: Data<Addr<Lobby>>) {
pub fn run(pool: DbPool, ws_server: Data<Addr<WsServer>>) {
actix_rt::spawn(async move {
let mut interval = actix_rt::time::interval(Duration::from_secs(60));
loop {
interval.tick().await;
if let Ok(mut conn) = get_conn(&pool).await {
let lobby = lobby.clone();
let ws_server = ws_server.clone();
let _ = conn
.transaction::<_, anyhow::Error, _>(move |tc| {
async move {
Expand Down Expand Up @@ -90,7 +90,7 @@ pub fn run(pool: DbPool, lobby: Data<Addr<Lobby>>) {
serialized,
from: None,
};
lobby.do_send(cam);
ws_server.do_send(cam);
}
}
Ok(())
Expand Down
1 change: 1 addition & 0 deletions apis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod common;
pub mod components;
pub mod functions;
pub mod pages;
pub mod ping;
pub mod providers;
pub mod responses;

Expand Down
23 changes: 14 additions & 9 deletions apis/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
pub mod common;
pub mod functions;
pub mod jobs;
pub mod ping;
pub mod responses;
pub mod websockets;
use actix_session::config::PersistentSession;
use actix_web::cookie::time::Duration;
use actix_web::middleware::Compress;
use ping::pings::Pings;
use std::sync::{Arc, RwLock};
use websockets::tournament_game_start::TournamentGameStart;

cfg_if::cfg_if! { if #[cfg(feature = "ssr")] {

#[actix_web::main]
async fn main() -> std::io::Result<()> {
use crate::websockets::{chat::Chats, lobby::Lobby,start_connection};
use crate::websockets::{chat::Chats, start_connection, ws_server::WsServer};
use actix::Actor;
use actix_files::Files;
use actix_identity::IdentityMiddleware;
use actix_session::{storage::CookieSessionStore, SessionMiddleware};
use actix_web::{cookie::Key, App, HttpServer, web::Data,};
use actix_web::{cookie::Key, web::Data, App, HttpServer};
use apis::app::App;
use db_lib::{config::DbConfig, get_pool};
use diesel::pg::PgConnection;
Expand All @@ -38,7 +41,8 @@ async fn main() -> std::io::Result<()> {
let database_url = &config.database_url;
let mut conn = PgConnection::establish(database_url)
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url));
conn.run_pending_migrations(MIGRATIONS).expect("Ran migrations");
conn.run_pending_migrations(MIGRATIONS)
.expect("Ran migrations");

let hash: [u8; 64] = Sha512::digest(&config.session_secret)
.as_slice()
Expand All @@ -49,11 +53,13 @@ async fn main() -> std::io::Result<()> {
.await
.expect("Failed to get pool");
let chat_history = Data::new(Chats::new());
let websocket_server = Data::new(Lobby::new(pool.clone()).start());
let pings = Data::new(Arc::new(RwLock::new(Pings::new())));
let websocket_server = Data::new(WsServer::new(pings.clone(), pool.clone()).start());
let tournament_game_start = Data::new(TournamentGameStart::new());

jobs::tournament_start::run(pool.clone(), Data::clone(&websocket_server));
jobs::heartbeat::run(Data::clone(&websocket_server));
jobs::ping::run(Data::clone(&websocket_server));

println!("listening on http://{}", &addr);

Expand All @@ -66,6 +72,7 @@ async fn main() -> std::io::Result<()> {
.app_data(Data::clone(&chat_history))
.app_data(Data::clone(&websocket_server))
.app_data(Data::clone(&tournament_game_start))
.app_data(Data::clone(&pings))
// serve JS/WASM/CSS from `pkg`
.service(Files::new("/pkg", format!("{site_root}/pkg")))
// serve other assets from the `assets` directory
Expand All @@ -87,12 +94,10 @@ async fn main() -> std::io::Result<()> {
// SessionMiddleware so SessionMiddleware needs to be present
.wrap(
SessionMiddleware::builder(CookieSessionStore::default(), cookie_key.clone())
.session_lifecycle(
PersistentSession::default().session_ttl(Duration::weeks(1))
)
.build()
.session_lifecycle(PersistentSession::default().session_ttl(Duration::weeks(1)))
.build(),
)
.wrap(Compress::default())
.wrap(Compress::default())
})
.bind(&addr)?
.run()
Expand Down
2 changes: 2 additions & 0 deletions apis/src/ping/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod pings;
pub mod stats;
Loading

0 comments on commit 226132f

Please sign in to comment.