// Copyright (C) 2025 Emilis Bliūdžius // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . use core::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use tokio::sync::Mutex; use werewolves_proto::player::PlayerId; #[derive(Debug, Clone)] pub struct ConnectUpdate { updated: Arc, connected: Arc>>, } impl ConnectUpdate { pub fn new() -> Self { Self { updated: Arc::new(AtomicBool::new(false)), connected: Arc::new(Mutex::new(Vec::new())), } } pub async fn connect(&self, pid: PlayerId) { let mut connected = self.connected.lock().await; if connected.iter().any(|c| c == &pid) { return; } connected.push(pid); self.updated.store(true, Ordering::SeqCst); } pub async fn disconnect(&self, pid: PlayerId) { let mut connected = self.connected.lock().await; if let Some(idx) = connected .iter() .enumerate() .find_map(|(idx, c)| (c == &pid).then_some(idx)) { connected.swap_remove(idx); self.updated.store(true, Ordering::SeqCst); } } } impl Future for ConnectUpdate { type Output = Arc<[PlayerId]>; fn poll( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { if self.updated.load(Ordering::SeqCst) && let Ok(connected) = self.connected.try_lock() { self.updated.store(false, Ordering::SeqCst); std::task::Poll::Ready(connected.clone().into()) } else { cx.waker().wake_by_ref(); std::task::Poll::Pending } } }