メインコンテンツまでスキップ

Use SDK to create match sessions - Create joinable sessions with peer-to-peer - (Unity module)

Last updated on July 28, 2025
備考

AccelByte Gaming Services (AGS) SDK for Unity does not support P2P on WebGL. This module cannot be used in WebGL builds.

Unwrap the wrapper

In this tutorial, you will learn how to implement AccelByte Gaming Services (AGS) Matchmaking using the AGS Software Development Kit (SDK). In Byte Wars, a game instance wrapper is defined in the MatchSessionP2PWrapper class. This wrapper manages game session related actions such as creating and browsing game sessions. For this tutorial, you will use the MatchSessionP2PWrapper_Starter class, which is starter version of the MatchSessionP2PWrapper class.

What's in the starter pack

To follow this tutorial, you will use the starter wrapper class called MatchSessionP2PWrapper_Starter. This class inherits from the MatchSessionEssentialsWrapper class, which is defined in the following file:

  • C# file: Assets/Resources/Modules/Play/MatchSessionEssentials/Scripts/MatchSessionEssentialsWrapper.cs

The MatchSessionP2PWrapper_Starter class is defined in the files below:

  • C# file: Assets/Resources/Modules/Play/MatchSessionP2P/Scripts/MatchSessionP2PWrapper_Starter.cs

Additionally, there are model classes containing helper functions and variables, defined in the following files:

  • C# file: Assets/Resources/Modules/Play/OnlineSessionUtils/Scripts/AccelByteWarsOnlineSessionModels.cs
  • C# file: Assets/Resources/Modules/Play/MatchSessionEssentials/Scripts/MatchSessionEssentialsModels.cs

The MatchSessionEssentialsWrapper itself inherits from the SessionEssentialsWrapper class, which contains basic session management functionality such as joining, leaving, and rejecting session invitations. You learned how to implement this functionality in the previous Introduction to Session module.

The root parent class of MatchSessionEssentialsWrapper named AccelByteWarsOnlineSession class also contains several predefined helper components, including:

  • A helper method to start peer-to-peer host and connect game client to peer-to-peer host. You need to use the AccelByteNetworkTransportManager plugin as your network transport to integrate P2P networking using AccelByte SDK.

    public virtual void TravelToP2PHost(SessionV2GameSession session, InGameMode gameMode)
    {
    AccelByteNetworkTransportManager transportManager = NetworkManager.Singleton.GetComponent<AccelByteNetworkTransportManager>();
    if (transportManager == null)
    {
    transportManager = NetworkManager.Singleton.gameObject.AddComponent<AccelByteNetworkTransportManager>();
    transportManager.Initialize(AccelByteSDK.GetClientRegistry().GetApi());
    transportManager.OnTransportEvent += GameManager.Instance.OnTransportEvent;
    }

    InitialConnectionData initialData = new InitialConnectionData()
    {
    inGameMode = gameMode,
    serverSessionId = session.id,
    userId = GameData.CachedPlayerState.PlayerId
    };
    NetworkManager.Singleton.NetworkConfig.ConnectionData = GameUtility.ToByteArray(initialData);
    NetworkManager.Singleton.NetworkConfig.NetworkTransport = transportManager;

    bool isHost = session.leaderId == GameData.CachedPlayerState.PlayerId;
    GameManager.Instance.ShowTravelingLoading(() =>
    {
    GameManager.Instance.ResetCache();
    GameData.ServerType = ServerType.OnlinePeer2Peer;

    if (isHost)
    {
    BytewarsLogger.Log("Start as P2P host");
    GameData.ServerSessionID = session.id;
    NetworkManager.Singleton.StartHost();
    }
    else
    {
    BytewarsLogger.Log($"Start as P2P client. Target host: {session.leaderId}");
    transportManager.SetTargetHostUserId(session.leaderId);
    NetworkManager.Singleton.StartClient();
    }
    },
    isHost ? StartingAsHostMessage : WaitingHostMessage);
    }

