Cluster Sample

Summary

This sample creates a lobby which manages game instances. Players connect to the lobby and are sent to an available game instance. The game selects a group of winners and then spawns a new game instance for those players. Other players are sent back to the lobby.

Requirements

Define consts

In 'ReactorScripts/Common', create a Consts.cs file to hold const values we will use on the client and server. The consts are ids for properties and RPCs. Add the following to your script: Consts.cs

public class Prop
{
    public const uint NAME = 0;
}

public class RPC
{
    public const uint TOKEN = 0;
    public const uint GAME_OVER = 1;
    public const uint MOVE_TO_ROOM = 2;
    public const uint RETURN_TO_LOBBY = 3;
    public const uint PLAYER_DATA = 4;
    public const uint ACKNOWLEDGE_PLAYER_DATA = 5;
}

Create the lobby scene

  1. Create a new scene and name it 'Lobby'.
  2. Create a game object named 'Lobby Room' and add a ksRoomType.
  3. Save the scene.
  4. Add the scene to the build settings with 'File->Build Settings->Add Open Scenes'.

Create the game scene

  1. Save your scene and create a new scene named 'Game'.
  2. Create a game object named 'Game Room' and add a ksRoomType.
  3. Save the scene.
  4. Add the scene to the build settings with 'File->Build Settings->Add Open Scenes'.

Create a room launcher script

The room launcher script will be used to launch rooms and send players to those rooms when they are ready.

  1. Open the 'Lobby' scene.
  2. Add a new server room script to the 'Lobby Room' game object and name it 'sRoomLauncher'.

sRoomLauncher.cs

using System.Collections.Generic;
using KS.Reactor.Server;
using KS.Reactor;

public class sRoomLauncher : ksServerRoomScript
{
    private ksAsyncResult<ksRoomInfo> m_task;
    private List<ksIServerPlayer> m_players = new List<ksIServerPlayer>();

    public bool IsLaunchingRoom
    {
        get { return m_task != null; }
    }

    public override void Initialize()
    {
        Room.OnUpdate[0] += Update;
    }

    public override void Detached()
    {
        Room.OnUpdate[0] -= Update;
    }

    private void Update()
    {
        // Check if the launch room task is complete
        if (!Cluster.IsConnected || m_task == null || !m_task.IsCompleted)
        {
            return;
        }
        if (!string.IsNullOrEmpty(m_task.Error))
        {
            ksLog.Error(this, "Error starting game: " + m_task.Error);
        }
        else
        {
            SendPlayers(m_task.Result);
        }
        m_task = null;
    }

    // Launches a room and sends players in the given player list to that room once it is ready
    public void LaunchRoom(IEnumerable<ksIServerPlayer> players, string name, string scene, string roomType)
    {
        if (Cluster.IsConnected && !IsLaunchingRoom)
        {
            m_players.Clear();
            m_players.AddRange(players);
            ksLog.Info(this, "Starting room " + scene);
            m_task = Cluster.StartRoom(name, scene, roomType);
        }
    }

    // Sends players to the given room
    private void SendPlayers(ksRoomInfo roomInfo)
    {
        // Convert room info to json and send it to players
        Room.CallRPC(m_players, RPC.MOVE_TO_ROOM, roomInfo.ToJSON().Print());
    }
}

Create a lobby script

The lobby starts a game room and sends players to it once there are at least 10 players or after 30 seconds.

  1. Add a new server room script to the 'Lobby Room' game object and name it 'sLobby'.

sLobby.cs

using System.Collections.Generic;
using KS.Reactor.Server;
using KS.Reactor;

public class sLobby : ksServerRoomScript
{
    private sRoomLauncher m_roomLauncher;
    private float m_timer = 0f;

    public override void Initialize()
    {
        Room.OnUpdate[0] += Update;
        m_roomLauncher = Scripts.Get<sRoomLauncher>();
    }

