feat: partially implemented not yet refactored lobbyManager

This commit is contained in:
Yannik Bretschneider 2021-06-06 03:49:24 +02:00
parent e7a8f0e1e4
commit 7db692f790
1 changed files with 196 additions and 31 deletions

View File

@ -1,48 +1,196 @@
package uulm.teamname.marvelous.server.lobbymanager;
import org.tinylog.Logger;
import uulm.teamname.marvelous.gamelibrary.config.CharacterConfig;
import uulm.teamname.marvelous.gamelibrary.config.PartyConfig;
import uulm.teamname.marvelous.gamelibrary.config.ScenarioConfig;
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.requests.Request;
import uulm.teamname.marvelous.gamelibrary.messages.client.CharacterSelectionMessage;
import uulm.teamname.marvelous.gamelibrary.messages.client.RequestMessage;
import uulm.teamname.marvelous.gamelibrary.messages.server.GameAssignmentMessage;
import uulm.teamname.marvelous.server.Server;
import uulm.teamname.marvelous.server.lobby.Lobby;
import uulm.teamname.marvelous.server.netconnector.UserManager;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
/**
* A class that handles the connection to the lobby. It contains distinct websockets for player1, player2 and spectators.
* A class that handles the connection to the lobby. It contains the participants inside of the lobby.
* The class is meant to be used in conjecture with {@link MessageRelay}.
*/
public class LobbyConnection implements Runnable {
private final Lobby lobby;
private Lobby lobby;
public final String gameID;
private Participant player1, player2;
private boolean characterSelection;
/** Whether the character selection phase is reached */
private boolean inGame;
private final HashSet<Participant> spectators;
private final BlockingQueue<Request[]> incomingRequests;
private final BlockingQueue<Tuple<Participant, BasicMessage>> incomingMessages;
/**
* A callback executed to send a message, originating from the
* {@link uulm.teamname.marvelous.server.netconnector.UserManager}
*/
private final BiConsumer<Participant, BasicMessage> sendMessageCallback;
// TODO: FIX THIS JAVADOC
/**
* Creates a new LobbyConnection, whereby a new {@link Lobby} is initialized.
*/
public LobbyConnection(String gameID,
PartyConfig partyConfig,
CharacterConfig characterConfig,
ScenarioConfig scenarioConfig) {
/** Creates a new LobbyConnection */
public LobbyConnection(String gameID, BiConsumer<Participant, BasicMessage> sendMessageCallback) {
this.gameID = gameID;
this.sendMessageCallback = sendMessageCallback;
this.spectators = new HashSet<>(10);
this.incomingMessages = new LinkedBlockingQueue<>();
this.characterSelection = false;
this.inGame = false;
}
@Override
public void run() {
Logger.info("Starting Lobby thread for lobby '{}'", gameID);
Logger.trace("Initializing lobby...");
initializeLobby();
Logger.trace("Activating characterSelection state");
this.characterSelection = true;
Logger.info("Starting character selection process");
Logger.trace("Finding twenty-four random characters");
Tuple<CharacterProperties[], CharacterProperties[]> selectionPossibilities =
Server.getCharacterConfig().getDisjointSetsOfPropertiesOfSize(12);
Logger.info("Sending GameAssignment message with random characters to players");
var gameAssignmentMessage = new GameAssignmentMessage();
gameAssignmentMessage.gameID = this.gameID;
// Send to player one with characters for player one
gameAssignmentMessage.characterSelection = selectionPossibilities.item1;
UserManager.getInstance().sendMessage(player1.getConnection(), gameAssignmentMessage);
// And send the others to player 2
gameAssignmentMessage.characterSelection = selectionPossibilities.item2;
UserManager.getInstance().sendMessage(player2.getConnection(), gameAssignmentMessage);
CharacterProperties[] playerOneSelection = null;
CharacterProperties[] playerTwoSelection = null;
Logger.info("Entering GameAssignment state. Waiting for answer about selected characters from players.");
while (characterSelection) {
Tuple<Participant, BasicMessage> currentMessage = null;
try { // TODO: Exact duplication. Maybe extract?
Logger.trace("Checking for messages. Currently the amount of messages is {}",
incomingMessages.size());
if (incomingMessages.isEmpty()) {
Logger.trace("LobbyConnection thread waiting for new messages...");
Thread.currentThread().wait();
Logger.trace("Lobby '{}' woken up", gameID);
}
Logger.trace("Polling incoming message queue");
currentMessage = incomingMessages.poll(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Logger.warn("LobbyConnection thread got interrupted. Exception: " + e.getMessage());
}
if (currentMessage == null) {
continue;
} else if (currentMessage.item2 instanceof CharacterSelectionMessage) {
var origin = currentMessage.item1;
var message = (CharacterSelectionMessage) currentMessage.item2;
if (origin.type.equals(ParticipantType.Spectator)) {
Logger.info("Spectator sent CharacterSelectionMessage. Sending error...");
UserManager.getInstance().sendError(
origin.getConnection(),
"Spectators can't select characters");
} else if (origin.type.equals(ParticipantType.PlayerOne)) {
if (playerOneSelection == null) { // TODO: Extract this. This isn't beautiful at all.
ArrayList<CharacterProperties> chosenCharacters = new ArrayList<>();
for (int i = 0; i < 12; i++) {
if (message.characters[i]) {
chosenCharacters.add(selectionPossibilities.item1[i]);
}
}
playerOneSelection = chosenCharacters.toArray(new CharacterProperties[0]);
}
Logger.info("Player 1 has selected their characters");
} else {
if (playerTwoSelection == null) {
ArrayList<CharacterProperties> chosenCharacters = new ArrayList<>();
for (int i = 0; i < 12; i++) {
if (message.characters[i]) {
chosenCharacters.add(selectionPossibilities.item2[i]);
}
}
playerTwoSelection = chosenCharacters.toArray(new CharacterProperties[0]);
}
Logger.info("Player 2 has selected their characters");
}
}
}
while(inGame) {
Tuple<Participant, BasicMessage> currentMessage = null;
try {
Logger.trace("Checking for messages. Currently the amount of messages is {}",
incomingMessages.size());
if (incomingMessages.isEmpty()) {
Logger.trace("LobbyConnection thread waiting for new messages...");
Thread.currentThread().wait();
Logger.trace("Lobby '{}' woken up", gameID);
}
Logger.trace("Polling incoming message queue");
currentMessage = incomingMessages.poll(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Logger.warn("LobbyConnection thread got interrupted. Exception: " + e.getMessage());
}
if (currentMessage == null) {
Logger.trace("Message was null, continuing");
continue;
} else if (currentMessage.item2 instanceof CharacterSelectionMessage) {
receiveCharacterSelection((CharacterSelectionMessage) currentMessage.item2);
} else if (currentMessage.item2 instanceof RequestMessage) {
receiveRequests((RequestMessage) currentMessage.item2);
} else {
}
}
}
/** Send messages to the lobbyConnection */
public void receiveMessage(Participant participant, BasicMessage message) {
this.incomingMessages.add(Tuple.of(participant, message));
}
private void receiveRequests(RequestMessage message) {
// TODO: implement this
}
private void receiveCharacterSelection(CharacterSelectionMessage message) {
// TODO: Implement proper character selection
}
private void initializeLobby() {
Logger.trace("Initializing lobby...");
this.lobby = new Lobby(
gameID,
this,
partyConfig,
characterConfig,
scenarioConfig);
this.gameID = gameID;
this.spectators = new HashSet<>(10);
this.incomingRequests = new LinkedBlockingQueue<>();
Server.getPartyConfig(),
Server.getCharacterConfig(),
Server.getScenarioConfig());
}
/**
@ -52,9 +200,7 @@ public class LobbyConnection implements Runnable {
return player1 != null;
}
/**
* @return whether there is a player2
*/
/** @return whether there is a player2 */
public boolean hasPlayer2() {
return player2 != null;
}
@ -176,16 +322,35 @@ public class LobbyConnection implements Runnable {
/** Kills all connections to client, as well as the lobby */
public void terminateConnection() {
MessageRelay.getInstance().terminate(this);
// TODO: implement this
}
/** Send requests to the lobby that the LobbyConnection connects to */
public void receiveRequests(Request... requests) {
this.incomingRequests.add(requests);
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LobbyConnection that = (LobbyConnection) o;
return characterSelection == that.characterSelection && inGame == that.inGame && Objects.equals(lobby, that.lobby) && Objects.equals(gameID, that.gameID) && Objects.equals(player1, that.player1) && Objects.equals(player2, that.player2) && Objects.equals(spectators, that.spectators) && Objects.equals(incomingMessages, that.incomingMessages) && Objects.equals(sendMessageCallback, that.sendMessageCallback);
}
@Override
public void run() {
public int hashCode() {
return Objects.hash(lobby, gameID, player1, player2, characterSelection, inGame, spectators, incomingMessages, sendMessageCallback);
}
@Override
public String toString() {
return "LobbyConnection{" +
"lobby=" + lobby +
", gameID='" + gameID + '\'' +
", player1=" + player1 +
", player2=" + player2 +
", characterSelection=" + characterSelection +
", inGame=" + inGame +
", spectators=" + spectators +
", incomingMessages=" + incomingMessages +
", sendMessageCallback=" + sendMessageCallback +
'}';
}
}