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
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
|
|
}
|
|
|