    public override void Detached()
    {
        Room.OnUpdate[0] -= Update;
    }

    private void Update()
    {
        // Do nothing if we aren't connected to the cluster, we are already launching a room, or no players are
        // connected.
        if (!Cluster.IsConnected || m_roomLauncher.IsLaunchingRoom || Room.ConnectedPlayerCount == 0)
        {
            return;
        }
        // Start a game room when there are 10 or more players and at least 2 seconds have passed since the last game,
        // or 30 seconds have passed.
        if (m_timer < 2f)
        {
            m_timer += Time.Delta;
            return;
        }
        m_timer += Time.Delta;
        if (Room.Players.Count >= 10 || m_timer >= 30f)
        {
            // "Game1" is the name of the room (can be anything you want).
            // "Game" is the name of the scene to launch.
            // "Game Room" is the name of the room type game object in the scene to launch.
            m_roomLauncher.LaunchRoom(Room.Players, "Game1", "Game", "Game Room");
            m_timer = 0f;
        }
    }
}

Create a connect script

The connect script has a static ksRoomInfo field it will connect to from Start if it is set, otherwise it connects to a local or online server depending on how it is configured.

  1. Add a new Monobehaviour to the 'Lobby Room' game object and name it 'Connect' Connect.cs
using System.Collections.Generic;
using UnityEngine;
using KS.Reactor;
using KS.Reactor.Client;
using KS.Reactor.Client.Unity;

public class Connect : MonoBehaviour
{
    public bool UseLiveServer = false;
    private ksRoom m_room;

    public static ksRoomInfo RoomInfo;

    void Start()
    {
        // If RoomInfo is set, connect to that room.
        if (RoomInfo != null)
        {
            ConnectToServer(RoomInfo);
            RoomInfo = null;
        }
        else if (UseLiveServer)
        {
            ksReactor.GetServers(OnGetServers);
        }
        else
        {
            ConnectToServer(GetComponent<ksRoomType>().GetRoomInfo());
        }
    }

    private void OnGetServers(List<ksRoomInfo> roomList, string error)
    {
        if (error != null)
        {
            ksLog.Error("Error fetching servers. " + error);
            return;
        }

        if (roomList == null || roomList.Count == 0)
        {
            ksLog.Warning("No servers found.");
            return;
        }

        ConnectToServer(roomList[0]);
    }

    private void ConnectToServer(ksRoomInfo roomInfo)
    {
        m_room = new ksRoom(roomInfo);
        m_room.OnConnect += OnConnect;
        m_room.OnDisconnect += OnDisconnect;
        m_room.Connect();
    }

    private void OnConnect(ksBaseRoom.ConnectStatus status, ksAuthenticationResult result)
    {
        ksLog.Info("Connect status " + status);
    }

    private void OnDisconnect(ksBaseRoom.ConnectStatus status)
    {
        ksLog.Info("Disconnect status " + status);
    }
}

Create a client room script

The client room responds to server RPCs to switch scenes and connect to new rooms.

  1. Add a new client room script to the 'Lobby Room' game object and name it 'cRoom'.
  2. Save the scene.

cRoom.cs

using UnityEngine.SceneManagement;
using KS.Reactor.Client.Unity;
using KS.Reactor;

public class cRoom : ksRoomScript
{
    [ksRPC(RPC.MOVE_TO_ROOM)]
    private void OnMoveToRoom(string jsonStr)
    {
        // Disconnect from the current room
        ksReactor.Service.LeaveRoom(Room);
        // Parse new room info from json
        Connect.RoomInfo = ksRoomInfo.FromJSON(ksJSON.Parse(jsonStr));
        // Load the scene for the new room
        SceneManager.LoadScene(Connect.RoomInfo.Scene);
    }
}

Add scripts to the game room

  1. Open the 'Game' scene.
  2. Add a 'Connect' script to the 'Game Room' game object.
  3. Add a 'cRoom' script to the 'Game Room' game object.
  4. Save the scene.

