cancellation fixes, aura fixes, etc.
This commit is contained in:
parent
06294d872e
commit
852973eddf
|
|
@ -11,7 +11,7 @@ $village_border: color.change($village_color, $alpha: 1.0);
|
||||||
$wolves_border: color.change($wolves_color, $alpha: 1.0);
|
$wolves_border: color.change($wolves_color, $alpha: 1.0);
|
||||||
$intel_color: color.adjust($village_color, $hue: -30deg);
|
$intel_color: color.adjust($village_color, $hue: -30deg);
|
||||||
$intel_border: color.change($intel_color, $alpha: 1.0);
|
$intel_border: color.change($intel_color, $alpha: 1.0);
|
||||||
$defensive_color: color.adjust($village_color, $hue: -60deg);
|
$defensive_color: rgba(0, 128, 32, 0.9); //color.adjust(rgba(0, 16, 128, 0.9), $hue: -60deg);
|
||||||
$defensive_border: color.change($defensive_color, $alpha: 1.0);
|
$defensive_border: color.change($defensive_color, $alpha: 1.0);
|
||||||
$offensive_color: color.adjust($village_color, $hue: 30deg);
|
$offensive_color: color.adjust($village_color, $hue: 30deg);
|
||||||
$offensive_border: color.change($offensive_color, $alpha: 1.0);
|
$offensive_border: color.change($offensive_color, $alpha: 1.0);
|
||||||
|
|
@ -446,6 +446,17 @@ dialog::backdrop {
|
||||||
gap: 0.25ch;
|
gap: 0.25ch;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.missing {
|
||||||
|
word-break: normal;
|
||||||
|
user-select: none;
|
||||||
|
font-size: 0.75em;
|
||||||
|
color: rgb(128, 0, 0);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(255, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.setup-slot {
|
.setup-slot {
|
||||||
color: rgba(255, 255, 255, 0.9);
|
color: rgba(255, 255, 255, 0.9);
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ use crate::{
|
||||||
aura::AuraTitle,
|
aura::AuraTitle,
|
||||||
character::{Character, CharacterId},
|
character::{Character, CharacterId},
|
||||||
error::GameError,
|
error::GameError,
|
||||||
|
id_impl,
|
||||||
message::Identification,
|
message::Identification,
|
||||||
player::PlayerId,
|
player::PlayerId,
|
||||||
role::{Role, RoleTitle},
|
role::{Role, RoleTitle},
|
||||||
|
|
@ -426,20 +427,7 @@ impl From<RoleTitle> for SetupRole {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
id_impl!(SlotId);
|
||||||
pub struct SlotId(Uuid);
|
|
||||||
|
|
||||||
impl SlotId {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(Uuid::new_v4())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for SlotId {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
Display::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct SetupSlot {
|
pub struct SetupSlot {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ mod time;
|
||||||
use crate::{
|
use crate::{
|
||||||
character::{Character, CharacterId},
|
character::{Character, CharacterId},
|
||||||
diedto::DiedToTitle,
|
diedto::DiedToTitle,
|
||||||
error::GameError,
|
error::{GameError, ServerError},
|
||||||
game::{Game, GameOver, GameSettings, OrRandom, SetupRole, SetupSlot},
|
game::{Game, GameOver, GameSettings, OrRandom, SetupRole, SetupSlot},
|
||||||
message::{
|
message::{
|
||||||
CharacterState, Identification, PublicIdentity,
|
CharacterState, Identification, PublicIdentity,
|
||||||
|
|
@ -803,7 +803,8 @@ fn wolfpack_kill_all_targets_valid() {
|
||||||
|
|
||||||
for (idx, target) in living_villagers.into_iter().enumerate() {
|
for (idx, target) in living_villagers.into_iter().enumerate() {
|
||||||
let mut attempt = game.clone();
|
let mut attempt = game.clone();
|
||||||
if let ServerToHostMessage::Error(GameError::InvalidTarget) = attempt
|
if let ServerToHostMessage::Error(ServerError::GameError(GameError::InvalidTarget)) =
|
||||||
|
attempt
|
||||||
.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||||
ActionResponse::MarkTarget(target.character_id),
|
ActionResponse::MarkTarget(target.character_id),
|
||||||
)))
|
)))
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ pub struct DayCharacter {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Titles)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Titles)]
|
||||||
pub enum ServerToClientMessage {
|
pub enum ServerToClientMessage {
|
||||||
|
GameCancelled,
|
||||||
Disconnect,
|
Disconnect,
|
||||||
LobbyInfo {
|
LobbyInfo {
|
||||||
joined: bool,
|
joined: bool,
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@ pub enum HostLobbyMessage {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, werewolves_macros::Titles)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, werewolves_macros::Titles)]
|
||||||
pub enum ServerToHostMessage {
|
pub enum ServerToHostMessage {
|
||||||
|
GameCancelled,
|
||||||
Disconnect,
|
Disconnect,
|
||||||
Daytime {
|
Daytime {
|
||||||
characters: Box<[CharacterState]>,
|
characters: Box<[CharacterState]>,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
components::Nav,
|
components::{ErrorBox, Nav},
|
||||||
pages::{GamePage, Main, NotFound, Signin, Signup, UserSettings},
|
pages::{GamePage, Main, NotFound, Signin, Signup, UserSettings},
|
||||||
storage::{
|
storage::{
|
||||||
Stored,
|
Stored,
|
||||||
|
|
@ -96,6 +96,7 @@ pub fn App() -> impl IntoView {
|
||||||
.then_some(auth_store.session().get().is_some())
|
.then_some(auth_store.session().get().is_some())
|
||||||
};
|
};
|
||||||
let not_logged_in = move || Some(auth_store.session().get().is_none());
|
let not_logged_in = move || Some(auth_store.session().get().is_none());
|
||||||
|
let error = RwSignal::new(None);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Stylesheet id="leptos" href="/pkg/werewolves.css" />
|
<Stylesheet id="leptos" href="/pkg/werewolves.css" />
|
||||||
|
|
@ -106,6 +107,7 @@ pub fn App() -> impl IntoView {
|
||||||
<Router>
|
<Router>
|
||||||
<main>
|
<main>
|
||||||
<Nav />
|
<Nav />
|
||||||
|
<ErrorBox msg=error />
|
||||||
<Routes fallback=NotFound>
|
<Routes fallback=NotFound>
|
||||||
<Route path=path!("/") view=Main />
|
<Route path=path!("/") view=Main />
|
||||||
<ProtectedRoute
|
<ProtectedRoute
|
||||||
|
|
@ -126,7 +128,10 @@ pub fn App() -> impl IntoView {
|
||||||
condition=is_logged_in
|
condition=is_logged_in
|
||||||
redirect_path=|| "/"
|
redirect_path=|| "/"
|
||||||
/>
|
/>
|
||||||
<Route path=path!("/games/:id") view=|| view! { <GamePage /> } />
|
<Route
|
||||||
|
path=path!("/games/:id")
|
||||||
|
view=move || view! { <GamePage error=error.write_only() /> }
|
||||||
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,14 @@ use codee::binary::MsgpackSerdeCodec;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_router::hooks;
|
use leptos_router::hooks;
|
||||||
use leptos_use::{
|
use leptos_use::{
|
||||||
ReconnectLimit, UseWebSocketOptions,
|
ReconnectLimit, UseWebSocketOptions, UseWebSocketReturn, core::ConnectionReadyState,
|
||||||
UseWebSocketReturn, core::ConnectionReadyState, use_websocket_with_options,
|
use_websocket_with_options,
|
||||||
};
|
};
|
||||||
use reactive_stores::Store;
|
use reactive_stores::Store;
|
||||||
use werewolves_proto::message::{
|
use werewolves_proto::message::{ClientMessage, host::HostMessage};
|
||||||
ClientMessage,
|
|
||||||
host::HostMessage,
|
|
||||||
};
|
|
||||||
use werewolves_proto::message::{IntoClientResponse, WrappedServerMessage};
|
use werewolves_proto::message::{IntoClientResponse, WrappedServerMessage};
|
||||||
|
|
||||||
|
use crate::app::components::ErrorBox;
|
||||||
use crate::{
|
use crate::{
|
||||||
ConsoleLogError,
|
ConsoleLogError,
|
||||||
app::{
|
app::{
|
||||||
|
|
@ -27,7 +25,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn GamePage() -> impl IntoView {
|
pub fn GamePage(error: WriteSignal<Option<String>>) -> impl IntoView {
|
||||||
move || {
|
move || {
|
||||||
let params = hooks::use_params_map();
|
let params = hooks::use_params_map();
|
||||||
let auth = expect_context::<Store<AuthContext>>();
|
let auth = expect_context::<Store<AuthContext>>();
|
||||||
|
|
@ -186,8 +184,9 @@ pub fn GamePage() -> impl IntoView {
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
{status}
|
{status}
|
||||||
<HostGamePage message=host_message.into() reply=host_reply.write_only() />
|
<HostGamePage error=error message=host_message.into() reply=host_reply.write_only() />
|
||||||
<PlayerGamePage
|
<PlayerGamePage
|
||||||
|
error=error
|
||||||
message=player_message.into()
|
message=player_message.into()
|
||||||
reply=player_reply.write_only()
|
reply=player_reply.write_only()
|
||||||
disconnect=disconnect
|
disconnect=disconnect
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,28 @@ use werewolves_proto::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "hydrate")]
|
|
||||||
use crate::ConsoleLogError;
|
use crate::ConsoleLogError;
|
||||||
use crate::app::{Preferences, components::DialogModal};
|
use crate::app::{Preferences, components::DialogModal};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
enum HostPage {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Settings,
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn HostGamePage(
|
pub fn HostGamePage(
|
||||||
|
error: WriteSignal<Option<String>>,
|
||||||
message: Signal<Option<Srv2Host>>,
|
message: Signal<Option<Srv2Host>>,
|
||||||
reply: WriteSignal<Option<HostMessage>>,
|
reply: WriteSignal<Option<HostMessage>>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let prefs = expect_context::<(Signal<Preferences>, WriteSignal<Preferences>)>().0;
|
let prefs = expect_context::<(Signal<Preferences>, WriteSignal<Preferences>)>().0;
|
||||||
|
let page = RwSignal::new(HostPage::default());
|
||||||
let settings = RwSignal::new(GameSettings::default());
|
let settings = RwSignal::new(GameSettings::default());
|
||||||
let qr_mode = RwSignal::new(false);
|
let qr_mode = RwSignal::new(false);
|
||||||
let players: RwSignal<Box<[PlayerState]>> = RwSignal::new(Box::new([]));
|
let players: RwSignal<Box<[PlayerState]>> = RwSignal::new(Box::new([]));
|
||||||
let dialog_open = RwSignal::new(false);
|
let dialog_open = RwSignal::new(HashMap::new());
|
||||||
let open_categories = RwSignal::new(
|
let open_categories = RwSignal::new(
|
||||||
Category::ALL
|
Category::ALL
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -32,21 +40,12 @@ pub fn HostGamePage(
|
||||||
.collect::<HashMap<_, _>>(),
|
.collect::<HashMap<_, _>>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Effect::watch(
|
|
||||||
move || settings.get(),
|
|
||||||
move |s: &GameSettings, _, _| {
|
|
||||||
reply.set(Some(HostMessage::Lobby(HostLobbyMessage::SetGameSettings(
|
|
||||||
s.clone(),
|
|
||||||
))));
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
Effect::watch(
|
Effect::watch(
|
||||||
move || qr_mode.get(),
|
move || qr_mode.get(),
|
||||||
move |q, _, _| reply.set(Some(HostMessage::Lobby(HostLobbyMessage::SetQrMode(*q)))),
|
move |q, _, _| reply.set(Some(HostMessage::Lobby(HostLobbyMessage::SetQrMode(*q)))),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
let content = move || {
|
Effect::new(move || {
|
||||||
if let Some(message) = message.get() {
|
if let Some(message) = message.get() {
|
||||||
match message {
|
match message {
|
||||||
Srv2Host::Lobby {
|
Srv2Host::Lobby {
|
||||||
|
|
@ -54,32 +53,50 @@ pub fn HostGamePage(
|
||||||
settings: s,
|
settings: s,
|
||||||
qr_mode: q,
|
qr_mode: q,
|
||||||
} => {
|
} => {
|
||||||
log::info!("setting setties");
|
page.set(HostPage::Settings);
|
||||||
settings.set(s);
|
settings.set(s);
|
||||||
*qr_mode.write_untracked() = q;
|
*qr_mode.write_untracked() = q;
|
||||||
players.set(p);
|
players.set(p);
|
||||||
|
{
|
||||||
view! {
|
let mut d = dialog_open.write();
|
||||||
|
for slot in settings.read().slots().iter() {
|
||||||
|
d.entry(slot.slot_id).or_insert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Srv2Host::GameCancelled => {
|
||||||
|
error.set(Some("game was cancelled".into()));
|
||||||
|
gloo::utils::window()
|
||||||
|
.location()
|
||||||
|
.replace("/")
|
||||||
|
.console_log_warn();
|
||||||
|
}
|
||||||
|
Srv2Host::Error(err) => {
|
||||||
|
error.set(Some(err.to_string()));
|
||||||
|
}
|
||||||
|
_ => log::error!("{message:#?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let cancel = move || {
|
||||||
|
message
|
||||||
|
.get()
|
||||||
|
.is_some()
|
||||||
|
.then_some(view! { <CancelGame reply=reply prefs=prefs /> })
|
||||||
|
};
|
||||||
|
let content = move || match page.get() {
|
||||||
|
HostPage::None => ().into_any(),
|
||||||
|
HostPage::Settings => view! {
|
||||||
<Settings
|
<Settings
|
||||||
open_categories=open_categories
|
open_categories=open_categories
|
||||||
settings=settings
|
settings=settings.read_only()
|
||||||
players=players.read_only()
|
players=players.read_only()
|
||||||
qr_mode=qr_mode
|
qr_mode=qr_mode
|
||||||
dialog_open=dialog_open
|
dialog_open=dialog_open
|
||||||
|
reply=reply
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
.into_any()
|
.into_any(),
|
||||||
}
|
|
||||||
_ => view! { <h2>{format!("{message:#?}")}</h2> }.into_any(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
().into_any()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let cancel = move || {
|
|
||||||
view! {
|
|
||||||
<CancelGame reply=reply prefs=prefs />
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
view! {
|
view! {
|
||||||
{cancel}
|
{cancel}
|
||||||
|
|
@ -96,13 +113,6 @@ fn CancelGame(
|
||||||
let cancel = move |_| {
|
let cancel = move |_| {
|
||||||
open.set(false);
|
open.set(false);
|
||||||
reply.set(Some(HostMessage::CancelGame));
|
reply.set(Some(HostMessage::CancelGame));
|
||||||
#[cfg(not(feature = "ssr"))]
|
|
||||||
{
|
|
||||||
gloo::utils::window()
|
|
||||||
.location()
|
|
||||||
.replace("/")
|
|
||||||
.console_log_warn();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let derive_hidden = RwSignal::new(false);
|
let derive_hidden = RwSignal::new(false);
|
||||||
Effect::new(move || derive_hidden.set(!prefs.get().show_cancel_game));
|
Effect::new(move || derive_hidden.set(!prefs.get().show_cancel_game));
|
||||||
|
|
@ -123,5 +133,5 @@ fn CancelGame(
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
view! { {content} }
|
move || view! { {content} }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,11 @@ use convert_case::{Case, Casing};
|
||||||
use leptos::{ev::MouseEvent, prelude::*};
|
use leptos::{ev::MouseEvent, prelude::*};
|
||||||
use werewolves_proto::{
|
use werewolves_proto::{
|
||||||
aura::AuraTitle,
|
aura::AuraTitle,
|
||||||
game::{Category, GameSettings, SetupSlot},
|
game::{Category, GameSettings, SetupSlot, SlotId},
|
||||||
message::PlayerState,
|
message::{
|
||||||
|
PlayerState,
|
||||||
|
host::{HostLobbyMessage, HostMessage},
|
||||||
|
},
|
||||||
role::RoleTitle,
|
role::RoleTitle,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -18,11 +21,12 @@ use crate::app::{
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Settings(
|
pub fn Settings(
|
||||||
settings: RwSignal<GameSettings>,
|
settings: ReadSignal<GameSettings>,
|
||||||
players: ReadSignal<Box<[PlayerState]>>,
|
players: ReadSignal<Box<[PlayerState]>>,
|
||||||
qr_mode: RwSignal<bool>,
|
qr_mode: RwSignal<bool>,
|
||||||
dialog_open: RwSignal<bool>,
|
dialog_open: RwSignal<HashMap<SlotId, bool>>,
|
||||||
open_categories: RwSignal<HashMap<Category, bool>>,
|
open_categories: RwSignal<HashMap<Category, bool>>,
|
||||||
|
reply: WriteSignal<Option<HostMessage>>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let slots = move || {
|
let slots = move || {
|
||||||
settings
|
settings
|
||||||
|
|
@ -32,11 +36,11 @@ pub fn Settings(
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(move |s| {
|
.map(move |s| {
|
||||||
let signal = RwSignal::new(s);
|
let signal = RwSignal::new(s);
|
||||||
Effect::watch(
|
Effect::new(move || {
|
||||||
move || signal.get(),
|
let mut s = settings.get();
|
||||||
move |slot_update, _, _| settings.write().update_slot(slot_update.clone()),
|
s.update_slot(signal.get());
|
||||||
false,
|
reply.set(Some(HostMessage::Lobby(HostLobbyMessage::SetGameSettings(s))));
|
||||||
);
|
});
|
||||||
|
|
||||||
view! { <SettingsSetupSlot setup_slot=signal players=players dialog_open=dialog_open /> }
|
view! { <SettingsSetupSlot setup_slot=signal players=players dialog_open=dialog_open /> }
|
||||||
})
|
})
|
||||||
|
|
@ -68,7 +72,6 @@ pub fn Settings(
|
||||||
k.sort();
|
k.sort();
|
||||||
k
|
k
|
||||||
};
|
};
|
||||||
Effect::new(|| log::debug!("rendering settings"));
|
|
||||||
let categories = ordered_keys
|
let categories = ordered_keys
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|c| {
|
.map(|c| {
|
||||||
|
|
@ -85,7 +88,11 @@ pub fn Settings(
|
||||||
.map(|r| {
|
.map(|r| {
|
||||||
let add_role = move |ev: MouseEvent| {
|
let add_role = move |ev: MouseEvent| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
settings.write().new_slot(r);
|
let mut s = settings.get();
|
||||||
|
s.new_slot(r);
|
||||||
|
reply.set(Some(HostMessage::Lobby(
|
||||||
|
HostLobbyMessage::SetGameSettings(s),
|
||||||
|
)));
|
||||||
};
|
};
|
||||||
let classes =
|
let classes =
|
||||||
["add-role", r.class(), "faint", "hover", "box"].as_classes();
|
["add-role", r.class(), "faint", "hover", "box"].as_classes();
|
||||||
|
|
@ -129,7 +136,7 @@ pub fn Settings(
|
||||||
fn SettingsSetupSlot(
|
fn SettingsSetupSlot(
|
||||||
setup_slot: RwSignal<SetupSlot>,
|
setup_slot: RwSignal<SetupSlot>,
|
||||||
players: ReadSignal<Box<[PlayerState]>>,
|
players: ReadSignal<Box<[PlayerState]>>,
|
||||||
dialog_open: RwSignal<bool>,
|
dialog_open: RwSignal<HashMap<SlotId, bool>>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let auras = move || {
|
let auras = move || {
|
||||||
let slot = setup_slot.read();
|
let slot = setup_slot.read();
|
||||||
|
|
@ -158,18 +165,42 @@ fn SettingsSetupSlot(
|
||||||
}
|
}
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
None => {
|
None => view! { <span class="missing error">"assigned player not in lobby"</span> }
|
||||||
view! { <span class="missing error">"missing player "{a.to_string()}</span> }
|
.into_any(),
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
let open_signal = RwSignal::new(false);
|
||||||
|
Effect::new(move || {
|
||||||
|
open_signal.set(
|
||||||
|
dialog_open
|
||||||
|
.read()
|
||||||
|
.get(&setup_slot.read().slot_id)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Effect::new(move || {
|
||||||
|
let current = dialog_open
|
||||||
|
.read_untracked()
|
||||||
|
.get(&setup_slot.read_untracked().slot_id)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let new = open_signal.get();
|
||||||
|
if current == new {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog_open
|
||||||
|
.write()
|
||||||
|
.insert(setup_slot.read_untracked().slot_id, new);
|
||||||
|
});
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
view! {
|
view! {
|
||||||
<div class="setup-slot-container">
|
<div class="setup-slot-container">
|
||||||
<DialogModal
|
<DialogModal
|
||||||
open=dialog_open
|
open=open_signal
|
||||||
mode=DialogMode::Box
|
mode=DialogMode::Box
|
||||||
button_class=[
|
button_class=[
|
||||||
"setup-slot",
|
"setup-slot",
|
||||||
|
|
@ -272,8 +303,10 @@ fn AuraSelection(setup_slot: RwSignal<SetupSlot>) -> impl IntoView {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
let mut slot = setup_slot.write();
|
let mut slot = setup_slot.write();
|
||||||
if slot.auras.contains(&aura) {
|
if slot.auras.contains(&aura) {
|
||||||
|
log::debug!("removing aura {aura}");
|
||||||
slot.auras.retain(|a| aura != *a);
|
slot.auras.retain(|a| aura != *a);
|
||||||
} else {
|
} else {
|
||||||
|
log::debug!("adding aura {aura}");
|
||||||
slot.auras.push(aura);
|
slot.auras.push(aura);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -291,7 +324,7 @@ fn AuraSelection(setup_slot: RwSignal<SetupSlot>) -> impl IntoView {
|
||||||
.collect_view()
|
.collect_view()
|
||||||
};
|
};
|
||||||
|
|
||||||
view! { <div class="toggle-list">{auras}</div> }
|
move || view! { <div class="toggle-list">{auras}</div> }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
|
@ -325,6 +358,15 @@ fn AssignmentSelection(
|
||||||
})
|
})
|
||||||
.collect_view()
|
.collect_view()
|
||||||
};
|
};
|
||||||
|
let unassign = move || {
|
||||||
|
setup_slot.get().assign_to.map(|_| {
|
||||||
|
view! {
|
||||||
|
<button on:click=move |_| {
|
||||||
|
setup_slot.write().assign_to.take();
|
||||||
|
}>"unassign"</button>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
view! { <div class="toggle-list">{players}</div> }
|
move || view! { <div class="toggle-list">{unassign}{players}</div> }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,13 @@
|
||||||
werewolves_macros::include_path!("werewolves/src/app/pages/game/player");
|
werewolves_macros::include_path!("werewolves/src/app/pages/game/player");
|
||||||
|
|
||||||
use core::{
|
use core::{hash::Hash, num::NonZeroU8, ops::Deref};
|
||||||
hash::Hash,
|
|
||||||
num::NonZeroU8,
|
|
||||||
ops::Deref,
|
|
||||||
};
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use werewolves_proto::{
|
use werewolves_proto::{
|
||||||
message::{
|
message::{
|
||||||
ClientDeadChat, ClientMessage, ServerToClientMessage as Srv2Client,
|
ClientDeadChat, ClientMessage, ServerToClientMessage as Srv2Client, dead::DeadChatMessage,
|
||||||
dead::DeadChatMessage,
|
|
||||||
},
|
},
|
||||||
role::RoleTitle,
|
role::RoleTitle,
|
||||||
};
|
};
|
||||||
|
|
@ -35,6 +30,7 @@ enum Page {
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn PlayerGamePage(
|
pub fn PlayerGamePage(
|
||||||
|
error: WriteSignal<Option<String>>,
|
||||||
message: Signal<Option<Srv2Client>>,
|
message: Signal<Option<Srv2Client>>,
|
||||||
reply: WriteSignal<Option<ClientMessage>>,
|
reply: WriteSignal<Option<ClientMessage>>,
|
||||||
disconnect: RwSignal<bool>,
|
disconnect: RwSignal<bool>,
|
||||||
|
|
@ -47,6 +43,13 @@ pub fn PlayerGamePage(
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match message {
|
match message {
|
||||||
|
Srv2Client::GameCancelled => {
|
||||||
|
error.set(Some("game was cancelled".into()));
|
||||||
|
gloo::utils::window()
|
||||||
|
.location()
|
||||||
|
.replace("/")
|
||||||
|
.console_log_warn();
|
||||||
|
}
|
||||||
Srv2Client::Disconnect => disconnect.set(true),
|
Srv2Client::Disconnect => disconnect.set(true),
|
||||||
Srv2Client::LobbyInfo {
|
Srv2Client::LobbyInfo {
|
||||||
joined,
|
joined,
|
||||||
|
|
|
||||||
|
|
@ -39,21 +39,23 @@ pub trait ConsoleLogError {
|
||||||
fn console_log_debug(self);
|
fn console_log_debug(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "hydrate")]
|
impl<T> ConsoleLogError for Result<T, leptos::wasm_bindgen::JsValue> {
|
||||||
impl<T> ConsoleLogError for Result<T, wasm_bindgen::JsValue> {
|
|
||||||
fn console_log_warn(self) {
|
fn console_log_warn(self) {
|
||||||
|
#[cfg(feature = "hydrate")]
|
||||||
if let Err(err) = self {
|
if let Err(err) = self {
|
||||||
gloo::console::warn!(err);
|
gloo::console::warn!(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn console_log_err(self) {
|
fn console_log_err(self) {
|
||||||
|
#[cfg(feature = "hydrate")]
|
||||||
if let Err(err) = self {
|
if let Err(err) = self {
|
||||||
gloo::console::error!(err);
|
gloo::console::error!(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn console_log_debug(self) {
|
fn console_log_debug(self) {
|
||||||
|
#[cfg(feature = "hydrate")]
|
||||||
if let Err(err) = self {
|
if let Err(err) = self {
|
||||||
gloo::console::debug!(err);
|
gloo::console::debug!(err);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ impl<'a> Lobby<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn settings(&self) -> &GameSettings {
|
pub const fn settings(&self) -> &GameSettings {
|
||||||
&self.settings
|
self.settings
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_lobby_info_to_clients(&mut self) -> Result<(), ServerError> {
|
pub async fn send_lobby_info_to_clients(&mut self) -> Result<(), ServerError> {
|
||||||
|
|
@ -242,7 +242,7 @@ impl<'a> Lobby<'a> {
|
||||||
))) => {
|
))) => {
|
||||||
self.db
|
self.db
|
||||||
.game()
|
.game()
|
||||||
.set_player_number(self.game_id, pid.into(), Some(num))
|
.set_player_number(self.game_id, pid, Some(num))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.send_lobby_info_to_clients().await.log_debug(loc!());
|
self.send_lobby_info_to_clients().await.log_debug(loc!());
|
||||||
|
|
@ -257,11 +257,7 @@ impl<'a> Lobby<'a> {
|
||||||
}) => {
|
}) => {
|
||||||
self.db
|
self.db
|
||||||
.game()
|
.game()
|
||||||
.join_game(
|
.join_game(self.game_id, identity.player_id, identity.public.number)
|
||||||
self.game_id,
|
|
||||||
identity.player_id.into(),
|
|
||||||
identity.public.number,
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.send_lobby_info_to_clients().await.log_debug(loc!());
|
self.send_lobby_info_to_clients().await.log_debug(loc!());
|
||||||
|
|
@ -272,10 +268,7 @@ impl<'a> Lobby<'a> {
|
||||||
identity: Identification { player_id, .. },
|
identity: Identification { player_id, .. },
|
||||||
update: ClientUpdate::Message(ClientMessage::Goodbye),
|
update: ClientUpdate::Message(ClientMessage::Goodbye),
|
||||||
}) => {
|
}) => {
|
||||||
self.db
|
self.db.game().leave_game(self.game_id, player_id).await?;
|
||||||
.game()
|
|
||||||
.leave_game(self.game_id, player_id.into())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
self.send_lobby_info_to_host().await?;
|
self.send_lobby_info_to_host().await?;
|
||||||
self.send_lobby_info_to_clients().await.log_debug(loc!());
|
self.send_lobby_info_to_clients().await.log_debug(loc!());
|
||||||
|
|
@ -306,7 +299,7 @@ impl<'a> Lobby<'a> {
|
||||||
}) => {
|
}) => {
|
||||||
self.db
|
self.db
|
||||||
.game()
|
.game()
|
||||||
.set_player_number(self.game_id, player_id.into(), Some(number))
|
.set_player_number(self.game_id, player_id, Some(number))
|
||||||
.await?;
|
.await?;
|
||||||
self.send_lobby_info_to_clients().await.log_debug(loc!());
|
self.send_lobby_info_to_clients().await.log_debug(loc!());
|
||||||
self.send_lobby_info_to_host().await.log_warn(loc!());
|
self.send_lobby_info_to_host().await.log_warn(loc!());
|
||||||
|
|
|
||||||
|
|
@ -394,4 +394,5 @@ pub async fn delete_game(game: GameId) {
|
||||||
for key in player_keys {
|
for key in player_keys {
|
||||||
players.remove(&key);
|
players.remove(&key);
|
||||||
}
|
}
|
||||||
|
log::info!("game {game} is cancelled server-side");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ use core::num::NonZeroU8;
|
||||||
use tokio::sync::mpsc::UnboundedReceiver;
|
use tokio::sync::mpsc::UnboundedReceiver;
|
||||||
use werewolves_proto::game::GameId;
|
use werewolves_proto::game::GameId;
|
||||||
use werewolves_proto::game_record::{GameRecord, GameRecordState};
|
use werewolves_proto::game_record::{GameRecord, GameRecordState};
|
||||||
|
use werewolves_proto::message::ServerToClientMessage;
|
||||||
|
use werewolves_proto::message::host::ServerToHostMessage;
|
||||||
use werewolves_proto::message::{ClientMessage, Identification, host::HostMessage};
|
use werewolves_proto::message::{ClientMessage, Identification, host::HostMessage};
|
||||||
use werewolves_proto::{LogError, loc};
|
use werewolves_proto::{LogError, loc};
|
||||||
|
|
||||||
|
|
@ -55,8 +57,8 @@ async fn next_message(
|
||||||
host_msg = host_recv.recv() => {
|
host_msg = host_recv.recv() => {
|
||||||
match host_msg {
|
match host_msg {
|
||||||
Some(HostMessage::CancelGame) => {
|
Some(HostMessage::CancelGame) => {
|
||||||
cancel_game(game_id, db).await;
|
|
||||||
log::info!("got game cancellation request for {game_id}");
|
log::info!("got game cancellation request for {game_id}");
|
||||||
|
cancel_game(game_id, db).await;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Some(msg) => Some(HostOrClientMessage::Host(msg)),
|
Some(msg) => Some(HostOrClientMessage::Host(msg)),
|
||||||
|
|
@ -70,6 +72,12 @@ async fn next_message(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn cancel_game(game_id: GameId, db: &Database) {
|
async fn cancel_game(game_id: GameId, db: &Database) {
|
||||||
|
static RUNTIME: std::sync::LazyLock<tokio::runtime::Runtime> = std::sync::LazyLock::new(|| {
|
||||||
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.expect("building game destroyer runtime")
|
||||||
|
});
|
||||||
let game = match db.game().get_game(game_id).await {
|
let game = match db.game().get_game(game_id).await {
|
||||||
Ok(g) => g,
|
Ok(g) => g,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
@ -77,11 +85,13 @@ async fn cancel_game(game_id: GameId, db: &Database) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
super::send_to_all_players_in_game(game_id, ServerToClientMessage::GameCancelled).await;
|
||||||
|
super::send_host(game_id, ServerToHostMessage::GameCancelled).await;
|
||||||
if let Err(err) = db.game().cancel_game(game.host, game_id).await {
|
if let Err(err) = db.game().cancel_game(game.host, game_id).await {
|
||||||
log::error!("error cancelling game: {err}");
|
log::error!("error cancelling game: {err}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tokio::spawn(crate::server::delete_game(game_id));
|
RUNTIME.spawn(crate::server::delete_game(game_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_dummies(game_id: GameId, db: &Database, dummy_count: usize) {
|
async fn add_dummies(game_id: GameId, db: &Database, dummy_count: usize) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue