use std::convert::TryFrom;
use futures::{channel::mpsc::{Receiver, Sender, channel}, SinkExt};
-use redis::{AsyncCommands, ErrorKind, FromRedisValue, Msg, RedisError, RedisResult, RedisWrite, ToRedisArgs, Value, aio::MultiplexedConnection};
+use redis::{AsyncCommands, ErrorKind, FromRedisValue, Msg, RedisError, RedisResult, RedisWrite, Script, ToRedisArgs, Value, aio::MultiplexedConnection};
use serde::{Serialize, Deserialize};
use crate::auth::Auth;
}
}
+const TAKE_ACTION_LUA_SCRIPT: &'static str = r#"
+ local len = redis.call('llen', KEYS[1])
+ local expected = tonumber(ARGV[1])
+ if (len == expected) then
+ return redis.call('rpush', KEYS[1], ARGV[2])
+ elseif (len > expected) then
+ return 0
+ else
+ local error = string.format('Inconsistent game state - database reports %d actions but expected at least %d', len, expected)
+ return redis.error_reply(error)
+ end
+"#;
+
impl Server {
pub fn new(redis: MultiplexedConnection, register_update_stream: Sender<ClientInterestSender>) -> Self {
Self {
if let Err(err) = self.register_update_stream.clone().send(client_interest_sender).await {
error!("new_state: Send failed: {}", err);
}
- (ServerState{redis: self.redis.clone(), register_interest}, interest)
+ let take_action_script = Script::new(TAKE_ACTION_LUA_SCRIPT);
+ (ServerState{redis: self.redis.clone(), register_interest, take_action_script}, interest)
}
}
pub struct ServerState {
redis: MultiplexedConnection,
register_interest: Sender<HashSet<ClientInterest>>,
+ take_action_script: Script,
}
fn user_key(username: &str) -> String {
format!("game:{}", id)
}
-const TAKE_ACTION_LUA_SCRIPT: &'static str = r#"
- local len = redis.call('llen', KEYS[1])
- local expected = tonumber(ARGV[1])
- if (len == expected) then
- return redis.call('rpush', KEYS[1], ARGV[2])
- elseif (len > expected) then
- return 0
- else
- local error = string.format('Inconsistent game state - database reports %d actions but expected at least %d', len, expected)
- return redis.error_reply(error)
- end
-"#;
-
impl ServerState {
pub async fn create_user(&mut self, username: &str, auth: Auth, nickname: &str) -> RedisResult<()> {
let key = user_key(username);
pub async fn take_action(&mut self, id: u32, len: usize, action: &UserAction) -> RedisResult<ActionStatus> {
let key = game_key(id);
- debug!("redis: executing: EVAL {:?} 1 {:?} {} {:?}", TAKE_ACTION_LUA_SCRIPT, key, len, serde_json::to_string(&action).unwrap());
- redis::cmd("EVAL").arg(TAKE_ACTION_LUA_SCRIPT).arg(1).arg(key).arg(len).arg(AsJson(action)).query_async(&mut self.redis).await
+ 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
}
pub async fn register_interests(&mut self, interests: HashSet<ClientInterest>) {