Using Lobby

You can use Lobby and Cloud Code together to define common triggers in games. Consider the situation where a game lobby is ready to start a match. You could trigger the following script in response to a game lobby becoming ready. This example also features integration with Remote Config.

Before you follow this example, enable both Lobby and Cloud Code through the Unity Cloud Dashboard, and follow the steps to install the required SDKs. Create a lobby in your project using service authentication, and note its ID. You can do this through Cloud Code (check examples for Unity Lobby).

The following example gives the script two parameters: the service ID used to create the lobby, and the lobby ID. We get the lobby and then update it with several changes to its metadata. These changes are:

  • Locking the lobby so new players can no longer join.
  • Constructing a random “player order” for the game by using the lobby’s player IDs.
  • Randomly choosing a map for the game by using the LOBBY_CURRENT_MAPS config defined in Remote Config. The script returns a basic Boolean that indicates success or failure.

JavaScript

const _ = require("lodash-4.17");
const { LobbyApi, DataObjectVisibilityEnum } = require("@unity-services/lobby-1.2");
const { SettingsApi } = require("@unity-services/remote-config-1.1");

module.exports = async ({params,context,logger}) => {
    const { serviceId, lobbyId } = params;
    const { projectId, environmentId, playerId } = context;
    const lobbyApi = new LobbyApi(context);
    const remoteConfig = new SettingsApi(context);

    try {
        const {data: lobby} = await lobbyApi.getLobby(lobbyId, serviceId);
        return setUpGame(remoteConfig, projectId, environmentId, serviceId, lobbyApi, logger, lobby);
    } catch (err) {
       logger.error(`Failed to set up lobby: ${err.response.data.detail}`);
       return false;
    }

};

// Updates a lobby to set up for the start of a game, including reading a value from Remote Config to randomly assign to the lobby.
async function setUpGame(remoteConfig, projectId, environmentId, serviceId, lobbyApi, logger, lobby) {
    let playerIds = [];
    for (let i = 0; i < lobby.players.length; i++) {
        playerIds.push(lobby.players[i].id);
    }

    // Generate a turn order for the game by shuffling the player IDs.
    _.shuffle(playerIds);

    // Load all of the maps that are currently configured as available in Remote Config and pick a random one for this game.
    let maps = getCurrentMapsFromRemoteConfig(remoteConfig, projectId, environmentId)
    if (maps == null) {
      throw "Maps loaded from Remote Config are null.";
    }
    let gameMap = _.sample(maps);

    try {
      const updateResponse = await lobbyApi.updateLobby(
          lobby.id,
          serviceId,
          null,
          {
              isLocked: true, // Lock the lobby so that new players cannot join.
              hostId: playerIds[0], // Transfer host ownership from the service to one of the players.
              data: {
                  "playerOrder": {
                      value: playerIds.toString(),
                      visibility: DataObjectVisibilityEnum.Member // Set the visibility of the player order so that only lobby members can access it.
                  },
                  "map": {
                      value: gameMap,
                      visibility: DataObjectVisibilityEnum.Public // Set the visibility of the game map so that anyone who views the lobby can access it.
                  }
              }
          }
      );

      return true;
    } catch (err) {
      throw err;
    }

    return lobbyUpdateSuccess;
}

// Loads the Remote Config JSON value for LOBBY_CURRENT_MAPS and returns the array of maps inside of it.
// If the JSON is invalid, the return value is null.
async function getCurrentMapsFromRemoteConfig(remoteConfig, projectId, environmentId) {
  const currentMapsId = 'LOBBY_CURRENT_MAPS';
  const remoteConfigResponse = await remoteConfig.assignSettingsGet(
    projectId,
    environmentId,
    'settings',
    [currentMapsId]
  );

  let mapsJSON = remoteConfigResponse?.data?.configs?.settings?.[currentMapsId];
  if (!mapsJSON || !mapsJSON.maps || mapsJSON.maps.length == 0) {
    return null;
  }

  return mapsJSON.maps;
}

// Uncomment the code below to enable the inline parameter definition
// - Requires Cloud Code JS dev environment setup with NodeJS (https://docs.unity3d.com/Packages/com.unity.services.cloudcode@2.5/manual/Authoring/javascript_project.html)
//
// module.exports.params = {
//   serviceId: { type: "String", required: true },
//   lobbyId: { type: "String", required: true },
// };

Note: If you are using Unity Editor to manage your scripts, you can uncomment the code at the bottom of the script to declare the required serviceId and lobbyId parameters in-script. To learn more about how to manage in-script parameters, refer to Modify script parameters within the Unity Editor.

The Remote Config JSON here has a simple structure, but you can extend this example configuration to achieve more fine-grained control over your game. Likewise, there are a number of different triggers for Cloud Code that you might want to consider in addition to game setup, for example:

  • Triggering on lobby creation to adjust metadata values and sanitize strings.
  • Using Cloud Save player data to filter a player’s query results, approve/deny a join, and set metadata.
  • Using Economy data to set player or lobby metadata.