feat: implemented a pipelining system for the lobby to pipeline events through a number of segments for processing them
This commit is contained in:
parent
d2d9712b61
commit
5515478fe6
@ -1,2 +1,99 @@
|
||||
package uulm.teamname.marvelous.server.Lobby.pipelining;public class PauseSegment {
|
||||
package uulm.teamname.marvelous.server.Lobby.pipelining;
|
||||
|
||||
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
||||
import uulm.teamname.marvelous.gamelibrary.events.Event;
|
||||
import uulm.teamname.marvelous.gamelibrary.events.EventBuilder;
|
||||
import uulm.teamname.marvelous.gamelibrary.events.EventType;
|
||||
import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
||||
import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
|
||||
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class PauseSegment implements Segment {
|
||||
|
||||
private boolean paused;
|
||||
|
||||
public PauseSegment(){
|
||||
paused = false;
|
||||
}
|
||||
|
||||
public void pauseGame(){
|
||||
if(!paused)
|
||||
paused = true;
|
||||
}
|
||||
public void pauseEnd(){
|
||||
if(paused)
|
||||
paused = false;
|
||||
}
|
||||
|
||||
public boolean isPaused() {
|
||||
return paused;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pipelining method to process a set of requests.
|
||||
* The list of requests will be processed according to the following rules:
|
||||
* <ul>
|
||||
* <li>If there is a PauseStartRequest, the paused value will be set to true</li>
|
||||
* <li>Any CharacterRequests will be removed from the requests if the game is paused. These include:
|
||||
* <ul>
|
||||
* <li>MeeleAttackRequest</li>
|
||||
* <li>RangedAttackRequest</li>
|
||||
* <li>MoveRequest</li>
|
||||
* <li>ExchangeInfinityStoneRequest</li>
|
||||
* <li>UseInfinityStoneRequest</li>
|
||||
* <li>EndRoundRequest</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>If a mistake is made (request is sent while pause active), the client will get an error</li>
|
||||
* <li>If there is a PauseStopRequest the game will be unpaused</li>
|
||||
* </ul>
|
||||
* @param packet
|
||||
*/
|
||||
@Override
|
||||
public void processRequests(List<Request> packet, List<Event> carrier, AtomicBoolean abort) {
|
||||
// check if there is a pause request (either start or stop)
|
||||
if (packet.contains(new RequestBuilder(RequestType.PauseStartRequest).buildGameRequest())) {
|
||||
if (!paused) {
|
||||
// pause the game
|
||||
pauseGame();
|
||||
// create a new PauseStartEvent
|
||||
carrier.add(new EventBuilder(EventType.PauseStartEvent).buildGameEvent());
|
||||
} else { // if the game is already paused
|
||||
abort.set(true);
|
||||
return;
|
||||
}
|
||||
} else if (packet.contains(new RequestBuilder(RequestType.PauseStopRequest).buildGameRequest())) {
|
||||
if (paused) {
|
||||
// pause the game
|
||||
pauseEnd();
|
||||
// create a new PauseStartRequest
|
||||
carrier.add(new EventBuilder(EventType.PauseStopEvent).buildGameEvent());
|
||||
} else { // if the game is not paused
|
||||
abort.set(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// filter the events
|
||||
if (paused) {
|
||||
for (Request request: packet) {
|
||||
switch (request.type) {
|
||||
case MeleeAttackRequest,
|
||||
RangedAttackRequest,
|
||||
MoveRequest,
|
||||
ExchangeInfinityStoneRequest,
|
||||
UseInfinityStoneRequest,
|
||||
EndRoundRequest,
|
||||
PauseStopRequest,
|
||||
PauseStartRequest -> packet.remove(request);
|
||||
|
||||
case DisconnectRequest, Req -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,65 @@
|
||||
package uulm.teamname.marvelous.server.Lobby;
|
||||
package uulm.teamname.marvelous.server.Lobby.pipelining;
|
||||
|
||||
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
||||
import uulm.teamname.marvelous.gamelibrary.events.Event;
|
||||
import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* The {@link Pipeline} class pipelines {@link Request Requests} through {@link Segment Segments} to
|
||||
* create {@link Event Events}. To add functionality without dabbling in the game library, create a new {@link Segment}.
|
||||
*/
|
||||
public class Pipeline {
|
||||
|
||||
private final Segment[] segments;
|
||||
/** The segments of the pipeline, which will be executed from left to right (index 0 to segments.size()) */
|
||||
private final ArrayList<Segment> segments;
|
||||
|
||||
public Pipeline(Segment[] segments) {
|
||||
this.segments = segments;
|
||||
/**
|
||||
* Creates a new empty {@link Pipeline} object
|
||||
*/
|
||||
public Pipeline() {
|
||||
this.segments = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void processEvents(Request[] requests) {
|
||||
Request[] out;
|
||||
/**
|
||||
* Pipeline the {@link Request Requests} through all {@link Segment Segments} that are in the pipeline.
|
||||
* The {@link Request Requests} are declared as the <b>Packet</b>. The {@link Request Requests} are filtered
|
||||
* by each {@link Segment segment}, whereby each {@link Segment segment} takes (and thereby removes) all
|
||||
* {@link Event events} relevant to the {@link Segment segment}. The {@link Event Events} are returned at the
|
||||
* end of the pipeline.
|
||||
* @param requests are the requests that are being pipelined through the pipeline
|
||||
* @return a {@link Tuple}<{@link Boolean}, {@link Event}[]>, whereby the {@link Boolean} declares whether the
|
||||
* execution of the {@link Pipeline} is successful. <b>If that is the case, the events can be ignored,
|
||||
* and an error message can be sent to the client</b>.
|
||||
*/
|
||||
public Tuple<Boolean, Event[]> processRequests(Request[] requests) {
|
||||
List<Request> packet = Arrays.asList(requests);
|
||||
List<Event> carrier = new ArrayList<>();
|
||||
AtomicBoolean abort = new AtomicBoolean();
|
||||
abort.set(false);
|
||||
for (Segment segment: segments) {
|
||||
out = segment.processEvents(requests);
|
||||
segment.processRequests(packet, carrier, abort);
|
||||
if (packet.size() == 0 || abort.get()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new Tuple<Boolean, Event[]> (abort.get(), carrier.toArray(new Event[0]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a segment to the pipeline. Note that <b>ORDER MATTERS!</b> The first segment added will be the
|
||||
* first segment to process events. Also note that segments <b>cannot be removed once added</b>. This is by
|
||||
* design, as removing segments would give the pipeline runtime customizability, which it is not supposed to
|
||||
* possess in the first place.
|
||||
* @param toAdd is the segment that will be added to the pipeline
|
||||
* @return the pipeline itself for chaining
|
||||
*/
|
||||
public Pipeline addSegment(Segment toAdd) {
|
||||
this.segments.add(toAdd);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,26 @@
|
||||
package uulm.teamname.marvelous.server.Lobby;
|
||||
package uulm.teamname.marvelous.server.Lobby.pipelining;
|
||||
|
||||
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
||||
import uulm.teamname.marvelous.gamelibrary.events.Event;
|
||||
import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public interface Segment {
|
||||
public Request[] processEvents(Request[] events);
|
||||
/**
|
||||
* Pipelining method to process a set of requests.
|
||||
* The list of requests will be processed according to some set of rules. Hereby,
|
||||
* the {@link Request Requests} in the packet will be filtered out as appropriate,
|
||||
* whereby the resulting {@link Event Events} are appended to the carrier.
|
||||
* @param packet is a {@link List} of {@link Request Requests} that is filtered
|
||||
* by the {@link Segment} as appropriate
|
||||
* @param carrier is a {@link List} of {@link Event Events} that is appended to
|
||||
* if new requests are generated from the execution of the {@link Segment}
|
||||
* @param abort is an {@link AtomicBoolean} describing whether an error has occurred during the execution of
|
||||
* the {@link Segment} Note that <b>error</b> here does <b>not describe an execution error</b>
|
||||
* of the segment, but instead an error in the events passed to it, like for example moving into a Rock.
|
||||
* The conventional way of setting this boolean is to write {@code abort.set(true); return;}
|
||||
*/
|
||||
public void processRequests(List<Request> packet, List<Event> carrier, AtomicBoolean abort);
|
||||
}
|
||||
|
@ -1,5 +1,44 @@
|
||||
package uulm.teamname.marvelous.server.Lobby.pipelining;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class PauseSegmentTest {
|
||||
|
||||
PauseSegment pauseSegment;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
pauseSegment = new PauseSegment();
|
||||
}
|
||||
|
||||
@Test
|
||||
void pauseGame() {
|
||||
assertThat(pauseSegment.isPaused()).isFalse();
|
||||
pauseSegment.pauseGame();
|
||||
assertThat(pauseSegment.isPaused()).isTrue();
|
||||
pauseSegment.pauseGame();
|
||||
assertThat(pauseSegment.isPaused()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void pauseEnd() {
|
||||
assertThat(pauseSegment.isPaused()).isFalse();
|
||||
pauseSegment.pauseEnd();
|
||||
assertThat(pauseSegment.isPaused()).isFalse();
|
||||
pauseSegment.pauseGame();
|
||||
assertThat(pauseSegment.isPaused()).isTrue();
|
||||
pauseSegment.pauseEnd();
|
||||
assertThat(pauseSegment.isPaused()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void processEvents() {
|
||||
// TODO: check that events get pipelined normally if not paused, but filtered if paused
|
||||
// and check that events work properly
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user