From 11923b89a0e6f0091155375d90eedab04adba18c Mon Sep 17 00:00:00 2001 From: Yannik Bretschneider Date: Mon, 7 Jun 2021 16:44:41 +0200 Subject: [PATCH] feat: implemented Lifetime- and TimeoutTimer --- .../marvelous/server/lobby/LifetimeTimer.java | 34 +++++++ .../marvelous/server/lobby/TimeoutTimer.java | 89 +++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 Server/src/main/java/uulm/teamname/marvelous/server/lobby/LifetimeTimer.java create mode 100644 Server/src/main/java/uulm/teamname/marvelous/server/lobby/TimeoutTimer.java diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/LifetimeTimer.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/LifetimeTimer.java new file mode 100644 index 0000000..ff47751 --- /dev/null +++ b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/LifetimeTimer.java @@ -0,0 +1,34 @@ +package uulm.teamname.marvelous.server.lobby; + +import uulm.teamname.marvelous.server.Server; +import uulm.teamname.marvelous.server.lobbymanager.Participant; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * A timer meant to time the entire Lifetime of the lobby, and if it is higher than the maximum permitted value, + * call a callback given by the lobby to compute a winner, and quit the lobby. + */ +public class LifetimeTimer { + + private final ScheduledExecutorService timer; + private final int maxGameTime; + private final Runnable callback; + + LifetimeTimer(int maxGameTime, Runnable callback) { + String lobbyThreadName = Thread.currentThread().getName(); + ThreadFactory threadFactory = r -> new Thread(r, lobbyThreadName + "-LifetimeTimerThread"); + this.timer = Executors.newSingleThreadScheduledExecutor(threadFactory); + + this.maxGameTime = maxGameTime; + this.callback = callback; + } + + void startTimer() { + timer.schedule(callback, maxGameTime, TimeUnit.SECONDS); + } +} diff --git a/Server/src/main/java/uulm/teamname/marvelous/server/lobby/TimeoutTimer.java b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/TimeoutTimer.java new file mode 100644 index 0000000..af6df57 --- /dev/null +++ b/Server/src/main/java/uulm/teamname/marvelous/server/lobby/TimeoutTimer.java @@ -0,0 +1,89 @@ +package uulm.teamname.marvelous.server.lobby; + +import org.tinylog.Logger; +import uulm.teamname.marvelous.server.Server; +import uulm.teamname.marvelous.server.lobbymanager.Participant; + +import java.util.concurrent.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class TimeoutTimer { + + private final ScheduledExecutorService timer; + + private ScheduledFuture player1AlmostTimeout, player1Timeout; + private ScheduledFuture player2AlmostTimeout, player2Timeout; + + private final BiConsumer almostTimeoutCallback; + private final Consumer timeoutCallback; + + private final int almostTimeoutTime, timeoutTime; + + /** + * Class that manages timeouts of players after not sending a message for a long time. + * + * @param timeoutTime is the time that a player has to send any message + * @param almostTimeoutCallback is the callback that is called if the timeoutTime is almost over + * @param timeoutCallback is the callback that is called when the timeoutTime is over + */ + public TimeoutTimer(int timeoutTime, + BiConsumer almostTimeoutCallback, + Consumer timeoutCallback) { + this.almostTimeoutCallback = almostTimeoutCallback; + this.timeoutCallback = timeoutCallback; + + this.timeoutTime = timeoutTime; + this.almostTimeoutTime = Math.max(timeoutTime - 15, Math.min(timeoutTime, 5)); + + String lobbyThreadName = Thread.currentThread().getName(); + ThreadFactory threadFactory = r -> new Thread(r, lobbyThreadName + "-timeoutTimer"); + timer = Executors.newSingleThreadScheduledExecutor(threadFactory); + } + + /** Refreshes the timeout timer for the given Participant. This is meant to be used for Players, not Spectators. */ + public void refreshTimer(Participant participant) { + Logger.debug("Refreshing turnTimer for participant of type '{}'", participant.type); + switch (participant.type) { + case PlayerOne -> { + Logger.trace("Was playerOne, refreshing..."); + if (player1AlmostTimeout != null) player1AlmostTimeout.cancel(false); + player1AlmostTimeout = + timer.schedule(() -> { + almostTimeoutCallback.accept(participant, almostTimeoutTime - timeoutTime); + return participant; + }, this.almostTimeoutTime, TimeUnit.SECONDS); + + if (player1Timeout != null) player1Timeout.cancel(false); + player1Timeout = + timer.schedule(() -> { + timeoutCallback.accept(participant); + return participant; + }, this.timeoutTime, TimeUnit.SECONDS); + } + case PlayerTwo -> { + Logger.trace("Was playerOne, refreshing..."); + if (player2AlmostTimeout != null) player2AlmostTimeout.cancel(false); + player2AlmostTimeout = + timer.schedule(() -> { + almostTimeoutCallback.accept(participant, almostTimeoutTime - timeoutTime); + return participant; + }, this.almostTimeoutTime, TimeUnit.SECONDS); + + if (player2Timeout != null) player2Timeout.cancel(false); + player2Timeout = + timer.schedule(() -> { + timeoutCallback.accept(participant); + return participant; + }, this.timeoutTime, TimeUnit.SECONDS); + } + case Spectator -> Logger.warn("Timeout called on spectator '{}'. This is probably a bug.", + participant.id.getName()); + } + } +} + + + + +