store ids in game list and settings in separate structure
authorGeoffrey Allott <geoffrey@allott.email>
Sat, 27 Feb 2021 13:38:48 +0000 (13:38 +0000)
committerGeoffrey Allott <geoffrey@allott.email>
Sat, 27 Feb 2021 13:38:48 +0000 (13:38 +0000)
src/server.rs

index 36e9ed72ad0be1e78dfcb2a42998c42645457b29..42bb1fa90ec6cf90b39e171b58f082add3df8c75 100644 (file)
@@ -1,7 +1,7 @@
 use std::collections::HashSet;
 use std::convert::TryFrom;
 
-use futures::{channel::mpsc::{Receiver, Sender, channel}, SinkExt};
+use futures::{channel::mpsc::{Receiver, Sender, channel}, SinkExt, future::try_join_all};
 use redis::{AsyncCommands, ErrorKind, FromRedisValue, Msg, RedisError, RedisResult, RedisWrite, Script, ToRedisArgs, Value, aio::MultiplexedConnection};
 use serde::{Serialize, Deserialize};
 
@@ -47,12 +47,12 @@ impl TryFrom<Msg> for ClientInterest {
     type Error = ClientInterestFromMsgError;
     fn try_from(msg: Msg) -> Result<Self, Self::Error> {
         let channel_name = msg.get_channel_name();
-        if channel_name == "__keyspace@0__:games" {
+        if channel_name == "__keyspace@0__:game:list" {
             Ok(ClientInterest::GameList)
         } else if let Some(username) = channel_name.strip_prefix("__keyspace@0__:user:") {
             username.parse().map_err(ClientInterestFromMsgError::UsernameParseError)
                 .map(|username| ClientInterest::User{username})
-        } else if let Some(Ok(id)) = channel_name.strip_prefix("__keyspace@0__:game:").map(str::parse) {
+        } else if let Some(Ok(id)) = channel_name.strip_prefix("__keyspace@0__:game:").and_then(|str| str.strip_suffix(":actions")).map(str::parse) {
             Ok(ClientInterest::Game{id})
         } else {
             Err(ClientInterestFromMsgError::InvalidChannelName{channel_name: channel_name.to_string()})
@@ -65,8 +65,8 @@ impl ToRedisArgs for ClientInterest {
         where W: ?Sized + RedisWrite
     {
         match self {
-            ClientInterest::GameList => out.write_arg(b"__keyspace@0__:games"),
-            ClientInterest::Game{id} => out.write_arg_fmt(format!("__keyspace@0__:game:{}", id)),
+            ClientInterest::GameList => out.write_arg(b"__keyspace@0__:game:list"),
+            ClientInterest::Game{id} => out.write_arg_fmt(format!("__keyspace@0__:game:{}:actions", id)),
             ClientInterest::User{username} => out.write_arg_fmt(format!("__keyspace@0__:user:{}", username)),
         }
     }
@@ -113,8 +113,12 @@ fn user_key(username: Username) -> String {
     format!("user:{}", username)
 }
 
-fn game_key(id: u32) -> String {
-    format!("game:{}", id)
+fn game_settings_key(id: u32) -> String {
+    format!("game:{}:settings", id)
+}
+
+fn game_actions_key(id: u32) -> String {
+    format!("game:{}:actions", id)
 }
 
 impl ServerState {
@@ -149,28 +153,36 @@ impl ServerState {
     }
 
     pub async fn create_game(&mut self, settings: GameSettings) -> RedisResult<u32> {
-        self.redis.rpush("games", AsJson(settings)).await.map(|i: u32| i - 1)
+        let id = self.redis.incr("game:next_id", 1u32).await?;
+        let key = game_settings_key(id);
+        let () = self.redis.set(key, AsJson(settings)).await?;
+        let () = self.redis.rpush("game:list", id).await?;
+        Ok(id)
     }
 
     pub async fn game_list(&mut self, from: usize) -> RedisResult<Vec<GameSummary>> {
         debug!("game_list(from: {})", from);
-        let games: Vec<AsJson<GameSettings>> = self.redis.lrange("games", from as isize, -1).await?;
-        Ok(games.into_iter().map(AsJson::get).enumerate().map(|(id, settings)| GameSummary::new((from + id) as u32, settings)).collect())
+        let games: Vec<u32> = self.redis.lrange("game:list", from as isize, -1).await?;
+        let mut summaries = Vec::new();
+        for id in games {
+            summaries.push(self.game_summary(id).await?);
+        }
+        Ok(summaries)
     }
 
     pub async fn game_state(&mut self, id: u32, from: usize) -> RedisResult<Vec<ValidatedUserAction>> {
-        let key = game_key(id);
+        let key = game_actions_key(id);
         let actions: Vec<AsJson<ValidatedUserAction>> = self.redis.lrange(&key, from as isize, -1).await?;
         Ok(actions.into_iter().map(AsJson::get).collect())
     }
 
     pub async fn game_summary(&mut self, id: u32) -> RedisResult<GameSummary> {
-        let settings = self.redis.lindex("games", id as isize).await.map(AsJson::get)?;
-        Ok(GameSummary::new(id, settings))
+        let key = game_settings_key(id);
+        self.redis.get(key).await.map(AsJson::get).map(|settings| GameSummary::new(id, settings))
     }
 
     pub async fn take_action(&mut self, id: u32, len: usize, action: &ValidatedUserAction) -> RedisResult<ActionStatus> {
-        let key = game_key(id);
+        let key = game_actions_key(id);
         debug!("take_action: EVAL {{TAKE_ACTION_LUA_SCRIPT}} 1 {} {} {:?}", key, len, action);
         self.take_action_script.key(key).arg(len).arg(AsJson(action)).invoke_async(&mut self.redis).await
     }