warn fixes for server and proto
This commit is contained in:
parent
242691a05f
commit
8c2791054a
|
|
@ -1,7 +1,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{player::CharacterId, role::RoleTitle};
|
use crate::role::RoleTitle;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Error, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Error, Serialize, Deserialize)]
|
||||||
pub enum GameError {
|
pub enum GameError {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
use core::{
|
use core::{num::NonZeroU8, ops::Not};
|
||||||
num::NonZeroU8,
|
|
||||||
ops::{Deref, Not},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::Result;
|
use super::Result;
|
||||||
use crate::{
|
use crate::{
|
||||||
diedto::DiedTo,
|
diedto::DiedTo,
|
||||||
error::GameError,
|
error::GameError,
|
||||||
game::{Village, kill::taken::Taken, night::NightChange},
|
game::{Village, night::NightChange},
|
||||||
player::{CharacterId, Protection},
|
player::{CharacterId, Protection},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -26,10 +23,13 @@ pub enum KillOutcome {
|
||||||
impl KillOutcome {
|
impl KillOutcome {
|
||||||
pub fn apply_to_village(self, village: &mut Village) -> Result<()> {
|
pub fn apply_to_village(self, village: &mut Village) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
KillOutcome::Single(character_id, died_to) => Ok(village
|
KillOutcome::Single(character_id, died_to) => {
|
||||||
|
village
|
||||||
.character_by_id_mut(&character_id)
|
.character_by_id_mut(&character_id)
|
||||||
.ok_or(GameError::InvalidTarget)?
|
.ok_or(GameError::InvalidTarget)?
|
||||||
.kill(died_to)),
|
.kill(died_to);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
KillOutcome::Guarding {
|
KillOutcome::Guarding {
|
||||||
original_killer,
|
original_killer,
|
||||||
original_target,
|
original_target,
|
||||||
|
|
@ -146,7 +146,7 @@ pub fn resolve_kill(
|
||||||
None => return Ok(Some(KillOutcome::Single(target.clone(), died_to.clone()))),
|
None => return Ok(Some(KillOutcome::Single(target.clone(), died_to.clone()))),
|
||||||
};
|
};
|
||||||
|
|
||||||
match protection.deref() {
|
match protection {
|
||||||
Protection::Guardian {
|
Protection::Guardian {
|
||||||
source,
|
source,
|
||||||
guarding: true,
|
guarding: true,
|
||||||
|
|
@ -188,17 +188,7 @@ impl<'a> ChangesLookup<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release<T>(&mut self, taken: Taken<'a, T>) {
|
pub fn protected_take(&mut self, target: &CharacterId) -> Option<Protection> {
|
||||||
self.1.swap_remove(
|
|
||||||
self.1
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find_map(|(idx, c)| (*c == taken.idx()).then_some(idx))
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn protected_take(&mut self, target: &CharacterId) -> Option<Taken<'a, Protection>> {
|
|
||||||
if let Some((idx, c)) = self.0.iter().enumerate().find_map(|(idx, c)| {
|
if let Some((idx, c)) = self.0.iter().enumerate().find_map(|(idx, c)| {
|
||||||
self.1
|
self.1
|
||||||
.contains(&idx)
|
.contains(&idx)
|
||||||
|
|
@ -213,7 +203,7 @@ impl<'a> ChangesLookup<'a> {
|
||||||
.flatten()
|
.flatten()
|
||||||
}) {
|
}) {
|
||||||
self.1.push(idx);
|
self.1.push(idx);
|
||||||
Some(Taken::new(idx, c))
|
Some(c.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -267,26 +257,3 @@ impl<'a> ChangesLookup<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod taken {
|
|
||||||
use core::ops::Deref;
|
|
||||||
|
|
||||||
pub struct Taken<'a, T>(usize, &'a T);
|
|
||||||
impl<'a, T> Taken<'a, T> {
|
|
||||||
pub const fn new(idx: usize, item: &'a T) -> Self {
|
|
||||||
Self(idx, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn idx(&self) -> usize {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Deref for Taken<'a, T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ use crate::{
|
||||||
message::{
|
message::{
|
||||||
CharacterState, Identification,
|
CharacterState, Identification,
|
||||||
host::{HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage},
|
host::{HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage},
|
||||||
night::ActionResponse,
|
|
||||||
},
|
},
|
||||||
player::CharacterId,
|
player::CharacterId,
|
||||||
};
|
};
|
||||||
|
|
@ -193,6 +192,7 @@ impl Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum GameState {
|
pub enum GameState {
|
||||||
Day {
|
Day {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use crate::{
|
||||||
DateTime, Village,
|
DateTime, Village,
|
||||||
kill::{self, ChangesLookup},
|
kill::{self, ChangesLookup},
|
||||||
},
|
},
|
||||||
message::night::{ActionPrompt, ActionResponse, ActionResult, ActionType},
|
message::night::{ActionPrompt, ActionResponse, ActionResult},
|
||||||
player::{Character, CharacterId, Protection},
|
player::{Character, CharacterId, Protection},
|
||||||
role::{PreviousGuardianAction, Role, RoleBlock, RoleTitle},
|
role::{PreviousGuardianAction, Role, RoleBlock, RoleTitle},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ impl Village {
|
||||||
.map(|(player, role)| {
|
.map(|(player, role)| {
|
||||||
let player_str = player.public.to_string();
|
let player_str = player.public.to_string();
|
||||||
Character::new(player, role)
|
Character::new(player, role)
|
||||||
.ok_or_else(|| GameError::PlayerNotAssignedNumber(player_str))
|
.ok_or(GameError::PlayerNotAssignedNumber(player_str))
|
||||||
})
|
})
|
||||||
.collect::<Result<Box<[_]>>>()?,
|
.collect::<Result<Box<[_]>>>()?,
|
||||||
date_time: DateTime::Night { number: 0 },
|
date_time: DateTime::Night { number: 0 },
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ trait GameExt {
|
||||||
fn next(&mut self) -> ActionPrompt;
|
fn next(&mut self) -> ActionPrompt;
|
||||||
fn next_expect_day(&mut self) -> (Box<[CharacterState]>, Box<[CharacterId]>, NonZeroU8);
|
fn next_expect_day(&mut self) -> (Box<[CharacterState]>, Box<[CharacterId]>, NonZeroU8);
|
||||||
fn response(&mut self, resp: ActionResponse) -> ActionResult;
|
fn response(&mut self, resp: ActionResponse) -> ActionResult;
|
||||||
fn get_state(&mut self) -> ServerToHostMessage;
|
|
||||||
fn execute(&mut self) -> ActionPrompt;
|
fn execute(&mut self) -> ActionPrompt;
|
||||||
fn mark_for_execution(
|
fn mark_for_execution(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -111,10 +110,6 @@ impl GameExt for Game {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.prompt()
|
.prompt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_state(&mut self) -> ServerToHostMessage {
|
|
||||||
self.process(HostGameMessage::GetState).unwrap()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_log() {
|
fn init_log() {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
#![allow(clippy::new_without_default)]
|
#![allow(clippy::new_without_default)]
|
||||||
use error::GameError;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
// pub mod action;
|
|
||||||
pub mod diedto;
|
pub mod diedto;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod game;
|
pub mod game;
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,12 @@ pub mod host;
|
||||||
mod ident;
|
mod ident;
|
||||||
pub mod night;
|
pub mod night;
|
||||||
|
|
||||||
use core::{fmt::Display, num::NonZeroU8};
|
use core::num::NonZeroU8;
|
||||||
|
|
||||||
pub use ident::*;
|
pub use ident::*;
|
||||||
use night::{ActionPrompt, ActionResponse, ActionResult};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{game::GameOver, player::CharacterId, role::RoleTitle};
|
||||||
error::GameError,
|
|
||||||
game::GameOver,
|
|
||||||
player::{Character, CharacterId},
|
|
||||||
role::RoleTitle,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub enum ClientMessage {
|
pub enum ClientMessage {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
error::GameError,
|
error::GameError,
|
||||||
game::{GameOver, GameSettings},
|
game::{GameOver, GameSettings},
|
||||||
message::{
|
message::{
|
||||||
CharacterIdentity, PublicIdentity,
|
CharacterIdentity,
|
||||||
night::{ActionPrompt, ActionResponse, ActionResult},
|
night::{ActionPrompt, ActionResponse, ActionResult},
|
||||||
},
|
},
|
||||||
player::{CharacterId, PlayerId},
|
player::{CharacterId, PlayerId},
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ pub async fn handler(
|
||||||
recv,
|
recv,
|
||||||
connection_id.clone(),
|
connection_id.clone(),
|
||||||
ident.public.name.clone(),
|
ident.public.name.clone(),
|
||||||
ident.public.number.clone(),
|
ident.public.number,
|
||||||
ident.public.pronouns.clone(),
|
ident.public.pronouns.clone(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -161,11 +161,7 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_recv(
|
async fn on_recv(&mut self, msg: Result<Message, axum::Error>) -> Result<(), anyhow::Error> {
|
||||||
&mut self,
|
|
||||||
msg: Result<Message, axum::Error>,
|
|
||||||
conn_id: ConnectionId,
|
|
||||||
) -> Result<(), anyhow::Error> {
|
|
||||||
use crate::LogError;
|
use crate::LogError;
|
||||||
|
|
||||||
let msg = match msg {
|
let msg = match msg {
|
||||||
|
|
@ -231,7 +227,7 @@ impl Client {
|
||||||
if let Err(err) = tokio::select! {
|
if let Err(err) = tokio::select! {
|
||||||
msg = self.socket.recv() => {
|
msg = self.socket.recv() => {
|
||||||
match msg {
|
match msg {
|
||||||
Some(msg) => self.on_recv(msg, self.connection_id.clone()).await,
|
Some(msg) => self.on_recv(msg).await,
|
||||||
None => {
|
None => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,7 @@ impl HostComms {
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub async fn recv(&mut self) -> Option<HostMessage> {
|
pub async fn recv(&mut self) -> Option<HostMessage> {
|
||||||
match self.recv.recv().await {
|
self.recv.recv().await
|
||||||
Some(msg) => Some(msg),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ impl LobbyComms {
|
||||||
(self.comms, self.connect_recv)
|
(self.comms, self.connect_recv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub const fn player(&mut self) -> &mut PlayerIdComms {
|
pub const fn player(&mut self) -> &mut PlayerIdComms {
|
||||||
self.comms.player()
|
self.comms.player()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ impl Comms {
|
||||||
&mut self.host
|
&mut self.host
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub const fn player(&mut self) -> &mut PlayerIdComms {
|
pub const fn player(&mut self) -> &mut PlayerIdComms {
|
||||||
&mut self.player
|
&mut self.player
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use tokio::sync::broadcast::Receiver;
|
use tokio::sync::broadcast::Receiver;
|
||||||
use werewolves_proto::{error::GameError, player::PlayerId};
|
use werewolves_proto::error::GameError;
|
||||||
|
|
||||||
use crate::{connection::JoinedPlayers, runner::IdentifiedClientMessage};
|
use crate::runner::IdentifiedClientMessage;
|
||||||
|
|
||||||
pub struct PlayerIdComms {
|
pub struct PlayerIdComms {
|
||||||
// joined_players: JoinedPlayers,
|
// joined_players: JoinedPlayers,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ use tokio::{
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
use werewolves_proto::{
|
use werewolves_proto::{
|
||||||
error::GameError,
|
|
||||||
message::{PublicIdentity, ServerMessage},
|
message::{PublicIdentity, ServerMessage},
|
||||||
player::PlayerId,
|
player::PlayerId,
|
||||||
};
|
};
|
||||||
|
|
@ -142,30 +141,6 @@ impl JoinedPlayers {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start_game_with(&self, players: &[PlayerId]) -> Result<InGameToken, GameError> {
|
|
||||||
let mut map = self.players.lock().await;
|
|
||||||
|
|
||||||
for player in players {
|
|
||||||
if let Some(player) = map.get_mut(player) {
|
|
||||||
player.in_game = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(InGameToken::new(
|
|
||||||
self.clone(),
|
|
||||||
players.iter().cloned().collect(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn release_from_game(&self, players: &[PlayerId]) {
|
|
||||||
self.players
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.iter_mut()
|
|
||||||
.filter(|(p, _)| players.contains(*p))
|
|
||||||
.for_each(|(_, p)| p.in_game = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_sender(&self, player_id: &PlayerId) -> Option<Sender<ServerMessage>> {
|
pub async fn get_sender(&self, player_id: &PlayerId) -> Option<Sender<ServerMessage>> {
|
||||||
self.players
|
self.players
|
||||||
.lock()
|
.lock()
|
||||||
|
|
@ -196,27 +171,3 @@ impl JoinedPlayers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InGameToken {
|
|
||||||
joined_players: JoinedPlayers,
|
|
||||||
players_in_game: Option<Box<[PlayerId]>>,
|
|
||||||
}
|
|
||||||
impl InGameToken {
|
|
||||||
const fn new(joined_players: JoinedPlayers, players_in_game: Box<[PlayerId]>) -> Self {
|
|
||||||
Self {
|
|
||||||
joined_players,
|
|
||||||
players_in_game: Some(players_in_game),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Drop for InGameToken {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let joined_players = self.joined_players.clone();
|
|
||||||
if let Some(players) = self.players_in_game.take() {
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let players_in_game = players;
|
|
||||||
joined_players.release_from_game(&players_in_game).await;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use crate::{
|
||||||
lobby::{Lobby, LobbyPlayers},
|
lobby::{Lobby, LobbyPlayers},
|
||||||
runner::{IdentifiedClientMessage, Message},
|
runner::{IdentifiedClientMessage, Message},
|
||||||
};
|
};
|
||||||
use futures::SinkExt;
|
|
||||||
use tokio::{sync::broadcast::Receiver, time::Instant};
|
use tokio::{sync::broadcast::Receiver, time::Instant};
|
||||||
use werewolves_proto::{
|
use werewolves_proto::{
|
||||||
error::GameError,
|
error::GameError,
|
||||||
|
|
@ -53,7 +52,6 @@ impl GameRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_lobby(self) -> Lobby {
|
pub fn into_lobby(self) -> Lobby {
|
||||||
// core::mem::drop(self._release_token);
|
|
||||||
let mut lobby = Lobby::new(
|
let mut lobby = Lobby::new(
|
||||||
self.joined_players,
|
self.joined_players,
|
||||||
LobbyComms::new(self.comms, self.connect_recv),
|
LobbyComms::new(self.comms, self.connect_recv),
|
||||||
|
|
@ -214,6 +212,12 @@ impl GameRunner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for char in self.game.village().characters() {
|
||||||
|
if let Some(sender) = self.joined_players.get_sender(char.player_id()).await {
|
||||||
|
let _ = sender.send(ServerMessage::Disconnect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.roles_revealed = true;
|
self.roles_revealed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,11 @@
|
||||||
use core::{
|
use core::ops::{Deref, DerefMut};
|
||||||
num::NonZeroU8,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
use std::{collections::HashMap, os::unix::raw::pid_t};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tokio::sync::broadcast::Sender;
|
use tokio::sync::broadcast::Sender;
|
||||||
use werewolves_proto::{
|
use werewolves_proto::{
|
||||||
error::GameError,
|
error::GameError,
|
||||||
game::{Game, GameSettings, Village},
|
game::{Game, GameSettings},
|
||||||
message::{
|
message::{
|
||||||
CharacterState, ClientMessage, DayCharacter, Identification, PlayerState, ServerMessage,
|
ClientMessage, Identification, PlayerState, ServerMessage,
|
||||||
UpdateSelf,
|
|
||||||
host::{HostLobbyMessage, HostMessage, ServerToHostMessage},
|
host::{HostLobbyMessage, HostMessage, ServerToHostMessage},
|
||||||
},
|
},
|
||||||
player::PlayerId,
|
player::PlayerId,
|
||||||
|
|
@ -353,10 +346,4 @@ impl LobbyPlayers {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drain(&mut self) -> Self {
|
|
||||||
let mut swapped = Self(vec![]);
|
|
||||||
core::mem::swap(&mut swapped, self);
|
|
||||||
swapped
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,9 @@ use axum_extra::headers;
|
||||||
use communication::lobby::LobbyComms;
|
use communication::lobby::LobbyComms;
|
||||||
use connection::JoinedPlayers;
|
use connection::JoinedPlayers;
|
||||||
use core::{fmt::Display, net::SocketAddr, str::FromStr};
|
use core::{fmt::Display, net::SocketAddr, str::FromStr};
|
||||||
use log::Record;
|
|
||||||
use runner::IdentifiedClientMessage;
|
use runner::IdentifiedClientMessage;
|
||||||
use std::{env, io::Write, path::Path};
|
use std::{env, io::Write, path::Path};
|
||||||
use tokio::{
|
use tokio::sync::{broadcast, mpsc};
|
||||||
sync::{broadcast, mpsc},
|
|
||||||
time::Instant,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
communication::{Comms, host::HostComms, player::PlayerIdComms},
|
communication::{Comms, host::HostComms, player::PlayerIdComms},
|
||||||
|
|
@ -114,7 +110,7 @@ async fn main() {
|
||||||
|
|
||||||
let path = Path::new(option_env!("SAVE_PATH").unwrap_or(DEFAULT_SAVE_DIR));
|
let path = Path::new(option_env!("SAVE_PATH").unwrap_or(DEFAULT_SAVE_DIR));
|
||||||
|
|
||||||
if let Err(err) = std::fs::create_dir(&path)
|
if let Err(err) = std::fs::create_dir(path)
|
||||||
&& !matches!(err.kind(), std::io::ErrorKind::AlreadyExists)
|
&& !matches!(err.kind(), std::io::ErrorKind::AlreadyExists)
|
||||||
{
|
{
|
||||||
panic!("creating save dir at [{path:?}]: {err}")
|
panic!("creating save dir at [{path:?}]: {err}")
|
||||||
|
|
|
||||||
|
|
@ -586,7 +586,7 @@ clients {
|
||||||
margin-left: 5vw;
|
margin-left: 5vw;
|
||||||
margin-right: 5vw;
|
margin-right: 5vw;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
display: flexbox;
|
display: flex;
|
||||||
flex-basis: content;
|
flex-basis: content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,17 @@
|
||||||
use core::{num::NonZeroU8, sync::atomic::AtomicBool, time::Duration};
|
use std::rc::Rc;
|
||||||
use std::{collections::VecDeque, rc::Rc};
|
|
||||||
|
|
||||||
use futures::{
|
use gloo::storage::errors::StorageError;
|
||||||
SinkExt, StreamExt,
|
|
||||||
channel::mpsc::{Receiver, Sender},
|
|
||||||
};
|
|
||||||
use gloo::{
|
|
||||||
net::websocket::{self, futures::WebSocket},
|
|
||||||
storage::{LocalStorage, Storage, errors::StorageError},
|
|
||||||
};
|
|
||||||
use instant::Instant;
|
|
||||||
use serde::Serialize;
|
|
||||||
use werewolves_proto::{
|
use werewolves_proto::{
|
||||||
error::GameError,
|
message::{ClientMessage, Identification, PublicIdentity},
|
||||||
game::GameOver,
|
|
||||||
message::{
|
|
||||||
ClientMessage, DayCharacter, Identification, PlayerUpdate, PublicIdentity, ServerMessage,
|
|
||||||
night::{ActionPrompt, ActionResponse, ActionResult},
|
|
||||||
},
|
|
||||||
player::PlayerId,
|
player::PlayerId,
|
||||||
role::RoleTitle,
|
role::RoleTitle,
|
||||||
};
|
};
|
||||||
use yew::{html::Scope, prelude::*, suspense::use_future};
|
use yew::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
clients::client::connection::{Connection2, ConnectionError},
|
clients::client::connection::{Connection2, ConnectionError},
|
||||||
components::{
|
components::{
|
||||||
Button, Identity, Notification,
|
Button, Identity,
|
||||||
client::{ClientNav, Signin},
|
client::{ClientNav, Signin},
|
||||||
},
|
},
|
||||||
storage::StorageKey,
|
storage::StorageKey,
|
||||||
|
|
@ -34,197 +19,10 @@ use crate::{
|
||||||
|
|
||||||
use crate::WerewolfError;
|
use crate::WerewolfError;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Message {
|
|
||||||
SetErrorCallback(Callback<Option<WerewolfError>>),
|
|
||||||
SetPublicIdentity(PublicIdentity),
|
|
||||||
RecvServerMessage(ServerMessage),
|
|
||||||
Connect,
|
|
||||||
ForceIdentity(Identification),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Connection {
|
|
||||||
scope: Scope<Client>,
|
|
||||||
ident: Identification,
|
|
||||||
recv: Receiver<ClientMessage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn url() -> String {
|
|
||||||
format!(
|
|
||||||
"{}client",
|
|
||||||
option_env!("LOCAL")
|
|
||||||
.map(|_| crate::clients::DEBUG_URL)
|
|
||||||
.unwrap_or(crate::clients::LIVE_URL)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Connection {
|
|
||||||
async fn connect_ws() -> WebSocket {
|
|
||||||
let url = url();
|
|
||||||
loop {
|
|
||||||
match WebSocket::open(&url) {
|
|
||||||
Ok(ws) => break ws,
|
|
||||||
Err(err) => {
|
|
||||||
log::error!("connect: {err}");
|
|
||||||
yew::platform::time::sleep(Duration::from_secs(1)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_message(msg: &impl Serialize) -> websocket::Message {
|
|
||||||
#[cfg(feature = "json")]
|
|
||||||
{
|
|
||||||
websocket::Message::Text(serde_json::to_string(msg).expect("message serialization"))
|
|
||||||
}
|
|
||||||
#[cfg(feature = "cbor")]
|
|
||||||
{
|
|
||||||
websocket::Message::Bytes({
|
|
||||||
let mut v = Vec::new();
|
|
||||||
ciborium::into_writer(msg, &mut v).expect("serializing message");
|
|
||||||
v
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(mut self) {
|
|
||||||
const CONNECT_WAIT: Duration = Duration::from_secs(3);
|
|
||||||
let url = url();
|
|
||||||
let mut last_connect: Option<Instant> = None;
|
|
||||||
'outer: loop {
|
|
||||||
if let Some(last_connect) = last_connect.as_ref() {
|
|
||||||
let time_since_last = Instant::now() - *last_connect;
|
|
||||||
if time_since_last <= CONNECT_WAIT {
|
|
||||||
yew::platform::time::sleep(CONNECT_WAIT.saturating_sub(time_since_last)).await;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
last_connect = Some(Instant::now());
|
|
||||||
log::info!("connecting to {url}");
|
|
||||||
let mut ws = Self::connect_ws().await.fuse();
|
|
||||||
log::info!("connected to {url}");
|
|
||||||
|
|
||||||
if let Err(err) = ws.send(Self::encode_message(&self.ident)).await {
|
|
||||||
log::error!("websocket identification send: {err}");
|
|
||||||
continue 'outer;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(err) = ws
|
|
||||||
.send(Self::encode_message(&ClientMessage::GetState))
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
log::error!("websocket identification send: {err}");
|
|
||||||
continue 'outer;
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let msg = futures::select! {
|
|
||||||
r = ws.next() => {
|
|
||||||
match r {
|
|
||||||
Some(Ok(msg)) => msg,
|
|
||||||
Some(Err(err)) => {
|
|
||||||
log::error!("websocket recv: {err}");
|
|
||||||
continue 'outer;
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
log::warn!("websocket closed");
|
|
||||||
continue 'outer;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r = self.recv.next() => {
|
|
||||||
match r {
|
|
||||||
Some(msg) => {
|
|
||||||
log::info!("sending message: {msg:?}");
|
|
||||||
if let Err(err) = ws.send(
|
|
||||||
Self::encode_message(&msg)
|
|
||||||
).await {
|
|
||||||
log::error!("websocket send error: {err}");
|
|
||||||
continue 'outer;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
log::info!("recv channel closed");
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let parse = {
|
|
||||||
#[cfg(feature = "json")]
|
|
||||||
{
|
|
||||||
match msg {
|
|
||||||
websocket::Message::Text(text) => {
|
|
||||||
serde_json::from_str::<ServerMessage>(&text)
|
|
||||||
}
|
|
||||||
websocket::Message::Bytes(items) => serde_json::from_slice(&items),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "cbor")]
|
|
||||||
{
|
|
||||||
match msg {
|
|
||||||
websocket::Message::Text(_) => {
|
|
||||||
log::error!("text messages not supported in cbor mode; discarding");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
websocket::Message::Bytes(bytes) => {
|
|
||||||
ciborium::from_reader::<ServerMessage, _>(bytes.as_slice())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match parse {
|
|
||||||
Ok(msg) => self.scope.send_message(Message::RecvServerMessage(msg)),
|
|
||||||
Err(err) => {
|
|
||||||
log::error!("parsing server message: {err}; ignoring.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
|
||||||
pub enum ClientEvent {
|
|
||||||
Disconnected,
|
|
||||||
Waiting,
|
|
||||||
ShowRole(RoleTitle),
|
|
||||||
NotInLobby(Box<[PublicIdentity]>),
|
|
||||||
InLobby(Box<[PublicIdentity]>),
|
|
||||||
GameInProgress,
|
|
||||||
GameOver(GameOver),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<ServerMessage> for ClientEvent {
|
|
||||||
type Error = ServerMessage;
|
|
||||||
|
|
||||||
fn try_from(msg: ServerMessage) -> Result<Self, Self::Error> {
|
|
||||||
Ok(match msg {
|
|
||||||
ServerMessage::Disconnect => Self::Disconnected,
|
|
||||||
ServerMessage::LobbyInfo {
|
|
||||||
joined,
|
|
||||||
mut players,
|
|
||||||
} => {
|
|
||||||
const LAST: NonZeroU8 = NonZeroU8::new(0xFF).unwrap();
|
|
||||||
players.sort_by(|l, r| l.number.unwrap_or(LAST).cmp(&r.number.unwrap_or(LAST)));
|
|
||||||
let players = players.into_iter().collect();
|
|
||||||
match joined {
|
|
||||||
true => Self::InLobby(players),
|
|
||||||
false => Self::NotInLobby(players),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ServerMessage::GameInProgress => Self::GameInProgress,
|
|
||||||
_ => return Err(msg),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub enum ClientEvent2 {
|
pub enum ClientEvent2 {
|
||||||
Disconnected,
|
Disconnected,
|
||||||
Connecting,
|
Connecting,
|
||||||
Waiting,
|
|
||||||
ShowRole(RoleTitle),
|
ShowRole(RoleTitle),
|
||||||
|
|
||||||
Lobby {
|
Lobby {
|
||||||
|
|
@ -299,7 +97,6 @@ pub fn Client2(ClientProps { auto_join }: &ClientProps) -> Html {
|
||||||
}
|
}
|
||||||
html! {<p>{"connecting..."}</p>}
|
html! {<p>{"connecting..."}</p>}
|
||||||
}
|
}
|
||||||
ClientEvent2::Waiting => html! {<p>{"waiting..."}</p>},
|
|
||||||
ClientEvent2::ShowRole(role_title) => {
|
ClientEvent2::ShowRole(role_title) => {
|
||||||
let send = (*send).clone();
|
let send = (*send).clone();
|
||||||
let error_cb = error_cb.clone();
|
let error_cb = error_cb.clone();
|
||||||
|
|
@ -386,346 +183,8 @@ pub fn Client2(ClientProps { auto_join }: &ClientProps) -> Html {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Client {
|
|
||||||
player: Option<Identification>,
|
|
||||||
send: Sender<ClientMessage>,
|
|
||||||
recv: Option<Receiver<ClientMessage>>,
|
|
||||||
current_event: Option<ClientEvent>,
|
|
||||||
auto_join: bool,
|
|
||||||
|
|
||||||
error_callback: Callback<Option<WerewolfError>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Client {
|
|
||||||
fn error(&self, err: WerewolfError) {
|
|
||||||
self.error_callback.emit(Some(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_error(&self) {
|
|
||||||
self.error_callback.emit(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bug(&self, msg: &str) {
|
|
||||||
log::warn!("BUG: {msg}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Copy, Properties)]
|
#[derive(Debug, Clone, PartialEq, Copy, Properties)]
|
||||||
pub struct ClientProps {
|
pub struct ClientProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub auto_join: bool,
|
pub auto_join: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Client {
|
|
||||||
type Message = Message;
|
|
||||||
|
|
||||||
type Properties = ClientProps;
|
|
||||||
|
|
||||||
fn create(ctx: &Context<Self>) -> Self {
|
|
||||||
gloo::utils::document().set_title("Werewolves Player");
|
|
||||||
let player = PlayerId::load_from_storage()
|
|
||||||
.ok()
|
|
||||||
.and_then(|p| PublicIdentity::load_from_storage().ok().map(|n| (p, n)))
|
|
||||||
.map(|(player_id, public)| Identification { player_id, public });
|
|
||||||
|
|
||||||
let (send, recv) = futures::channel::mpsc::channel::<ClientMessage>(100);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
player,
|
|
||||||
send,
|
|
||||||
recv: Some(recv),
|
|
||||||
auto_join: ctx.props().auto_join,
|
|
||||||
error_callback: Callback::from(|err| {
|
|
||||||
if let Some(err) = err {
|
|
||||||
log::error!("{err}")
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
current_event: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
||||||
if self.player.is_none() {
|
|
||||||
let scope = ctx.link().clone();
|
|
||||||
let callback = Callback::from(move |public: PublicIdentity| {
|
|
||||||
scope.send_message(Message::SetPublicIdentity(public));
|
|
||||||
});
|
|
||||||
return html! {
|
|
||||||
<Signin callback={callback}/>
|
|
||||||
};
|
|
||||||
} else if self.recv.is_some() {
|
|
||||||
// Player info loaded, but connection isn't started
|
|
||||||
ctx.link().send_message(Message::Connect);
|
|
||||||
return html! {};
|
|
||||||
}
|
|
||||||
|
|
||||||
let msg = match self.current_event.as_ref() {
|
|
||||||
Some(msg) => msg,
|
|
||||||
None => {
|
|
||||||
return html! {
|
|
||||||
<div class="connecting">
|
|
||||||
<p>{"Connecting..."}</p>
|
|
||||||
</div>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let content = match msg {
|
|
||||||
ClientEvent::Disconnected => html! {
|
|
||||||
<div class="disconnected">
|
|
||||||
<p>{"You were disconnected"}</p>
|
|
||||||
</div>
|
|
||||||
},
|
|
||||||
ClientEvent::Waiting => {
|
|
||||||
html! {
|
|
||||||
<div class="waiting-lobby">
|
|
||||||
<p>{"Waiting"}</p>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClientEvent::ShowRole(role_title) => {
|
|
||||||
let send = self.send.clone();
|
|
||||||
let on_click =
|
|
||||||
Callback::from(
|
|
||||||
move |_| {
|
|
||||||
while send.clone().try_send(ClientMessage::RoleAck).is_err() {}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
html! {
|
|
||||||
<div class="game-start-role">
|
|
||||||
<p>{format!("Your role: {role_title}")}</p>
|
|
||||||
<button onclick={on_click}>{"got it"}</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClientEvent::NotInLobby(players) => {
|
|
||||||
let send = self.send.clone();
|
|
||||||
let on_click =
|
|
||||||
Callback::from(
|
|
||||||
move |_| {
|
|
||||||
while send.clone().try_send(ClientMessage::Hello).is_err() {}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
html! {
|
|
||||||
<div class="lobby">
|
|
||||||
<Button on_click={on_click}>{"Join"}</Button>
|
|
||||||
<p>{format!("Players in lobby: {}", players.len())}</p>
|
|
||||||
<ul class="players">
|
|
||||||
{players.iter().map(|p| html!{<p>{p.to_string()}</p>}).collect::<Html>()}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClientEvent::InLobby(players) => {
|
|
||||||
let send = self.send.clone();
|
|
||||||
let on_click =
|
|
||||||
Callback::from(
|
|
||||||
move |_| {
|
|
||||||
while send.clone().try_send(ClientMessage::Goodbye).is_err() {}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
html! {
|
|
||||||
<div class="lobby">
|
|
||||||
<Button on_click={on_click}>{"Leave"}</Button>
|
|
||||||
<p>{format!("Players in lobby: {}", players.len())}</p>
|
|
||||||
<ul class="players">
|
|
||||||
{players.iter().map(|p| html!{<p>{p.to_string()}</p>}).collect::<Html>()}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClientEvent::GameInProgress => html! {
|
|
||||||
<div class="waiting-lobby">
|
|
||||||
<p>{"game in progress"}</p>
|
|
||||||
</div>
|
|
||||||
},
|
|
||||||
ClientEvent::GameOver(result) => html! {
|
|
||||||
<div>
|
|
||||||
<h2>{"game over"}</h2>
|
|
||||||
<p>{
|
|
||||||
match result {
|
|
||||||
GameOver::VillageWins => "village wins",
|
|
||||||
GameOver::WolvesWin => "wolves win",
|
|
||||||
}}</p>
|
|
||||||
</div>
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let player = self
|
|
||||||
.player
|
|
||||||
.as_ref()
|
|
||||||
.map(|player| {
|
|
||||||
html! {
|
|
||||||
<Identity ident={player.public.clone()} class="zoom">
|
|
||||||
</Identity>
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or(html!());
|
|
||||||
|
|
||||||
let send = self.send.clone();
|
|
||||||
let client_nav_msg_cb = move |msg| {
|
|
||||||
let mut send = send.clone();
|
|
||||||
yew::platform::spawn_local(async move {
|
|
||||||
if let Err(err) = send.send(msg).await {
|
|
||||||
log::error!("sending nav message: {err}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
let nav = self.player.as_ref().map(|_| {
|
|
||||||
html! {
|
|
||||||
<ClientNav message_callback={client_nav_msg_cb} />
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
html! {
|
|
||||||
<>
|
|
||||||
{nav}
|
|
||||||
<client>
|
|
||||||
{player}
|
|
||||||
{content}
|
|
||||||
</client>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
|
||||||
log::info!("update: {msg:?}");
|
|
||||||
match msg {
|
|
||||||
Message::ForceIdentity(id) => {
|
|
||||||
self.player.replace(id);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
Message::SetErrorCallback(cb) => {
|
|
||||||
self.error_callback = cb;
|
|
||||||
false
|
|
||||||
}
|
|
||||||
Message::SetPublicIdentity(public) => {
|
|
||||||
match self.player.as_mut() {
|
|
||||||
Some(p) => {
|
|
||||||
if let Err(err) = public.save_to_storage() {
|
|
||||||
self.error(err.into());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
p.public = public;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let player_id = match PlayerId::load_from_storage() {
|
|
||||||
Ok(pid) => pid,
|
|
||||||
Err(StorageError::KeyNotFound(_)) => {
|
|
||||||
let pid = PlayerId::new();
|
|
||||||
if let Err(err) = pid.save_to_storage() {
|
|
||||||
self.error(err.into());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pid
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
self.error(err.into());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Err(err) = public.save_to_storage() {
|
|
||||||
self.error(err.into());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let ident = Identification { player_id, public };
|
|
||||||
self.player = Some(ident.clone());
|
|
||||||
|
|
||||||
if let Some(recv) = self.recv.take() {
|
|
||||||
yew::platform::spawn_local(
|
|
||||||
Connection {
|
|
||||||
recv,
|
|
||||||
ident,
|
|
||||||
scope: ctx.link().clone(),
|
|
||||||
}
|
|
||||||
.run(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
Message::RecvServerMessage(msg) => {
|
|
||||||
if let ServerMessage::LobbyInfo {
|
|
||||||
joined: false,
|
|
||||||
players: _,
|
|
||||||
} = &msg
|
|
||||||
&& self.auto_join
|
|
||||||
{
|
|
||||||
let mut send = self.send.clone();
|
|
||||||
yew::platform::spawn_local(async move {
|
|
||||||
if let Err(err) = send.send(ClientMessage::Hello).await {
|
|
||||||
log::error!("send: {err}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
self.auto_join = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let msg = match msg.try_into() {
|
|
||||||
Ok(event) => {
|
|
||||||
self.current_event.replace(event);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Err(msg) => msg,
|
|
||||||
};
|
|
||||||
match msg {
|
|
||||||
ServerMessage::GameStart { role } => {
|
|
||||||
self.current_event.replace(ClientEvent::ShowRole(role));
|
|
||||||
}
|
|
||||||
ServerMessage::InvalidMessageForGameState => self.error(
|
|
||||||
WerewolfError::GameError(GameError::InvalidMessageForGameState),
|
|
||||||
),
|
|
||||||
ServerMessage::NoSuchTarget => {
|
|
||||||
self.error(WerewolfError::InvalidTarget);
|
|
||||||
}
|
|
||||||
ServerMessage::GameInProgress
|
|
||||||
| ServerMessage::LobbyInfo {
|
|
||||||
joined: _,
|
|
||||||
players: _,
|
|
||||||
}
|
|
||||||
| ServerMessage::Disconnect => return false,
|
|
||||||
ServerMessage::GameOver(game_over) => {
|
|
||||||
self.current_event = Some(ClientEvent::GameOver(game_over));
|
|
||||||
}
|
|
||||||
ServerMessage::Reset => {
|
|
||||||
let mut send = self.send.clone();
|
|
||||||
self.current_event = Some(ClientEvent::Disconnected);
|
|
||||||
yew::platform::spawn_local(async move {
|
|
||||||
if let Err(err) = send.send(ClientMessage::GetState).await {
|
|
||||||
log::error!("send: {err}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ServerMessage::Sleep => self.current_event = Some(ClientEvent::Waiting),
|
|
||||||
ServerMessage::Update(update) => match (update, self.player.as_mut()) {
|
|
||||||
(PlayerUpdate::Number(num), Some(player)) => {
|
|
||||||
player.public.number = Some(num);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
(_, None) => return false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
true
|
|
||||||
}
|
|
||||||
Message::Connect => {
|
|
||||||
if let Some(player) = self.player.as_ref()
|
|
||||||
&& let Some(recv) = self.recv.take()
|
|
||||||
{
|
|
||||||
yew::platform::spawn_local(
|
|
||||||
Connection {
|
|
||||||
scope: ctx.link().clone(),
|
|
||||||
ident: player.clone(),
|
|
||||||
recv,
|
|
||||||
}
|
|
||||||
.run(),
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
while let Err(err) = self.send.try_send(ClientMessage::GetState) {
|
|
||||||
log::error!("send IsThereALobby: {err}")
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ impl Connection2 {
|
||||||
.map_err(|_| ConnectionError::ConnectionAlreadyActive)?;
|
.map_err(|_| ConnectionError::ConnectionAlreadyActive)?;
|
||||||
core::mem::drop(active);
|
core::mem::drop(active);
|
||||||
let mut conn = self.clone();
|
let mut conn = self.clone();
|
||||||
|
#[allow(clippy::await_holding_refcell_ref)]
|
||||||
yew::platform::spawn_local(async move {
|
yew::platform::spawn_local(async move {
|
||||||
let active = conn.active.clone();
|
let active = conn.active.clone();
|
||||||
conn.active = Rc::new(RefCell::new(()));
|
conn.active = Rc::new(RefCell::new(()));
|
||||||
|
|
@ -105,6 +106,7 @@ impl Connection2 {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::await_holding_refcell_ref)]
|
||||||
async fn run(&mut self) {
|
async fn run(&mut self) {
|
||||||
const CONNECT_WAIT: Duration = Duration::from_secs(3);
|
const CONNECT_WAIT: Duration = Duration::from_secs(3);
|
||||||
let url = url();
|
let url = url();
|
||||||
|
|
@ -138,7 +140,8 @@ impl Connection2 {
|
||||||
};
|
};
|
||||||
log::debug!("beginning listening loop");
|
log::debug!("beginning listening loop");
|
||||||
|
|
||||||
loop {
|
let mut quit = false;
|
||||||
|
while !quit {
|
||||||
let mut recv = self.receiver.borrow_mut();
|
let mut recv = self.receiver.borrow_mut();
|
||||||
let msg = futures::select! {
|
let msg = futures::select! {
|
||||||
r = ws.next() => {
|
r = ws.next() => {
|
||||||
|
|
@ -199,6 +202,7 @@ impl Connection2 {
|
||||||
};
|
};
|
||||||
match parse {
|
match parse {
|
||||||
Ok(msg) => {
|
Ok(msg) => {
|
||||||
|
quit = matches!(msg, ServerMessage::Disconnect);
|
||||||
if let Some(state) = self.message_to_client_state(msg) {
|
if let Some(state) = self.message_to_client_state(msg) {
|
||||||
self.state.set(state);
|
self.state.set(state);
|
||||||
}
|
}
|
||||||
|
|
@ -245,6 +249,7 @@ impl Connection2 {
|
||||||
| ServerMessage::Sleep
|
| ServerMessage::Sleep
|
||||||
| ServerMessage::Reset
|
| ServerMessage::Reset
|
||||||
| ServerMessage::GameInProgress => {
|
| ServerMessage::GameInProgress => {
|
||||||
|
log::info!("ignoring: {msg:?}");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,10 @@ pub struct TwoTargetProps {
|
||||||
pub target_selection: Option<Callback<(CharacterId, CharacterId)>>,
|
pub target_selection: Option<Callback<(CharacterId, CharacterId)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ChecksAs, Clone)]
|
#[derive(Clone)]
|
||||||
enum TwoTargetSelection {
|
enum TwoTargetSelection {
|
||||||
None,
|
None,
|
||||||
One(CharacterId),
|
One(CharacterId),
|
||||||
#[checks]
|
|
||||||
Two(CharacterId, CharacterId),
|
Two(CharacterId, CharacterId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Properties)]
|
// #[derive(Debug, PartialEq, Properties)]
|
||||||
pub struct NotificationProps {
|
// pub struct NotificationProps {
|
||||||
pub text: String,
|
// pub text: String,
|
||||||
pub callback: Callback<()>,
|
// pub callback: Callback<()>,
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[function_component]
|
// #[function_component]
|
||||||
pub fn Notification(props: &NotificationProps) -> Html {
|
// pub fn Notification(props: &NotificationProps) -> Html {
|
||||||
let cb = props.callback.clone();
|
// let cb = props.callback.clone();
|
||||||
let on_click = Callback::from(move |_| cb.clone().emit(()));
|
// let on_click = Callback::from(move |_| cb.clone().emit(()));
|
||||||
html! {
|
// html! {
|
||||||
<stack>
|
// <stack>
|
||||||
<h2>{props.text.clone()}</h2>
|
// <h2>{props.text.clone()}</h2>
|
||||||
<button class="confirm" onclick={on_click}>{"Ok"}</button>
|
// <button class="confirm" onclick={on_click}>{"Ok"}</button>
|
||||||
</stack>
|
// </stack>
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use werewolves_proto::{
|
||||||
use yew::{context::ContextProviderProps, prelude::*};
|
use yew::{context::ContextProviderProps, prelude::*};
|
||||||
|
|
||||||
use crate::clients::{
|
use crate::clients::{
|
||||||
client::{Client, Client2, ClientContext, ClientProps, Message},
|
client::{Client2, ClientContext},
|
||||||
host::{Host, HostEvent},
|
host::{Host, HostEvent},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -70,7 +70,7 @@ fn main() {
|
||||||
clients.append_child(&dupe).unwrap();
|
clients.append_child(&dupe).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = yew::Renderer::<ContextProvider<ClientContext>>::with_root_and_props(
|
yew::Renderer::<ContextProvider<ClientContext>>::with_root_and_props(
|
||||||
dupe,
|
dupe,
|
||||||
ContextProviderProps {
|
ContextProviderProps {
|
||||||
context: ClientContext {
|
context: ClientContext {
|
||||||
|
|
@ -90,25 +90,9 @@ fn main() {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.render();
|
.render();
|
||||||
|
|
||||||
// let client = yew::Renderer::<Client2>::with_root_and_props(
|
|
||||||
// dupe,
|
|
||||||
// ClientProps { auto_join: true },
|
|
||||||
// )
|
|
||||||
// .render();
|
|
||||||
|
|
||||||
// client.send_message(Message::ForceIdentity(Identification {
|
|
||||||
// player_id,
|
|
||||||
// public: PublicIdentity {
|
|
||||||
// name: name.to_string(),
|
|
||||||
// pronouns: Some(String::from("he/him")),
|
|
||||||
// number: None,
|
|
||||||
// },
|
|
||||||
// }));
|
|
||||||
// client.send_message(Message::SetErrorCallback(error_callback.clone()));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let client = yew::Renderer::<ContextProvider<ClientContext>>::with_root_and_props(
|
yew::Renderer::<ContextProvider<ClientContext>>::with_root_and_props(
|
||||||
app_element,
|
app_element,
|
||||||
ContextProviderProps {
|
ContextProviderProps {
|
||||||
context: ClientContext {
|
context: ClientContext {
|
||||||
|
|
@ -121,11 +105,5 @@ fn main() {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.render();
|
.render();
|
||||||
// let client = yew::Renderer::<Client>::with_root_and_props(
|
|
||||||
// app_element,
|
|
||||||
// ClientProps { auto_join: false },
|
|
||||||
// )
|
|
||||||
// .render();
|
|
||||||
// client.send_message(Message::SetErrorCallback(error_callback));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue