Server/Server/src/main/java/uulm/teamname/marvelous/server/lobby/pipelining/Pipeline.java

85 lines
4.0 KiB
Java

package uulm.teamname.marvelous.server.lobby.pipelining;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.requests.Request;
import uulm.teamname.marvelous.server.lobbymanager.Participant;
import java.util.*;
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 {
/** The segments of the pipeline, which will be executed from left to right (index 0 to segments.size()) */
private final ArrayList<Segment> segments;
/**
* Creates a new empty {@link Pipeline} object
*/
public Pipeline() {
this.segments = new ArrayList<>();
}
/**
* 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 Optional}<{@link Event}[]>, whereby the state of the {@link Optional} declares whether the
* execution of the {@link Pipeline} was successful. <b>If the optional is empty, the input requests were invalid,
* and an error message can be sent to the client</b>. To get the {@link Event Events} 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<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);
// The packet is filled by the requests resulting from events per segment
List<Event> carrier = new ArrayList<>();
// The abort boolean describes whether an abort happened in a segment
AtomicBoolean abort = new AtomicBoolean(false);
Logger.trace("Iterating through segments");
// Loop through all segments
for (Segment segment : segments) {
// Give the segment the packet, carrier and abort, and let it process requests
segment.processRequests(packet, carrier, abort);
if (packet.isEmpty() || abort.get()) { // if packet is empty (all requests processed) or abort initiated
break; // (abort boolean true), break out of the loop
}
}
if (abort.get()) { // if abort is true, return empty optional
Logger.debug("Abort triggered in 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);
}
}
/**
* 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;
}
public boolean contains(Segment segment) {
return segments.contains(segment);
}
}