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,
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
*/
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) {
Logger.trace("Resetting bad requests as new participant sent data");
activePlayer = source;
badRequests = 0;
}
Logger.info("got {} requests from participant {}",
requests.length,
source);
source.name);
Logger.trace("Processing requests through pipeline");
Optional<Event[]> resultingEvents = pipeline.processRequests(requests, source);
Logger.debug("generated {} events from the pipeline", resultingEvents.map(x -> x.length).orElse(0));
Optional<List<Event>> resultingEvents = pipeline.processRequests(requests, source);
Logger.debug("generated {} events from the pipeline", resultingEvents.map(List::size).orElse(0));
//resultingEvents isEmpty when a wrong request appeared
Logger.trace("Checking whether resultingEvents (an optional) is empty");
if (resultingEvents.isEmpty()) {
Logger.debug("Rejecting requests from participant '{}'", source.name);
reject(source);
} else {
var events = resultingEvents.get();
connection.sendEvents(source, events);
badRequests = 0;
accept(source, resultingEvents.get());
}
updateTimer();
}
@ -117,26 +123,41 @@ public class Lobby {
*/
void updateTimer() {
var currentActiveCharacterType = game.state.getActiveCharacter().type;
Logger.trace("Updating turnTimer with current entityType {}", currentActiveCharacterType);
if (pauseSegment.isPaused()) {
Logger.trace("Game is paused, clearing turnTimer");
turnTimer.clear();
} else if (currentActiveCharacterType == EntityType.P1) {
Logger.trace("Scheduling turnTimer for Player1");
turnTimer.startTurnTimer(connection.getPlayer1());
} else if (currentActiveCharacterType == EntityType.P2) {
Logger.trace("Scheduling turnTimer for Player2");
turnTimer.startTurnTimer(connection.getPlayer2());
} else {
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.
* @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());
badRequests ++;
//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);
if(connection.hasPlayer1()){
generateWin(connection.getPlayer1());
@ -178,9 +199,12 @@ public class Lobby {
}
}
public synchronized void turnTimeout(Participant source){
//source round is over next players turn
connection.sendEvents(source, game.endTurn().toArray(new Event[0]));
/** Skips the current turn, and starts a new one. Exclusively called in the {@link TurnTimer}. */
private synchronized void turnTimeout(Participant source){
var nextTurnEvents = game.endTurn();
nextTurnEvents.add(game.getGameStateEvent());
connection.broadcastEvents();
updateTimer();
}
/**

View File

@ -1,23 +1,34 @@
package uulm.teamname.marvelous.server.lobby;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
import uulm.teamname.marvelous.server.lobbymanager.Participant;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.*;
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.
*/
public class TurnTimer {
private final Timer timer;
private final ScheduledExecutorService timer;
private final Consumer<Participant> callback;
private final int maxRoundTime;
private ScheduledFuture<Participant> current;
public TurnTimer(int maxRoundTime, Consumer<Participant> callback) {
this.timer = new Timer();
this.maxRoundTime = maxRoundTime;
String lobbyThreadName = Thread.currentThread().getName();
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;
}
@ -26,21 +37,23 @@ public class TurnTimer {
* @param participant the timer is for
*/
public void startTurnTimer(Participant participant) {
if (participant.type == ParticipantType.Spectator)
if (participant.type == ParticipantType.Spectator) {
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 */
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
* {@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");
// The packet carries the requests, and gets smaller per segment
Packet packet = new Packet(requests, origin);
@ -60,7 +60,7 @@ public class Pipeline {
return Optional.empty();
} else { // else return an optional of the array
Logger.trace("Pipeline executed normally. Returning {} events.", carrier.size());
return Optional.of(carrier.toArray(new Event[0]));
return Optional.of(carrier);
}
}