commit b4d2e6abe2f1dfd33c47ab9c68bd584996e5735b (patch)
parent 1391e546a4ae2a3f235e14fb019c458ddb5b5ced
Author: Alex Karle <alex@karle.co>
Date: Wed, 22 Apr 2020 23:34:12 -0400
Euchre::Dealer: Add resource cleanup on players leaving
Previously there was absolutely 0 cleanup of the %GAMES variable. This
is, by definition, a nice memory leak :) and the poor 1GB RAM of VM
space would slowly be chipped away at as the uptime grew.
Thankfully, Mojo::IOLoop made it pretty easy to add a check after a
player has left to ensure that the game is cleaned up properly. It goes
like this:
On player leaving:
If they're in a game:
Make note to delete game no players are active after 30 min
Players "leave" when they disconnect for any reason. Tab closing,
dropped connection, refreshed tab, whatever. In these cases, we delete
the reference to the player in our global %PLAYERS state, but we do NOT
delete the object in the $game->{players}.
This is intentional, as we want to allow that player to rejoin the game,
in case of network disruption / accidental tab closage. They can rejoin,
and we'll swap them in and mark them as active again.
So as long as no player is active in the game (has the tab open and
pinging our server), we'll delete the game after 30 mins of inactivity.
Tracking "activity" is as easy as assuming they're active when they
register and marking inactive when they leave. When we swap the player
in, we take most of the player's previous values, so we mark the old
player active. No other actions change activity (that's a mouthful..
it's late).
Diffstat:
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/gloat.pl b/gloat.pl
@@ -42,7 +42,6 @@ websocket '/play' => sub {
});
$c->on(finish => sub {
- app->log->debug("Player $id exiting");
gloaters_never_win($id);
});
};
diff --git a/lib/Euchre/Dealer.pm b/lib/Euchre/Dealer.pm
@@ -57,6 +57,7 @@ our @EXPORT = qw(
# seat => 0-3,
# ws => websocket obj,
# hand => cards in hand,
+# active => is connection active,
# }
#
# The players keyed on ws id is key (pun) because the closure in
@@ -73,7 +74,7 @@ our %PLAYERS;
sub register_player {
my ($tx) = @_;
my $id = ''.$tx;
- $PLAYERS{$id} = { id => $id, ws => $tx };
+ $PLAYERS{$id} = { id => $id, ws => $tx, active => 1 };
print "Player $id has joined the server\n";
}
@@ -82,8 +83,27 @@ sub gloaters_never_win {
my ($id) = @_;
if (!exists $PLAYERS{$id}) {
warn "gloaters_never_win called on unknown player\n";
+ return;
+ }
+ $PLAYERS{$id}->{active} = 0;
+ my $game = $PLAYERS{$id}->{game};
+ if (defined $game) {
+ # Player was in a game... if no one else is still there,
+ # we should clean up the game after some inactivity
+ my $timeout = $ENV{DEBUG} ? 1 : (60 * 60 * 30); # 30 mins
+ Mojo::IOLoop->timer($timeout => sub {
+ if (!grep { defined($_) && $_->{active} } @{$game->{players}}) {
+ print "Deleting inactive Game $game->{id}\n";
+ delete $GAMES{$game->{id}};
+ }
+ });
}
- # TODO: handle the game cleanup...? should we quit game? pause?
+
+ print "Player $PLAYERS{$id}->{name} went inactive\n";
+
+ # Remove reference in %PLAYERS, but NOTE: still referenced in $game
+ # potentially. This is by design. Don't throw away their hand / seat
+ # whatever until no active players are at the game and a timeout passes
delete $PLAYERS{$id};
}
@@ -584,6 +604,7 @@ sub swap_player {
my $old_id = $game->{$list}->[$i]->{id};
$game->{$list}->[$i]->{id} = $new_p->{id};
$game->{$list}->[$i]->{ws} = $new_p->{ws};
+ $game->{$list}->[$i]->{active} = 1; # May have disconnected previously
$PLAYERS{$new_p->{id}} = $game->{$list}->[$i];
# NOTE: For now, don't delete from %PLAYERS here...