Run and connect to the local cluster

  1. Load the Lobby scene.
  2. Build configs (CTRL+F2).
  3. Click 'Reactor->Launch Local Cluster' to bring up the local cluster window.
  4. Set the Launch Scene to 'Lobby'. The Launch Room should say 'Lobby Room'. Leave the other settings as the defaults.
  5. Click 'Launch Local Cluster'.
  6. The local cluster should now be running and the local server logs panel will show you the cluster logs. You can use the drop down in the log panel to view logs for the rooms. The first entry after the cluster should be for the lobby. Select it to view the lobby logs.
  7. Make sure 'Use Live Server' is unchecked when inspecting the 'Lobby Room' 'Connect' script.
  8. Enter play mode. You will connect to the lobby.
  9. After 30 seconds, the lobby server logs should say 'Starting room Game'.
  10. Once the game room is started, you will load the Game scene and connect to the game room.
  11. Stop the cluster opening the local cluster window ('Reactor->Launch Local Cluster') and clicking 'Stop Local Cluster'.

Create a game room script

The game room randomly chooses winners and losers. If there are any winners, it launches a new game room and sends the winners to the new room. Losers are sent back to the lobby. The game room shutsdown after the game is finished and players are sent to the next room, or if no players are connected for 60 seconds.

  1. Load the Game scene.
  2. Add an 'sRoomLauncher' to the 'Game Room' game object.
  3. Add a new server room script to the 'Game Room' game object and name it 'sGameRoom'.

sGameRoom.cs

using System.Collections.Generic;
using KS.Reactor.Server;
using KS.Reactor;

public class sGameRoom : ksServerRoomScript
{
    private ksRandom m_rand = new ksRandom();
    private sRoomLauncher m_roomLauncher;
    private float m_gameTimer = 0f;
    private float m_shutdownTimer = 0f;
    private bool m_gameOver = false;

    public override void Initialize()
    {
        // Setting IsPublic to false prevents this room from appearing in the server list when clients call
        // ksReactor.GetServers.
        Room.Info.IsPublic = false;
        Room.OnUpdate[0] += Update;
        m_roomLauncher = Scripts.Get<sRoomLauncher>();
    }

    public override void Detached()
    {
        Room.OnUpdate[0] -= Update;
    }

    private void Update()
    {
        if (m_gameOver)
        {
            if (!m_roomLauncher.IsLaunchingRoom)
            {
                // Stop the room one second after the game is over and the next game (if there is one) is started.
                m_shutdownTimer += Time.Delta;
                if (m_shutdownTimer >= 1f)
                {
                    Room.ShutDown();
                }
            }
            return;
        }

        if (Room.ConnectedPlayerCount <= 0)
        {
            // Stop the room if no players are connected for 60s.
            m_shutdownTimer += Time.Delta;
            if (m_shutdownTimer >= 60f)
            {
                Room.ShutDown();
            }
            return;
        }
        m_shutdownTimer = 0f;

        // Choose random winners after 10 seconds.
        if (m_gameTimer < 10f)
        {
            m_gameTimer += Time.Delta;
            if (m_gameTimer >= 10f)
            {
                List<ksIServerPlayer> winners = new List<ksIServerPlayer>();
                List<ksIServerPlayer> losers = new List<ksIServerPlayer>();
                // Each player has a 50-50 chance of being a winner of loser.
                foreach (ksIServerPlayer player in Room.Players)
                {
                    if (m_rand.Next(2) == 0)
                    {
                        winners.Add(player);
                    }
                    else
                    {
                        losers.Add(player);
                    }
                }
                FinishGame(winners, losers);
            }
        }
    }

