Matchmaking Guide

使用Photon,進入一個房間與人遊戲(或對戰)是非常容易的。 基本上有三種方法: 要麼告訴伺服器找到一個匹配的房間,要麼跟隨朋友進入她的房間,要麼獲取一個房間列表,讓用戶選擇一個。 所有這三種變數都被Photon支持,您甚至可以推出您自己的。

我們認為,對於大多數遊戲來說,最好使用快速和簡單的匹配,所以我們建議使用隨機匹配,也許還可以對技能、等級等進行過濾。

Contents

Matchmaking Checklist

If you are having issues matching players, here is a quick checklist:

  • Verify that you are using same AppId in all clients.
  • Verify that clients are connected to the same Region. Only players connected to same region can play with each other no matter what device or platform they're using. Make sure to read about "Best Region Considerations" if you use "Best Region".
  • Verify that you are using same AppVersion in all clients. More information can be found here.
  • Verify that players have different unique UserIDs. Players with same UserID cannot join the same room.
  • Before trying to join a room by name, make sure that this room is created. Alternatively use JoinOrCreateRoom.
  • If you are trying to join a random room, make sure to choose the same lobby (name and type) used when creating it.
  • If you are doing random matchmaking using room properties as a filter make sure to set the keys of those properties to be visible from the lobby when creating the room.
  • If you are doing random matchmaking with SQL filters make sure to set the reserved filtering properties keys used to be visible from the lobby. It is also important to relax the SQL filter with each random matchmaking attempt or use chained filters or create new rooms at some point after a number of failed attempts.
  • If you are implementing asynchronous matchmaking, make sure to use webhooks with proper configuration (enable "AsyncJoin") or use AsyncRandomLobby.

Back To Top

快速匹配

現在大多數玩家只想直接進入比賽。 他們想馬上進入遊戲。 這就是為什麼大多數遊戲提供快速比賽作為第一種模式。

這裡描述的建議工作流程讓玩家進入房間,而不要求他們從一長串的房間列表中挑選一個(隨機)。

如果您單純想讓玩家快速進入一個房間,請執行以下操作:

Back To Top

JoinRandomOrCreateRoom

只需調用JoinRandomOrCreateRoom(確切的函數或方法名稱可能會有所不同,取決於您的客戶端SDK)。 如果找到一個房間,它將被加入,或將創建一個新房間。

using Photon.Realtime;
using System.Collections.Generic;

public class QuickMatchExample : IMatchmakingCallbacks
{
    private LoadBalancingClient loadBalancingClient;

