From b4d2e6abe2f1dfd33c47ab9c68bd584996e5735b Mon Sep 17 00:00:00 2001 From: Alex Karle Date: Wed, 22 Apr 2020 23:34:12 -0400 Subject: [PATCH] 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). --- gloat.pl | 1 - lib/Euchre/Dealer.pm | 25 +++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/gloat.pl b/gloat.pl index 4e4b2fe..d51c918 100755 --- 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 index 1740f7f..56035d5 100644 --- 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... -- libgit2 1.1.1