Skip to main content

Using MPPM with brainCloud -- Unique Anonymous Profiles per Virtual Player

How to configure Unity Multiplayer Play Mode (MPPM) with brainCloud so each virtual player instance authenticates as a separate anonymous user with a unique profileId.

Written by Jason Liang
Updated today

Overview

When using Unity's Multiplayer Play Mode (MPPM) to test with multiple simultaneous editor instances, each instance needs its own brainCloud identity. Without extra configuration, all instances share the same PlayerPrefs and end up with the same anonymous profile.

The solution is to assign a unique WrapperName to each instance's BrainCloudWrapper. brainCloud prefixes all stored identity data (anonymousId, profileId) with this name, keeping each instance's credentials isolated.

How It Works

Internally, BrainCloudWrapper stores identity in PlayerPrefs using the WrapperName as a prefix:

MPPMPlayer1.brainCloud.profileId   → 947ba7df-...
MPPMPlayer2.brainCloud.profileId   → 3f812c1a-...
MPPMPlayer3.brainCloud.profileId   → 769e4703-...
MPPMPlayer4.brainCloud.profileId   → abc12345-...

Each MPPM instance reads its MPPM tag (p1, p2, etc.) at runtime and sets its wrapper name accordingly — so every virtual player authenticates as a separate brainCloud user.

Prerequisites

  • Unity 6.3+

  • Multiplayer Play Mode package (com.unity.multiplayer.playmode) version 2.x

  • brainCloud Unity SDK installed via Package Manager (com.bitheads.braincloud)

  • A brainCloud app with App ID and Secret

Step 1 — Install the brainCloud SDK via Package Manager

  1. Open Window → Package Manager

  2. Click +Add package from git URL

  3. Click Add and wait for the import to complete

The SDK installs with assembly name BrainCloud. Do not fill in credentials in the brainCloud Settings window — you will pass them directly in code.

Step 2 — Add the Assembly Reference

For CurrentPlayer.ReadOnlyTags() to compile in runtime scripts, your .asmdef must reference the MPPM runtime assembly.

Open your .asmdef file and add Unity.Multiplayer.PlayMode.Common.Runtime:

{
    "name": "YourProject",
    "references": [
        "Unity.Multiplayer.PlayMode.Common.Runtime"
    ]
}

Step 3 — Configure MPPM Play Mode Scenario

  1. Open Window → Multiplayer → Multiplayer Playmode

  2. Click + to create a new scenario

  3. Assign tags for each instance (use lowercase):

Instance

Tag

Role

Editor (main)

p1

Host

Player 2

p2

Client

Player 3

p3

Client

Player 4

p4

Client

Note: Unity's documentation does not state a hard cap on additional editor instances. In practice, the number may be limited by your machine's available memory and CPU. Keyboard shortcuts in the MPPM window go up to Player 4 (Ctrl+F9–Ctrl+F12), suggesting 4 is the well-tested range.

Step 4 — BCAuthManager Script

Create a BCAuthManager.cs MonoBehaviour that:

  1. Reads the MPPM tag to determine a player index (1–N)

  2. Sets BrainCloudWrapper.WrapperName to "MPPMPlayer1", "MPPMPlayer2", etc.

  3. Calls Init(url, secret, appId, version) then AuthenticateAnonymous()

The wrapper is accessed via reflection so the script compiles regardless of how the SDK is installed.

