feat: implemented proper filtering of EndRoundRequests

This commit is contained in:
Yannik Bretschneider 2021-07-07 14:47:54 +02:00
parent 51239a0aed
commit e02fd0879a
4 changed files with 119 additions and 0 deletions

View File

@ -63,12 +63,14 @@ public class Lobby {
var reqSegment = new RequestGameStateSegment(this.game); var reqSegment = new RequestGameStateSegment(this.game);
this.pauseSegment = new PauseSegment(); this.pauseSegment = new PauseSegment();
var filterEndRoundRequestSegment = new FilterEndRoundRequestSegment(this::getActivePlayer);
var requestTurnEndSegment = new RequestTurnEndSegment(this.game); var requestTurnEndSegment = new RequestTurnEndSegment(this.game);
var disconnectSegment = new DisconnectSegment(this); var disconnectSegment = new DisconnectSegment(this);
var gameLogicSegment = new GameLogicSegment(this.game); var gameLogicSegment = new GameLogicSegment(this.game);
pipeline.addSegment(reqSegment) pipeline.addSegment(reqSegment)
.addSegment(pauseSegment) .addSegment(pauseSegment)
.addSegment(filterEndRoundRequestSegment)
.addSegment(requestTurnEndSegment) .addSegment(requestTurnEndSegment)
.addSegment(disconnectSegment) .addSegment(disconnectSegment)
.addSegment(gameLogicSegment); .addSegment(gameLogicSegment);

View File

@ -0,0 +1,46 @@
package uulm.teamname.marvelous.server.lobby.pipelining;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
import uulm.teamname.marvelous.server.lobbymanager.Participant;
import uulm.teamname.marvelous.server.lobby.Lobby;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
/**
* {@link Segment} that checks for an {@link RequestType#EndRoundRequest}, and
* if it exists, checks for the player that has sent it. If the player sending
* this request is not the active player, it flags the message as an error.
*/
public class FilterEndRoundRequestSegment implements Segment {
private final Supplier<Participant> activeParticipantCallback;
/**
* Creates a new {@link FilterEndRoundRequestSegment}
* @param activeParticipantCallback is a {@link Supplier}<{@link Participant}>
* that supplies the currently active participant
* in a {@link Lobby}. It should normally be bound
* to {@link Lobby#getActivePlayer()}, by executing
* {@code new Filter...Segment(this::getActivePlayer)}.
*/
public FilterEndRoundRequestSegment(Supplier<Participant> activeParticipantCallback) {
this.activeParticipantCallback = activeParticipantCallback;
}
@Override
public void processRequests(Packet packet, List<Event> carrier, AtomicBoolean abort) {
Logger.trace("FilterEndRoundSegment has received {} requests", packet.size());
if (packet.containsRequestOfType(RequestType.EndRoundRequest)) {
Logger.trace("Packet contains EndRoundRequest");
if (!packet.getOrigin().equals(activeParticipantCallback.get())) {
Logger.debug("Packet contained EndRoundRequest but was sent from non-active participant, aborting...");
abort.set(true);
}
}
}
}

View File

@ -68,6 +68,11 @@ public class Packet extends ArrayList<Request> {
return Objects.equals(origin, packet.origin); return Objects.equals(origin, packet.origin);
} }
@Override
public Object clone() {
return new Packet(this.toArray(new Request[0]), this.origin);
}
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(super.hashCode(), origin); return Objects.hash(super.hashCode(), origin);

View File

@ -0,0 +1,66 @@
package uulm.teamname.marvelous.server.lobby.pipelining;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import uulm.teamname.marvelous.gamelibrary.requests.Request;
import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
import uulm.teamname.marvelous.server.lobbymanager.Participant;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.*;
class FilterEndRoundRequestSegmentTest {
FilterEndRoundRequestSegment segment;
Participant activeParticipant;
Request[] requests;
@BeforeEach
void beforeEach() {
this.segment = new FilterEndRoundRequestSegment(this::getActiveParticipant);
this.activeParticipant = mock(Participant.class);
requests = new Request[] {
new RequestBuilder(RequestType.EndRoundRequest).buildGameRequest()
};
}
private Participant getActiveParticipant() {
return activeParticipant;
}
@Test
@DisplayName("Request from active participant doesn't get filtered")
void packetFromActiveParticipantTest() {
var packet = new Packet(requests, activeParticipant);
var atomicBoolean = new AtomicBoolean(false);
var processedPacket = (Packet) packet.clone();
segment.processRequests(processedPacket, new ArrayList<>(), atomicBoolean);
assertThat(processedPacket).isEqualTo(packet);
assertThat(atomicBoolean.get()).isFalse();
}
@Test
@DisplayName("Request from non-active participant gets flagged as an error")
void packetFromNonActiveParticipantTest() {
var packet = new Packet(requests, mock(Participant.class));
var atomicBoolean = new AtomicBoolean(false);
var processedPacket = (Packet) packet.clone();
segment.processRequests(processedPacket, new ArrayList<>(), atomicBoolean);
// assertThat(processedPacket).isEqualTo(packet); is not necessary as there's no actual filtering going on
assertThat(atomicBoolean.get()).isTrue();
}
}