Refactor so game loop can hold "player" objects

This commit is contained in:
Dane Johnson 2022-10-14 11:36:45 -05:00
parent 92f7e5aa5f
commit 368d6585c4
4 changed files with 48 additions and 42 deletions

View File

@ -57,7 +57,7 @@
break; break;
case "CHAT": case "CHAT":
let text = $("textarea").val(); let text = $("textarea").val();
text = text += `\n${args[0]}`; text = text += `${args[0]}\n`;
$("textarea").val(text); $("textarea").val(text);
default: default:
console.log("Unhandled message", msg); console.log("Unhandled message", msg);

View File

@ -1,5 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Mutex;
use futures::{select, FutureExt}; use futures::{select, FutureExt};
use crate::{msg, GlobalState}; use crate::{msg, GlobalState};
@ -44,18 +45,23 @@ impl Client {
match msg.command.as_str() { match msg.command.as_str() {
"HOST" => { "HOST" => {
let room_code = self.global_state.code_generator.lock().await.generate(); let room_code = self.global_state.code_generator.lock().await.generate();
let game_controller = GameController::default(); let game_controller = Arc::new(Mutex::new(GameController::default()));
let (client_channel, server_channel) = channel_pair(); let (client_channel, server_channel) = channel_pair();
self.channel = Some(client_channel); self.channel = Some(client_channel);
game_controller.channels.lock().await.push(server_channel); game_controller.lock().await.push(server_channel);
self.global_state self.global_state
.rooms .rooms
.lock() .lock()
.await .await
.insert(room_code.clone(), game_controller.clone()); .insert(room_code.clone(), Arc::clone(&game_controller));
tokio::spawn(async move { game_controller.run_loop().await }); tokio::spawn(async move {
loop {
game_controller.lock().await.poll().await;
tokio::task::yield_now().await;
}
});
self.ws.send(msg!(ROOM_CODE, room_code)).await.unwrap(); self.ws.send(msg!(ROOM_CODE, room_code)).await.unwrap();
} }
"JOIN" => { "JOIN" => {
@ -67,8 +73,9 @@ impl Client {
Some(room) => { Some(room) => {
let (client_channel, server_channel) = channel_pair(); let (client_channel, server_channel) = channel_pair();
self.channel = Some(client_channel); self.channel = Some(client_channel);
room.channels.lock().await.push(server_channel); room.lock().await.push(server_channel);
self.ws.send(msg!(JOIN_OK)).await.unwrap(); self.ws.send(msg!(JOIN_OK)).await.unwrap();
} }
None => { None => {
self.ws.send(msg!(JOIN_INVALID)).await.unwrap(); self.ws.send(msg!(JOIN_INVALID)).await.unwrap();

View File

@ -1,20 +1,17 @@
use std::sync::Arc; use tokio::sync::mpsc;
use tokio::sync::{ mpsc, Mutex };
use tokio::sync::mpsc::error::TryRecvError; use tokio::sync::mpsc::error::TryRecvError;
use crate::message::Message; use crate::message::Message;
#[derive(Clone)] struct Player {
pub struct GameController { name: String,
pub channels: Arc<Mutex<Channels>>, channel: Option<Channel>,
seat: Option<usize>,
} }
impl std::default::Default for GameController { #[derive(Default)]
fn default() -> Self { pub struct GameController {
let channels = Arc::new(Mutex::new(Channels::default())); players: Vec<Player>,
GameController { channels }
}
} }
pub struct Channel { pub struct Channel {
@ -22,26 +19,32 @@ pub struct Channel {
pub rx: mpsc::Receiver<Message>, pub rx: mpsc::Receiver<Message>,
} }
#[derive(Default)] impl GameController {
pub struct Channels(Vec<Channel>);
impl Channels {
pub fn try_recv(&mut self) -> Result<Message, TryRecvError> { pub fn try_recv(&mut self) -> Result<Message, TryRecvError> {
for channel in self.0.iter_mut() { for player in self.players.iter_mut() {
let res = channel.rx.try_recv(); if let Some(channel) = &mut player.channel {
if !(res == Err(TryRecvError::Empty)) { let res = channel.rx.try_recv();
return res if !(res == Err(TryRecvError::Empty)) {
return res
}
} }
} }
Err(TryRecvError::Empty) Err(TryRecvError::Empty)
} }
pub async fn broadcast(&mut self, msg: Message) { pub async fn broadcast(&mut self, msg: Message) {
for channel in self.0.iter_mut() { for player in self.players.iter() {
channel.tx.send(msg.clone()).await.unwrap(); if let Some(channel) = &player.channel {
channel.tx.send(msg.clone()).await.unwrap();
}
} }
} }
pub fn push(&mut self, channel: Channel) { pub fn push(&mut self, channel: Channel) {
self.0.push(channel); let player = Player {
name: "Guest".to_string(),
channel: Some(channel),
seat: None, // TODO
};
self.players.push(player);
} }
} }
@ -52,19 +55,15 @@ pub fn channel_pair() -> (Channel, Channel) {
} }
impl GameController { impl GameController {
pub async fn run_loop(&self) { pub async fn poll(&mut self) {
loop { if let Ok(msg) = self.try_recv() {
let mut channels = self.channels.lock().await; self.dispatch(msg).await;
if let Ok(msg) = channels.try_recv() {
dispatch(&mut channels, msg).await;
}
} }
} }
} async fn dispatch(&mut self, msg: Message) {
match msg.command.as_str() {
async fn dispatch(channels: &mut Channels, msg: Message) { "CHAT" => self.broadcast(msg).await,
match msg.command.as_str() { _ => ()
"CHAT" => channels.broadcast(msg).await, };
_ => () }
};
} }

View File

@ -14,5 +14,5 @@ use code_generator::CodeGenerator;
#[derive(Default)] #[derive(Default)]
pub struct GlobalState { pub struct GlobalState {
pub code_generator: Arc<Mutex<CodeGenerator>>, pub code_generator: Arc<Mutex<CodeGenerator>>,
pub rooms: Arc<Mutex<HashMap<String, GameController>>>, pub rooms: Arc<Mutex<HashMap<String, Arc<Mutex<GameController>>>>>,
} }