warn fixes for server and proto
This commit is contained in:
parent
242691a05f
commit
8c2791054a
|
|
@ -1,7 +1,7 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{player::CharacterId, role::RoleTitle};
|
||||
use crate::role::RoleTitle;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Error, Serialize, Deserialize)]
|
||||
pub enum GameError {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
use core::{
|
||||
num::NonZeroU8,
|
||||
ops::{Deref, Not},
|
||||
};
|
||||
use core::{num::NonZeroU8, ops::Not};
|
||||
|
||||
use super::Result;
|
||||
use crate::{
|
||||
diedto::DiedTo,
|
||||
error::GameError,
|
||||
game::{Village, kill::taken::Taken, night::NightChange},
|
||||
game::{Village, night::NightChange},
|
||||
player::{CharacterId, Protection},
|
||||
};
|
||||
|
||||
|
|
@ -26,10 +23,13 @@ pub enum KillOutcome {
|
|||
impl KillOutcome {
|
||||
pub fn apply_to_village(self, village: &mut Village) -> Result<()> {
|
||||
match self {
|
||||
KillOutcome::Single(character_id, died_to) => Ok(village
|
||||
KillOutcome::Single(character_id, died_to) => {
|
||||
village
|
||||
.character_by_id_mut(&character_id)
|
||||
.ok_or(GameError::InvalidTarget)?
|
||||
.kill(died_to)),
|
||||
.kill(died_to);
|
||||
Ok(())
|
||||
}
|
||||
KillOutcome::Guarding {
|
||||
original_killer,
|
||||
original_target,
|
||||
|
|
@ -146,7 +146,7 @@ pub fn resolve_kill(
|
|||
None => return Ok(Some(KillOutcome::Single(target.clone(), died_to.clone()))),
|
||||
};
|
||||
|
||||
match protection.deref() {
|
||||
match protection {
|
||||
Protection::Guardian {
|
||||
source,
|
||||
guarding: true,
|
||||
|
|
@ -188,17 +188,7 @@ impl<'a> ChangesLookup<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn release<T>(&mut self, taken: Taken<'a, T>) {
|
||||
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>> {
|
||||
pub fn protected_take(&mut self, target: &CharacterId) -> Option<Protection> {
|
||||
if let Some((idx, c)) = self.0.iter().enumerate().find_map(|(idx, c)| {
|
||||
self.1
|
||||
.contains(&idx)
|
||||
|
|
@ -213,7 +203,7 @@ impl<'a> ChangesLookup<'a> {
|
|||
.flatten()
|
||||
}) {
|
||||
self.1.push(idx);
|
||||
Some(Taken::new(idx, c))
|
||||
Some(c.clone())
|
||||
} else {
|
||||
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::{
|
||||
CharacterState, Identification,
|
||||
host::{HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage},
|
||||
night::ActionResponse,
|
||||
},
|
||||
player::CharacterId,
|
||||
};
|
||||
|
|
@ -193,6 +192,7 @@ impl Game {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum GameState {
|
||||
Day {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
DateTime, Village,
|
||||
kill::{self, ChangesLookup},
|
||||
},
|
||||
message::night::{ActionPrompt, ActionResponse, ActionResult, ActionType},
|
||||
message::night::{ActionPrompt, ActionResponse, ActionResult},
|
||||
player::{Character, CharacterId, Protection},
|
||||
role::{PreviousGuardianAction, Role, RoleBlock, RoleTitle},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ impl Village {
|
|||
.map(|(player, role)| {
|
||||
let player_str = player.public.to_string();
|
||||
Character::new(player, role)
|
||||
.ok_or_else(|| GameError::PlayerNotAssignedNumber(player_str))
|
||||
.ok_or(GameError::PlayerNotAssignedNumber(player_str))
|
||||
})
|
||||
.collect::<Result<Box<[_]>>>()?,
|
||||
date_time: DateTime::Night { number: 0 },
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ trait GameExt {
|
|||
fn next(&mut self) -> ActionPrompt;
|
||||
fn next_expect_day(&mut self) -> (Box<[CharacterState]>, Box<[CharacterId]>, NonZeroU8);
|
||||
fn response(&mut self, resp: ActionResponse) -> ActionResult;
|
||||
fn get_state(&mut self) -> ServerToHostMessage;
|
||||
fn execute(&mut self) -> ActionPrompt;
|
||||
fn mark_for_execution(
|
||||
&mut self,
|
||||
|
|
@ -111,10 +110,6 @@ impl GameExt for Game {
|
|||
.unwrap()
|
||||
.prompt()
|
||||
}
|
||||
|
||||
fn get_state(&mut self) -> ServerToHostMessage {
|
||||
self.process(HostGameMessage::GetState).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn init_log() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
#![allow(clippy::new_without_default)]
|
||||
use error::GameError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
// pub mod action;
|
||||
pub mod diedto;
|
||||
pub mod error;
|
||||
pub mod game;
|
||||
|
|
|
|||
|
|
@ -2,18 +2,12 @@ pub mod host;
|
|||
mod ident;
|
||||
pub mod night;
|
||||
|
||||
use core::{fmt::Display, num::NonZeroU8};
|
||||
use core::num::NonZeroU8;
|
||||
|
||||
pub use ident::*;
|
||||
use night::{ActionPrompt, ActionResponse, ActionResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
error::GameError,
|
||||
game::GameOver,
|
||||
player::{Character, CharacterId},
|
||||
role::RoleTitle,
|
||||
};
|
||||
use crate::{game::GameOver, player::CharacterId, role::RoleTitle};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum ClientMessage {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
error::GameError,
|
||||
game::{GameOver, GameSettings},
|
||||
message::{
|
||||
CharacterIdentity, PublicIdentity,
|
||||
CharacterIdentity,
|
||||
night::{ActionPrompt, ActionResponse, ActionResult},
|
||||
},
|
||||
player::{CharacterId, PlayerId},
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ pub async fn handler(
|
|||
recv,
|
||||
connection_id.clone(),
|
||||
ident.public.name.clone(),
|
||||
ident.public.number.clone(),
|
||||
ident.public.number,
|
||||
ident.public.pronouns.clone(),
|
||||
),
|
||||
)
|
||||
|
|
@ -161,11 +161,7 @@ impl Client {
|
|||
}
|
||||
}
|
||||
|
||||
async fn on_recv(
|
||||
&mut self,
|
||||
msg: Result<Message, axum::Error>,
|
||||
conn_id: ConnectionId,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
async fn on_recv(&mut self, msg: Result<Message, axum::Error>) -> Result<(), anyhow::Error> {
|
||||
use crate::LogError;
|
||||
|
||||
let msg = match msg {
|
||||
|
|
@ -231,7 +227,7 @@ impl Client {
|
|||
if let Err(err) = tokio::select! {
|
||||
msg = self.socket.recv() => {
|
||||
match msg {
|
||||
Some(msg) => self.on_recv(msg, self.connection_id.clone()).await,
|
||||
Some(msg) => self.on_recv(msg).await,
|
||||
None => {
|
||||
return;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -22,10 +22,7 @@ impl HostComms {
|
|||
|
||||
#[cfg(debug_assertions)]
|
||||
pub async fn recv(&mut self) -> Option<HostMessage> {
|
||||
match self.recv.recv().await {
|
||||
Some(msg) => Some(msg),
|
||||
None => None,
|
||||
}
|
||||
self.recv.recv().await
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ impl LobbyComms {
|
|||
(self.comms, self.connect_recv)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub const fn player(&mut self) -> &mut PlayerIdComms {
|
||||
self.comms.player()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ impl Comms {
|
|||
&mut self.host
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub const fn player(&mut self) -> &mut PlayerIdComms {
|
||||
&mut self.player
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use colored::Colorize;
|
||||
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 {
|
||||
// joined_players: JoinedPlayers,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use tokio::{
|
|||
time::Instant,
|
||||
};
|
||||
use werewolves_proto::{
|
||||
error::GameError,
|
||||
message::{PublicIdentity, ServerMessage},
|
||||
player::PlayerId,
|
||||
};
|
||||
|
|
@ -142,30 +141,6 @@ impl JoinedPlayers {
|
|||
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>> {
|
||||
self.players
|
||||
.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},
|
||||
runner::{IdentifiedClientMessage, Message},
|
||||
};
|
||||
use futures::SinkExt;
|
||||
use tokio::{sync::broadcast::Receiver, time::Instant};
|
||||
use werewolves_proto::{
|
||||
error::GameError,
|
||||
|
|
@ -53,7 +52,6 @@ impl GameRunner {
|
|||
}
|
||||
|
||||
pub fn into_lobby(self) -> Lobby {
|
||||
// core::mem::drop(self._release_token);
|
||||
let mut lobby = Lobby::new(
|
||||
self.joined_players,
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,11 @@
|
|||
use core::{
|
||||
num::NonZeroU8,
|
||||
ops::{Deref, DerefMut},
|
||||
time::Duration,
|
||||
};
|
||||
use std::{collections::HashMap, os::unix::raw::pid_t};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::broadcast::Sender;
|
||||
use werewolves_proto::{
|
||||
error::GameError,
|
||||
game::{Game, GameSettings, Village},
|
||||
game::{Game, GameSettings},
|
||||
message::{
|
||||
CharacterState, ClientMessage, DayCharacter, Identification, PlayerState, ServerMessage,
|
||||
UpdateSelf,
|
||||
ClientMessage, Identification, PlayerState, ServerMessage,
|
||||
host::{HostLobbyMessage, HostMessage, ServerToHostMessage},
|
||||
},
|
||||
player::PlayerId,
|
||||
|
|
@ -353,10 +346,4 @@ impl LobbyPlayers {
|
|||
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 connection::JoinedPlayers;
|
||||
use core::{fmt::Display, net::SocketAddr, str::FromStr};
|
||||
use log::Record;
|
||||
use runner::IdentifiedClientMessage;
|
||||
use std::{env, io::Write, path::Path};
|
||||
use tokio::{
|
||||
sync::{broadcast, mpsc},
|
||||
time::Instant,
|
||||
};
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
|
||||
use crate::{
|
||||
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));
|
||||
|
||||
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)
|
||||
{
|
||||
panic!("creating save dir at [{path:?}]: {err}")
|
||||
|
|
|
|||
|
|
@ -586,7 +586,7 @@ clients {
|
|||
margin-left: 5vw;
|
||||
margin-right: 5vw;
|
||||
margin-top: 30px;
|
||||
display: flexbox;
|
||||
display: flex;
|
||||
flex-basis: content;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +1,17 @@
|
|||
use core::{num::NonZeroU8, sync::atomic::AtomicBool, time::Duration};
|
||||
use std::{collections::VecDeque, rc::Rc};
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::{
|
||||
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 gloo::storage::errors::StorageError;
|
||||
use werewolves_proto::{
|
||||
error::GameError,
|
||||
game::GameOver,
|
||||
message::{
|
||||
ClientMessage, DayCharacter, Identification, PlayerUpdate, PublicIdentity, ServerMessage,
|
||||
night::{ActionPrompt, ActionResponse, ActionResult},
|
||||
},
|
||||
message::{ClientMessage, Identification, PublicIdentity},
|
||||
player::PlayerId,
|
||||
role::RoleTitle,
|
||||
};
|
||||
use yew::{html::Scope, prelude::*, suspense::use_future};
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::{
|
||||
clients::client::connection::{Connection2, ConnectionError},
|
||||
components::{
|
||||
Button, Identity, Notification,
|
||||
Button, Identity,
|
||||
client::{ClientNav, Signin},
|
||||
},
|
||||
storage::StorageKey,
|
||||
|
|
@ -34,197 +19,10 @@ use crate::{
|
|||
|
||||
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)]
|
||||
pub enum ClientEvent2 {
|
||||
Disconnected,
|
||||
Connecting,
|
||||
Waiting,
|
||||
ShowRole(RoleTitle),
|
||||
|
||||
Lobby {
|
||||
|
|
@ -299,7 +97,6 @@ pub fn Client2(ClientProps { auto_join }: &ClientProps) -> Html {
|
|||
}
|
||||
html! {<p>{"connecting..."}</p>}
|
||||
}
|
||||
ClientEvent2::Waiting => html! {<p>{"waiting..."}</p>},
|
||||
ClientEvent2::ShowRole(role_title) => {
|
||||
let send = (*send).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)]
|
||||
pub struct ClientProps {
|
||||
#[prop_or_default]
|
||||
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)?;
|
||||
core::mem::drop(active);
|
||||
let mut conn = self.clone();
|
||||
#[allow(clippy::await_holding_refcell_ref)]
|
||||
yew::platform::spawn_local(async move {
|
||||
let active = conn.active.clone();
|
||||
conn.active = Rc::new(RefCell::new(()));
|
||||
|
|
@ -105,6 +106,7 @@ impl Connection2 {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::await_holding_refcell_ref)]
|
||||
async fn run(&mut self) {
|
||||
const CONNECT_WAIT: Duration = Duration::from_secs(3);
|
||||
let url = url();
|
||||
|
|
@ -138,7 +140,8 @@ impl Connection2 {
|
|||
};
|
||||
log::debug!("beginning listening loop");
|
||||
|
||||
loop {
|
||||
let mut quit = false;
|
||||
while !quit {
|
||||
let mut recv = self.receiver.borrow_mut();
|
||||
let msg = futures::select! {
|
||||
r = ws.next() => {
|
||||
|
|
@ -199,6 +202,7 @@ impl Connection2 {
|
|||
};
|
||||
match parse {
|
||||
Ok(msg) => {
|
||||
quit = matches!(msg, ServerMessage::Disconnect);
|
||||
if let Some(state) = self.message_to_client_state(msg) {
|
||||
self.state.set(state);
|
||||
}
|
||||
|
|
@ -245,6 +249,7 @@ impl Connection2 {
|
|||
| ServerMessage::Sleep
|
||||
| ServerMessage::Reset
|
||||
| ServerMessage::GameInProgress => {
|
||||
log::info!("ignoring: {msg:?}");
|
||||
return None;
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -19,11 +19,10 @@ pub struct TwoTargetProps {
|
|||
pub target_selection: Option<Callback<(CharacterId, CharacterId)>>,
|
||||
}
|
||||
|
||||
#[derive(ChecksAs, Clone)]
|
||||
#[derive(Clone)]
|
||||
enum TwoTargetSelection {
|
||||
None,
|
||||
One(CharacterId),
|
||||
#[checks]
|
||||
Two(CharacterId, CharacterId),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Properties)]
|
||||
pub struct NotificationProps {
|
||||
pub text: String,
|
||||
pub callback: Callback<()>,
|
||||
}
|
||||
// #[derive(Debug, PartialEq, Properties)]
|
||||
// pub struct NotificationProps {
|
||||
// pub text: String,
|
||||
// pub callback: Callback<()>,
|
||||
// }
|
||||
|
||||
#[function_component]
|
||||
pub fn Notification(props: &NotificationProps) -> Html {
|
||||
let cb = props.callback.clone();
|
||||
let on_click = Callback::from(move |_| cb.clone().emit(()));
|
||||
html! {
|
||||
<stack>
|
||||
<h2>{props.text.clone()}</h2>
|
||||
<button class="confirm" onclick={on_click}>{"Ok"}</button>
|
||||
</stack>
|
||||
}
|
||||
}
|
||||
// #[function_component]
|
||||
// pub fn Notification(props: &NotificationProps) -> Html {
|
||||
// let cb = props.callback.clone();
|
||||
// let on_click = Callback::from(move |_| cb.clone().emit(()));
|
||||
// html! {
|
||||
// <stack>
|
||||
// <h2>{props.text.clone()}</h2>
|
||||
// <button class="confirm" onclick={on_click}>{"Ok"}</button>
|
||||
// </stack>
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use werewolves_proto::{
|
|||
use yew::{context::ContextProviderProps, prelude::*};
|
||||
|
||||
use crate::clients::{
|
||||
client::{Client, Client2, ClientContext, ClientProps, Message},
|
||||
client::{Client2, ClientContext},
|
||||
host::{Host, HostEvent},
|
||||
};
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ fn main() {
|
|||
clients.append_child(&dupe).unwrap();
|
||||
}
|
||||
|
||||
let client = yew::Renderer::<ContextProvider<ClientContext>>::with_root_and_props(
|
||||
yew::Renderer::<ContextProvider<ClientContext>>::with_root_and_props(
|
||||
dupe,
|
||||
ContextProviderProps {
|
||||
context: ClientContext {
|
||||
|
|
@ -90,25 +90,9 @@ fn main() {
|
|||
},
|
||||
)
|
||||
.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 {
|
||||
let client = yew::Renderer::<ContextProvider<ClientContext>>::with_root_and_props(
|
||||
yew::Renderer::<ContextProvider<ClientContext>>::with_root_and_props(
|
||||
app_element,
|
||||
ContextProviderProps {
|
||||
context: ClientContext {
|
||||
|
|
@ -121,11 +105,5 @@ fn main() {
|
|||
},
|
||||
)
|
||||
.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