The AccelByteWarsOnlineSessionModels file contains several predefined helper components, including:

  • A string constant to store the session template name, based on the Session Template configured in the Admin Portal:

    public const string EliminationP2PSessionTemplateName = "unity-elimination-p2p";
    public const string TeamDeathmatchP2PSessionTemplateName = "unity-teamdeathmatch-p2p";
  • A helper dictionary that maps in-game modes to session request models based on the session template name:

    public static readonly Dictionary<InGameMode, Dictionary<GameSessionServerType, SessionV2GameSessionCreateRequest>> SessionCreateRequestModels = new()
    {
    ...
    {
    InGameMode.CreateMatchElimination, new()
    {
    ...
    {
    GameSessionServerType.PeerToPeer,
    new SessionV2GameSessionCreateRequest()
    {
    type = SessionConfigurationTemplateType.P2P,
    joinability = SessionV2Joinability.OPEN,
    configurationName = EliminationP2PSessionTemplateName,
    matchPool = EliminationP2PSessionTemplateName,
    }
    },
    ...
    }
    },
    {
    InGameMode.CreateMatchTeamDeathmatch, new()
    {
    ...
    {
    GameSessionServerType.PeerToPeer,
    new SessionV2GameSessionCreateRequest()
    {
    type = SessionConfigurationTemplateType.P2P,
    joinability = SessionV2Joinability.OPEN,
    configurationName = TeamDeathmatchP2PSessionTemplateName,
    matchPool = TeamDeathmatchP2PSessionTemplateName,
    }
    },
    ...
    }
    }
    ...
    };
  • A helper function to retrieve the session request model by in-game mode:

    public static SessionV2GameSessionCreateRequest GetGameSessionRequestModel(
    InGameMode gameMode,
    GameSessionServerType serverType)
    {
    if (!SessionCreateRequestModels.TryGetValue(gameMode, out var matchTypeDict))
    {
    return null;
    }

    matchTypeDict.TryGetValue(serverType, out var request);
    return request;
    }

The MatchSessionEssentialsModels file contains several predefined helper components, including:

  • Custom constants to be used to filter game session queries based on the server type.

    public static readonly string MatchSessionAttributeKey = "match_session_type";
    public static readonly string MatchSessionP2PAttributeValue = "unity_match_session_p2p";

    public static readonly Dictionary<string, object> MatchSessionP2PAttribute = new()
    {
    { MatchSessionAttributeKey, MatchSessionP2PAttributeValue }
    };
  • Helper class to construct the browse game sessions result.

    public class BrowseSessionModel
    {
    public SessionV2GameSession Session { get; private set; }
    public AccountUserPlatformData Owner { get; private set; }
    public int CurrentMemberCount { get; private set; }
    public int MaxMemberCount { get; private set; }
    public InGameMode GameMode { get; private set; }
    public GameSessionServerType ServerType { get; private set; }

    public BrowseSessionModel()
    {
    Session = null;
    Owner = null;

    CurrentMemberCount = 0;
    MaxMemberCount = 0;

    GameMode = InGameMode.None;
    ServerType = GameSessionServerType.None;
    }

    public BrowseSessionModel(SessionV2GameSession session, AccountUserPlatformData owner)
    {
    Session = session;
    Owner = owner;

    CurrentMemberCount = session.members.Count(member => member.status == SessionV2MemberStatus.JOINED);
    MaxMemberCount = session.configuration.maxPlayers;

    GameMode = GetGameSessionGameMode(session);
    ServerType = GetGameSessionServerType(session);
    }
    }

    public class BrowseSessionResult
    {
    public readonly Dictionary<string, object> QueryAttribute;
    public readonly PaginatedResponse<BrowseSessionModel> Result;

    public BrowseSessionResult(
    Dictionary<string, object> queryAttribute,
    PaginatedResponse<BrowseSessionModel> result)
    {
    QueryAttribute = queryAttribute;
    Result = result;
    }
    }

Create match session

This section will guide you on how to create game session with peer-to-peer using the AGS SDK.

  1. Open the MatchSessionP2PWrapper_Starter class and create the following function. This function enrich the session creation request by adding several attributes. Then, it send the session creation request by calling the parent class function, which is the what you've learn from the Introduction to Session module. Once the request is completed, it checks whether the peer-to-peer host is ready to connect. From the code below, you may notice that it also enriches the request by adding several values to the optional attributes. Here's what those attributes do:

    • members: The attribute used to add session members. In this case, it is used to add party members to join the game session.
    • attributes: The custom attribute list you can add for your game session. In this case, it is used to filter the session by server type using the custom MatchSessionP2PAttribute attribute.
    public override void CreateGameSession(
    SessionV2GameSessionCreateRequest request,
    ResultCallback<SessionV2GameSession> onComplete)
    {
    // Add party session id for playing with party feature.
    SessionV2PartySession partySession = PartyEssentialsModels.PartyHelper.CurrentPartySession;
    if (partySession != null && !string.IsNullOrEmpty(partySession.id))
    {
    request.members = partySession.members;
    }

    // Add custom attribute as filter for session browser.
    request.attributes = MatchSessionP2PAttribute;

    base.CreateGameSession(request, (result =>
    {
    if (!result.IsError)
    {
    InGameMode requestedGameMode = GetGameSessionGameMode(result.Value);
    if (requestedGameMode == InGameMode.None)
    {
    BytewarsLogger.LogWarning($"Failed to travel to the P2P host. Session's game mode is not supported by the game.");
    onComplete?.Invoke(Result<SessionV2GameSession>.CreateError(ErrorCode.NotAcceptable, InvalidSessionTypeMessage));
    return;
    }
    TravelToP2PHost(result.Value, requestedGameMode);
    }

    onComplete?.Invoke(result);
    }));
    }

Join match session

