274 lines
9.5 KiB
Java
274 lines
9.5 KiB
Java
package uulm.teamname.marvelous.server.lobbymanager;
|
|
|
|
import org.tinylog.Logger;
|
|
import uulm.teamname.marvelous.gamelibrary.config.CharacterProperties;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.RoleEnum;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.client.CharacterSelectionMessage;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.client.PlayerReadyMessage;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.client.RequestMessage;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.server.ConfirmSelectionMessage;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.server.GameAssignmentMessage;
|
|
import uulm.teamname.marvelous.gamelibrary.messages.server.GeneralAssignmentMessage;
|
|
import uulm.teamname.marvelous.server.netconnector.Client;
|
|
import uulm.teamname.marvelous.server.netconnector.ClientState;
|
|
import uulm.teamname.marvelous.server.netconnector.SUID;
|
|
import uulm.teamname.marvelous.server.netconnector.UserManager;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
public class LobbyManager {
|
|
private static LobbyManager instance;
|
|
|
|
/**
|
|
* @return the current instance of the LobbyManager
|
|
*/
|
|
public static LobbyManager getInstance() {
|
|
if (instance == null) {
|
|
instance = new LobbyManager();
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
private final HashMap<String, LobbyConnection> lobbies = new HashMap<>();
|
|
|
|
private final HashMap<SUID, Participant> participants = new HashMap<>();
|
|
|
|
|
|
/**
|
|
* Handles a newly connected Client, and returns whether the game is already running in the given {@link
|
|
* AtomicBoolean}.
|
|
*/
|
|
public boolean handleConnect(Client client, AtomicBoolean running) {
|
|
Logger.debug("Connecting new client");
|
|
|
|
Participant participant = participants.get(client.getId());
|
|
if (participant != null) {
|
|
LobbyConnection lobby = lobbies.get(participant.lobby);
|
|
if (lobby != null) {
|
|
running.set(lobby.state == LobbyConnectionState.Started);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public boolean handleReady(Client client, PlayerReadyMessage message) {
|
|
if (participants.containsKey(client.getId())) {
|
|
return false;
|
|
}
|
|
|
|
addParticipant(client, client.socket.getResourceDescriptor(), message.role);
|
|
|
|
return true;
|
|
}
|
|
|
|
public boolean handleReconnect(Client client) {
|
|
if (!participants.containsKey(client.getId())) {
|
|
return false;
|
|
}
|
|
|
|
Participant participant = participants.get(client.getId());
|
|
participant.setClient(client);
|
|
|
|
LobbyConnection lobby = lobbies.get(participant.lobby);
|
|
|
|
if (lobby == null) {
|
|
return false;
|
|
}
|
|
|
|
lobby.handleReconnect(participant);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Handles a {@link CharacterSelectionMessage}, computes the characters that have been selected and relays that
|
|
* information to the {@link LobbyConnection} concerned by this information.
|
|
*
|
|
* @param client is the client that sent the message
|
|
* @param message is the message sent by the client
|
|
* @return true if handled successfully, and false otherwise
|
|
*/
|
|
public boolean handleSelection(Client client, CharacterSelectionMessage message) {
|
|
Logger.debug("Handling characterSelection...");
|
|
if (!participants.containsKey(client.getId())) {
|
|
Logger.trace("Participant didn't exist, returning...");
|
|
return false;
|
|
}
|
|
|
|
Participant participant = participants.get(client.getId());
|
|
|
|
if (participant.state != ParticipantState.Assigned) {
|
|
Logger.trace("Participant wasn't assigned, exiting...");
|
|
return false;
|
|
} else if (participant.type == ParticipantType.Spectator) {
|
|
Logger.trace("Spectator sent message, returning...");
|
|
return false;
|
|
}
|
|
|
|
LobbyConnection lobby = lobbies.get(participant.lobby);
|
|
|
|
if (lobby == null) {
|
|
return false;
|
|
}
|
|
|
|
Integer[] selected = new Integer[6];
|
|
|
|
CharacterProperties[] options = lobby.options.get(participant.type);
|
|
|
|
int n = 0;
|
|
for (int i = 0; i < 12; i++) {
|
|
if (Boolean.TRUE.equals(message.characters[i])) {
|
|
selected[n++] = options[i].characterID;
|
|
}
|
|
}
|
|
|
|
if (n != 6) {
|
|
return false;
|
|
}
|
|
|
|
participant.state = ParticipantState.Selected;
|
|
|
|
boolean complete = lobby.setSelection(participant, selected);
|
|
|
|
if (complete) {
|
|
lobby.getPlayer1().getClient().setState(ClientState.Playing);
|
|
lobby.getPlayer2().getClient().setState(ClientState.Playing);
|
|
lobby.getSpectators().forEach(spectator -> spectator.getClient().setState(ClientState.Playing));
|
|
LobbyRunner.getInstance().startLobby(lobby);
|
|
} else {
|
|
ConfirmSelectionMessage response = new ConfirmSelectionMessage();
|
|
response.selectionComplete = false;
|
|
participant.sendMessage(response);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Handles a requestMessage, and relays it to the Lobby if valid
|
|
*
|
|
* @param client is the client that sent the message
|
|
* @param message is the message sent by the client
|
|
* @return true if handled successfully, and false otherwise
|
|
*/
|
|
public boolean handleRequests(Client client, RequestMessage message) {
|
|
if (!participants.containsKey(client.getId())) {
|
|
return false;
|
|
}
|
|
|
|
Participant participant = participants.get(client.getId());
|
|
|
|
if (participant.state != ParticipantState.Playing) {
|
|
return false;
|
|
}
|
|
|
|
LobbyConnection lobby = lobbies.get(participant.lobby);
|
|
|
|
if (lobby == null) {
|
|
return false;
|
|
}
|
|
|
|
lobby.handleMessage(participant, message.messages);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Handles the disconnect of a WebSocket.
|
|
*/
|
|
public void handleDisconnect(Client client, boolean byRemote) {
|
|
Logger.trace("Handling disconnect of Client");
|
|
|
|
if (!participants.containsKey(client.getId())) {
|
|
return;
|
|
}
|
|
|
|
Participant participant = participants.get(client.getId());
|
|
LobbyConnection lobby = lobbies.get(participant.lobby);
|
|
|
|
if (lobby == null) {
|
|
return;
|
|
}
|
|
|
|
if(lobby.state == LobbyConnectionState.Started) {
|
|
lobby.handleDisconnect(participant);
|
|
|
|
}else {
|
|
Logger.debug("Deleting participant after leaving a non-started lobby");
|
|
participants.remove(client.getId());
|
|
|
|
if(lobby.hasFreePlayerSlot()) {
|
|
Logger.debug("Destroying lobby after last player left");
|
|
lobby.terminate();
|
|
lobbies.remove(participant.lobby);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void removeLobby(String lobbyID) {
|
|
lobbies.remove(lobbyID);
|
|
}
|
|
|
|
/**
|
|
* Adds a participant to a lobby. If the maximum amount of lobbies is already filled, or if the lobby requested
|
|
* isn't free, the participant is disconnected.
|
|
*/
|
|
private void addParticipant(Client client, String lobbyID, RoleEnum role) {
|
|
Logger.trace("Adding participant '{}' to the lobby '{}'", client.getId(), lobbyID);
|
|
|
|
if (!lobbies.containsKey(lobbyID)) {
|
|
if (!LobbyRunner.getInstance().canAddLobby()) {
|
|
Logger.info("Rejecting participant '{}' as server is already full", client.getId());
|
|
UserManager.getInstance().removeClient(client, "The server is currently full. Please connect as a spectator instead.");
|
|
return;
|
|
}
|
|
Logger.info("Lobby '{}' didn't exist yet, initializing", lobbyID);
|
|
lobbies.put(lobbyID, new LobbyConnection(lobbyID));
|
|
}
|
|
|
|
LobbyConnection lobby = lobbies.get(lobbyID);
|
|
|
|
if (!lobby.hasFreePlayerSlot() && role != RoleEnum.SPECTATOR) {
|
|
Logger.debug("No free player slots available, disconnecting client '{}'", client.getId());
|
|
UserManager.getInstance().removeClient(client, "The lobby your requested is already full. Please connect as a spectator instead.");
|
|
return;
|
|
}
|
|
|
|
ParticipantType type = lobby.freeSlot();
|
|
|
|
Logger.trace("New participant '{}' has the role '{}'", client.getId(), type);
|
|
|
|
Participant participant = new Participant(client, lobbyID, type, role == RoleEnum.KI);
|
|
participants.put(client.getId(), participant);
|
|
|
|
lobby.addParticipant(participant);
|
|
|
|
if (type != ParticipantType.Spectator) {
|
|
Logger.debug("Sending GameAssignment message to user '{}'", client.getId());
|
|
GameAssignmentMessage response = new GameAssignmentMessage();
|
|
response.gameID = lobby.gameID;
|
|
response.characterSelection = lobby.options.get(type);
|
|
participant.sendMessage(response);
|
|
} else {
|
|
Logger.debug("Sending GeneralAssignment message to user '{}'", client.getId());
|
|
GeneralAssignmentMessage response = new GeneralAssignmentMessage();
|
|
response.gameID = lobby.gameID;
|
|
participant.sendMessage(response);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes a participant from the game entirely. This is done when for example a player gets removed from the
|
|
* Lobby because of a timeout.
|
|
*/
|
|
public void removeParticipant(Participant participant) {
|
|
if (participant != null) {
|
|
participants.remove(participant.id);
|
|
}
|
|
}
|
|
}
|