    private void FinishGame(List<ksIServerPlayer> winners, List<ksIServerPlayer> losers)
    {
        m_gameOver = true;
        m_shutdownTimer = 0f;
        // Tell the winners they won.
        Room.CallRPC(winners, RPC.GAME_OVER, true);
        // Tell the losers they lost.
        Room.CallRPC(losers, RPC.GAME_OVER, false);

        if (winners.Count <= 0)
        {
            // Send all players back to the lobby.
            Room.CallRPC(RPC.RETURN_TO_LOBBY);
        }
        else
        {
            // Send losers back to the lobby.
            Room.CallRPC(losers, RPC.RETURN_TO_LOBBY);
            // Start the next game and send the winners to it.
            m_roomLauncher.LaunchRoom(winners, "Game2", "Game", "Game Room");
        }
    }
}

Handle game over and return to lobby RPCs in the client room script

  1. Open 'cRoom.cs'.
  2. Add a OnGameOver function to log whether you win or lose.
  3. Add a OnReturnToLobby function to return to the lobby.

cRoom.cs

public class cRoom : ksRoomScript
{
    ...
    [ksRPC(RPC.GAME_OVER)]
    private void OnGameOver(bool isWinner)
    {
        if (isWinner)
        {
            ksLog.Info(this, "You Win!");
        }
        else
        {
            ksLog.Info(this, "You Lose!");
        }
    }

    [ksRPC(RPC.RETURN_TO_LOBBY)]
    private void OnReturnToLobby()
    {
        ksReactor.Service.LeaveRoom(Room);
        SceneManager.LoadScene("Lobby");
    }
}

Run and connect to the local cluster

  1. Save the scene.
  2. Build configs (CTRL+F2).
  3. Open the 'Lobby' scene.
  4. Launch the local cluster with the same settings as before.
  5. Make sure 'Use Live Server' is unchecked when inspecting the 'Lobby Room' 'Connect' script.
  6. Enter play mode. You will connect to the lobby.
  7. After 30 seconds, the lobby server logs should say 'Starting room Game'.
  8. One the game room is started, you will load the 'Game' scene and connect to the game room.
  9. After 10 seconds in the game room, you will randomly win or lose and see a 'You Win!' or 'You Lose!' message in the Unity logs. If you win, you will reload the scene and connect to a new game room. If you lose, you will load the Lobby scene and reconnect to the lobby.
  10. Stop the local cluster.

Create a player script

The player script has a token field used to identify and authenticate players when they move between rooms.

  1. Add a new server player script to the 'Lobby Room' game object and name it 'sPlayer'.

sPlayer.cs

using KS.Reactor.Server;

public class sPlayer : ksServerPlayerScript
{
    public long Token;
}
  1. Save the scene.
  2. Open the 'Game' scene.
  3. Add the 'sPlayer' script to the 'Game Room' game object and save the scene.

Authenticate players and add bots in the lobby script

  1. Open 'sLobby.cs'.
  2. Add a ksRandom field.
  3. Add a HashSet field to store tokens.
  4. Add a GenerateToken function that generates a unique token.
  5. Add PlayerJoin and PlayerLeave functions. The PlayerJoin function assigns a token to the player and adds it to the token set. The PlayerLeave function removes it from the set so it can be reused.
  6. Add an Authenticate fuction that sets the player's name to a value provided by the client.
  7. Modify the Update function to add a virtual player (bot) every 2 seconds.

sLobby.cs

public class sLobby : ksServerRoomScript
{
    ...
    private ksRandom m_rand = new ksRandom();
    private HashSet<long> m_tokens = new HashSet<long>();

    public override void Initialize()
    {
        ...
        Room.OnAuthenticate += Authenticate;
        Room.OnPlayerJoin += PlayerJoin;
        Room.OnPlayerLeave += PlayerLeave;
    }

    public override void Detached()
    {
        ...
        Room.OnAuthenticate -= Authenticate;
        Room.OnPlayerJoin -= PlayerJoin;
        Room.OnPlayerLeave -= PlayerLeave;
    }

