feat: implemented lobby creation and general management
This commit is contained in:
		@ -2,20 +2,28 @@ package uulm.teamname.marvelous.server.lobbymanager;
 | 
			
		||||
 | 
			
		||||
import org.java_websocket.WebSocket;
 | 
			
		||||
import org.tinylog.Logger;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.messages.BasicMessage;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.messages.RoleEnum;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.messages.client.PlayerReadyMessage;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.messages.server.GameAssignmentMessage;
 | 
			
		||||
import uulm.teamname.marvelous.server.Server;
 | 
			
		||||
import uulm.teamname.marvelous.server.lobby.Lobby;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.ThreadLocalRandom;
 | 
			
		||||
import java.util.function.BiConsumer;
 | 
			
		||||
 | 
			
		||||
public class LobbyManager {
 | 
			
		||||
    private final HashMap<Participant, LobbyConnection> lobbies;
 | 
			
		||||
    private final HashMap<String, LobbyConnection> resourceDescriptorToLobby;
 | 
			
		||||
    private final BiConsumer<Participant, BasicMessage> sendMessageCallback;
 | 
			
		||||
    private String localResourceDescriptor;
 | 
			
		||||
 | 
			
		||||
    public LobbyManager() {
 | 
			
		||||
    public LobbyManager(BiConsumer<Participant, BasicMessage> sendMessageCallback) {
 | 
			
		||||
        this.sendMessageCallback = sendMessageCallback;
 | 
			
		||||
        this.lobbies = new HashMap<>();
 | 
			
		||||
        this.resourceDescriptorToLobby = new HashMap<>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -30,10 +38,86 @@ public class LobbyManager {
 | 
			
		||||
        Logger.info("Assigning lobby to player '{}'", playerName);
 | 
			
		||||
 | 
			
		||||
        var resourceDescriptor = connection.getResourceDescriptor();
 | 
			
		||||
        if (resourceDescriptor.length() == 0) {
 | 
			
		||||
            // TODO: generate new ResourceDescriptor
 | 
			
		||||
        if (resourceDescriptor == null || resourceDescriptor.length() == 0) {
 | 
			
		||||
            Logger.trace("Resource descriptor is null, getting local one");
 | 
			
		||||
            resourceDescriptor = getLocalResourceDescriptor();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LobbyConnection targetedLobby = resourceDescriptorToLobby.get(resourceDescriptor);
 | 
			
		||||
 | 
			
		||||
        if (targetedLobby == null || !targetedLobby.hasFreePlayerSpot()) {
 | 
			
		||||
            Logger.info("Lobby '{}' is non-existent, initializing lobby...", resourceDescriptor);
 | 
			
		||||
            targetedLobby = initializeNewLobby(resourceDescriptor);
 | 
			
		||||
            Logger.info("Adding mapping from resourceDescriptor {} to new lobby...", resourceDescriptor);
 | 
			
		||||
            synchronized (resourceDescriptorToLobby) {
 | 
			
		||||
                resourceDescriptorToLobby.put(resourceDescriptor, targetedLobby);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Logger.trace("Obtaining lock on targetedLobby '{}'", targetedLobby.gameID);
 | 
			
		||||
        synchronized (targetedLobby) {
 | 
			
		||||
 | 
			
		||||
            Participant participant;
 | 
			
		||||
 | 
			
		||||
            Logger.debug("Assigning participant to lobby");
 | 
			
		||||
            if (message.role.equals(RoleEnum.SPECTATOR)) {
 | 
			
		||||
                Logger.trace("Generating new participant with type spectator");
 | 
			
		||||
                participant = new Participant(connection, ParticipantType.Spectator, playerName);
 | 
			
		||||
                targetedLobby.addSpectator(participant);
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
                Logger.trace("Checking whether Player1 or Player2 spot is free in lobby '{}'",
 | 
			
		||||
                        targetedLobby.gameID);
 | 
			
		||||
                var participantType =
 | 
			
		||||
                        !targetedLobby.hasPlayer1()
 | 
			
		||||
                                ? ParticipantType.PlayerOne
 | 
			
		||||
                                : ParticipantType.PlayerTwo;
 | 
			
		||||
 | 
			
		||||
                Logger.trace("Generating new participant with type {}", participantType);
 | 
			
		||||
                participant = new Participant(connection, participantType, playerName);
 | 
			
		||||
                if (!targetedLobby.addPlayer(participant)) {
 | 
			
		||||
                    Logger.warn("Participant could not be added to lobby. This is probably a bug.");
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Logger.trace("Adding mapping from participant '{}' to lobby {}",
 | 
			
		||||
                    participant.name, targetedLobby.gameID);
 | 
			
		||||
            synchronized (lobbies) {
 | 
			
		||||
                lobbies.put(participant, targetedLobby);
 | 
			
		||||
            }
 | 
			
		||||
            Logger.trace("Relaying message to newly created Lobby");
 | 
			
		||||
            targetedLobby.receiveMessage(participant, message);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private LobbyConnection initializeNewLobby(String gameID) {
 | 
			
		||||
        return new LobbyConnection(gameID, sendMessageCallback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the local resource descriptor if it is pointing to a not yet filled lobby (joinable lobby), or
 | 
			
		||||
     * generates a new one if the lobby described by the resourceDescriptor is full already
 | 
			
		||||
     */
 | 
			
		||||
    private String getLocalResourceDescriptor() {
 | 
			
		||||
        Logger.trace("Getting local resourceDescriptor. Currently this is '{}'", localResourceDescriptor);
 | 
			
		||||
        if (localResourceDescriptor == null) {
 | 
			
		||||
            Logger.trace("local resourceDescriptor is null. Initializing local resourceDescriptor...");
 | 
			
		||||
            localResourceDescriptor = RandomWordGenerator.generateTwoWords();
 | 
			
		||||
            Logger.debug("Local resoucrceDescriptor initialized as '{}'", localResourceDescriptor);
 | 
			
		||||
        }
 | 
			
		||||
        var lobby = resourceDescriptorToLobby.get(localResourceDescriptor);
 | 
			
		||||
        if (lobby != null) {
 | 
			
		||||
            if (!lobby.hasFreePlayerSpot()) {
 | 
			
		||||
                Logger.debug("Lobby is full, generating new local resourceDescriptor");
 | 
			
		||||
                while (resourceDescriptorToLobby.get(localResourceDescriptor) != null) {
 | 
			
		||||
                    localResourceDescriptor = RandomWordGenerator.generateTwoWords();
 | 
			
		||||
                }
 | 
			
		||||
                Logger.debug("New resourceDescriptor is '{}'", localResourceDescriptor);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Logger.trace("Returning local resourceDescriptor");
 | 
			
		||||
        return localResourceDescriptor;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -42,4 +126,13 @@ public class LobbyManager {
 | 
			
		||||
    Map<Participant, LobbyConnection> getLobbies() {
 | 
			
		||||
        return lobbies;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A method to obtain the resourceDescriptorToLobby HashMap.
 | 
			
		||||
     * Meant for testing, and shouldn't be used anywhere else.
 | 
			
		||||
     */
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    Map<String, LobbyConnection> getResourceDescriptorToLobby() {
 | 
			
		||||
        return resourceDescriptorToLobby;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,195 @@
 | 
			
		||||
package uulm.teamname.marvelous.server.lobbymanager;
 | 
			
		||||
 | 
			
		||||
import org.java_websocket.WebSocket;
 | 
			
		||||
import org.junit.jupiter.api.BeforeEach;
 | 
			
		||||
import org.junit.jupiter.api.DisplayName;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.mockito.MockedStatic;
 | 
			
		||||
import org.mockito.Mockito;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.config.FieldType;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.config.ScenarioConfig;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.messages.ParticipantType;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.messages.RoleEnum;
 | 
			
		||||
import uulm.teamname.marvelous.gamelibrary.messages.client.PlayerReadyMessage;
 | 
			
		||||
import uulm.teamname.marvelous.server.Server;
 | 
			
		||||
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import static org.mockito.Mockito.*;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.*;
 | 
			
		||||
import static org.assertj.core.api.Assertions.*;
 | 
			
		||||
 | 
			
		||||
class LobbyManagerTest {
 | 
			
		||||
 | 
			
		||||
    LobbyManager manager;
 | 
			
		||||
    WebSocket player1;
 | 
			
		||||
    WebSocket player2;
 | 
			
		||||
    WebSocket player3;
 | 
			
		||||
    WebSocket spectator;
 | 
			
		||||
 | 
			
		||||
    PlayerReadyMessage playerReady;
 | 
			
		||||
    PlayerReadyMessage spectatorReady;
 | 
			
		||||
    PlayerReadyMessage aiReady;
 | 
			
		||||
 | 
			
		||||
    Participant player1Participant;
 | 
			
		||||
    Participant player2Participant;
 | 
			
		||||
    Participant player3Participant;
 | 
			
		||||
 | 
			
		||||
    @BeforeEach
 | 
			
		||||
    void beforeEach() {
 | 
			
		||||
        manager = new LobbyManager();
 | 
			
		||||
 | 
			
		||||
        player1 = mock(WebSocket.class);
 | 
			
		||||
        player2 = mock(WebSocket.class);
 | 
			
		||||
        player3 = mock(WebSocket.class);
 | 
			
		||||
        spectator = mock(WebSocket.class);
 | 
			
		||||
 | 
			
		||||
        playerReady = new PlayerReadyMessage();
 | 
			
		||||
        playerReady.role = RoleEnum.PLAYER;
 | 
			
		||||
 | 
			
		||||
        spectatorReady = new PlayerReadyMessage();
 | 
			
		||||
        spectatorReady.role = RoleEnum.SPECTATOR;
 | 
			
		||||
 | 
			
		||||
        aiReady = new PlayerReadyMessage();
 | 
			
		||||
        aiReady.role = RoleEnum.KI;
 | 
			
		||||
 | 
			
		||||
        player1Participant = new Participant(
 | 
			
		||||
                player1,
 | 
			
		||||
                ParticipantType.PlayerOne,
 | 
			
		||||
                "AwesomePlayer");
 | 
			
		||||
        player2Participant = new Participant(
 | 
			
		||||
                player2,
 | 
			
		||||
                ParticipantType.PlayerTwo,
 | 
			
		||||
                "MoreAwesomePlayer");
 | 
			
		||||
        player3Participant = new Participant(
 | 
			
		||||
                player3,
 | 
			
		||||
                ParticipantType.PlayerOne,
 | 
			
		||||
                "AwesomestAwesomePlayer");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void lobbyManagerGetsCreatedEmpty() {
 | 
			
		||||
        assertThat(manager.getLobbies()).isEmpty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("When a participant is added, a new Lobby is created")
 | 
			
		||||
    void lobbyCreationTest() {
 | 
			
		||||
        var message = new PlayerReadyMessage();
 | 
			
		||||
        message.role = RoleEnum.PLAYER;
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getLobbies()).isEmpty();
 | 
			
		||||
 | 
			
		||||
        when(player1.getResourceDescriptor()).thenReturn("/ResourcesFTW");
 | 
			
		||||
 | 
			
		||||
        manager.assignLobbyToParticipant(player1, "AwesomePlayer", message);
 | 
			
		||||
 | 
			
		||||
        Participant player1Participant = new Participant(player1, ParticipantType.PlayerOne, "AwesomePlayer");
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getLobbies()).containsOnlyKeys(player1Participant);
 | 
			
		||||
        assertThat(manager.getLobbies().get(player1Participant)).isNotNull();
 | 
			
		||||
        assertThat(manager.getLobbies().get(player1Participant).getPlayer1()).isEqualTo(player1Participant);
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby()).containsOnlyKeys("/ResourcesFTW");
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby().get("/ResourcesFTW"))
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player1Participant))
 | 
			
		||||
                .isNotNull();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("when two participants with same ResourceDescriptor connect, they get assigned to the same lobby")
 | 
			
		||||
    void twoParticipantsSameLobbyTest() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            when(player1.getResourceDescriptor()).thenReturn("/fancyResourceDescriptor");
 | 
			
		||||
            when(player2.getResourceDescriptor()).thenReturn("/fancyResourceDescriptor");
 | 
			
		||||
 | 
			
		||||
            manager.assignLobbyToParticipant(player1, "AwesomePlayer", playerReady);
 | 
			
		||||
            manager.assignLobbyToParticipant(player2, "MoreAwesomePlayer", playerReady);
 | 
			
		||||
 | 
			
		||||
            Participant player1Participant = new Participant(
 | 
			
		||||
                    player1, ParticipantType.PlayerOne, "AwesomePlayer");
 | 
			
		||||
 | 
			
		||||
            Participant player2Participant = new Participant(
 | 
			
		||||
                    player2, ParticipantType.PlayerTwo, "MoreAwesomePlayer");
 | 
			
		||||
 | 
			
		||||
            assertThat(manager.getLobbies()).containsOnlyKeys(player1Participant, player2Participant);
 | 
			
		||||
            assertThat(manager.getLobbies().get(player1Participant))
 | 
			
		||||
                    .isEqualTo(manager.getLobbies().get(player2Participant))
 | 
			
		||||
                    .isNotNull();
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby()).containsOnlyKeys("/fancyResourceDescriptor");
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby().get("/fancyResourceDescriptor"))
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player1Participant))
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player2Participant))
 | 
			
		||||
                .isNotNull();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("On Participant without ResourceDescriptor random lobby gets generated")
 | 
			
		||||
    void randomLobbyTest() {
 | 
			
		||||
        when(player1.getResourceDescriptor()).thenReturn(null);
 | 
			
		||||
 | 
			
		||||
        manager.assignLobbyToParticipant(player1, "AwesomePlayer", playerReady);
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getLobbies()).hasSize(1);
 | 
			
		||||
        assertThat(manager.getLobbies().get(player1Participant)).isNotNull();
 | 
			
		||||
        assertThat(manager.getLobbies().get(player1Participant).gameID).isNotNull();
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby()).hasSize(1);
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby().get(manager.getLobbies().get(player1Participant).gameID))
 | 
			
		||||
                .isNotNull()
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player1Participant));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("When two people without ResourceDescriptor join, they get allocated to the same lobby")
 | 
			
		||||
    void randomLobbyTwoPlayerTest() {
 | 
			
		||||
        when(player1.getResourceDescriptor()).thenReturn(null);
 | 
			
		||||
        when(player2.getResourceDescriptor()).thenReturn(null);
 | 
			
		||||
        manager.assignLobbyToParticipant(player1, "AwesomePlayer", playerReady);
 | 
			
		||||
        manager.assignLobbyToParticipant(player2, "MoreAwesomePlayer", playerReady);
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getLobbies()).hasSize(2);
 | 
			
		||||
        assertThat(manager.getLobbies().get(player1Participant))
 | 
			
		||||
                .isNotNull()
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player2Participant));
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby()).hasSize(1);
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby().get(manager.getLobbies().get(player1Participant).gameID))
 | 
			
		||||
                .isNotNull()
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player1Participant))
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player2Participant));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @DisplayName("When three players connect, a new lobby gets created")
 | 
			
		||||
    void randomLobbyThreePlayerTest() {
 | 
			
		||||
        when(player1.getResourceDescriptor()).thenReturn(null);
 | 
			
		||||
        when(player2.getResourceDescriptor()).thenReturn(null);
 | 
			
		||||
        when(player3.getResourceDescriptor()).thenReturn(null);
 | 
			
		||||
        manager.assignLobbyToParticipant(player1, "AwesomePlayer", playerReady);
 | 
			
		||||
        manager.assignLobbyToParticipant(player2, "MoreAwesomePlayer", playerReady);
 | 
			
		||||
        manager.assignLobbyToParticipant(player3, "AwesomestAwesomePlayer", playerReady);
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getLobbies()).hasSize(3);
 | 
			
		||||
        assertThat(manager.getLobbies().get(player1Participant))
 | 
			
		||||
                .isNotNull()
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player2Participant))
 | 
			
		||||
                .isNotEqualTo(manager.getLobbies().get(player3Participant));
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getLobbies().get(player3Participant)).isNotNull();
 | 
			
		||||
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby()).hasSize(2);
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby().get(manager.getLobbies().get(player1Participant).gameID))
 | 
			
		||||
                .isNotNull()
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player1Participant))
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player2Participant))
 | 
			
		||||
                .isNotEqualTo(manager.getLobbies().get(player3Participant));
 | 
			
		||||
        assertThat(manager.getResourceDescriptorToLobby().get(manager.getLobbies().get(player3Participant).gameID))
 | 
			
		||||
                .isNotNull()
 | 
			
		||||
                .isEqualTo(manager.getLobbies().get(player3Participant));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user