    private void QuickMatch()
    {
        loadBalancingClient.OpJoinRandomOrCreateRoom(null, null);;
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

Back To Top

JoinRandomRoom 或 CreateRoom

  • 嘗試JoinRandomRoom(確切的函數或方法名稱可能會有所不同,取決於您的客戶端SDK)。
    • 在最好的情況下,這就是。 您的客戶端將成功加入一個房間。
    • 在最壞的情況下,沒有房間存在或沒有房間可以加入(關閉、不可見或已滿)。
  • 如果這不能立即找到一個房間,就創建一個。
    • 如果您從不顯示房間名稱(為什麼要顯示),不要創造一個名稱。 讓伺服器來執行這個動作。 在創建房間時,設置空或空字符串作為 "房間名稱"。 房間得到一個GUID,這是唯一的。
    • 為 "最大玩家 "設置一個值。 這樣一來,伺服器最終會在房間滿員時停止添加玩家。
  • 如果您的客戶在房間裡只有一個(玩家數==1)。 等待。 顯示一個您正在等待對手的屏幕。
  • 當房間裡有足夠的玩家時,您可能會 "開始 "遊戲。 為了不讓新玩家進入,"關閉 "房間。 伺服器會停止填充房間,即使它還沒有滿。
    • 注意:當您關閉房間時,有一小段時間,玩家也許已經在進入中。 如果有人在客戶端關閉房間'後'仍可加入,請不要驚訝。
using Photon.Realtime;
using System.Collections.Generic;

public class QuickMatchExample : IMatchmakingCallbacks
{
    [SerializeField]
    private maxPlayers = 4;
    private LoadBalancingClient loadBalancingClient;

    private void CreateRoom()
    {
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.MaxPlayers = maxPlayers;
        EnterRoomParams enterRoomParams = new EnterRoomParams();
        enterRoomParams.RoomOptions = roomOptions;
        loadBalancingClient.OpCreateRoom(enterRoomParams);
    }

    private void QuickMatch()
    {
        loadBalancingClient.OpJoinRandomRoom();
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnJoinRandomFailed(short returnCode, string message)
    {
        CreateRoom();
    }

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

使用這個工作流程,加入遊戲對您的玩家來說是一件很容易的事。

Back To Top

隨機匹配

有時,玩家想要的不僅僅是快速匹配,他們想要玩某個地圖或模式(例如,兩兩對戰,等等)。

您可以設置任意的 "自定義房間屬性",並在 JoinRandomRoom請求中使用它們作為過濾器(確切的函數或方法名稱可能因您的客戶端SDK而不同)。

Back To Top

在大廳中公開部分屬性

自定義房間屬性會同步給房間裡的所有玩家,並且可以用來追蹤當前的地圖、遊戲模式、難度、回合、輪次、開始時間,等等。 它們被處理成帶有字符串鍵的Hashtable。

默認情況下,為了保持精簡,這些屬性只能在房間內訪問,不會被發送到主伺服器(存在大廳的地方)。 您可以選擇一些自定義的房間屬性來在大廳中公開。 這些屬性將被用作隨機匹配的過濾器,它們將在大廳中可見(作為房間列表中房間信息的一部分發送,只有默認類型的大廳發送房間列表)。

Example:

為了使 "地圖 "和 "gm "可用於匹配,您可以在創建房間時設置一個 "大廳中可見的房間屬性 "的列表。 提示:簡短的名字更好,所以用 "gm "而不是 "GameMode"。

using Photon.Realtime;
using System.Collections.Generic;
using Hashtable = ExitGames.Client.Photon.Hashtable;

public class CreateRoomWithLobbyPropertiesExample : IMatchmakingCallbacks
{
    public const string MAP_PROP_KEY = "map";
    public const string GAME_MODE_PROP_KEY = "gm";
    public const string AI_PROP_KEY = "ai";

    private LoadBalancingClient loadBalancingClient;

    private void CreateRoom()
    {
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.CustomRoomPropertiesForLobby = { MAP_PROP_KEY, GAME_MODE_PROP_KEY, AI_PROP_KEY };
        roomOptions.CustomRoomProperties = new Hashtable { { MAP_PROP_KEY, 1 }, { GAME_MODE_PROP_KEY, 0 } };
        EnterRoomParams enterRoomParams = new EnterRoomParams();
        enterRoomParams.RoomOptions = roomOptions;
        loadBalancingClient.OpCreateRoom(enterRoomParams);
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnCreateRoomFailed(short returnCode, string message)
    {
       // log error message and code
    }

    void IMatchmakingCallbacks.OnCreatedRoom()
    {
    }

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully, OpCreateRoom leads here on success
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

注意,"ai "沒有初始值。 它不會顯示在大廳裡,直到它在房間裡被設置(在C# SDK中,這是通過Room.SetCustomProperties完成的)。 當您改變 "map"、"gm "或 "ai "的值時,它們也會在大廳中以短暫的延遲更新。

稍後(房間創建後),您也可以改變大廳中可見的房間屬性鍵(添加或刪除)(在C# SDK中,這是通過Room.PropertiesListedInLobby完成的)。 提示:保持大廳屬性列表的簡短,以確保您的客戶性能不會因為在加入房間或加入大廳時加載它們而受到影響(只有默認類型的大廳發送房間列表)。

再強調一次:您不需要加入大廳(並得到非常長的房間列表)來利用這個。 當您為大廳設置了,它們也可以作為過濾器使用。

Back To Top

在加入隨機中過濾房間屬性

This section excludes SQL Lobby type. Read more about SQL matchmaking here.

當試圖找到一個隨機房間時,您可以選擇預期的房間屬性或預期的最大玩家。 當伺服器為您選擇一個 "合適的 "房間時,這些可以作為過濾器。

Example:

using Photon.Realtime;
using System.Collections.Generic;
using Hashtable = ExitGames.Client.Photon.Hashtable;

public class RandomMatchmakingExample : IMatchmakingCallbacks
{
    public const string MAP_PROP_KEY = "map";

    private LoadBalancingClient loadBalancingClient;

    public void JoinRandomRoom(byte mapCode, byte expectedMaxPlayers)
    {
        Hashtable expectedCustomRoomProperties = new Hashtable { { MAP_PROP_KEY, mapCode } };
        OpJoinRandomRoomParams opJoinRandomRoomParams = new OpJoinRandomRoomParams();
        opJoinRandomRoomParams.ExpectedMaxPlayers = expectedMaxPlayers;
        opJoinRandomRoomParams.ExpectedCustomRoomProperties = expectedCustomRoomProperties:
        loadBalancingClient.OpJoinRandomRoom();
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnJoinRandomFailed(short returnCode, string message)
    {
        // log error code and message
        // here usually you create a new room
    }

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully, OpJoinRandomRoom leads here on success
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

如果您通過更多的過濾屬性,那麼房間與之匹配的機會就會降低。 最好限制選項。

確保您總是使用大廳可見的屬性來過濾,如點此所示。

Back To Top

和您的朋友一起玩

如果您的用戶與朋友交流(例如使用Photon Chat),他們可以很容易地創建一個房間名稱,每個人只需使用JoinOrCreateRoom(確切的函數或方法名稱可能會有所不同,取決於您的客戶端SDK)來進入這個房間。

Example:

一個獨特的房間名稱可以被組成(例如):"friendName1 + friendName2 + randomInteger"。

為了避免其他人加入,創建一個不可見的房間,例如:

using Photon.Realtime;
using System.Collections.Generic;

public class PrivateRoomExample : IMatchmakingCallbacks
{
    private LoadBalancingClient loadBalancingClient;

    public void JoinOrCreatePrivateRoom(string nameEveryFriendKnows)
    {
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.IsVisible = false;
        EnterRoomParams enterRoomParams = new EnterRoomParams();
        enterRoomParams.RoomName = nameEveryFriendKnows;
        enterRoomParams.RoomOptions = roomOptions;
        loadBalancingClient.OpJoinOrCreateRoom(enterRoomParams);
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnJoinRoomFailed(short returnCode, string message)
    {
      // log error code and message
    }

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully, OpJoinOrCreateRoom leads here on success
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

如果您使用唯一的用戶ID,您也可以使用FindFriends(確切的函數或方法名稱可能取決於您的客戶端SDK)來尋找您的朋友。

Back To Top

Publishing UserIDs In A Room

Photon uses a UserID in various places. For example, you can find friends only with a suitable UserID per player. We added an option to Photon, which makes the UserID of players known per room. In C# SDKs, set RoomOptions.PublishUserId to true, when you create a room. The server will then provide the UserID and you can access it on the client. In C# SDKs, it's done via PhotonPlayer.UserId.

Notes:

  • UserIDs are broadcasted, with player properties, in the Photon join event.
  • The UserID for a client can be set in three ways:
    1. Client sets UserID before connecting.
    2. Returned by an external web service using Custom Authentication. It will override the value sent by the client.
    3. Photon will make up UserIDs (GUID) for users that don't explicitly set theirs.
  • Generally, UserIDs, are not intended to be displayed.

Back To Top

Matchmaking Slot Reservation

Sometimes, a player joins a room, knowing that a friend should join as well. With Slot Reservation, Photon can block a slot for specific users and take that into account for matchmaking. To reserve slots there is an expectedUsers parameter (exact parameter or argument name may vary depending on your client SDK) in the methods that get you in a room (JoinRoom, JoinOrCreateRoom, JoinRandomRoom and CreateRoom. Exact functions or methods names may vary depending on your client SDK).

EnterRoomParams enterRoomParams = new EnterRoomParams();
enterRoomParams.ExpectedUsers = expectedUsers;
// create room example
loadBalancingClient.OpCreateRoom(enterRoomParams);
// join room example
loadBalancingClient.OpJoinRoom(enterRoomParams);
// join or create room example
loadBalancingClient.OpJoinOrCreateRoom(enterRoomParams);
// join random room example
OpJoinRandomRoomParams opJoinRandomRoomParams = new OpJoinRandomRoomParams();
opJoinRandomRoomParams.ExpectedUsers = expectedUsers;
loadBalancingClient.OpJoinRandomRoom(opJoinRandomRoomParams);

When you know someone should join, pass an array of UserIDs. For JoinRandomRoom, the server will attempt to find a room with enough slots for you and your expected players (plus all active and expected players already in the room). The server will update clients in a room with the current expectedUsers, should they change.

You can update the list of expected users inside a room (add or remove one or more users), this is done via a well known room property. (In C# SDKs, you can get and set Room.ExpectedUsers).

To support Slot Reservation, you need to enable publishing UserIDs inside rooms.

Back To Top

Example Use Case: Teams Matchmaking

You can use this to support teams in matchmaking. The leader of a team does the actual matchmaking. He/She can join a room and reserve slots for all members:

Try to find a random room:

OpJoinRandomRoomParams opJoinRandomRoomParams = new OpJoinRandomRoomParams();
opJoinRandomRoomParams.ExpectedUsers = teamMembersUserIds;
loadBalancingClient.OpJoinRandomRoom(opJoinRandomRoomParams);

Create a new one if none found:

EnterRoomParams enterRoomParams = new EnterRoomParams();
enterRoomParams.ExpectedUsers = teamMembersUserIds;
loadBalancingClient.OpCreateRoom(enterRoomParams);

The others don't have to do any matchmaking but instead repeatedly call ('periodic poll', every few frames/(milli)seconds):

loadBalancingClient.OpFindFriends(new string[1]{ leaderUserId });

When the leader arrives in a room, the FindFriends operation will reveal that room's name and everyone can join it:

EnterRoomParams enterRoomParams = new EnterRoomParams();
enterRoomParams.RoomName = roomNameWhereTheLeaderIs;
loadBalancingClient.OpJoinRoom(enterRoomParams);

Back To Top

大廳

Photon在所謂的 "大廳 "中組織您的房間。 所以所有的房間都屬於大廳。 大廳是通過其名稱和類型來識別的。 名稱可以是任何字符串,但只有三種類型的大廳。默認SQLAsync。 每一種都有獨特的功能,適合特定的使用情況。

所有應用程序都從一個預先存在的大廳開始。The Default Lobby。 大多數應用程序不需要其他大廳。 然而,客戶端可以在運行中創建其他大廳。 當您在操作請求中指定一個新的大廳定義時,大廳開始存在:JoinLobby, CreateRoomJoinOrCreateRoom

像房間一樣,大廳可以被加入,您也可以離開。 在一個大廳裡,客戶只在適用時獲得該大廳的房間列表。 沒有其他東西。 在大廳裡沒有辦法與其他人交流。

當一個客戶加入到一個大廳並試圖創建(或JoinOrCreate)一個房間而不明確設置一個大廳時,如果創建成功/發生,這個房間將被添加到當前加入的大廳。 當客戶沒有加入一個大廳,並試圖在沒有明確設置大廳的情況下創建(或JoinOrCreate)一個房間,如果創建成功/發生,該房間將被添加到默認大廳。 當客戶端加入到一個大廳,並試圖通過明確設置大廳來創建(或JoinOrCreate)一個房間,如果創建成功/發生:

  • 如果大廳名稱為空或空:該房間將被添加到當前加入的大廳。 這意味著當您加入一個自定義/不同的大廳時,您不能在默認大廳中創建房間。
  • 如果大廳名稱不是空:房間將被添加到房間創建請求所指定的大廳。

當一個客戶加入到一個大廳並試圖加入一個隨機的房間而不明確設置一個大廳時,伺服器將在當前加入的大廳中尋找這個房間。 當一個客戶沒有加入大廳,並且試圖加入一個隨機房間而沒有明確設置大廳時,伺服器將在默認大廳中尋找房間。 當一個客戶加入到一個大廳,並試圖通過明確設置一個大廳來加入一個隨機的房間。

  • 如果大廳名稱為空或空:伺服器將在當前加入的大廳中尋找房間。 這意味著當您加入一個自定義/不同的大廳時,您不能在默認大廳中加入隨機房間。
  • 如果大廳名稱不是空:伺服器將在房間創建請求所指定的大廳中尋找房間。

當客戶加入到一個房間並想切換到另一個房間時,可以直接調用JoinLobby,而不需要通過明確調用LeaveLobby離開第一個房間。

Back To Top

默認大廳類型

最適合於synchronous random matchmaking。 可能是不太復雜和最常用的類型。

當加入到一個默認的大廳類型時,客戶端將收到定期的房間列表更新。

當客戶端加入一個默認類型的大廳時,它立即得到一個初始的可用房間列表。 此后,客戶將收到定期的房間列表更新。

列表使用兩個標准進行排序:開放或關閉,滿員或不滿員。 所以列表由三組組成,順序如下:

  • 第一組:開放且未滿(可加入)。
  • 第二組:完整但不封閉(不可加入)。
  • 第三組:已關閉(不可加入,可能是滿的,也可能不是)。

在每組中,條目沒有任何特定的順序(隨機)。

房間列表(或房間的更新)也有數量限制,見大廳限制

using Photon.Realtime;
using System.Collections.Generic;

public class RoomListCachingExample : ILobbyCallbacks, IConnectionCallbacks
{
    private TypedLobby customLobby = new TypedLobby("customLobby", LobbyType.Default);
    private LoadBalancingClient loadBalancingClient;

    private Dictionary<string, RoomInfo> cachedRoomList = new Dictionary<string, RoomInfo>();

    public void JoinLobby()
    {
        loadBalancingClient.JoinLobby(customLobby);
    }

    private void UpdateCachedRoomList(List<RoomInfo> roomList)
    {
        for(int i=0; i<roomList.Count; i++)
        {
            RoomInfo info = roomList[i];
            if (info.RemovedFromList)
            {
                cachedRoomList.Remove(info.Name);
            }
            else
            {
                cachedRoomList[info.Name] = info;
            }
        }
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region ILobbyCallbacks

    void ILobbyCallbacks.OnJoinedLobby()
    {
        cachedRoomList.Clear();
    }

    void ILobbyCallbacks.OnLeftLobby()
    {
        cachedRoomList.Clear();
    }

    void ILobbyCallbacks.OnRoomListUpdate(List<RoomInfo> roomList)
    {
        // here you get the response, empty list if no rooms found
        UpdateCachedRoomList(roomList);
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif

    #region IConnectionCallbacks

    void IConnectionCallbacks.OnDisconnected(DisconnectCause cause)
    {
        cachedRoomList.Clear();
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endregion
}

Back To Top

默認的大廳

它有一個null的名字,它的類型是默認大廳類型。 在C# SDKs中,它被定義在TypedLobby.Default中。 默認大廳的名字是保留的。 只有默認大廳可以有一個null的名字,所有其他的大廳都需要有一個既不是空也不是空的名字字符串。 如果您使用一個空或空的字符串作為大廳的名字,它將指向默認的大廳,不管指定的類型是什麼。

Back To Top

推薦流程

我們鼓勵大家跳過加入大廳,除非絕對必要。 如果需要,當您希望房間被添加到特定或自定義的大廳時,客戶可以在創建新房間時指定大廳。

加入默認類型的大廳可以得到房間列表,但在大多數情況下無法用:

  • 列表中的條目之間在ping方面沒有區別
  • 通常玩家都在尋找快速匹配
  • 接收房間列表會增加額外的延遲並消耗流量
  • 一個有太多信息的長列表會對用戶體驗產生不良影響

相反,為了讓您的玩家對匹配有更多的控制,使用過濾器進行隨機匹配。 多個大廳仍然是有用的,因為它們也被用於(伺服器端)隨機匹配,您可以利用大廳的統計數據。

Back To Top

SQL大廳類型

在SQL大廳類型中,JoinRandomRoom中的字符串過濾器取代了默認的預期大廳屬性。 另外,在SQL大廳類型中,只支持一種MatchmakingMode:FillRoom(默認為0)。 此外,"自定義房間列表 "取代了僅存在於默認大廳類型中的自動定期房間列表。

這種大廳類型增加了一個更精細的匹配過濾,可用於伺服器端的基於技能匹配,完全由客戶端驅動。

在內部,SQL大廳將房間保存在一個SQLite表中,其中有多達10個特殊的 "SQL過濾屬性"。 這些SQL屬性的命名被固定為:"C0"、"C1 "到 "C9"。 只允許整數類型和字符串類型的值,一旦一個值被分配給特定大廳的任何列,這個列就被鎖定為該類型的值。 盡管有靜態的命名,但客戶必須定義哪些是大廳中需要的。 要小心,因為當您把SQL屬性定義為大廳屬性或設置它們的值時,是區分大小寫的,但在SQL過濾器裡面是不區分大小寫的。

在創建房間或加入房間後,您仍然可以使用除SQL屬性以外的自定義房間屬性,對大廳可見或不可見。 然而,這些將不會被用於匹配。

查詢可以在JoinRandomRoom操作中發送。 過濾查詢基本上是基於 "C0" ... "C9 "值的SQL WHERE條件。 找到所有SQLite支持的操作符的列表以及如何使用它們點此。 考慮到關鍵字排除

Example:

using Photon.Realtime;
using System.Collections.Generic;
using Hashtable = ExitGames.Client.Photon.Hashtable;

public class RandomMatchmakingExample : IMatchmakingCallbacks
{
    public const string ELO_PROP_KEY = "C0";
    public const string MAP_PROP_KEY = "C1";
    private TypedLobby sqlLobby = new TypedLobby("customSqlLobby", LobbyType.SqlLobby);
    private LoadBalancingClient loadBalancingClient;

    private void CreateRoom()
    {
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.CustomRoomProperties = new Hashtable { { ELO_PROP_KEY, 400 }, { MAP_PROP_KEY, "Map3" } };
        roomOptions.CustomRoomPropertiesForLobby = { ELO_PROP_KEY, MAP_PROP_KEY }; // makes "C0" and "C3" available in the lobby
        EnterRoomParams enterRoomParams = new EnterRoomParams();
        enterRoomParams.RoomOptions = roomOptions;
        enterRoomParams.Lobby = sqlLobby;
        loadBalancingClient.OpCreateRoom(enterRoomParams);
    }

    private void JoinRandomRoom()
    {
        string sqlLobbyFilter = "C0 BETWEEN 345 AND 475 AND C3 = 'Map2'";
        //string sqlLobbyFilter = "C0 > 345 AND C0 < 475 AND (C3 = 'Map2' OR C3 = \"Map3\")";
        //string sqlLobbyFilter = "C0 >= 345 AND C0 <= 475 AND C3 IN ('Map1', 'Map2', 'Map3')";
        OpJoinRandomRoomParams opJoinRandomRoomParams = new OpJoinRandomRoomParams();
        opJoinRandomRoomParams.SqlLobbyFilter = sqlLobbyFilter;
        loadBalancingClient.OpJoinRandomRoom(opJoinRandomRoomParams);
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region IMatchmakingCallbacks

    void IMatchmakingCallbacks.OnJoinRandomFailed(short returnCode, string message)
    {
        CreateRoom();
    }

    void IMatchmakingCallbacks.OnCreateRoomFailed(short returnCode, string message)
    {
        Debug.LogErrorFormat("Room creation failed with error code {0} and error message {1}", returnCode, message);
    }

    void IMatchmakingCallbacks.OnJoinedRoom()
    {
        // joined a room successfully, both JoinRandomRoom or CreateRoom lead here on success
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

Back To Top

鏈式過濾器

您可以在一個 "JoinRandomRoom "操作中同時發送多達3個逗號分隔的過濾器。 這些被稱為鏈式過濾器。 Photon伺服器將嘗試按順序使用這些過濾器。 如果任何一個過濾器與一個房間相匹配,那麼這個房間就會被加入。 否則將向客戶返回NoMatchFound錯誤。

鏈式過濾器可以幫助節省匹配請求並加快其進程。 這對基於技能匹配來說特別有用,因為您需要在嘗試失敗後 "鬆開 "過濾器。

可能的過濾器字符串格式:

  • 1(最小)過濾器值:{filter1} (或 {filter1};)。
  • 2個過濾值:{filter1};{filter2} (或 {filter1};{filter2};)。
  • 3個(最大)過濾值:{filter1};{filter2};{filter3} (或{filter1};{filter2};{filter3};)

Examples:

  • C0 BETWEEN 345 AND 475
  • C0 BETWEEN 345 AND 475;C0 BETWEEN 475 AND 575
  • C0 BETWEEN 345 AND 475;C0 BETWEEN 475 AND 575;C0 >= 575

Back To Top

自定義房間列表

客戶端也可以使用類似SQL的查詢方式從SqlLobby中請求一個自定義的房間列表。 這個方法將返回多達100個可見的和可加入的房間(即開放且未滿)。

using Photon.Realtime;
using System.Collections.Generic;

public class GetCustomRoomListExample : ILobbyCallbacks
{
    private TypedLobby sqlLobby = new TypedLobby("customSqlLobby", LobbyType.SqlLobby);

    public void GetCustomRoomList(string sqlLobbyFilter)
    {
      loadBalancingClient.OpGetGameList(sqlLobby, sqlLobbyFilter);
    }

    // do not forget to register callbacks via loadBalancingClient.AddCallbackTarget
    // also deregister via loadBalancingClient.RemoveCallbackTarget
    #region ILobbyCallbacks

    void ILobbyCallbacks.OnRoomListUpdate(List<RoomInfo> roomList)
    {
        // here you get the response, empty list if no rooms found
    }

    // [..] Other callbacks implementations are stripped out for brevity, they are empty in this case as not used.

    #endif
}

Back To Top

基於技能匹配

您可以使用SQL類型的大廳來實現您自己的基於技能的匹配。

首先,每個房間都有一個固定的技能,玩家應該有這個技能才能加入房間。 這個值不應該改變,否則它基本上會使房間裡的玩家之前做的任何匹配都無效。

像往常一樣,玩家應該嘗試通過JoinRandomRoom進入一個房間。 過濾器應該是基於用戶的技能。 客戶端可以很容易地過濾出 "技能+/-X "的房間。

JoinRandomRoom會像往常一樣得到回應,但是如果它沒有馬上找到匹配的房間,客戶端應該等待幾秒鐘,然後再試。 您可以隨心所欲地做很多或很少的請求。 如果您使用SQL大廳類型,您可以利用[鏈式過濾器](#chainedsqlfilters)。 最重要的是。客戶端可以隨著時間的推移開始鬆開過濾規則。

鬆開過濾器是很重要的。 認可:一個房間可能會有一個技能不太適合的玩家加入,但顯然沒有其他房間更適合,最好是和某人一起玩。

您可以定義一個最大偏差和一個超時。 如果沒有找到房間,這個客戶端必須用這個用戶的技能開一個新房間。 然後,它必須等待其他人做同樣的事情。

很明顯,當可用的房間很少時,這個工作流程可能需要一些時間。 您可以通過檢查 "程序統計 "來拯救您的玩家,它告訴您有多少房間可用。 見為低CCU匹配。 您可以為 "少於100個房間 "調整過濾器和時間,為 "100到1000個房間 "使用不同的設置,為 "甚至更多 "再次調整。

Back To Top

排除的SQL關鍵詞

SQL過濾器將不接受以下關鍵詞:

  • ALTER
  • CREATE
  • DELETE
  • DROP
  • EXEC
  • EXECUTE
  • INSERT
  • INSERT INTO
  • MERGE
  • SELECT
  • UPDATE
  • UNION
  • UNION ALL

如果您在SQL過濾字符串中使用這些詞,相應的操作將失敗。

Back To Top

異步隨機大廳類型

這種大廳類似於默認的大廳類型,有兩個主要區別。

  1. 房間條目從遊戲伺服器中刪除後,會在大廳列表中停留一個小時(可用於匹配)。 房間需要是可見的和開放的,以考慮在asynchronous matchmaking
  2. 房間列表不會被發送到客戶端。
This type of lobby should be combined with webhooks or any other mean of persisting room states in order to get asynchronous (re)join fully working.

Back To Top

大廳類型比較

LobbyType Periodic Rooms List Updates SQL Filter Max Players Filter Custom Room Properties Filter Matchmaking Modes Removed Rooms Entries TTL (minutes)
Default 0
SQL 0
Asynchronous 60

Back To Top

低CCU的匹配

對於真正好的匹配,一個遊戲需要有幾百個玩家在線。 隨著在線玩家的減少,找到一個有價值的對手將變得更加困難,在某些時候,接受幾乎任何比賽是有意義的。

當您在客戶端建立一個更復雜的匹配時,您必須考慮到這一點。 為此,Photon Master伺服器提供了連接用戶、房間和玩家(在一個房間內)的數量,所以您可以在運行時調整客戶端驅動的匹配。

房間的數量應該是一個很好的通用指標,說明遊戲目前的繁忙程度。 顯然,您也可以根據有多少玩家不在一個房間裡來調整匹配。 不在一個房間裡的人可能正在尋找一個房間。

例如,您可以把低CCU的情況定義為少於20個房間。 因此,如果房間的數量低於20個,您的客戶端就不使用過濾,而是運行快速匹配 程序。

Back To Top

開發早期測試匹配功能

如果您在開發的早期階段測試匹配,並試圖從兩個客戶端同時加入一個隨機房間,有可能兩個客戶端最終都在不同的房間:這是因為加入隨機房間不會為兩個客戶端返回一個匹配,每個客戶端可能會創建一個新的房間,因為沒有找到。 所以這是預料之中的事情,也是允許的。 為了避免這種情況,請使用JoinRandom或CreateRoom(見快速匹配),而不是JoinRandomRoom然後CreateRoom。 否則,一個可能的解決方法(僅用於開發目的)是在嘗試加入一個房間或再次重試之前(或之後)添加一個隨機延遲。 您也可以監看應用程序或大廳的統計數據,以確保一個房間至少存在或已被創建。

Back To Top

其他匹配選項

如果您想推出您自己的匹配,確保大部分是在伺服器端完成的。 由於從伺服器到客戶端的房間列表更新頻率很低(~1...2秒),客戶端沒有關於房間滿員情況的完美信息。

當您有成千上萬的玩家時,一些玩家會在同一時間發出他們的 "加入 "請求。 如果一個房間很快就滿了,您的玩家就會經常無法加入房間,匹配的時間就會越來越長。

另一方面,伺服器可以完美地分配玩家,傾向於幾乎滿員的房間,並尊重您的過濾功能。

您可以做的是,通過HTTP網絡服務,在Photon外部進行匹配,然後使用Photon來創建和加入房間(或者調用JoinOrCreate)。 這樣的匹配服務可以利用(結合)Photon的 "本地HTTP模塊"(自定義認証/WebRPCs/WebHooks),甚至是一個自定義插件來向您的網絡服務報告房間可用性。 匹配時需要考慮的事項(關鍵詞):區域(以及適用時的集群)、AppVersion、AppId、UserId、RoomName/GameId、Auth Cookie(Custom Auth)、URL標簽(WebHooks),等等。

另一個選擇是修改LoadBalancing伺服器應用程序,特別是MasterServer,匹配部分。 當然,這個選項只適用於自我托管的情況。

也就是說,在實際遊戲開始之前,使用一個房間作為 "大廳 "或 "匹配場所",在大多數情況下對流行遊戲來說不是一個好主意。

Back To Top

大廳 V1 應用程序

  • SQL大廳類型曾經發送房間列表。 現在他們不這樣做了。
  • 房間列表是沒有限制的。 現在它們是有上限的。
  • 房間列表沒有按可連接性進行排序。 現在,可連接的房間在列表中排在第一位。

Back To Top

大廳限制

Photon有以下與大廳相關的默認限制。

  • 每個應用程序的最大大廳數量:10000。
  • GameList事件中房間列表條目的最大數量(加入默認類型的大廳時的初始列表):500。
  • GameListUpdate事件中的最大更新房間條目數(加入默認類型的大廳):500。 這個限制不考慮被刪除的房間條目(對應不再可見的房間或直接消失)。
  • GetGameList操作響應中房間列表條目的最大數量(SQL大廳):100。

注意:

在大廳V2中,只有在GameList或GameListUpdate事件中,發送給加入到同一各自大廳的默認類型的客戶的初始/更新房間條目的數量限制為500。 對於GameList事件,客戶收到的數組長度將不超過500。 對於GameListUpdate事件,客戶端收到的數組長度可能超過500,因為我們不限制刪除的房間條目的數量(這些條目也在這裡發送),只限制更新的。

Lobbies v2中的新限制並不影響伺服器端的其他:房間仍然存在於大廳中。 我們只限制向加入大廳的客戶廣播的內容,是為了帶寬的原因,也是為了不使客戶不堪重負,因為客戶可能在規格上受到限制。 500是一個相當大的數字,沒有玩家會滾動查看完整的列表。 此外,在理論上,一個客戶如果在大廳裡停留足夠長的時間,可能最終會看到伺服器上的全部房間列表,因為伺服器會將所有的更新添加到一個隊列中,並以最大長度500的批次發送。 當然,如果一個房間存在於大廳中,並且有一段時間沒有變化,客戶可能會錯過。

所以在伺服器上,每個大廳的房間數量是沒有限制的。 FindFriends, CreateRoom, JoinRoom或JoinOrCreateRoom不受轉移到Lobbies v2的影響,也沒有限制,這意味著客戶可以無限期地創建房間,或者加入或找到在大廳列表更新中沒有發送給客戶的房間的朋友。

To Document Top