Server/Server/src/main/java/uulm/teamname/marvelous/server/lobbymanager/LobbyConnection.java

327 lines
12 KiB
Java

package uulm.teamname.marvelous.server.lobbymanager;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.ArrayTools;
import uulm.teamname.marvelous.gamelibrary.Tuple;
import uulm.teamname.marvelous.gamelibrary.config.CharacterProperties;
import uulm.teamname.marvelous.gamelibrary.events.Event;
import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
import uulm.teamname.marvelous.gamelibrary.messages.server.*;
import uulm.teamname.marvelous.gamelibrary.requests.Request;
import uulm.teamname.marvelous.server.Server;
import uulm.teamname.marvelous.server.lobby.Lobby;
import uulm.teamname.marvelous.server.netconnector.ClientState;
import uulm.teamname.marvelous.server.netconnector.SUID;
import uulm.teamname.marvelous.server.netconnector.UserManager;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class LobbyConnection implements Runnable {
private static boolean synchronous = false;
public final String gameID;
public LobbyConnectionState state = LobbyConnectionState.Waiting;
private Participant player1;
private Participant player2;
private final Set<Participant> spectators = new HashSet<>(10);
private final Map<SUID, List<Integer>> selection = new HashMap<>(2);
public final Map<ParticipantType, CharacterProperties[]> options = new HashMap<>(2);
private final BlockingQueue<Tuple<Participant, Request[]>> requestQueue = new LinkedBlockingQueue<>();
private Lobby lobby;
/** Creates a new LobbyConnection */
public LobbyConnection(String gameID) {
this.gameID = gameID;
Tuple<CharacterProperties[], CharacterProperties[]> picked = Server.getCharacterConfig().getDisjointSetsOfPropertiesOfSize(12);
this.options.put(ParticipantType.PlayerOne, picked.item1);
this.options.put(ParticipantType.PlayerTwo, picked.item2);
}
public boolean setSelection(Participant participant, Integer[] selection) {
this.selection.put(participant.id, ArrayTools.toArrayList(selection));
return this.selection.size() == 2;
}
public void addParticipant(Participant participant) {
if (this.state == LobbyConnectionState.Started) {
Logger.trace("Set client state to playing");
participant.getClient().setState(ClientState.Playing);
participant.state = ParticipantState.Playing;
participant.sendMessage(generateGameStructure(participant.type));
}
if (participant.type == ParticipantType.Spectator) {
Logger.trace("Adding spectator");
spectators.add(participant);
} else if (participant.type == ParticipantType.PlayerOne) {
player1 = participant;
} else {
player2 = participant;
}
}
/** Disconnects a participant from the LobbyConnection */
public void removeParticipant(Participant participant) {
removeParticipant(participant, "You have been disconnected.");
}
/** Disconnects a participant from the LobbyConnection */
public void removeParticipant(Participant participant, String reason) {
if (participant != null) {
Logger.debug("Removing participant '{}' with role {} from lobby",
participant.id.getName(), participant.type);
LobbyManager.getInstance().removeParticipant(participant);
UserManager.getInstance().removeClient(participant.getClient(), reason);
if (participant.type == ParticipantType.Spectator) {
spectators.remove(participant);
} else if (participant.type == ParticipantType.PlayerOne) {
player1 = null;
} else {
player2 = null;
}
}
}
/** Returns the next free slot in the lobby as a {@link ParticipantType} */
public ParticipantType freeSlot() {
if (player1 == null) {
return ParticipantType.PlayerOne;
} else if (player2 == null) {
return ParticipantType.PlayerTwo;
} else {
return ParticipantType.Spectator;
}
}
/** Returns whether there is a player slot available in the lobby */
public boolean hasFreePlayerSlot() {
return player1 == null || player2 == null;
}
public Participant getPlayer1() {
return player1;
}
public Participant getPlayer2() {
return player2;
}
public Set<Participant> getSpectators() {
return spectators;
}
public boolean hasPlayer1() {
return player1 != null;
}
public boolean hasPlayer2() {
return player2 != null;
}
public void handleMessage(Participant participant, Request[] requests) {
if(synchronous) {
lobby.receiveRequests(requests, participant);
return;
}
try {
this.requestQueue.put(Tuple.of(participant, requests));
} catch (InterruptedException ignored) {
}
}
/** Handles disconnect of a Participant. Hereby, the participant is made ready for reconnection */
public void handleDisconnect(Participant participant) {
participant.disconnected = true;
if (state == LobbyConnectionState.Started) {
lobby.handleDisconnect(participant);
}
}
/** Handles reconnect of a Participant. Hereby, the participant is made ready for reconnection */
public void handleReconnect(Participant participant) {
participant.disconnected = false;
if (state == LobbyConnectionState.Started) {
GeneralAssignmentMessage response = new GeneralAssignmentMessage();
response.gameID = gameID;
participant.sendMessage(response);
participant.sendMessage(generateGameStructure(participant.type));
lobby.handleReconnect(participant);
}
}
public void runSynchronous() {
synchronous = true;
run();
}
@Override
public void run() {
state = LobbyConnectionState.Started;
player1.state = ParticipantState.Playing;
player2.state = ParticipantState.Playing;
for (Participant spectator : spectators) {
spectator.state = ParticipantState.Playing;
}
if(!synchronous) {
Logger.info("Starting Lobby thread for lobby '{}'", gameID);
}else {
Logger.info("Starting Lobby in main thread for lobby '{}'", gameID);
}
broadcastGameStructure();
this.lobby = new Lobby(
gameID,
this,
Server.getPartyConfig(),
Server.getCharacterConfig(),
Server.getScenarioConfig(),
selection.get(player1.id),
selection.get(player2.id)
);
if(synchronous) {
return;
}
while (state == LobbyConnectionState.Started) {
Tuple<Participant, Request[]> currentRequests = pollQueueAsync();
if (currentRequests != null) {
lobby.receiveRequests(currentRequests.item2, currentRequests.item1);
}
}
}
public void terminate() {
LobbyRunner.getInstance().removeLobby(this);
LobbyManager.getInstance().removeLobby(gameID);
state = LobbyConnectionState.Aborted;
removeParticipant(player1);
removeParticipant(player2);
spectators.forEach(this::removeParticipant);
}
private void broadcastGameStructure() {
// Sending GameStructure message with fitting assignment
player1.sendMessage(generateGameStructure(ParticipantType.PlayerOne));
player2.sendMessage(generateGameStructure(ParticipantType.PlayerTwo));
broadcastToSpectators(generateGameStructure(ParticipantType.Spectator));
}
private GameStructureMessage generateGameStructure(ParticipantType assignment) {
GameStructureMessage gameStructureMessage = new GameStructureMessage();
gameStructureMessage.playerOneName = player1.id.getName();
gameStructureMessage.playerTwoName = player2.id.getName();
gameStructureMessage.playerOneCharacters = new CharacterProperties[6];
gameStructureMessage.playerTwoCharacters = new CharacterProperties[6];
int i = 0;
for (Integer id : selection.get(player1.id)) {
gameStructureMessage.playerOneCharacters[i++] = Server.getCharacterConfig().getIDMap().get(id);
}
i = 0;
for (Integer id : selection.get(player2.id)) {
gameStructureMessage.playerTwoCharacters[i++] = Server.getCharacterConfig().getIDMap().get(id);
}
gameStructureMessage.matchconfig = Server.getPartyConfig();
gameStructureMessage.scenarioconfig = Server.getScenarioConfig();
gameStructureMessage.assignment = assignment;
return gameStructureMessage;
}
private Tuple<Participant, Request[]> pollQueueAsync() {
Tuple<Participant, Request[]> current = null;
try {
current = requestQueue.poll(1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {
}
return current;
}
private void broadcast(BasicMessage message) {
Logger.trace("Broadcasting message of type {} to all members of lobby", message.messageType);
if (player1 != null) player1.sendMessage(message);
if (player2 != null) player2.sendMessage(message);
spectators.forEach(spectator -> spectator.sendMessage(message));
}
private void broadcastToSpectators(BasicMessage message) {
Logger.trace("Broadcasting message of type {} to all spectators", message.messageType);
spectators.forEach(spectator -> spectator.sendMessage(message));
}
private void broadcastToAllExcept(Participant except, BasicMessage message) {
Logger.trace("Sending message of type {} to all except participant with role {}",
message.messageType, except == null ? "NONE" : except.type);
if (except != null) {
if (!except.equals(player1)) player1.sendMessage(message);
if (!except.equals(player2)) player2.sendMessage(message);
for (Participant spectator : spectators) {
if (!except.equals(spectator)) {
spectator.sendMessage(message);
}
}
} else {
broadcast(message);
}
}
public void sendEvents(Participant recipient, Event... events) {
Logger.trace("Sending {} events to participant with role {}",
events.length, recipient == null ? "NONE" : recipient.type);
if (recipient != null) {
EventMessage message = new EventMessage();
message.messages = events;
recipient.sendMessage(message);
}
}
public void broadcastEvents(List<Event> events) {
broadcastEvents(events.toArray(new Event[0]));
}
public void broadcastEvents(Event... events) {
Logger.trace("Broadcasting {} events", events.length);
EventMessage message = new EventMessage();
message.messages = events;
broadcast(message);
}
public void broadcastToAllExcept(Participant except, Event... events) {
Logger.trace("Broadcasting {} events to all except participant with role {}",
events.length, except == null ? "NONE" : except.type);
var message = new EventMessage();
message.messages = events;
broadcastToAllExcept(except, message);
}
}