276 lines
11 KiB
Java
276 lines
11 KiB
Java
package uulm.teamname.marvelous.server.game;
|
|
|
|
import uulm.teamname.marvelous.gamelibrary.ArrayTools;
|
|
import uulm.teamname.marvelous.gamelibrary.config.CharacterProperties;
|
|
import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
|
|
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.gamelogic.GameInstance;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.server.EventMessage;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.server.GameStructureMessage;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.server.GeneralAssignmentMessage;
|
|
import uulm.teamname.marvelous.gamelibrary.requests.Request;
|
|
import uulm.teamname.marvelous.server.ServerApplication;
|
|
import uulm.teamname.marvelous.server.game.pipelining.*;
|
|
import uulm.teamname.marvelous.server.net.Client;
|
|
import uulm.teamname.marvelous.server.net.ClientState;
|
|
|
|
import java.util.*;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
import java.util.concurrent.ScheduledFuture;
|
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
public class GameSession {
|
|
public final String id = UUID.randomUUID().toString();
|
|
|
|
private final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(3);
|
|
|
|
public final HashMap<ParticipantType, CharacterProperties[]> characterChoices = new HashMap<>();
|
|
private final HashMap<ParticipantType, Integer[]> characterIndices = new HashMap<>();
|
|
private final HashMap<ParticipantType, List<Integer>> characterSelection = new HashMap<>();
|
|
|
|
private final HashMap<ParticipantType, Integer> badRequests = new HashMap<>();
|
|
|
|
private ParticipantType turnWaiting;
|
|
private ScheduledFuture<?> turnTimer;
|
|
|
|
private boolean started = false;
|
|
|
|
private GameInstance instance;
|
|
private boolean paused = false;
|
|
|
|
private Pipeline pipeline;
|
|
|
|
public GameSession() {
|
|
List<CharacterProperties> characters = ArrayTools.toArrayList(ServerApplication.getCharacterConfig().characters);
|
|
characterChoices.put(ParticipantType.PlayerOne, new CharacterProperties[characters.size() / 2]);
|
|
characterChoices.put(ParticipantType.PlayerTwo, new CharacterProperties[characters.size() / 2]);
|
|
characterIndices.put(ParticipantType.PlayerOne, new Integer[characters.size() / 2]);
|
|
characterIndices.put(ParticipantType.PlayerTwo, new Integer[characters.size() / 2]);
|
|
int n1 = 0;
|
|
int n2 = 0;
|
|
int i = 0;
|
|
Collections.shuffle(characters);
|
|
for(CharacterProperties character: characters) {
|
|
if(i < characters.size() / 2) {
|
|
characterChoices.get(ParticipantType.PlayerOne)[n1] = character;
|
|
characterIndices.get(ParticipantType.PlayerOne)[n1] = character.characterID;
|
|
n1++;
|
|
}else {
|
|
characterChoices.get(ParticipantType.PlayerTwo)[n2] = character;
|
|
characterIndices.get(ParticipantType.PlayerTwo)[n2] = character.characterID;
|
|
n2++;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
public void setPaused(boolean paused) {
|
|
this.paused = paused;
|
|
}
|
|
|
|
public boolean getPaused() {
|
|
return paused;
|
|
}
|
|
|
|
public GameInstance getInstance() {
|
|
return instance;
|
|
}
|
|
|
|
public void addSpectator(Client client) {
|
|
GeneralAssignmentMessage response = new GeneralAssignmentMessage();
|
|
response.gameID = id;
|
|
client.sendMessage(response);
|
|
|
|
client.sendMessage(getGameStructure(client.getType()));
|
|
}
|
|
|
|
public void handleReconnect(Client client) {
|
|
GeneralAssignmentMessage response = new GeneralAssignmentMessage();
|
|
response.gameID = id;
|
|
client.sendMessage(response);
|
|
|
|
client.sendMessage(getGameStructure(client.getType()));
|
|
}
|
|
|
|
public void handleSelection(Client client, boolean[] selection) {
|
|
ArrayList<Integer> indices = new ArrayList<>();
|
|
for(int i = 0; i < selection.length; i++) {
|
|
if(selection[i]) {
|
|
indices.add(characterIndices.get(client.getType())[i]);
|
|
}
|
|
}
|
|
characterSelection.put(client.getType(), indices);
|
|
}
|
|
|
|
public void handleRequests(Client client, Request[] messages) {
|
|
if(client.getType() == ParticipantType.Spectator || paused) {
|
|
return;
|
|
}
|
|
|
|
Optional<List<Event>> resultingEvents = pipeline.processRequests(client, messages);
|
|
|
|
if(resultingEvents.isEmpty()) {
|
|
reject(client);
|
|
}else {
|
|
accept(client, resultingEvents.get());
|
|
|
|
if(instance.state.isWon()) {
|
|
ServerApplication.getSession().reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void handleDisconnect(Client client) {
|
|
if(started && client.getType() != ParticipantType.Spectator) {
|
|
winner(client.getType() == ParticipantType.PlayerOne ? ParticipantType.PlayerTwo : ParticipantType.PlayerOne);
|
|
}
|
|
}
|
|
|
|
|
|
public void start() {
|
|
initialize();
|
|
|
|
List<Event> start = instance.startGame(characterSelection.get(ParticipantType.PlayerOne), characterSelection.get(ParticipantType.PlayerTwo));
|
|
|
|
EventMessage message = new EventMessage();
|
|
message.messages = start.toArray(new Event[0]);
|
|
ServerApplication.getSession().broadcast(message);
|
|
|
|
started = true;
|
|
|
|
turnWaiting = instance.state.getActiveCharacter().type == EntityType.P1 ? ParticipantType.PlayerOne : ParticipantType.PlayerTwo;
|
|
turnTimer = scheduler.schedule(this::timeout, ServerApplication.getPartyConfig().maxRoundTime, TimeUnit.SECONDS);
|
|
}
|
|
|
|
private void initialize() {
|
|
pipeline = new Pipeline();
|
|
|
|
var reqSegment = new RequestGameStateSegment(this);
|
|
var pauseSegment = new PauseSegment(this);
|
|
var filterEndRoundRequestSegment = new FilterEndRoundRequestSegment(this);
|
|
var disconnectSegment = new DisconnectSegment();
|
|
var playerFilterSegment = new PlayerFilterSegment();
|
|
var gameLogicSegment = new GameLogicSegment(this);
|
|
pipeline.addSegment(reqSegment)
|
|
.addSegment(pauseSegment)
|
|
.addSegment(filterEndRoundRequestSegment)
|
|
.addSegment(disconnectSegment)
|
|
.addSegment(playerFilterSegment)
|
|
.addSegment(gameLogicSegment);
|
|
|
|
instance = new GameInstance(ServerApplication.getPartyConfig(), ServerApplication.getCharacterConfig(), ServerApplication.getScenarioConfig());
|
|
|
|
for(Client player: ServerApplication.getSession().getPlayers()) {
|
|
player.setState(ClientState.Playing);
|
|
player.sendMessage(getGameStructure(player.getType()));
|
|
}
|
|
GameStructureMessage spectators = getGameStructure(ParticipantType.Spectator);
|
|
for(Client spectator: ServerApplication.getSession().getSpectators()) {
|
|
spectator.setState(ClientState.Playing);
|
|
spectator.sendMessage(spectators);
|
|
}
|
|
}
|
|
|
|
private void accept(Client source, List<Event> accepted) {
|
|
if(accepted.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
accepted.add(instance.getGameStateEvent());
|
|
|
|
EventMessage message = new EventMessage();
|
|
message.messages = accepted.toArray(new Event[0]);
|
|
ServerApplication.getSession().broadcast(message, source.getID());
|
|
|
|
accepted.add(0, new EventBuilder(EventType.Ack).buildGameEvent());
|
|
|
|
EventMessage response = new EventMessage();
|
|
response.messages = accepted.toArray(new Event[0]);
|
|
source.sendMessage(response);
|
|
|
|
badRequests.put(source.getType(), 0);
|
|
|
|
if(turnTimer != null) {
|
|
turnTimer.cancel(true);
|
|
}
|
|
|
|
turnWaiting = source.getType();
|
|
turnTimer = scheduler.schedule(this::timeout, ServerApplication.getPartyConfig().maxRoundTime, TimeUnit.SECONDS);
|
|
}
|
|
|
|
private void reject(Client source) {
|
|
EventMessage response = new EventMessage();
|
|
response.messages = new Event[]{
|
|
new EventBuilder(EventType.Nack).buildGameEvent(),
|
|
instance.getGameStateEvent()
|
|
};
|
|
source.sendMessage(response);
|
|
|
|
int bad = badRequests.getOrDefault(source.getType(), 0) + 1;
|
|
badRequests.put(source.getType(), bad);
|
|
|
|
if(bad >= 5) {
|
|
winner(source.getType() == ParticipantType.PlayerOne ? ParticipantType.PlayerTwo : ParticipantType.PlayerOne);
|
|
}
|
|
}
|
|
|
|
private synchronized void winner(ParticipantType winner) {
|
|
EventMessage message = new EventMessage();
|
|
message.messages = new Event[] {
|
|
new EventBuilder(EventType.WinEvent)
|
|
.withPlayerWon(winner.equals(ParticipantType.PlayerOne) ? 1 : 2)
|
|
.buildGameEvent(),
|
|
new EventBuilder(EventType.DisconnectEvent)
|
|
.buildGameEvent()
|
|
};
|
|
|
|
ServerApplication.getSession().broadcast(message);
|
|
ServerApplication.getSession().reset();
|
|
}
|
|
|
|
private void timeout() {
|
|
for(Client player: ServerApplication.getSession().getPlayers()) {
|
|
if(player != null && player.getType() != turnWaiting) {
|
|
EventMessage notification = new EventMessage();
|
|
notification.messages = new Event[] { new EventBuilder(EventType.TurnTimeoutEvent).buildGameEvent() };
|
|
player.sendMessage(notification);
|
|
break;
|
|
}
|
|
}
|
|
|
|
EventMessage message = new EventMessage();
|
|
message.messages = instance.endTurn().toArray(new Event[0]);
|
|
ServerApplication.getSession().broadcast(message);
|
|
}
|
|
|
|
|
|
private GameStructureMessage getGameStructure(ParticipantType assignment) {
|
|
GameStructureMessage message = new GameStructureMessage();
|
|
message.assignment = assignment;
|
|
message.matchconfig = ServerApplication.getPartyConfig();
|
|
message.scenarioconfig = ServerApplication.getScenarioConfig();
|
|
|
|
var characters = ServerApplication.getCharacterConfig().getIDMap();
|
|
for(Client player: ServerApplication.getSession().getPlayers()) {
|
|
int n = 0;
|
|
CharacterProperties[] selected = new CharacterProperties[characters.size() / 4];
|
|
for(Integer i: characterSelection.get(player.getType())) {
|
|
selected[n++] = characters.get(i);
|
|
}
|
|
if(player.getType() == ParticipantType.PlayerOne) {
|
|
message.playerOneName = player.getID().getName();
|
|
message.playerOneCharacters = selected;
|
|
}else {
|
|
message.playerTwoName = player.getID().getName();
|
|
message.playerTwoCharacters = selected;
|
|
}
|
|
}
|
|
|
|
return message;
|
|
}
|
|
}
|