add player for host

This commit is contained in:
emilis 2025-10-10 18:34:59 +01:00
parent 08db7f9bfc
commit 11bc54f996
No known key found for this signature in database
5 changed files with 114 additions and 40 deletions

View File

@ -13,7 +13,7 @@ use crate::{
player::PlayerId,
};
use super::{CharacterState, PlayerState};
use super::{CharacterState, PlayerState, PublicIdentity};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum HostMessage {
@ -55,6 +55,7 @@ impl From<HostDayMessage> for HostGameMessage {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum HostLobbyMessage {
GetState,
ManufacturePlayer(PublicIdentity),
Kick(PlayerId),
SetPlayerNumber(PlayerId, NonZeroU8),
GetGameSettings,

View File

@ -155,6 +155,18 @@ impl Lobby {
async fn next_inner(&mut self, msg: Message) -> Result<Option<GameRunner>, GameError> {
match msg {
Message::Host(HostMessage::Lobby(HostLobbyMessage::ManufacturePlayer(public))) => {
log::info!("adding player {public:?} by host request");
self.players_in_lobby.push((
Identification {
player_id: PlayerId::new(),
public,
},
None,
));
self.send_lobby_info_to_clients().await;
self.send_lobby_info_to_host().await.log_debug();
}
Message::Host(HostMessage::Lobby(HostLobbyMessage::SetQrMode(mode))) => {
self.qr_mode = mode;
self.send_lobby_info_to_host().await.log_debug();
@ -235,7 +247,7 @@ impl Lobby {
return Ok(None);
}
if let Some(sender) = self.joined_players.get_sender(identity.player_id).await {
self.players_in_lobby.push((identity, sender.clone()));
self.players_in_lobby.push((identity, Some(sender.clone())));
self.send_lobby_info_to_clients().await;
self.send_lobby_info_to_host().await?;
}
@ -321,10 +333,10 @@ impl Lobby {
}
#[derive(Clone)]
pub struct LobbyPlayers(Vec<(Identification, Sender<ServerMessage>)>);
pub struct LobbyPlayers(Vec<(Identification, Option<Sender<ServerMessage>>)>);
impl Deref for LobbyPlayers {
type Target = Vec<(Identification, Sender<ServerMessage>)>;
type Target = Vec<(Identification, Option<Sender<ServerMessage>>)>;
fn deref(&self) -> &Self::Target {
&self.0
@ -339,8 +351,6 @@ impl DerefMut for LobbyPlayers {
impl LobbyPlayers {
pub fn with_dummies(dummy_count: NonZeroU8) -> Self {
let (send, mut recv) = broadcast::channel(100);
tokio::spawn(async move { while recv.recv().await.is_ok() {} });
Self(
(0..dummy_count.get())
.map(|p| {
@ -353,7 +363,7 @@ impl LobbyPlayers {
number: NonZeroU8::new(p + 1),
},
},
send.clone(),
None,
)
})
.collect(),
@ -361,6 +371,7 @@ impl LobbyPlayers {
}
pub fn find(&self, player_id: PlayerId) -> Option<&Sender<ServerMessage>> {
self.iter()
.filter_map(|(id, s)| s.as_ref().map(|s| (id, s)))
.find_map(|(id, s)| (id.player_id == player_id).then_some(s))
}

View File

@ -201,8 +201,7 @@ nav.debug-nav {
}
.submenu {
background-color: black;
border: 1px solid rgba(255, 255, 255, 0.7);
// border: 1px solid rgba(255, 255, 255, 0.7);
padding: 10px;
position: relative;
// position: fixed;
@ -829,25 +828,6 @@ input {
margin: 10px;
}
.signin {
@extend .row-list;
justify-content: center;
text-align: center;
& label {
font-size: 1.5rem;
}
& input {
height: 2rem;
text-align: center;
&#number {
font-size: 2rem;
}
}
}
.info-update {
font-size: 2rem;
align-content: stretch;
@ -997,11 +977,17 @@ input {
align-items: center;
align-content: center;
&>p {
height: 100%;
width: 100%;
&>label {
// height: 100%;
// width: 100%;
flex-grow: 3;
}
&>button {
width: max-content;
font-size: 3rem;
flex-grow: 1;
}
}
.setup-slot {
@ -1013,15 +999,23 @@ input {
}
&>.submenu {
min-width: 5cm;
min-width: 30vw;
.assign-list {
min-width: 5cm;
// min-width: 5cm;
gap: 10px;
& .submenu button {
width: inherit;
width: 5cm;
}
}
.assignees {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 5px;
}
}
}
@ -1324,3 +1318,31 @@ input {
& .next {}
}
.signin {
@extend .row-list;
justify-content: center;
text-align: center;
& label {
font-size: 1.5rem;
}
& input {
height: 2rem;
text-align: center;
&#number {
font-size: 2rem;
}
}
}
.submenu:has(.signin) {
position: absolute;
width: max-content;
& input {
font-size: 1rem !important;
}
}

View File

@ -13,7 +13,7 @@ use werewolves_proto::{
error::GameError,
game::{GameOver, GameSettings},
message::{
CharacterIdentity, CharacterState, PlayerState,
CharacterIdentity, CharacterState, PlayerState, PublicIdentity,
host::{
HostDayMessage, HostGameMessage, HostLobbyMessage, HostMessage, HostNightMessage,
ServerToHostMessage,
@ -662,12 +662,17 @@ impl Host {
}
});
});
let on_add_player = crate::callback::send_fn(
|msg: PublicIdentity| HostMessage::Lobby(HostLobbyMessage::ManufacturePlayer(msg)),
self.send.clone(),
);
html! {
<Settings
settings={settings}
on_start={on_start}
on_update={on_changed}
players_in_lobby={players.clone()}
on_add_player={on_add_player}
qr_mode_button={qr_mode_toggle}
/>
}

View File

@ -5,12 +5,12 @@ use convert_case::{Case, Casing};
use werewolves_proto::{
error::GameError,
game::{GameSettings, OrRandom, SetupRole, SetupSlot, SlotId},
message::{Identification, PlayerState},
message::{Identification, PlayerState, PublicIdentity},
role::RoleTitle,
};
use yew::prelude::*;
use crate::components::{Button, ClickableField, Identity};
use crate::components::{Button, ClickableField, Identity, client::Signin};
#[derive(Debug, PartialEq, Properties)]
pub struct SettingsProps {
@ -18,6 +18,7 @@ pub struct SettingsProps {
pub players_in_lobby: Rc<[PlayerState]>,
pub on_update: Callback<GameSettings>,
pub on_start: Callback<()>,
pub on_add_player: Callback<PublicIdentity>,
pub qr_mode_button: Html,
}
@ -28,6 +29,7 @@ pub fn Settings(
players_in_lobby,
on_update,
on_start,
on_add_player,
qr_mode_button,
}: &SettingsProps,
) -> Html {
@ -72,6 +74,7 @@ pub fn Settings(
.map(|slot| {
html! {
<SettingsSlot
all_players={players.clone()}
players_for_assign={players_for_assign.clone()}
roles_in_setup={roles_in_setup.clone()}
slot={slot.clone()}
@ -248,6 +251,11 @@ pub fn Settings(
}
};
let add_player_open = use_state(|| false);
let add_player_opts = html! {
<Signin callback={on_add_player.clone()}/>
};
html! {
<div class="settings">
<div class="top-settings">
@ -257,6 +265,12 @@ pub fn Settings(
{clear_all_assignments}
{clear_bad_assigned}
</div>
<ClickableField
options={add_player_opts}
state={add_player_open}
>
{"add player"}
</ClickableField>
<p>{format!("min roles for setup: {}", settings.min_players_needed())}</p>
<p>{format!("current role count: {}", settings.slots().len())}</p>
<div class="roles-add-list">
@ -291,6 +305,7 @@ pub struct SettingsSlotProps {
pub roles_in_setup: Rc<[RoleTitle]>,
pub slot: SetupSlot,
pub update: Callback<SettingSlotAction>,
pub all_players: Rc<[Identification]>,
}
#[function_component]
@ -300,6 +315,7 @@ pub fn SettingsSlot(
roles_in_setup,
slot,
update,
all_players,
}: &SettingsSlotProps,
) -> Html {
let open = use_state(|| false);
@ -321,6 +337,19 @@ pub fn SettingsSlot(
let assign_to = assign_to_submenu(players_for_assign, slot, &update, &open.setter());
let options =
setup_options_for_slot(slot, &update, roles_in_setup, apprentice_open, open.clone());
let assign_text = slot
.assign_to
.as_ref()
.and_then(|assign_to| all_players.iter().find(|p| p.player_id == *assign_to))
.map(|assign_to| {
html! {
<>
<span>{"assigned to"}</span>
<Identity ident={assign_to.public.clone()} />
</>
}
})
.unwrap_or_else(|| html! {{"assign"}});
html! {
<>
<Button on_click={on_kick}>
@ -331,7 +360,7 @@ pub fn SettingsSlot(
state={assign_open}
class={classes!("assign-list")}
>
{"assign"}
{assign_text}
</ClickableField>
{options}
</>
@ -357,7 +386,7 @@ fn assign_to_submenu(
update: &Callback<SettingSlotAction>,
assign_setter: &UseStateSetter<bool>,
) -> Html {
players
let buttons = players
.iter()
.map(|p| {
let slot = slot.clone();
@ -376,7 +405,13 @@ fn assign_to_submenu(
</Button>
}
})
.collect()
.collect::<Html>();
html! {
<div class="assignees">
{buttons}
</div>
}
}
fn setup_options_for_slot(