feat: implemented Character selection and relaying to lobby
This commit is contained in:
parent
7af0fd40a1
commit
21bfac7d75
@ -1,5 +1,6 @@
|
|||||||
package uulm.teamname.marvelous.server.lobbymanager;
|
package uulm.teamname.marvelous.server.lobbymanager;
|
||||||
|
|
||||||
|
import org.java_websocket.WebSocket;
|
||||||
import org.tinylog.Logger;
|
import org.tinylog.Logger;
|
||||||
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
||||||
import uulm.teamname.marvelous.gamelibrary.config.CharacterProperties;
|
import uulm.teamname.marvelous.gamelibrary.config.CharacterProperties;
|
||||||
@ -8,10 +9,9 @@ import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
|
|||||||
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
|
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
|
||||||
import uulm.teamname.marvelous.gamelibrary.messages.client.CharacterSelectionMessage;
|
import uulm.teamname.marvelous.gamelibrary.messages.client.CharacterSelectionMessage;
|
||||||
import uulm.teamname.marvelous.gamelibrary.messages.client.RequestMessage;
|
import uulm.teamname.marvelous.gamelibrary.messages.client.RequestMessage;
|
||||||
import uulm.teamname.marvelous.gamelibrary.messages.server.GameAssignmentMessage;
|
import uulm.teamname.marvelous.gamelibrary.messages.server.*;
|
||||||
import uulm.teamname.marvelous.server.Server;
|
import uulm.teamname.marvelous.server.Server;
|
||||||
import uulm.teamname.marvelous.server.lobby.Lobby;
|
import uulm.teamname.marvelous.server.lobby.Lobby;
|
||||||
import uulm.teamname.marvelous.server.netconnector.UserManager;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
@ -20,44 +20,65 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that handles the connection to the lobby. It contains the participants inside of the lobby.
|
* A class that handles the connection to the lobby. It contains the participants inside of the lobby. The class
|
||||||
* The class is meant to be used in conjecture with {@link MessageRelay}.
|
* implements runnable and harbors a thread, but should exclusively be run from the {@link LobbyRunner} class.
|
||||||
*/
|
*/
|
||||||
public class LobbyConnection implements Runnable {
|
public class LobbyConnection implements Runnable {
|
||||||
private Lobby lobby;
|
private Lobby lobby;
|
||||||
public final String gameID;
|
public final String gameID;
|
||||||
private Participant player1, player2;
|
private Participant player1, player2;
|
||||||
private boolean characterSelection;
|
|
||||||
|
|
||||||
/** Whether the character selection phase is reached */
|
/** Whether the character selection phase is active. True while active, false after done */
|
||||||
|
private boolean characterSelectionActive;
|
||||||
|
|
||||||
|
/** Whether the ingame phase is reached */
|
||||||
private boolean inGame;
|
private boolean inGame;
|
||||||
|
|
||||||
|
/** A boolean set true if the thread should be closed. Basically a terminator. */
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
private final HashSet<Participant> spectators;
|
private final HashSet<Participant> spectators;
|
||||||
private final BlockingQueue<Tuple<Participant, BasicMessage>> incomingMessages;
|
private final BlockingQueue<Tuple<Participant, BasicMessage>> incomingMessages;
|
||||||
|
|
||||||
|
private final BiConsumer<WebSocket, BasicMessage> sendMessageCallback;
|
||||||
|
private final BiConsumer<WebSocket, String> sendErrorCallback;
|
||||||
|
|
||||||
// TODO: FIX THIS JAVADOC
|
// TODO: FIX THIS JAVADOC
|
||||||
|
|
||||||
/** Creates a new LobbyConnection */
|
/** Creates a new LobbyConnection */
|
||||||
public LobbyConnection(String gameID) {
|
public LobbyConnection(String gameID,
|
||||||
|
BiConsumer<WebSocket, BasicMessage> sendMessageCallback,
|
||||||
|
BiConsumer<WebSocket, String> sendErrorCallback) {
|
||||||
this.gameID = gameID;
|
this.gameID = gameID;
|
||||||
|
this.sendMessageCallback = sendMessageCallback;
|
||||||
|
this.sendErrorCallback = sendErrorCallback;
|
||||||
this.spectators = new HashSet<>(10);
|
this.spectators = new HashSet<>(10);
|
||||||
this.incomingMessages = new LinkedBlockingQueue<>();
|
this.incomingMessages = new LinkedBlockingQueue<>();
|
||||||
this.characterSelection = false;
|
this.characterSelectionActive = false;
|
||||||
this.inGame = false;
|
this.inGame = false;
|
||||||
|
this.active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Variables for Character Selection
|
||||||
|
Tuple<CharacterProperties[], CharacterProperties[]> selectionPossibilities;
|
||||||
|
|
||||||
|
CharacterProperties[] playerOneSelection = null;
|
||||||
|
CharacterProperties[] playerTwoSelection = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Logger.info("Starting Lobby thread for lobby '{}'", gameID);
|
Logger.info("Starting Lobby thread for lobby '{}'", gameID);
|
||||||
Logger.trace("Initializing lobby...");
|
|
||||||
initializeLobby();
|
|
||||||
|
|
||||||
Logger.trace("Activating characterSelection state");
|
Logger.debug("Activating lobbyConnection");
|
||||||
this.characterSelection = true;
|
this.active = true;
|
||||||
|
|
||||||
|
Logger.debug("Activating characterSelection state");
|
||||||
|
this.characterSelectionActive = true;
|
||||||
|
|
||||||
|
|
||||||
Logger.info("Starting character selection process");
|
Logger.info("Starting character selection process");
|
||||||
Logger.trace("Finding twenty-four random characters");
|
Logger.trace("Finding twenty-four random characters");
|
||||||
Tuple<CharacterProperties[], CharacterProperties[]> selectionPossibilities =
|
selectionPossibilities = Server.getCharacterConfig().getDisjointSetsOfPropertiesOfSize(12);
|
||||||
Server.getCharacterConfig().getDisjointSetsOfPropertiesOfSize(12);
|
|
||||||
|
|
||||||
Logger.info("Sending GameAssignment message with random characters to players");
|
Logger.info("Sending GameAssignment message with random characters to players");
|
||||||
var gameAssignmentMessage = new GameAssignmentMessage();
|
var gameAssignmentMessage = new GameAssignmentMessage();
|
||||||
@ -65,115 +86,226 @@ public class LobbyConnection implements Runnable {
|
|||||||
|
|
||||||
// Send to player one with characters for player one
|
// Send to player one with characters for player one
|
||||||
gameAssignmentMessage.characterSelection = selectionPossibilities.item1;
|
gameAssignmentMessage.characterSelection = selectionPossibilities.item1;
|
||||||
UserManager.getInstance().sendMessage(player1.getConnection(), gameAssignmentMessage);
|
sendMessage(player1, gameAssignmentMessage);
|
||||||
|
|
||||||
// And send the others to player 2
|
// And send the others to player 2
|
||||||
gameAssignmentMessage.characterSelection = selectionPossibilities.item2;
|
gameAssignmentMessage.characterSelection = selectionPossibilities.item2;
|
||||||
UserManager.getInstance().sendMessage(player2.getConnection(), gameAssignmentMessage);
|
sendMessage(player2, gameAssignmentMessage);
|
||||||
|
|
||||||
CharacterProperties[] playerOneSelection = null;
|
// Also, send the GeneralAssignment message to all spectators
|
||||||
CharacterProperties[] playerTwoSelection = null;
|
var generalAssignmentMessage = new GeneralAssignmentMessage();
|
||||||
|
generalAssignmentMessage.gameID = gameID;
|
||||||
|
broadcastToSpectators(generalAssignmentMessage);
|
||||||
|
|
||||||
Logger.info("Entering GameAssignment state. Waiting for answer about selected characters from players.");
|
Logger.info("Entering GameAssignment state. Waiting for answer about selected characters from players.");
|
||||||
while (characterSelection) {
|
while (characterSelectionActive && active) {
|
||||||
Tuple<Participant, BasicMessage> currentMessage = null;
|
var currentMessage = getMessageAsync(1000);
|
||||||
|
|
||||||
try { // TODO: Exact duplication. Maybe extract?
|
if (!active) {
|
||||||
Logger.trace("Checking for messages. Currently the amount of messages is {}",
|
Logger.info("Lobby '{}' is terminating. Exiting...", gameID);
|
||||||
incomingMessages.size());
|
return;
|
||||||
|
} else if (currentMessage == null) {
|
||||||
if (incomingMessages.isEmpty()) {
|
Logger.trace("Message was null, continuing"); // TODO: remove for production
|
||||||
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;
|
continue;
|
||||||
} else if (currentMessage.item2 instanceof CharacterSelectionMessage) {
|
} else if (currentMessage.item2 instanceof CharacterSelectionMessage) {
|
||||||
var origin = currentMessage.item1;
|
var origin = currentMessage.item1;
|
||||||
var message = (CharacterSelectionMessage) currentMessage.item2;
|
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Logger.debug("CharacterSelectionMessage sent by Player '{}' during character selection phase",
|
||||||
|
origin.name);
|
||||||
|
characterSelectionActive = receiveCharacterSelection(origin, message);
|
||||||
|
|
||||||
|
Logger.trace("Sending confirmSelectionMessage to player1");
|
||||||
|
var replyMessage = new ConfirmSelectionMessage();
|
||||||
|
replyMessage.selectionComplete = !characterSelectionActive;
|
||||||
|
sendMessage(origin, replyMessage);
|
||||||
|
} else if (currentMessage.item2 instanceof RequestMessage) {
|
||||||
|
var origin = currentMessage.item1;
|
||||||
|
// var message = (RequestMessage) currentMessage.item2; this is ignored here
|
||||||
|
|
||||||
|
Logger.debug("RequestMessage sent by Player '{}' during character selection phase",
|
||||||
|
origin.name);
|
||||||
|
sendError(
|
||||||
|
currentMessage.item1,
|
||||||
|
"Requests rejected as ingame phase not reached yet");
|
||||||
|
} else {
|
||||||
|
Logger.warn("Message that isn't of type RequestMessage or CharacacterSelectionMessage" +
|
||||||
|
"received in Lobby. This is probably a bug.");
|
||||||
|
sendError(
|
||||||
|
currentMessage.item1,
|
||||||
|
"Message couldn't be processed by the lobby, as its type was invalid");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while(inGame) {
|
Logger.trace("End of character selection phase reached. Lobby termination is '{}'", active);
|
||||||
Tuple<Participant, BasicMessage> currentMessage = null;
|
if (!active) {
|
||||||
try {
|
Logger.info("Lobby '{}' is terminating. Exiting... ", gameID);
|
||||||
Logger.trace("Checking for messages. Currently the amount of messages is {}",
|
return;
|
||||||
incomingMessages.size());
|
} else {
|
||||||
|
|
||||||
if (incomingMessages.isEmpty()) {
|
Logger.info("Sending GameStructure message to clients as the game is starting");
|
||||||
Logger.trace("LobbyConnection thread waiting for new messages...");
|
// Building GameStructure message
|
||||||
Thread.currentThread().wait();
|
var gameStructureMessage = new GameStructureMessage();
|
||||||
Logger.trace("Lobby '{}' woken up", gameID);
|
gameStructureMessage.playerOneName = player1.name;
|
||||||
}
|
gameStructureMessage.playerTwoName = player2.name;
|
||||||
|
gameStructureMessage.playerOneCharacters = playerOneSelection;
|
||||||
|
gameStructureMessage.playerTwoCharacters = playerTwoSelection;
|
||||||
|
gameStructureMessage.matchconfig = Server.getPartyConfig();
|
||||||
|
gameStructureMessage.scenarioconfig = Server.getScenarioConfig();
|
||||||
|
|
||||||
Logger.trace("Polling incoming message queue");
|
// Sending GameStructure message with fitting assignment
|
||||||
currentMessage = incomingMessages.poll(100, TimeUnit.MILLISECONDS);
|
gameStructureMessage.assignment = ParticipantType.PlayerOne;
|
||||||
|
sendMessage(player1, gameStructureMessage);
|
||||||
|
|
||||||
} catch (InterruptedException e) {
|
gameStructureMessage.assignment = ParticipantType.PlayerTwo;
|
||||||
Logger.warn("LobbyConnection thread got interrupted. Exception: " + e.getMessage());
|
sendMessage(player2, gameStructureMessage);
|
||||||
}
|
|
||||||
|
|
||||||
if (currentMessage == null) {
|
gameStructureMessage.assignment = ParticipantType.Spectator;
|
||||||
Logger.trace("Message was null, continuing");
|
broadcastToSpectators(gameStructureMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Logger.info("Entering Ingame phase");
|
||||||
|
inGame = true;
|
||||||
|
Logger.trace("Initializing lobby...");
|
||||||
|
initializeLobby();
|
||||||
|
while (inGame && active) {
|
||||||
|
Tuple<Participant, BasicMessage> currentMessage = getMessageAsync(1000);
|
||||||
|
|
||||||
|
if (!active) {
|
||||||
|
Logger.info("Lobby '{}' is terminating. Exiting...", gameID);
|
||||||
|
return;
|
||||||
|
} else if (currentMessage == null) {
|
||||||
|
Logger.trace("Message was null, continuing"); // TODO: remove for production
|
||||||
continue;
|
continue;
|
||||||
} else if (currentMessage.item2 instanceof CharacterSelectionMessage) {
|
} else if (currentMessage.item2 instanceof CharacterSelectionMessage) {
|
||||||
receiveCharacterSelection((CharacterSelectionMessage) currentMessage.item2);
|
var origin = currentMessage.item1;
|
||||||
|
var message = (CharacterSelectionMessage) currentMessage.item2;
|
||||||
|
Logger.debug("CharacterSelectionMessage sent by Player '{}' during ingame phase", origin.name);
|
||||||
|
receiveCharacterSelection(origin, message);
|
||||||
} else if (currentMessage.item2 instanceof RequestMessage) {
|
} else if (currentMessage.item2 instanceof RequestMessage) {
|
||||||
receiveRequests((RequestMessage) currentMessage.item2);
|
var origin = currentMessage.item1;
|
||||||
|
var message = (RequestMessage) currentMessage.item2;
|
||||||
|
Logger.debug("RequestMessage sent by Player '{}' during ingame phase",
|
||||||
|
origin.name);
|
||||||
|
receiveRequests(origin, message);
|
||||||
} else {
|
} else {
|
||||||
|
Logger.warn("Message that isn't of type RequestMessage or CharacacterSelectionMessage" +
|
||||||
|
"received in Lobby. This is probably a bug.");
|
||||||
|
sendError(
|
||||||
|
currentMessage.item1,
|
||||||
|
"Message couldn't be processed by the lobby, as its type was invalid");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send messages to the lobbyConnection */
|
/**
|
||||||
public void receiveMessage(Participant participant, BasicMessage message) {
|
* Tries to read the next message in the messageQueue. waits for timeoutMillis
|
||||||
this.incomingMessages.add(Tuple.of(participant, message));
|
* for a queue item
|
||||||
|
*
|
||||||
|
* @param timeoutMillis is the amount of time the method waits until continuing even though not being notified.
|
||||||
|
*/
|
||||||
|
private Tuple<Participant, BasicMessage> getMessageAsync(int timeoutMillis) {
|
||||||
|
Tuple<Participant, BasicMessage> currentMessage = null;
|
||||||
|
try {
|
||||||
|
Logger.trace("Checking for messages. Currently the amount of messages is {}",
|
||||||
|
incomingMessages.size());
|
||||||
|
|
||||||
|
Logger.trace("Polling incoming message queue");
|
||||||
|
currentMessage = incomingMessages.poll(timeoutMillis, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Logger.warn("LobbyConnection thread got interrupted. Exception: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return currentMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receiveRequests(RequestMessage message) {
|
/** Send messages to the lobbyConnection. This is thread-safe, and meant to be called from the outside. */
|
||||||
// TODO: implement this
|
public void receiveMessage(Participant origin, BasicMessage message) {
|
||||||
|
Logger.trace("Lobby '{}' received message from participant '{}'", gameID, origin.name);
|
||||||
|
this.incomingMessages.add(Tuple.of(origin, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receiveCharacterSelection(CharacterSelectionMessage message) {
|
private void receiveRequests(Participant origin, RequestMessage message) {
|
||||||
// TODO: Implement proper character selection
|
System.out.println("REMOVE THIS PRINTLN: received request message " + message.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method executed on Character selection. Hereby the local fields {@link LobbyConnection#selectionPossibilities},
|
||||||
|
* {@link LobbyConnection#playerOneSelection} and {@link LobbyConnection#playerTwoSelection} are checked and
|
||||||
|
* populated
|
||||||
|
*
|
||||||
|
* @param origin is the participant that sent the message
|
||||||
|
* @param message is the message triggering the CharacterSelection method
|
||||||
|
* @return true if CharacterSelection is now completed, and false otherwise. If the method is called outside of the
|
||||||
|
* CharacterSelection phase, true is returned, as the characterSelection is after all done
|
||||||
|
*/
|
||||||
|
private boolean receiveCharacterSelection(Participant origin, CharacterSelectionMessage message) {
|
||||||
|
if (this.characterSelectionActive) {
|
||||||
|
if (!hasRightNumberOfTrues(message.characters, 6)) {
|
||||||
|
Logger.debug("Player chose non-six number of characters, which is invalid. Sending error");
|
||||||
|
sendError(origin, "Cannot select characters as character choices wasn't 6");
|
||||||
|
} else {
|
||||||
|
switch (origin.type) {
|
||||||
|
case PlayerOne -> {
|
||||||
|
if (playerOneSelection == null) {
|
||||||
|
playerOneSelection = getChosenCharacters(
|
||||||
|
selectionPossibilities.item1,
|
||||||
|
message.characters);
|
||||||
|
Logger.info("Player 1 has selected their characters");
|
||||||
|
} else {
|
||||||
|
Logger.debug("Player 1 tried to select characters twice, sending error");
|
||||||
|
sendError(origin,
|
||||||
|
"Cannot select characters as characters were already selected"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PlayerTwo -> {
|
||||||
|
if (playerTwoSelection == null) {
|
||||||
|
Logger.trace("Checking whether there are actually 6 choices");
|
||||||
|
playerTwoSelection = getChosenCharacters(
|
||||||
|
selectionPossibilities.item1,
|
||||||
|
message.characters);
|
||||||
|
Logger.info("Player 1 has selected their characters");
|
||||||
|
} else {
|
||||||
|
Logger.debug("Player 1 tried to select characters twice, sending error");
|
||||||
|
sendError(origin,
|
||||||
|
"Cannot select characters as characters were already selected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Spectator -> {
|
||||||
|
Logger.info("Spectator sent CharacterSelectionMessage. Sending error...");
|
||||||
|
sendError(origin, "Spectators can't select characters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger.debug("Participant '{}' sent a CharacterSelectionMessage outside of " +
|
||||||
|
"the CharacterSelectionPhase, sending error", origin.name);
|
||||||
|
sendError(origin, "The character selection phase is already over");
|
||||||
|
}
|
||||||
|
return !characterSelectionActive || (playerOneSelection != null && playerTwoSelection != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CharacterProperties[] getChosenCharacters(CharacterProperties[] possibleChoices, Boolean[] choices) {
|
||||||
|
ArrayList<CharacterProperties> chosenCharacters = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
if (choices[i]) {
|
||||||
|
chosenCharacters.add(possibleChoices[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chosenCharacters.toArray(new CharacterProperties[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasRightNumberOfTrues(Boolean[] values, int expected) {
|
||||||
|
int choices = Arrays.stream(values)
|
||||||
|
.mapToInt(choice -> choice ? 1 : 0)
|
||||||
|
.sum();
|
||||||
|
if (choices == expected) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeLobby() {
|
private void initializeLobby() {
|
||||||
@ -199,8 +331,8 @@ public class LobbyConnection implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @return whether a player can join the lobby */
|
/** @return whether a player can join the lobby */
|
||||||
public boolean hasFreePlayerSpot() {
|
public boolean isFull() {
|
||||||
return !(hasPlayer1() && hasPlayer2());
|
return hasPlayer1() && hasPlayer2();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Participant getPlayer1() {
|
public Participant getPlayer1() {
|
||||||
@ -217,6 +349,7 @@ public class LobbyConnection implements Runnable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new player into the player1 slot
|
* Adds a new player into the player1 slot
|
||||||
|
*
|
||||||
* @param player is the websocket to be added
|
* @param player is the websocket to be added
|
||||||
* @return true if added successfully, and false otherwise
|
* @return true if added successfully, and false otherwise
|
||||||
*/
|
*/
|
||||||
@ -232,6 +365,7 @@ public class LobbyConnection implements Runnable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new player into the player1 slot
|
* Adds a new player into the player1 slot
|
||||||
|
*
|
||||||
* @param player is the websocket to be added
|
* @param player is the websocket to be added
|
||||||
* @return true if added successfully, and false otherwise
|
* @return true if added successfully, and false otherwise
|
||||||
*/
|
*/
|
||||||
@ -247,6 +381,7 @@ public class LobbyConnection implements Runnable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new player into either the player1, or if full, player2 slot
|
* Adds a new player into either the player1, or if full, player2 slot
|
||||||
|
*
|
||||||
* @param player is the websocket to be added
|
* @param player is the websocket to be added
|
||||||
* @return true if added successfully, and false otherwise
|
* @return true if added successfully, and false otherwise
|
||||||
*/
|
*/
|
||||||
@ -287,35 +422,65 @@ public class LobbyConnection implements Runnable {
|
|||||||
return player1 == participant || player2 == participant || spectators.contains(participant);
|
return player1 == participant || player2 == participant || spectators.contains(participant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Methods to send messages
|
||||||
|
|
||||||
|
private void sendMessage(Participant recipient, BasicMessage message) {
|
||||||
|
sendMessageCallback.accept(recipient.getConnection(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void broadcast(BasicMessage message) {
|
||||||
|
sendMessage(player1, message);
|
||||||
|
sendMessage(player2, message);
|
||||||
|
spectators.forEach(spectator -> sendMessage(spectator, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void broadcastToSpectators(BasicMessage message) {
|
||||||
|
spectators.forEach(spectator -> sendMessage(spectator, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void broadcastToAllExcept(Participant except, BasicMessage message) {
|
||||||
|
|
||||||
|
if (!except.equals(player1)) sendMessage(player1, message);
|
||||||
|
if (!except.equals(player2)) sendMessage(player2, message);
|
||||||
|
spectators.stream().filter(spectator -> !except.equals(spectator))
|
||||||
|
.forEach(spectator -> sendMessage(spectator, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendError(Participant recipient, String errorMessage) {
|
||||||
|
sendErrorCallback.accept(recipient.getConnection(), errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
// Methods to send events
|
// Methods to send events
|
||||||
|
|
||||||
public void sendEvents(Participant recipient, Event... events) {
|
public void sendEvents(Participant recipient, Event... events) {
|
||||||
MessageRelay.getInstance().sendMessage(this, recipient, events);
|
var message = new EventMessage();
|
||||||
|
message.messages = events;
|
||||||
|
|
||||||
|
sendMessage(recipient, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void broadcastEvents(Event... events) {
|
public void broadcastEvents(Event... events) {
|
||||||
// TODO: implement
|
var message = new EventMessage();
|
||||||
MessageRelay.getInstance().broadcastEvents(this, events);
|
message.messages = events;
|
||||||
|
|
||||||
|
broadcast(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void broadcastToAllExcept(Participant except, Event... events) {
|
public void broadcastToAllExcept(Participant except, Event... events) {
|
||||||
// TODO: implement
|
var message = new EventMessage();
|
||||||
var messageRelayInstance = MessageRelay.getInstance();
|
message.messages = events;
|
||||||
if (except.type == ParticipantType.Spectator) {
|
|
||||||
spectators.stream()
|
broadcastToAllExcept(except, message);
|
||||||
.filter(spectator -> !spectator.equals(except))
|
|
||||||
.forEach(spectator -> messageRelayInstance.sendMessage(this, spectator, events));
|
|
||||||
messageRelayInstance.sendMessage(this, player1, events);
|
|
||||||
messageRelayInstance.sendMessage(this, player2, events);
|
|
||||||
} else {
|
|
||||||
messageRelayInstance.sendMessage(this, except.equals(player1) ? player2 : player1, events);
|
|
||||||
spectators.forEach(spectator -> messageRelayInstance.sendMessage(this, spectator, events));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Kills all connections to client, as well as the lobby */
|
/** Kills all connections to client, as well as the lobby. Notifying the thread has to be done separately. */
|
||||||
public void terminateConnection() {
|
public void terminateConnection() {
|
||||||
// TODO: implement this
|
Logger.debug("Setting termination flag for lobby '{}'", gameID);
|
||||||
|
this.active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -323,12 +488,12 @@ public class LobbyConnection implements Runnable {
|
|||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
LobbyConnection that = (LobbyConnection) o;
|
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);
|
return characterSelectionActive == that.characterSelectionActive && 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(lobby, gameID, player1, player2, characterSelection, inGame, spectators, incomingMessages);
|
return Objects.hash(lobby, gameID, player1, player2, characterSelectionActive, inGame, spectators, incomingMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -338,7 +503,7 @@ public class LobbyConnection implements Runnable {
|
|||||||
", gameID='" + gameID + '\'' +
|
", gameID='" + gameID + '\'' +
|
||||||
", player1=" + player1 +
|
", player1=" + player1 +
|
||||||
", player2=" + player2 +
|
", player2=" + player2 +
|
||||||
", characterSelection=" + characterSelection +
|
", characterSelection=" + characterSelectionActive +
|
||||||
", inGame=" + inGame +
|
", inGame=" + inGame +
|
||||||
", spectators=" + spectators +
|
", spectators=" + spectators +
|
||||||
", incomingMessages=" + incomingMessages +
|
", incomingMessages=" + incomingMessages +
|
||||||
|
Loading…
Reference in New Issue
Block a user