327 lines
12 KiB
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);
|
|
}
|
|
}
|