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 uulm.teamname.marvelous.gamelibrary.requests.Request;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
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 {
|
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) {
|
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.events.Event;
|
||||||
import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public interface Segment {
|
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.mockito.Mockito.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
class PauseSegmentTest {
|
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