using System;
using UnityEngine;public class BCAuthManager : MonoBehaviour
{
    public static BCAuthManager Instance { get; private set; }    public string AuthStatus     { get; private set; } = "brainCloud: initializing…";
    public bool   IsAuthenticated { get; private set; } = false;
    public string ProfileId      { get; private set; } = "";    [SerializeField] private string _appId      = "YOUR_APP_ID";
    [SerializeField] private string _appSecret  = "YOUR_APP_SECRET";
    [SerializeField] private string _serverUrl  = "https://api.braincloudservers.com/dispatcherv2";
    [SerializeField] private string _appVersion = "1.0";    private Component _wrapper;
    private int _playerIndex = 0;    private System.Reflection.MethodInfo   _initMethod;
    private System.Reflection.MethodInfo   _authAnonMethod;
    private System.Reflection.MethodInfo   _updateMethod;
    private System.Reflection.PropertyInfo _wrapperNameProp;
    private System.Reflection.PropertyInfo _clientProp;    private void Awake()
    {
        if (Instance != null && Instance != this) { Destroy(gameObject); return; }
        Instance = this;
        DontDestroyOnLoad(gameObject);        // 1. Detect which MPPM instance this is
        _playerIndex = GetMPPMPlayerIndex();        // 2. Locate BrainCloudWrapper (installed via Package Manager)
        var wrapperType = Type.GetType("BrainCloudWrapper, BrainCloud")
                       ?? Type.GetType("BrainCloudWrapper, Assembly-CSharp")
                       ?? Type.GetType("BrainCloudWrapper");        if (wrapperType == null)
        {
            AuthStatus = "brainCloud: SDK not found";
            return;
        }        _wrapper = GetComponent(wrapperType) ?? gameObject.AddComponent(wrapperType);
        CacheReflectionMembers(wrapperType);        // 3. KEY STEP — unique WrapperName per MPPM instance
        if (_wrapperNameProp != null)
            _wrapperNameProp.SetValue(_wrapper, "MPPMPlayer" + _playerIndex);        Debug.Log("[BCAuthManager] P" + _playerIndex
            + " WrapperName=MPPMPlayer" + _playerIndex);        InitAndAuthenticate();
    }    private void InitAndAuthenticate()
    {
        if (_wrapper == null || _initMethod == null) return;        _initMethod.Invoke(_wrapper,
            new object[] { _serverUrl, _appSecret, _appId, _appVersion });        var assembly       = _clientProp?.GetValue(_wrapper)?.GetType().Assembly;
        var successDelType = assembly?.GetType("BrainCloud.SuccessCallback");
        var failureDelType = assembly?.GetType("BrainCloud.FailureCallback");        var onSuccess = successDelType != null
            ? Delegate.CreateDelegate(successDelType, this, nameof(OnAuthSuccess))
            : (Delegate)(Action<string, object>)OnAuthSuccess;        var onFailure = failureDelType != null
            ? Delegate.CreateDelegate(failureDelType, this, nameof(OnAuthFailure))
            : (Delegate)(Action<int, int, string, object>)OnAuthFailure;        // AuthenticateAnonymous requires 3 args: (success, failure, cbObject)
        _authAnonMethod.Invoke(_wrapper, new object[] { onSuccess, onFailure, null });
    }    private void OnAuthSuccess(string jsonResponse, object cbObject)
    {
        int idx = jsonResponse.IndexOf("\"profileId\"");
        if (idx >= 0)
        {
            int colon = jsonResponse.IndexOf(':', idx);
            int q1    = jsonResponse.IndexOf('"', colon + 1);
            int q2    = jsonResponse.IndexOf('"', q1 + 1);
            if (q1 >= 0 && q2 > q1)
                ProfileId = jsonResponse.Substring(q1 + 1, q2 - q1 - 1);
        }
        IsAuthenticated = true;
        AuthStatus = "brainCloud: OK (P" + _playerIndex + ")";
        Debug.Log("[BCAuthManager] P" + _playerIndex
            + " Auth SUCCESS — ProfileId=" + ProfileId);
    }    private void OnAuthFailure(int status, int reason, string msg, object cbObject)
    {
        IsAuthenticated = false;
        AuthStatus = "brainCloud: FAILED (" + status + "/" + reason + ")";
        Debug.LogError("[BCAuthManager] P" + _playerIndex + " Auth FAILED: " + msg);
    }    private void Update()
    {
        // brainCloud SDK must be ticked every frame
        if (_wrapper != null && _updateMethod != null)
            try { _updateMethod.Invoke(_wrapper, null); } catch { }
    }    private void CacheReflectionMembers(Type wrapperType)
    {
        var flags = System.Reflection.BindingFlags.Instance
                  | System.Reflection.BindingFlags.Public;        _initMethod = wrapperType.GetMethod("Init", flags, null,
                         new[] { typeof(string), typeof(string),
                                 typeof(string), typeof(string) }, null)
                   ?? wrapperType.GetMethod("Init", flags, null,
                         Type.EmptyTypes, null);        _authAnonMethod  = wrapperType.GetMethod("AuthenticateAnonymous");
        _updateMethod    = wrapperType.GetMethod("Update", flags);
        _wrapperNameProp = wrapperType.GetProperty("WrapperName");
        _clientProp      = wrapperType.GetProperty("Client");
    }    private static int GetMPPMPlayerIndex()
    {
#if UNITY_EDITOR
        try
        {
            var tags = Unity.Multiplayer.PlayMode.CurrentPlayer.ReadOnlyTags();
            for (int i = 1; i <= 8; i++)
                if (System.Array.IndexOf(tags, "p" + i) >= 0)
                    return i;
        }
        catch { }
        return 0;
#else
        return 0;
#endif
    }
}

Step 5 — Scene Setup

  1. Create a GameObject named BrainCloudManager

  2. Add the BCAuthManager component

  3. Fill in App Id and App Secret in the Inspector

BrainCloudWrapper is added automatically at runtime — no manual wiring needed.

Step 6 — Run and Verify

Press Play. Each instance logs its wrapper name and then its unique profile:

[BCAuthManager] P1 WrapperName=MPPMPlayer1
[BCAuthManager] P1 Auth SUCCESS — ProfileId=947ba7df-70a7-4a57-9be5-816f87fa32a2[BCAuthManager] P2 WrapperName=MPPMPlayer2
[BCAuthManager] P2 Auth SUCCESS — ProfileId=3f812c1a-aa01-4b2e-bb33-91d04e7f1234[BCAuthManager] P3 WrapperName=MPPMPlayer3
[BCAuthManager] P3 Auth SUCCESS — ProfileId=769e4703-6d50-40f3-9b39-4f5873b23972

Each ProfileId is unique — confirming that each MPPM instance authenticated as a separate brainCloud user.

Troubleshooting

Symptom

Cause

Fix

All players get the same profileId

MPPM tags not assigned or wrong case

Assign tags p1pN (lowercase) in the Play Mode Scenarios window

BrainCloudWrapper not found

SDK assembly name mismatch

Ensure SDK is installed via Package Manager; code tries BrainCloud then Assembly-CSharp

Number of parameters does not match on AuthenticateAnonymous

Wrong number of arguments

Pass 3 args: (success, failure, null)

Operator == cannot be applied to string[] and string

ReadOnlyTags() returns string[]

Use Array.IndexOf(tags, "p1") >= 0 not tags == "p1"

Menu Window/Multiplayer Play Mode not found

Menu path changed in MPPM 2.x

Use Window/Multiplayer/Multiplayer Playmode

Did this answer your question?