    private async Task<ksAuthenticationResult> Authenticate(ksIServerPlayer player, ksMultiType[] args, 
        CancellationToken cancellationToken)
    {
        if (args.Length != 1)
        {
            // Returning a non-zero result means authentication failed.
            return new ksAuthenticationResult(1);
        }
        // Set the name to the value provided by the client. You can do your own validation here.
        player.Properties[Prop.NAME] = args[0];
        // We await a completed Task to prevent a compiler warning that the async method never awaits anything and
        // will be completed synchronously.
        return await Task.FromResult(new ksAuthenticationResult(0));
    }

    private void Update()
    {
        if (!Cluster.IsConnected || m_roomLauncher.IsLaunchingRoom || Room.ConnectedPlayerCount == 0)
        {
            return;
        }
        if (Room.Time.Frame % (60 * 2) == 0)
        {
            string name = "bot" + m_rand.Next(1000);
            // Create a virtual player. The parameter 'name' is passed to the Authenticate function.
            Room.CreateVirtualPlayer(name);
        }
        ...
    }

    private void PlayerJoin(ksIServerPlayer player)
    {
        ksLog.Info(this, player.Properties[Prop.NAME] + " joined");
        if (!player.IsVirtual)
        {
            // Generate a unique token for this player.
            long token = GenerateToken();
            player.Scripts.Get<sPlayer>().Token = token;
            // Send the token to the player.
            Room.CallRPC(player, RPC.TOKEN, token);
        }
    }

    private void PlayerLeave(ksIServerPlayer player)
    {
        ksLog.Info(this, player.Properties[Prop.NAME] + " left");
        if (!player.IsVirtual)
        {
            // Remove the token so it can be reused.
            m_tokens.Remove(player.Scripts.Get<sPlayer>().Token);
        }
    }

    private long GenerateToken()
    {
        long token = 0;
        do
        {
            unchecked
            {
                token = (uint)m_rand.Next();
                token <<= 32;
                token += (uint)m_rand.Next();
            }
        } while (!m_tokens.Add(token) && token != 0);
        return token;
    }
}

Send a username or token when connecting

  1. Open 'Connect.cs'.
  2. Add an optional token parameter to ConnectToServer. If a token is non-zero, pass it to Room.Connect. Otherwise pass a username.
  3. Pass the token to ConnectToServer in the Start function if the RoomInfo is set.

Connect.cs

public class Connect : MonoBehaviour
{
    ...
    public static long Token;

    void Start()
    {
        // If RoomInfo is set, connect to that room.
        if (RoomInfo != null)
        {
            ConnectToServer(RoomInfo, Token);
            RoomInfo = null;
        }
        ...
    }
    ...
    private void ConnectToServer(ksRoomInfo roomInfo, long token = 0)
    {
        m_room = new ksRoom(roomInfo);
        m_room.OnConnect += OnConnect;
        m_room.OnDisconnect += OnDisconnect;
        // The parameters passed to Connect will be passed to the Authenticate function on the server.
        if (token != 0)
        {
            // If token is non-zero, we are connecting to a game room and send our token to authenticate and identify
            // us.
            m_room.Connect(token);
        }
        else
        {
            // We are connecting to a lobby. "MyName" is our username. You can change this to anything.
            m_room.Connect("MyName");
        }
    }
}

Set the token in the client room script

  1. Open 'cRoom.cs'.
  2. Add an OnSetToken function that sets the token.

cRoom.cs

public class cRoom : ksRoomScript
{
    [ksRPC(RPC.TOKEN)]
    private void OnSetToken(long token)
    {
        Connect.Token = token;
    }
    ...
}

