From 429fed7893c0dae2816cc0e8ba9698dc0f401668 Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Fri, 17 Mar 2023 11:55:45 +0100 Subject: [PATCH] Add a leaderboard with Redis --- board-server/Cargo.toml | 2 ++ board-server/src/leaderboard.rs | 62 +++++++++++++++++++++++++++++++++ board-server/src/main.rs | 1 + 3 files changed, 65 insertions(+) create mode 100644 board-server/src/leaderboard.rs diff --git a/board-server/Cargo.toml b/board-server/Cargo.toml index 0a23f4f..f14cc12 100644 --- a/board-server/Cargo.toml +++ b/board-server/Cargo.toml @@ -15,3 +15,5 @@ tungstenite = "0.18.0" anyhow = "1.0.69" rand = "0.8.5" serde_json = "1.0.93" +redis = { version = "0.22.3", features = ["aio", "async-std-comp"] } +async-trait = "0.1.66" diff --git a/board-server/src/leaderboard.rs b/board-server/src/leaderboard.rs new file mode 100644 index 0000000..24d3429 --- /dev/null +++ b/board-server/src/leaderboard.rs @@ -0,0 +1,62 @@ +use async_trait::async_trait; +use redis::{AsyncCommands, RedisError}; +use std::env; + +const LEADERBOARD: &str = "leaderboard"; +type LeaderboardEntry = (String, i32); + +#[async_trait] +trait Leaderboard { + async fn add_score(&self, player_name: &str, score: i32) -> Result<(), RedisError>; + async fn get_highscores(&self) -> Result, RedisError>; +} + +struct RedisLeaderboard { + client: redis::Client, +} + +impl RedisLeaderboard { + fn new(client: redis::Client) -> Self { + Self { client } + } +} + +#[async_trait] +impl Leaderboard for RedisLeaderboard { + async fn add_score(&self, player_name: &str, score: i32) -> Result<(), RedisError> { + let mut con = self.client.get_async_connection().await?; + con.zadd(LEADERBOARD, player_name, score).await?; + Ok(()) + } + + async fn get_highscores(&self) -> Result, RedisError> { + let mut con = self.client.get_async_connection().await?; + let count: isize = con.zcard(LEADERBOARD).await?; + let leaderboard: Vec = + con.zrange_withscores(LEADERBOARD, 0, count - 1).await?; + Ok(leaderboard) + } +} + +struct InMemoryLeaderboard(); + +#[async_trait] +impl Leaderboard for InMemoryLeaderboard { + async fn add_score(&self, _: &str, _: i32) -> Result<(), RedisError> { + Ok(()) + } + + async fn get_highscores(&self) -> Result, RedisError> { + Ok(Vec::new()) + } +} + +async fn provide_leaderboard() -> Box { + match env::var("REDIS_HOSTNAME") { + Ok(redis_host_name) => match redis::Client::open(format!("redis://{redis_host_name}/")) { + Ok(client) => Box::new(RedisLeaderboard::new(client)), + Err(_) => Box::new(InMemoryLeaderboard()), + }, + Err(_) => Box::new(InMemoryLeaderboard()), + } +} diff --git a/board-server/src/main.rs b/board-server/src/main.rs index 8ae1539..c57d4bb 100644 --- a/board-server/src/main.rs +++ b/board-server/src/main.rs @@ -1,3 +1,4 @@ +mod leaderboard; mod player; mod room;