This section will guide you on how to join game session with peer-to-peer using the AGS SDK.

  1. Open the MatchSessionDSWrapper_Starter class and create the following function. This function sends the join session request by calling the parent class function, which is what you learned from the Introduction to Session module. Once the request is completed, it checks whether the peer-to-peer host is ready to connect.

    public override void JoinGameSession(
    string sessionId,
    ResultCallback<SessionV2GameSession> onComplete)
    {
    base.JoinGameSession(sessionId, (result) =>
    {
    if (!result.IsError)
    {
    InGameMode requestedGameMode = GetGameSessionGameMode(result.Value);
    if (requestedGameMode == InGameMode.None)
    {
    BytewarsLogger.LogWarning($"Failed to travel to the P2P host. Session's game mode is not supported by the game.");
    onComplete?.Invoke(Result<SessionV2GameSession>.CreateError(ErrorCode.NotAcceptable, InvalidSessionTypeMessage));
    return;
    }
    TravelToP2PHost(result.Value, requestedGameMode);
    }

    onComplete?.Invoke(result);
    });
    }

Browse match sessions

This section will guide you on how to query game sessions using the AGS SDK.

  1. Open the MatchSessionP2PWrapper_Starter class and create the following function to query game sessions based on specific attribute. In this case, it filter only game session that has peer-to-peer type using the MatchSessionP2PAttribute. If it has specific page to load, it tried to parse the pagination URL and add it to the query attribute. Finally, it starts to query the game session and return the result along with the session owner details.

    public override void BrowseGameSessions(
    string pageUrl,
    ResultCallback<BrowseSessionResult> onComplete)
    {
    // If provided, try parse the pagination URL parameters and add them to the query attributes.
    Dictionary<string, object> queryAttributes = new(MatchSessionP2PAttribute);
    if (!string.IsNullOrEmpty(pageUrl))
    {
    Dictionary<string, object> queryParams = ParseQuerySessionParams(pageUrl);
    if (queryParams == null)
    {
    BytewarsLogger.LogWarning($"Failed to find game sessions. Pagination URL is invalid.");
    onComplete?.Invoke(Result<BrowseSessionResult>.CreateError(ErrorCode.InvalidArgument, InvalidSessionPaginationMessage));
    return;
    }
    queryParams.ToList().ForEach(x => queryAttributes[x.Key] = x.Value);
    }

    // Query game sessions and filter them based on the custom attributes.
    Session.QueryGameSession(queryAttributes, sessionResult =>
    {
    if (sessionResult.IsError)
    {
    BytewarsLogger.LogWarning(
    $"Failed to query game sessions. " +
    $"Error {sessionResult.Error.Code}: {sessionResult.Error.Message}");
    onComplete?.Invoke(Result<BrowseSessionResult>.CreateError(sessionResult.Error));
    return;
    }

    BytewarsLogger.Log("Success to query game sessions.");

    // Return immediately if result is empty.
    if (sessionResult.Value.data.Length <= 0)
    {
    onComplete?.Invoke(Result<BrowseSessionResult>.CreateOk(
    new BrowseSessionResult(
    queryAttributes,
    new PaginatedResponse<BrowseSessionModel>()
    {
    data = Array.Empty<BrowseSessionModel>(),
    paging = sessionResult.Value.paging
    })));
    return;
    }

    // Query the session owner user information.
    string[] sessionOwners = sessionResult.Value.data.Select(x => x.createdBy).ToArray();
    User.GetUserOtherPlatformBasicPublicInfo("ACCELBYTE", sessionOwners, (userResult) =>
    {
    if (userResult.IsError)
    {
    BytewarsLogger.LogWarning(
    $"Failed to query game sessions. " +
    $"Error {userResult.Error.Code}: {userResult.Error.Message}");
    onComplete?.Invoke(Result<BrowseSessionResult>.CreateError(userResult.Error));
    return;
    }

    // Construct session results.
    List<BrowseSessionModel> result = new();
    foreach (SessionV2GameSession session in sessionResult.Value.data)
    {
    AccountUserPlatformData ownerData = userResult.Value.Data.FirstOrDefault(x => x.UserId == session.createdBy);
    if (session != null && ownerData != null)
    {
    result.Add(new(session, ownerData));
    }
    }

    // Return results.
    onComplete?.Invoke(Result<BrowseSessionResult>.CreateOk(
    new BrowseSessionResult(
    queryAttributes,
    new PaginatedResponse<BrowseSessionModel>()
    {
    data = result.ToArray(),
    paging = sessionResult.Value.paging
    })));
    });
    });
    }

Listen to match session events

This section will guide you on how to listen on the game session events.

  1. Open the MatchSessionP2PWrapper_Starter class and replace the predefined Awake() function with the code below. This code binds a delegate to listen for when the browse menu is displayed. When that happens, it triggers the browse session function and returns the result to be displayed in the menu.

    protected override void Awake()
    {
    base.Awake();

    BrowseMatchMenu.OnBrowseP2PMatch += BrowseGameSessions;
    }

Resources