Use cluster RPCs to send player data to new rooms

  1. Open 'sRoomLauncher.cs'.
  2. Add a private ksRoomInfo field.
  3. Change the IsLaunchingRoom property to also check if that the ksRoomInfo is not null.
  4. In the Update function, when the task completes, instead of sending players to the new room, use a cluster RPC to send player data to the new room that it will use to authenticate players, and store the room info.
  5. In the LaunchRoom function, don't launch the room if all the players being sent to the room are bots.
  6. In the SendPlayers function, disconnect virtual players (bots).
  7. Add an OnAcknowledgePlayerData that sends the players to the new room when it acknowledges it received the player data.

sRoomLauncher.cs

public class sRoomLauncher : ksServerRoomScript
{
    ...
    private ksRoomInfo m_roomInfo;

    public bool IsLaunchingRoom
    {
        get { return m_task != null || m_roomInfo != null; }
    }
    ...
    public override void Update()
    {
        ...
        else
        {
            m_roomInfo = m_task.Result;
            // Send player data to the room we just started.
            ksMultiType[] args = new ksMultiType[m_players.Count * 2];
            int index = 0;
            for (int i = 0; i < m_players.Count; i++)
            {
                // The token identifies the player. Players use this token to connect.
                args[index++] = m_players[i].Scripts.Get<sPlayer>().Token;
                args[index++] = m_players[i].Properties[Prop.NAME];
            }
            Cluster.CallRoomRPC(new uint[] { m_roomInfo.Id }, RPC.PLAYER_DATA, args);
        }
        m_task = null;
    }

    // Launches a room and sends players in the given player list to that room once it is ready.
    public void LaunchRoom(IEnumerable<ksIServerPlayer> players, string name, string scene, string roomType)
    {
        if (Cluster.IsConnected && !IsLaunchingRoom)
        {
            // Check that there's at least one connected (non-virtual) player in the list before launching a room.
            bool hasConnectedPlayer = false;
            foreach (ksIServerPlayer player in players)
            {
                if (!player.IsVirtual)
                {
                    hasConnectedPlayer = true;
                    break;
                }
            }
            if (!hasConnectedPlayer)
            {
                return;
            }
            ...
        }
    }

    // Sends players to the given room
    private void SendPlayers(ksRoomInfo roomInfo)
    {
        ...
        // Disconnect virtual players (bots).
        foreach (ksIServerPlayer player in m_players)
        {
            if (player.IsVirtual)
            {
                player.Disconnect();
            }
        }
    }

    // Once the new room acknowledges it received the player data, tell the players to switch rooms
    [ksClusterRPC(RPC.ACKNOWLEDGE_PLAYER_DATA)]
    private void OnAcknowledgePlayerData(uint roomId, ksMultiType[] args)
    {
        if (m_roomInfo != null)
        {
            ksLog.Info(this, "Acknowledged player data. Sending players");
            SendPlayers(m_roomInfo);
            m_roomInfo = null;
        }
    }
}

Authenticate players in the game room

  1. Open 'sGameRoom.cs'.
  2. Add a private dictionary field m_playerData that maps tokens to usernames.
  3. Add an Authenticate function that sets a player's name by retrieving it from the player data using their token. If they do not provide a valid token, they do not pass authentication. Store their token in their player script.
  4. Log the names of eliminated players in the FinishGame function.
  5. Add an OnGetPlayerData function that fills the player data dictionary with the received data and calls a cluster RPCs to tell the room that started this room it got the player data.

sGameRoom.cs

public class sGameRoom : ksServerRoomScript
{
    ...
    private Dictionary<long, string> m_playerData = new Dictionary<long, string>();

