fix: fixed TurnTimer, Lobby and Pipeline

This commit is contained in:
Yannik Bretschneider 2021-06-07 01:44:18 +02:00
parent 67be103c42
commit 4ace772be0
3 changed files with 67 additions and 30 deletions

View File

@ -75,9 +75,11 @@ public class Lobby {
partyConfig.maxRoundTime, partyConfig.maxRoundTime,
this::turnTimeout); this::turnTimeout);
var response = this.game.startGame(player1Characters, player2Characters); var eventsDescribingGameStart = this.game.startGame(player1Characters, player2Characters);
this.connection.broadcastEvents(response); this.connection.broadcastEvents(eventsDescribingGameStart);
updateTimer();
} }
/** /**
@ -86,28 +88,32 @@ public class Lobby {
* @param source the player executing the requests * @param source the player executing the requests
*/ */
public synchronized void receiveRequests(Request[] requests, Participant source) { public synchronized void receiveRequests(Request[] requests, Participant source) {
Logger.trace("Received {} requests from participant '{}' of type {}",
requests.length,
source.name,
source.type);
if(activePlayer != source && source.type != ParticipantType.Spectator) { if(activePlayer != source && source.type != ParticipantType.Spectator) {
Logger.trace("Resetting bad requests as new participant sent data");
activePlayer = source; activePlayer = source;
badRequests = 0; badRequests = 0;
} }
Logger.info("got {} requests from participant {}", Logger.info("got {} requests from participant {}",
requests.length, requests.length,
source); source.name);
Logger.trace("Processing requests through pipeline"); Logger.trace("Processing requests through pipeline");
Optional<Event[]> resultingEvents = pipeline.processRequests(requests, source); Optional<List<Event>> resultingEvents = pipeline.processRequests(requests, source);
Logger.debug("generated {} events from the pipeline", resultingEvents.map(x -> x.length).orElse(0)); Logger.debug("generated {} events from the pipeline", resultingEvents.map(List::size).orElse(0));
//resultingEvents isEmpty when a wrong request appeared //resultingEvents isEmpty when a wrong request appeared
Logger.trace("Checking whether resultingEvents (an optional) is empty");
if (resultingEvents.isEmpty()) { if (resultingEvents.isEmpty()) {
Logger.debug("Rejecting requests from participant '{}'", source.name);
reject(source); reject(source);
} else { } else {
var events = resultingEvents.get(); accept(source, resultingEvents.get());
connection.sendEvents(source, events);
badRequests = 0;
} }
updateTimer(); updateTimer();
} }
@ -117,26 +123,41 @@ public class Lobby {
*/ */
void updateTimer() { void updateTimer() {
var currentActiveCharacterType = game.state.getActiveCharacter().type; var currentActiveCharacterType = game.state.getActiveCharacter().type;
Logger.trace("Updating turnTimer with current entityType {}", currentActiveCharacterType);
if (pauseSegment.isPaused()) { if (pauseSegment.isPaused()) {
Logger.trace("Game is paused, clearing turnTimer");
turnTimer.clear(); turnTimer.clear();
} else if (currentActiveCharacterType == EntityType.P1) { } else if (currentActiveCharacterType == EntityType.P1) {
Logger.trace("Scheduling turnTimer for Player1");
turnTimer.startTurnTimer(connection.getPlayer1()); turnTimer.startTurnTimer(connection.getPlayer1());
} else if (currentActiveCharacterType == EntityType.P2) { } else if (currentActiveCharacterType == EntityType.P2) {
Logger.trace("Scheduling turnTimer for Player2");
turnTimer.startTurnTimer(connection.getPlayer2()); turnTimer.startTurnTimer(connection.getPlayer2());
} else { } else {
turnTimer.clear(); turnTimer.clear();
} }
} }
private void accept(Participant source, List<Event> accepted) {
Logger.debug("Accepting requests from participant '{}', broadcasting events to all except source",
source.name);
connection.broadcastToAllExcept(source, accepted.toArray(new Event[0]));
Logger.trace("Adding ack and sending back to originParticipant");
accepted.add(0, new EventBuilder(EventType.Ack).buildGameStateEvent());
connection.sendEvents(source, accepted.toArray(new Event[0]));
badRequests = 0;
}
/** /**
* If the player executed a false request the request gets rejected. * If the player executed a false request the request gets rejected.
* @param source the executing player * @param source the executing player
*/ */
private synchronized void reject(Participant source) { private void reject(Participant source) {
connection.sendEvents(source, new EventBuilder(EventType.Nack).buildGameEvent(), game.getGameStateEvent()); connection.sendEvents(source, new EventBuilder(EventType.Nack).buildGameEvent(), game.getGameStateEvent());
badRequests ++; badRequests ++;
//if the player sends 2 bad messages after one another, the player gets kicked out of the lobby. //if the player sends 2 bad messages after one another, the player gets kicked out of the lobby.
if(badRequests == 2){ if(badRequests >= 5){
connection.removeParticipant(source); connection.removeParticipant(source);
if(connection.hasPlayer1()){ if(connection.hasPlayer1()){
generateWin(connection.getPlayer1()); generateWin(connection.getPlayer1());
@ -178,9 +199,12 @@ public class Lobby {
} }
} }
public synchronized void turnTimeout(Participant source){ /** Skips the current turn, and starts a new one. Exclusively called in the {@link TurnTimer}. */
//source round is over next players turn private synchronized void turnTimeout(Participant source){
connection.sendEvents(source, game.endTurn().toArray(new Event[0])); var nextTurnEvents = game.endTurn();
nextTurnEvents.add(game.getGameStateEvent());
connection.broadcastEvents();
updateTimer();
} }
/** /**

View File

@ -1,23 +1,34 @@
package uulm.teamname.marvelous.server.lobby; package uulm.teamname.marvelous.server.lobby;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType; import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
import uulm.teamname.marvelous.server.lobbymanager.Participant; import uulm.teamname.marvelous.server.lobbymanager.Participant;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.*;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
* The {@link TurnTimer} class is called by the {@link Lobby} to limit the amount of time a player has per round. * The {@link TurnTimer} class is called by the {@link Lobby} to limit the amount of time a player has per round.
*/ */
public class TurnTimer { public class TurnTimer {
private final Timer timer; private final ScheduledExecutorService timer;
private final Consumer<Participant> callback; private final Consumer<Participant> callback;
private final int maxRoundTime; private final int maxRoundTime;
private ScheduledFuture<Participant> current;
public TurnTimer(int maxRoundTime, Consumer<Participant> callback) { public TurnTimer(int maxRoundTime, Consumer<Participant> callback) {
this.timer = new Timer(); String lobbyThreadName = Thread.currentThread().getName();
this.maxRoundTime = maxRoundTime; ThreadFactory threadFactory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, lobbyThreadName + "-timerThread");
}
};
this.timer = Executors.newSingleThreadScheduledExecutor(threadFactory);
this.maxRoundTime = 3;
this.callback = callback; this.callback = callback;
} }
@ -26,21 +37,23 @@ public class TurnTimer {
* @param participant the timer is for * @param participant the timer is for
*/ */
public void startTurnTimer(Participant participant) { public void startTurnTimer(Participant participant) {
if (participant.type == ParticipantType.Spectator) if (participant.type == ParticipantType.Spectator) {
throw new IllegalStateException("Spectators don't have TurnTime"); throw new IllegalStateException("Spectators don't have TurnTime");
// cancel all current timers, and start a new timer
timer.cancel();
timer.schedule(new TimerTask() {
@Override
public void run() {
callback.accept(participant);
} }
}, maxRoundTime);
clear();
Logger.debug("Starting turn timer for participant '{}' with role {}",
participant.name, participant.type);
current = timer.schedule(() -> {callback.accept(participant); return participant;},
maxRoundTime,
TimeUnit.SECONDS);
} }
/** cancels all currently running timers */ /** cancels all currently running timers */
public void clear() { public void clear() {
timer.cancel(); Logger.trace("Clearing timer");
if (this.current != null) {
current.cancel(false);
}
} }
} }

View File

@ -37,7 +37,7 @@ public class Pipeline {
* out of the {@link Optional}, first check whether the {@link Optional} is empty by doing * out of the {@link Optional}, first check whether the {@link Optional} is empty by doing
* {@link Optional#isEmpty()} or {@link Optional#isPresent()}, and act accordingly. * {@link Optional#isEmpty()} or {@link Optional#isPresent()}, and act accordingly.
*/ */
public Optional<Event[]> processRequests(Request[] requests, Participant origin) { public Optional<List<Event>> processRequests(Request[] requests, Participant origin) {
Logger.trace("Pipeline started RequestProcessing"); Logger.trace("Pipeline started RequestProcessing");
// The packet carries the requests, and gets smaller per segment // The packet carries the requests, and gets smaller per segment
Packet packet = new Packet(requests, origin); Packet packet = new Packet(requests, origin);
@ -60,7 +60,7 @@ public class Pipeline {
return Optional.empty(); return Optional.empty();
} else { // else return an optional of the array } else { // else return an optional of the array
Logger.trace("Pipeline executed normally. Returning {} events.", carrier.size()); Logger.trace("Pipeline executed normally. Returning {} events.", carrier.size());
return Optional.of(carrier.toArray(new Event[0])); return Optional.of(carrier);
} }
} }