euchre-live

Euchre web-app for the socially distant family
git clone git://git.alexkarle.com/euchre-live.git
Log | Files | Refs | README | LICENSE

commit e7aa87e3756fbf4b44aa572dbd8d00fc6c8a50ac (patch)
parent 2612fc848f5139ea1ba4dac784bba5780fa24914
Author: Alex Karle <alex@karle.co>
Date:   Mon, 20 Apr 2020 00:11:53 -0400

Euchre::Dealer: Add force join_game option to take over hand

This is crucial to support dropped players. While it is definitely prone
to "euchre.live bombing" (in the parlance of our times), where someone
could guess a player+game combo and take over the hand, we'll trust our
beta testers (read: family) for now to be good citizens.

The mechanism works by adding a force flag to join_game. If present, and
if the username is already in the game, we hot-swap the player's WS
object and ID. Name/hand/game all stay the same. Works for both players
and spectators :)

Diffstat:
Mlib/Euchre/Dealer.pm | 59+++++++++++++++++++++++++++++++++++++++++++++--------------
Mpublic/debug.html | 7++++---
2 files changed, 49 insertions(+), 17 deletions(-)

diff --git a/lib/Euchre/Dealer.pm b/lib/Euchre/Dealer.pm @@ -134,6 +134,7 @@ sub pong { # player_name # game_id +# force sub join_game { my ($p, $msg) = @_; @@ -156,31 +157,36 @@ sub join_game { }; } - # Handle full game case - if ($GAMES{$id}->{phase} ne 'lobby') { - send_error($p, 'Game already in progress'); - } else { - my $game = $GAMES{$id}; + my $game = $GAMES{$id}; + + # Make sure name is unique to game + my @all_names = map { $_->{name} } + grep { defined } + (@{$game->{players}}, @{$game->{spectators}}); - # Make sure name is unique to game - my @all_names = map { $_->{name} } - grep { defined } - (@{$game->{players}}, @{$game->{spectators}}); + my $player_exists = grep { $_ eq $msg->{player_name} } @all_names; - if (grep { $_ eq $msg->{player_name} } @all_names) { - send_error($p, 'Username not unique'); + if ($player_exists) { + if (!$msg->{force}) { + send_error($p, 'Username not unique; is this you?'); + return; + } + $p->{name} = $msg->{player_name}; + swap_player($game, $p, 'players') || swap_player($game, $p, 'spectators'); + } else { + if ($game->{phase} ne 'lobby') { + send_error($p, 'Cant join as new player mid game'); return; } - # Add player object to Game # All players start as spectators and have to take a seat explicitly $p->{name} = $msg->{player_name}; $p->{hand} = []; $p->{game} = $game; push @{$game->{spectators}}, $p; - - broadcast_gamestate($game); } + + broadcast_gamestate($game); } # seat @@ -545,4 +551,29 @@ sub take_card { return 0; } +# Returns 0 or 1 on success +sub swap_player { + my ($game, $new_p, $list) = @_; + + for (my $i = 0; $i < @{$game->{$list}}; $i++) { + next unless defined $game->{$list}->[$i]; # undef potentially in lobby + if ($game->{$list}->[$i]->{name} eq $new_p->{name}) { + # Ye ole switcheroo + # Don't delete the old player cuz we need to preserve the hand, etc. + # Just swap out the WS and ID, and update PLAYERS + my $old_id = $game->{$list}->[$i]->{id}; + $game->{$list}->[$i]->{id} = $new_p->{id}; + $game->{$list}->[$i]->{ws} = $new_p->{ws}; + $PLAYERS{$new_p->{id}} = $game->{$list}->[$i]; + + # NOTE: For now, don't delete from %PLAYERS here... + # the old WS may still be playing ping/pong, so we just + # let them hang out until they close themselves or go + # inactive (and we time them out => delete from %PLAYERS) + return 1; + } + } + return 0; +} + 1; diff --git a/public/debug.html b/public/debug.html @@ -61,11 +61,11 @@ } }; - function joinGame() { + function joinGame(force) { uname = document.getElementById('username').value; gname = document.getElementById('gamename').value; console.log('U: ' + uname + ' G: ' + gname); - ws.send(JSON.stringify({action:'join_game', player_name: uname, game_id: gname})) + ws.send(JSON.stringify({action:'join_game', player_name: uname, game_id: gname, force: force})) } function sit() { seat = document.getElementById('seat_no').value; @@ -102,7 +102,8 @@ <input type="text" id="username"> <label for="gamename">Game:</label> <input type="text" id="gamename"> - <button onclick="joinGame()">Join Game</button> + <button onclick="joinGame(0)">Join Game</button> + <button onclick="joinGame(1)">Force Join</button> <br><br> <label for="seat_no">Seat:</label> <input type="number" id="seat_no">