fix: fixed TurnTimer, Lobby and Pipeline
This commit is contained in:
		@ -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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -189,7 +213,7 @@ public class Lobby {
 | 
			
		||||
     *
 | 
			
		||||
     * @param winner is the {@link Participant} that won
 | 
			
		||||
     */
 | 
			
		||||
    public synchronized void generateWin(Participant winner){
 | 
			
		||||
     public synchronized void generateWin(Participant winner){
 | 
			
		||||
        connection.broadcastEvents(
 | 
			
		||||
                new EventBuilder(EventType.WinEvent)
 | 
			
		||||
                        .withPlayerWon(winner.type.equals(ParticipantType.PlayerOne) ? 1 : 2)
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user