Tutorial 6 - Adding Custom Public Data to Running Rooms

Summary

Use public tags and data to share information to users before they connect to a server. This tutorial uses public data to report the number of connected users and uses public tags to mark empty rooms.

Requirements

Setup

Create a Room

  1. Right click in the hierarachy window and create a 'Reactor->Room' game object.

Scripting

Create a Server Room Script to Manage Public Data

The server room script stores the number of connected players in the room's public data, and adds or removes a public room tag named "empty" depending on if the the room is empty (has no connections) or not. This data is publically available without having to connect to the room in the response to a ksReactor.GetServers call.

'PublicData' is a ksJSON object and can store anything you could store in JSON format. 'PublicTags' is a ksHashSet of strings. While 'PublicData' is more flexible in the types of data it can store, 'PublicTags' can be used with the cluster to filter rooms by tag, though that is outside the scope of this tutorial.

  1. Select the 'Room' object.
  2. In the 'Add Component' menu, select 'Reactor->New Server Room Script'.
  3. Name the script 'ServerRoom'.

ServerRoom.cs

using KS.Reactor.Server;
using KS.Reactor;

public class ServerRoom : ksServerRoomScript
{
    // Called when the script is attached.
    public override void Initialize()
    {
        Room.OnPlayerJoin += PlayerJoin;
        Room.OnPlayerLeave += PlayerLeave;
        UpdateConnections();
    }

    // Called when the script is detached.
    public override void Detached()
    {
        Room.OnPlayerJoin -= PlayerJoin;
        Room.OnPlayerLeave -= PlayerLeave;
    }

    // Called when a player connects.
    private void PlayerJoin(ksIServerPlayer player)
    {
        ksLog.Info("Player " + player.Id + " joined.");

        // Update the number of connections if this player is not a bot.
        if (!player.IsVirtual)
        {
            UpdateConnections();
        }
    }

    // Called when a player disconnects.
    private void PlayerLeave(ksIServerPlayer player)
    {
        ksLog.Info("Player " + player.Id + " left.");

        // Update the number of connections if this player is not a bot.
        if (!player.IsVirtual)
        {
            UpdateConnections();
        }
    }

    private void UpdateConnections()
    {
        // Set the number of connections in the public data. This is available in the response to ksService.GetRooms()
        // as ksRoomInfo.PublicData.
        Room.PublicData["connections"] = Room.ConnectedPlayerCount;

        // Add or remove the "empty" tag depending on if the room has any connections. Tags are available in the
        // response to ksService.GetRooms() as ksRoomInfo.PublicTags.
        if (Room.ConnectedPlayerCount == 0)
        {
            Room.PublicTags.Add("empty");
        }
        else
        {
            Room.PublicTags.Remove("empty");
        }
    }
}

Create a ConnectHandler Script

This script handles ksConnect events and polls the online services for an updated list of rooms every 10 seonds. When a list of rooms is fetched it will log a summary of the rooms, including the 'PublicTags' and the number of connections we set in the 'PublicData'. When space is pressed, it connects to the first room in the list, or disconnects if there is already a room connection.

  1. Create a new MonoBehaviour named 'ConnectHandler'.
  2. Attach it to the 'Room' game object.
  3. Edit the file to match the code below.
  4. Select the ksConnect component on the room and add the 'ConnectHandler' 'OnGetRooms' method to the ksConnect 'OnGetRooms' event list.
  1. Set the ksConnect 'Connect Mode' property to 'ONLINE'.

ConnectHandler.cs

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

public class ConnectHandler : MonoBehaviour
{
    // How often to refresh the list of servers.
    private float m_refreshTime = 0f;

    // Room info to connect to.
    private ksRoomInfo m_roomInfo;

    // Handle room polling and connecting/disconnect keyboard events.
    private void Update()
    {
        ksConnect connect = GetComponent<ksConnect>();
        if (connect != null)
        {
            // When space is pressed, disconnect if we are connected, or connect if we aren't.
            if (Input.GetKeyDown(KeyCode.Space))
            {
                if (connect.Room != null && (connect.Room.IsConnected || connect.Room.IsConnecting))
                {
                    connect.Disconnect(false);
                }
                else if (m_roomInfo != null)
                {
                    connect.Connect(m_roomInfo);
                }
                else
                {
                    ksLog.Warning("No room to connect to.");
                }
            }

            // Every 10 seconds restart the connection process which starts with a request for a list
            // of all available rooms.
            m_refreshTime += Time.deltaTime;
            if (m_refreshTime >= 10f)
            {
                m_refreshTime = 0;
                connect.BeginConnect();
            }
        }
    }

    // Handler for ksConnect.OnGetRooms events.
    public void OnGetRooms(ksConnect.GetRoomsEvent e)
    {
        if (e.Status != ksBaseRoom.ConnectStatus.SUCCESS)
        {
            ksLog.Error(this, "Error fetching rooms: " + e.Error);
        }
        else if (e.Rooms == null || e.Rooms.Count == 0)
        {
            ksLog.Warning(this, "No rooms found.");
        }
        else
        {
            foreach (ksRoomInfo roomInfo in e.Rooms)
            {
                ksLog.Info("id: " + roomInfo.Id +
                    "\nscene: " + roomInfo.Scene +
                    "\ntype: " + roomInfo.Type +
                    "\ndata: " + (roomInfo.PublicData != null ? roomInfo.PublicData.Print() : null) +
                    "\ntags: " + PrintTags(roomInfo.PublicTags)
                );
            }

            // Store the first room in the list. The Update method will use this room information when a room connection is created.
            m_roomInfo = e.Rooms[0];
        }
    }

    // Convert rooms tags to a readable string.
    private string PrintTags(ksHashSet<string> tags)
    {
        string str = "[";
        bool first = true;
        foreach (string tag in tags)
        {
            if (!first)
            {
                str += ", ";
            }
            first = false;
            str += tag;
        }
        str += "]";
        return str;
    }
}

Testing

You cannot query a list of local servers to view their public data, so to test this you'll need to deploy live servers.

  1. Open the 'Menu Bar->Reactor->Publishing' menu.
  2. Login using your KinematicSoup account.
  3. Enter an image name and version in the form and click the 'Publish' button.
  4. Open the 'Menu Bar->Reactor->Servers' menu.
  5. Select the image you just published from the 'Image' dropdown.
  6. Check the 'Is Public' property and provide a name for this instance, then click the 'Launch Server' button.
    • After a moment your server should appear in a list of running instances.
  7. Once the server is started, select the room and change the 'Connect Mode' property of the ksConnect component to 'ONLINE'.
  8. Enter play mode. Every 10 seconds, you should see a summary of the running rooms in the Unity logs, which will include the number of connections and the tags. Click on the log entry to see the full summary. While you are not connected, it should show 0 connections and the tag "empty".
    • If you enter play mode before the server finishes launching, you will see a warning in the logs saying "No servers found.". Leave play mode and try again once the server is started.
  9. Pressing space should connect you to the room. Pressing space again should disconnect you. While you are connected, the next time the room summary prints it should show 1 connection and no tags.
  10. Once you are done testing, open the 'Servers' window and stop the server instance.