Server/Server/src/main/java/uulm/teamname/marvelous/server/game/GameSession.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;
}
}