You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

196 lines
4.6 KiB

use tokio::sync::RwLock;
use uuid::Uuid;
use std::sync::Arc;
use std::time::SystemTime;
use std::{collections::HashMap, time::Duration};
use crate::db;
type Message = (Vec<(String, u32, bool)>, bool);
#[derive(Debug)]
pub struct Modifiable<T> {
value: T,
last_modified: SystemTime,
}
impl<T> Modifiable<T> {
pub fn new(value: T) -> Self {
Self {
value,
last_modified: SystemTime::now(),
}
}
// pub fn has_changed(&self, t: SystemTime) -> bool {
// // log::info!("Status: {:?} > {:?}", self.last_modified, t);
// self.last_modified > t
// }
pub fn get(&self) -> &T {
&self.value
}
pub fn get_mut(&mut self) -> &mut T {
self.last_modified = SystemTime::now();
&mut self.value
}
pub fn set(&mut self, new: T) {
self.last_modified = SystemTime::now();
self.value = new;
}
}
const STATUS_TIMEOUT: Duration = Duration::from_secs(120);
type LastStatus = Arc<RwLock<HashMap<u32, Arc<RwLock<Modifiable<(Message, Option<SystemTime>)>>>>>>;
#[derive(Clone)]
pub struct VotingSystem {
conn: Arc<RwLock<db::DbClient>>,
// games: Arc<Vec<crate::games::Game>>,
// broadcast: broadcast::Sender<Message>,
last_status: LastStatus,
}
impl VotingSystem {
pub fn new(
conn: db::DbClient,
// games: Arc<Vec<crate::games::Game>>,
) -> Self {
// let (tx, rx) = broadcast::channel(1);
// tx.send((Vec::new(), false, 0)).unwrap();
Self {
conn: Arc::new(RwLock::new(conn)),
last_status: Arc::new(RwLock::new(HashMap::new())),
}
}
async fn change_status(&self, lobby: u32, new_message: Message) {
self.change_status_with_timeout(lobby, new_message, None)
.await
}
async fn change_status_with_timeout(
&self,
lobby: u32,
new_message: Message,
timeout: Option<SystemTime>,
) {
let mut lock = self.last_status.write().await;
#[allow(clippy::map_entry)]
if lock.contains_key(&lobby) {
lock.get(&lobby)
.unwrap()
.write()
.await
.set((new_message, timeout));
} else {
lock.insert(
lobby,
Arc::new(RwLock::new(Modifiable::new((new_message, timeout)))),
);
}
}
async fn delete_status(&self, lobby: &u32) {
let mut lock = self.last_status.write().await;
if lock.contains_key(lobby) {
lock.remove(lobby);
}
}
pub async fn status(&self, lobby: &u32) -> Option<Message> {
match self.last_status.read().await.get(lobby) {
None => None,
Some(v) => {
let (message, should_timeout) = v.read().await.get().clone();
if let Some(timeout) = should_timeout {
if SystemTime::now().duration_since(timeout).unwrap() > STATUS_TIMEOUT {
self.delete_status(lobby).await
}
}
Some(message)
}
}
}
// pub async fn has_new_status(&self, lobby: &u32, t: SystemTime) -> bool {
// // log::info!("Status lobby: {}", lobby);
// match self.last_status.read().await.get(lobby) {
// None => false,
// Some(v) => v.read().await.has_changed(t),
// }
// }
pub async fn updated_users(&self, lobby: &u32) {
let status = {
let lock = self.last_status.read().await;
lock.get(lobby).map(Clone::clone)
};
if let Some(v) = status {
v.write().await.get_mut();
} else {
// log::info!("Adding new lobby to voting system");
self.last_status.write().await.insert(
*lobby,
Arc::new(RwLock::new(Modifiable::new(Default::default()))),
);
// log::info!("Added new lobby to voting system");
}
}
pub async fn vote(&self, user: Uuid, game: u32) {
let mut conn = self.conn.write().await;
if let Some(lobby) = conn.get_lobby_for_user(user).await {
conn.vote(user, game).await;
let status = conn.get_votes(lobby).await;
self.change_status(lobby, (status, false)).await;
}
}
pub async fn ready(&self, user: Uuid) -> Option<Option<u32>> {
let mut conn = self.conn.write().await;
if let Some(lobby) = conn.get_lobby_for_user(user).await {
conn.vote_ready(user).await;
let finished = conn.is_poll_finished(lobby).await;
let status = conn.get_votes(lobby).await;
if finished {
conn.delete_votes(lobby).await;
let winner = get_winner(status.iter().map(|(_, g, _)| g));
self.change_status_with_timeout(lobby, (status, finished), Some(SystemTime::now()))
.await;
Some(Some(winner))
} else {
self.change_status(lobby, (status, finished)).await;
Some(None)
}
} else {
None
}
}
}
fn get_winner<'a, I: Iterator<Item = &'a u32>>(iter: I) -> u32 {
let mut count: HashMap<u32, u32> = Default::default();
for item in iter {
if let Some(e) = count.get_mut(item) {
*e += 1;
} else {
count.insert(*item, 1);
}
}
count
.into_iter()
.fold((0, 0), |(max_game, max_num), (game, num)| {
if num > max_num {
(game, num)
} else {
(max_game, max_num)
}
})
.0
}