    public override void Initialize()
    {
        ...
        Room.OnAuthenticate += Authenticate;
    }
    ...
    private async Task<ksAuthenticationResult> Authenticate(ksIServerPlayer player, ksMultiType[] args, 
        CancellationToken cancellationToken)
    {
        if (args.Length != 1)
        {
            return new ksAuthenticationResult(1);
        }
        if (player.IsVirtual)
        {
            // If the player is virtual, the argument is the player name.
            player.Properties[Prop.NAME] = args[0];
            return new ksAuthenticationResult(0);
        }
        long token = args[0];
        string name;
        // The player must supply a token that is in the player data to authenticate.
        if (m_playerData.TryGetValue(token, out name))
        {
            // Remove the token so it cannot be reused.
            m_playerData.Remove(token);
            player.Properties[Prop.NAME] = name;
            // Store the token in the player's sPlayer script.
            player.Scripts.Get<sPlayer>().Token = token;
            return new ksAuthenticationResult(0);
        }
        // Invalid token.
        // We await a completed Task to prevent a compiler warning that the async method never awaits anything and
        // will be completed synchronously.
        return await Task.FromResult(new ksAuthenticationResult(1));
    }

    private void FinishGame(List<ksIServerPlayer> winners, List<ksIServerPlayer> losers)
    {
        ...
        // Log elimination message.
        string message = "eliminated: ";
        for (int i = 0; i < losers.Count; i++)
        {
            if (i > 0)
            {
                message += ", ";
            }
            message += losers[i].Properties[Prop.NAME];
        }
        ksLog.Info(this, message);
    }

    [ksClusterRPC(RPC.PLAYER_DATA)]
    private void OnGetPlayerData(uint roomId, ksMultiType[] args)
    {
        for (int i = 0; i + 1 < args.Length; i += 2)
        {
            long token = args[i];
            string name = args[i + 1];
            if (token == 0)
            {
                // 0 token means this is a virtual player.
                Room.CreateVirtualPlayer(name);
            }
            else
            {
                m_playerData[token] = name;
            }
        }
        // Tell the room that started us that we received the player data.
        Cluster.CallRoomRPC(new uint[] { roomId }, RPC.ACKNOWLEDGE_PLAYER_DATA);
    }
}

Run and connect to the local cluster

  1. Open the 'Lobby' scene.
  2. Build the configs (CTRL+F2).
  3. Launch the local cluster with the same settings as before.
  4. Make sure 'Use Live Server' is not checked when inspecting the 'Lobby Room' 'Connect' script.
  5. Enter play mode. You will connect to the lobby.
  6. You should see a message in the server logs for the lobby saying 'MyName connected' (or whatever you changed 'MyName' to). Every 2 seconds you should see a message about a bot connecting.
  7. After 10 players (bots) are connected, the lobby server logs should say 'Starting room Game'.
  8. Once the game room is started, you will load the 'Game' scene and connect to the game room.
  9. After 10 seconds in the game room, you will randomly with or lose and see a 'You Win!' or 'You Lose!' message in the Unity logs. If you win, you will reload the scene and connect to a new game room. If you lose, you will load the 'Lobby' scene and reconnect to the lobby.
  10. The names of all players who lost will be logged in the Game room logs.
  11. Stop the local cluster.

Run and connect to an online cluster

  1. Click 'Reactor->Publishing'.
  2. Give the image a name and version and click 'Publish'.
  3. Login to https://console.kinematicsoup.com/.
  4. Click on your project in the 'Reactor Projects' section.
  5. Click the 'Images' tab.
  6. Click 'Launch' in the row for the image and version you just published.
  7. Check 'Launch Cluster'.
  8. Select 'Lobby' for the 'Scene'.
  9. The 'Room' should say 'Lobby Room'
  10. 'Public Instance' should be checked.
  11. Check 'Keep Alive'.
  12. Give your cluster a name.
  13. Click 'Launch'.
  14. In Unity, make sure 'Use Live Server' is checked when inspecting the 'Lobby Room' 'Connect' script.
  15. Enter play mode. You should connect to the live cluster this time. You should load the game scene and connect to the game room in about 30 seconds once enough bots are connected.

Stopping the cluster

  1. When you are done, go to the 'Sessions' tab in the web console.
  2. Expand the cluster to see the rooms running in the cluster.
  3. Click the 'X' in the 'Stop' column for the cluster, then click the checkmark to stop the cluster. It may take several seconds for the cluster to stop.