How to connect to your friend room in pun 2?

Hi guys I am using pun 2 as you can tell from the title and I want to ask how can I join to my friend party?
I have a list of my friends but I do not know how to do it pls help I am stuck on it a lot of time. I know there is a thing that called expected players and I check the Matchmaking Guide. didn't understand it ):

Comments

  • pls help
  • TeohTC
    TeohTC
    edited August 2020
    I working on party system also and basic functions already completed, what I did was using Photon Chat channel for making party with friends(You need to write your own custom chat message to determine message type like example party invite, party accept, party left and etc) and record down the players ID then put into expected players field when doing match making
  • Hi @TeohTC Can you send your code for this, I want to look. thx
  • Guy wrote: »
    Hi @TeohTC Can you send your code for this, I want to look. thx

    Don't expect it to work out of box...I have remove tons of unrelated code, but concept of party system is there

    Chat Manager
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Events;
    using Photon.Chat;
    using Photon.Realtime;
    using Photon.Pun;
    using ExitGames.Client.Photon;
    
    public class ChatManagerScript : MonoBehaviour, IChatClientListener{
    
        public enum ChatRegion { EU, US, ASIA }
    
        //Enum for message type
        public enum MessageType {PublicMessage, PrivateMessage, PartyInvite, PartyInviteWithPartyID, Party_AlreadyInParty, PartyAccept, PartyReject, PartyLeft, Party_MatchMaking, Party_MatchMaking_Cancel}
    
        //Singleton
        public static ChatManagerScript instance;
    
        //Photon Chat
        ChatClient chatClient;
        protected internal AppSettings chatAppSettings;
        private ChatRegion currentChatRegion;
        public ChatState chatState;
        bool chatConnected = false;
    
        [Header("Public Channel Configuration")]
        [SerializeField] private string[] publicChatChannel = new string[]{"Global"};
    
        //Party
        UnityEvent party_SuccessCallback, party_FailCallback;
        private string party_ChannelName;
    
        private void Awake() {
    
            instance = this;
    
        }
    
        /// <summary>To avoid that the Editor becomes unresponsive, disconnect all Photon connections in OnApplicationQuit.</summary>
        public void OnApplicationQuit() {
    
            if (this.chatClient != null) {
                this.chatClient.Disconnect();
            }
    
        }
    
        // Update is called once per frame
        void Update(){
    
            if (this.chatClient != null) {
                this.chatClient.Service(); // make sure to call this regularly! it limits effort internally, so calling often is ok!
            }
    
        }
    
        /// <summary>
        /// To initialize the chat region
        /// </summary>
        /// <param name="chatRegion"></param>
        public void InitializePhotonChat(ChatRegion chatRegion){
    
            if(chatConnected)
                return;
    
            //Set chat app setting
            chatAppSettings = PhotonNetwork.PhotonServerSettings.AppSettings;
    
            currentChatRegion = chatRegion;
    
            // In the C# SDKs, the callbacks are defined in the `IChatClientListener` interface.
            // In the demos, we instantiate and use the ChatClient class to implement the IChatClientListener interface.
            chatClient = new ChatClient(this);
    
            // Set your favourite region. "EU", "US", and "ASIA" are currently supported.
            chatClient.ChatRegion = chatRegion.ToString();
    
            var authValues = new Photon.Chat.AuthenticationValues();
            authValues.AuthType = Photon.Chat.CustomAuthenticationType.Custom;
            authValues.UserId = PhotonNetwork.LocalPlayer.UserId;
    
            chatClient.Connect(chatAppSettings.AppIdChat, Application.version, authValues);
    
        }
    
        /// <summary>
        /// To initialize the chat region
        /// </summary>
        /// <param name="chatRegion"></param>
        public void InitializePhotonChat(ref string photonConnectedRegion){
    
            if(chatConnected)
                return;
            
            currentChatRegion = GetChatRegion(ref photonConnectedRegion);
    
            InitializePhotonChat(currentChatRegion);
    
        }
    
        public void PhotonChat_Reconnect(){
    
            InitializePhotonChat(currentChatRegion);
    
        }
    
        /// <summary>
        /// Switch the chat region if needed
        /// </summary>
        /// <param name="chatRegion"></param>
        void SwitchRegion(ChatRegion chatRegion) {
    
            if(currentChatRegion == chatRegion)
                return;
    
            if (this.chatClient != null) {
                this.chatClient.Disconnect();
            }
    
            InitializePhotonChat(chatRegion);
    
        }
    
        #region Public Chat Channel
    
        /// <summary>
        /// To subscribe to public channel
        /// </summary>
        public void PublicChatChannel_Subscribe(){
            
            if(chatClient == null || chatConnected == false){
                StartCoroutine(PublicChatChannel_AttemptToConnect());
            }else{
                chatClient.Subscribe(publicChatChannel);
            }
    
        }
    
        /// <summary>
        /// To Unsubscribe the public channel
        /// </summary>
        public void PublicChatChannel_Unsubscribe(){
    
            chatClient.Unsubscribe(publicChatChannel);
    
        }
    
        IEnumerator PublicChatChannel_AttemptToConnect(){
    
            //If is null then pause till not null
            while(chatClient == null){
                yield return new WaitForFixedUpdate();
            }
    
            while(!chatConnected){
    
                yield return new WaitForFixedUpdate();
    
            }
    
            chatClient.Subscribe(publicChatChannel);
    
        }
    
        /// <summary>
        /// To send message to public channel
        /// </summary>
        /// <param name="message"></param>
        /// <param name="chatChannel"></param>
        public void PublicChatChannel_Send(ref string message){
           
            //Repackage the message
            MessageContainer mc;
            mc.senderUserIGN = PlayersDataScript.instance.playerProfile.player_IGN;
            mc.messageType = MessageType.PublicMessage;
            mc.message = message;
            mc.data = "";
    
            chatClient.PublishMessage(publicChatChannel[0], mc);
    
        }
    
        /// <summary>
        /// Send message to target channel
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="message"></param>
        public void PublicChatChannel_Send(ref string channel, ref MessageContainer message){
            
            chatClient.PublishMessage(channel, message);
    
        }
    
        /// <summary>
        /// To handle the received message
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="ign"></param>
        /// <param name="message"></param>
        void PublicChatChannel_Receive(MessageContainer message){
    
            //Ask Chat System to spawn
            MainMenuChatManagerScript.instance.Chat_ReceiveMessage(ref message.senderUserIGN, ref message.message);
    
        }
    
        #endregion
    
        #region Friend Chat
    
        /// <summary>
        /// Function to send out the message
        /// </summary>
        /// <param name="uid"></param>
        /// <param name="messageContainer"></param>
        public void FriendChat_Send(string uid, MessageContainer messageContainer){
            
            chatClient.SendPrivateMessage(uid, messageContainer);
    
        }
    
        /// <summary>
        /// Function to interpret the message
        /// </summary>
        /// <param name="uid"></param>
        /// <param name="messageData"></param>
        void FriendChat_ReceiveMessage(ref string sender, ref string channel, object messageData){
            
            MessageContainer message = (MessageContainer)messageData;
    
            //Debug.Log("Message Type: " + message.messageType.ToString() + ", Message: " + message.senderUserID + message.message + ", Data: " + message.data);
    
            switch(message.messageType){
                case MessageType.PartyInvite:
                case MessageType.PartyInviteWithPartyID:
                    PartySystemScript.instance.Party_Invite(ref sender, message);
                    break;
                case MessageType.PartyAccept:
                    PartySystemScript.instance.Party_Inviter_PlayerAccept(ref sender, ref message.senderUserIGN);
                    break;
                case MessageType.Party_AlreadyInParty:
                    PartySystemScript.instance.Party_Inviter_Error_PlayerAlreadyInParty(ref message.senderUserIGN);
                    break;
            }
    
        }
    
        #endregion
    
        #region Party
    
        public void Party_JoinParty(ref string channelName, UnityEvent successCallback, UnityEvent failCallback){
    
            party_SuccessCallback = successCallback;
            party_FailCallback = failCallback;
            party_ChannelName = channelName;
    
            ChannelCreationOptions cco = new ChannelCreationOptions();
            cco.PublishSubscribers = true;
            cco.MaxSubscribers = 3;
            chatClient.Subscribe(channelName, 0, -1, new ChannelCreationOptions(){PublishSubscribers = true, MaxSubscribers = 3});
    
        }
    
        void Party_SuccessCreated(){
            
            party_SuccessCallback?.Invoke();
    
        }
    
        void Party_FailToCreate(){
            
            party_FailCallback?.Invoke();
    
        }
    
        void Party_PartyTeamUpdate(bool isUnsubscribe, ref string userName){
            
            Photon.Chat.ChatChannel cc;
    
            if(chatClient.TryGetChannel(party_ChannelName, out cc)){
                
                int count = cc.Subscribers.Count;
                HashSet<string> subscribers = cc.Subscribers;
    
                PartySystemScript.instance.Party_UpdateInPartyCount(count, isUnsubscribe, ref subscribers, ref userName);
    
            }
    
        }
    
        /// <summary>
        /// To unsubscribe from the party chat channel
        /// </summary>
        public void Party_LeftParty(ref string userID){
    
            //Unsubscribe from the channel party
            chatClient.Unsubscribe(new string[]{party_ChannelName});
    
        }
    
        /// <summary>
        /// Function to interpret the received message
        /// </summary>
        /// <param name="message"></param>
        public void Party_ReceiveMessage(ref string sender, object message){
            
            MessageContainer mc = (MessageContainer)message;
    
            //Pass to party system to handle the message
            PartySystemScript.instance.Party_ReceiveMessage(ref sender, ref mc);
    
        }
    
        #endregion
    
        #region Helper
        ChatRegion GetChatRegion(ref string currentConenctedRegion){
            
            ChatRegion region = ChatRegion.ASIA;
    
            switch(currentConenctedRegion){
                case "asia":
                case "au":
                case "in":
                case "jp":
                case "rue":
                case "kr":
                    region = ChatRegion.ASIA;
                    break;
                case "eu":
                case "ru":
                    region = ChatRegion.EU;
                    break;
                case "us":
                case "cae":
                case "sa":
                case "usw":
                    region = ChatRegion.US;
                    break;
            }
    
            return region;
    
        }
    
        /// <summary>
        /// To get the channel name
        /// </summary>
        /// <param name="channelName"></param>
        /// <param name="channel"></param>
        /// <returns></returns>
        public bool GetChannel(ref string channelName, out Photon.Chat.ChatChannel channel){
            
            if(chatClient != null){
                return chatClient.TryGetChannel(channelName, false, out channel);
            }else{
                channel = null;
                return false;
            }
    
            
    
        }
    
        public bool Chat_IsConnected(){
    
            if(chatClient == null)
                return false;
    
            if(chatClient.State == ChatState.ConnectedToFrontEnd)
                return true;
            else
                return false;
            
        }
    
        #endregion
    
        #region Photon Chat Callback Function
    
        public void DebugReturn(DebugLevel level, string message) {
    
            if (level == ExitGames.Client.Photon.DebugLevel.ERROR) {
                Debug.LogError(message);
            } else if (level == ExitGames.Client.Photon.DebugLevel.WARNING) {
                Debug.LogWarning(message);
            } else {
                Debug.Log(message);
            }
    
        }
    
        public void OnDisconnected() {
    
            Debug.Log("Photon Chat: Disconnect, attempt to reconnect");
    
            chatConnected = false;
            PhotonChat_Reconnect();
    
        }
    
        public void OnConnected() {
    
            chatConnected = true;
            Debug.Log("Photon Chat: Connected");
    
        }
    
        public void OnChatStateChange(ChatState state) {
    
            chatState = state;
    
        }
    
        public void OnGetMessages(string channelName, string[] senders, object[] messages) {
            
            int loopCount = senders.Length;
    
            if(channelName == ConstStringContainer.chat_PublicChannel){
                
                //Spawn the message
                for(int i = 0; i < loopCount; i++){
    
                    //Convert the object to message
                    MessageContainer mc = (MessageContainer)messages[i];
    
                    PublicChatChannel_Receive(mc);
    
                }
    
            }else if(channelName == party_ChannelName){ //Party chat
    
                for(int i = 0; i < loopCount; i++){
                    Party_ReceiveMessage(ref senders[i], messages[i]);
                }
            }
    
        }
    
        public void OnPrivateMessage(string sender, object message, string channelName) {
            
            FriendChat_ReceiveMessage(ref sender, ref channelName, message);
    
        }
    
        public void OnSubscribed(string[] channels, bool[] results) {
            
            int loopCount = channels.Length;
    
            for(int i = 0; i < loopCount; i++){
                
                if(results[i]){
                    
                    Debug.Log("Chat: Success subscribe to channel " + channels[i]);
    
                    if(channels[i].Equals(party_ChannelName)){
    
                        Party_SuccessCreated();
    
                    }
    
                }else{
    
                    Debug.Log("Chat: Fail subscribe to channel " + channels[i]);
    
                    if(channels[i].Equals(party_ChannelName)){
    
                        Party_FailToCreate();
    
                    }
    
                }
    
            }
    
        }
    
        public void OnUnsubscribed(string[] channels) {
    
            int loopCount = channels.Length;
    
            for(int i = 0; i < loopCount; i++){
    
                Debug.Log("Chat: unsubscribe to channel " + channels[i]);
    
            }
    
        }
    
        public void OnStatusUpdate(string user, int status, bool gotMessage, object message) {
            throw new System.NotImplementedException();
        }
    
        public void OnUserSubscribed(string channel, string user) {
    
            //Check for party
            if(channel.Equals(party_ChannelName)){
    
                Party_PartyTeamUpdate(false, ref user);
    
            }
    
        }
    
        public void OnUserUnsubscribed(string channel, string user) {
    
            Debug.Log("Chat: User Unsubscribed " + user);
    
            //Check for party
            if(channel.Equals(party_ChannelName)){
    
                Party_PartyTeamUpdate(true, ref user);
    
            }
    
        }
    
        #endregion
    
    }
    
    public struct MessageContainer{
    
        public ChatManagerScript.MessageType messageType;   //To know which message type
    
        public string senderUserIGN;
    
        public string message;
    
        public string data;
    
    }
    

    Party System Manager
    using UnityEngine;
    using UnityEngine.Events;
    using Lean.Localization;
    using Photon.Pun;
    using Photon.Chat;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    
    public class PartySystemScript : MonoBehaviour{
    
        //Singleton
        public static PartySystemScript instance;
    
        //Instance
        ChatManagerScript chatManager;
        MainMenuScript mainMenuScript;
        PartySystemUIScript partySystemUIScript;
    
        private bool party_InParty = false;
        public bool party_Inparty { get { return party_InParty; } }
    
        //Caching
        private string senderIGN, senderUID;
    
        //Handle created party
        private string party_PartyName = "";
        private int party_TotalPlayer = 0;
        public int party_PlayerCount { get { return party_TotalPlayer; } }
        private bool party_Leader = false;
        public bool party_IsLeader { get { return party_Leader; } }
        private string party_Leader_UserID = "";
        private HashSet<string> party_List_PlayerList = new HashSet<string>();   //This list to store player ID list
    
        [Header("Join Party")]
        [SerializeField] private UnityEvent joinParty_Success_Event;
        [SerializeField] private UnityEvent joinParty_Fail_Event;
    
        IEnumerator Start() {
    
            chatManager = ChatManagerScript.instance;
            mainMenuScript = MainMenuScript.instance;
            partySystemUIScript = PartySystemUIScript.instance;
    
            joinParty_Success_Event.AddListener(JoinParty_Success);
            joinParty_Fail_Event.AddListener(JoinParty_Fail);
    
            yield return new WaitForSeconds(0.5f);
    
            InParty_GetInPartyStatus(); //Initialize
    
        }
    
        void OnEnable() {
            instance = this;    
        }
    
        void OnDisable() {
            instance = null;
        }
    
        private void OnApplicationQuit() {
            Party_SetInParty(false);
        }
    
        #region Invite Receiver
    
        /// <summary>
        /// To prompt user about party invite
        /// </summary>
        public void Party_Invite(ref string senderUserID, MessageContainer messageData){
    
            //If same sender name then dont know
            if(PhotonNetwork.LocalPlayer.UserId.Equals(senderUserID))
                return;
    
            //If already in party then dont show and reply to sender that this client in party
            if(party_InParty){
    
                Party_Invitee_ReplyAlreadyInParty(ref senderUserID);
    
                return;
    
            }
    
            //Generate message
            string message = senderIGN + (string)LeanLocalization.GetTranslation(ConstStringContainer.localize_Transl_Party_InviteMessage).Data;
    
            //Show the party board
            MainMenuNoticeScript.instance.Party_Invite_ShowInvite(ref message);
    
            //Cache
            this.senderIGN = messageData.senderUserIGN;  //The userID actually storing the IGN
            senderUID = senderUserID;
            party_Leader_UserID = messageData.message;
    
            //Only store then party name is not null
            if(messageData.data.Equals("")){
    
                party_PartyName = messageData.data;
    
            }
    
        }  
    
        /// <summary>
        /// To accept the party invite
        /// </summary>
        public void Party_Invitee_Accept(){
    
            MessageContainer messageContainer;
            messageContainer.senderUserIGN = PlayersDataScript.instance.playerProfile.player_IGN;
            messageContainer.messageType = ChatManagerScript.MessageType.PartyAccept;
            messageContainer.data = "";
            messageContainer.message = "";
    
            //Replay client with accept
            chatManager.FriendChat_Send(senderUID, messageContainer);
    
            //Create a party name which will be use for chat channel also
            if(party_PartyName.Equals(""))
                party_PartyName = senderUID + "_Party";
    
            //Set in party
            Party_SetInParty(true, ref party_PartyName);
    
            Party_SetPartyLeader(false);
    
            //join chat channel as party
            chatManager.Party_JoinParty(ref party_PartyName, joinParty_Success_Event, joinParty_Fail_Event);
    
        }
    
        /// <summary>
        /// To reject the party invite
        /// </summary>
        public void Party_Invitee_Reject(){
    
            party_PartyName = "";
            //Set in party
            Party_SetInParty(false, ref party_PartyName);
            party_Leader_UserID = "";
    
        }
    
        /// <summary>
        /// This function will reply the inviter that this player already in someone party
        /// </summary>
        void Party_Invitee_ReplyAlreadyInParty(ref string targetMessageReceiver){
    
            MessageContainer mc;
            mc.messageType = ChatManagerScript.MessageType.Party_AlreadyInParty;
            mc.data = "";
            mc.message = "";
            mc.senderUserIGN = PlayersDataScript.instance.playerProfile.player_IGN;
            
            chatManager.FriendChat_Send(targetMessageReceiver, mc);
    
        }
    
        #endregion
    
        #region Invite Sender
    
        /// <summary>
        /// Function to send invite to the player
        /// </summary>
        /// <param name="userID">Target Receiver</param>
        public void Party_Inviter_InvitePlayer(ref string userID){
    
            MessageContainer messageContainer;
    
            if(!party_InParty){
                
                messageContainer.senderUserIGN = PlayersDataScript.instance.playerProfile.player_IGN;
                messageContainer.messageType = ChatManagerScript.MessageType.PartyInvite;
                messageContainer.message = PhotonNetwork.LocalPlayer.UserId;  //This use to store the party host ID
                messageContainer.data = "Room ID";  //Might need to replace this
    
            }else{
    
                //Check is party full before send invite
                if(InParty_IsPartyFull()){
    
                    string message = (string)Lean.Localization.LeanLocalization.GetTranslation(ConstStringContainer.localize_Transl_Party_PartyFull).Data;
                    
                    //Show party full error
                    MainMenuNoticeScript.instance.ErrorNotice_Show(ref message);
    
                    return;
    
                }
    
                messageContainer.senderUserIGN = PlayersDataScript.instance.playerProfile.player_IGN;
                messageContainer.messageType = ChatManagerScript.MessageType.PartyInviteWithPartyID;
                messageContainer.message = party_Leader_UserID;  //Include the host ID
                messageContainer.data = party_PartyName;  //Might need to replace this
    
            }
    
            chatManager.FriendChat_Send(userID, messageContainer);
            
        }
        
        /// <summary>
        /// This function call when target player accept party invite
        /// </summary>
        public void Party_Inviter_PlayerAccept(ref string playerName, ref string playerID){
    
            if(party_InParty == false){
    
                //Create a party name which will be use for chat channel also
                party_PartyName = PhotonNetwork.LocalPlayer.UserId + "_Party";
    
                //Set in party
                Party_SetInParty(true, ref party_PartyName);
    
                //join chat channel as party
                chatManager.Party_JoinParty(ref party_PartyName, joinParty_Success_Event, joinParty_Fail_Event);
    
                Party_SetPartyLeader(true);
    
            }
    
        }
    
        /// <summary>
        /// Interpret message received from chat manager
        /// To handle the invited player already in party
        /// </summary>
        /// <param name="playerName"></param>
        public void Party_Inviter_Error_PlayerAlreadyInParty(ref string playerName){
    
            MainMenuNoticeScript.instance.ErrorNotice_Show((string)LeanLocalization.GetTranslation(ConstStringContainer.localize_Transl_Party_AlreadyInParty).Data);
    
        }
    
        #endregion
    
        #region Join Party
    
        void JoinParty_Success(){
    
            partySystemUIScript.InParty_Dialogue_Show(true);
    
            //Hide the game start button or set it to not clickable
            if(party_Leader == false)
                mainMenuScript.GameMode_Buttons_SetActive(false);
    
        }
    
        void JoinParty_Fail(){
    
            partySystemUIScript.InParty_Dialogue_Show(false);
    
            mainMenuScript.GameMode_Buttons_SetActive(true);
    
        }
    
        #endregion
    
        #region In party
    
        /// <summary>
        /// Initialzie function
        /// To check whether player is in party for situation when player return from gameplay
        /// </summary>
        void InParty_GetInPartyStatus(){
    
            //If chat haven't initialize then dont do the check
            if(ChatManagerScript.instance.Chat_IsConnected() == false){
                Party_SetInParty(false);
                return;
            }
    
            party_InParty = (PlayerPrefs.GetInt(ConstStringContainer.party_InParty_Key, 0) == 0)? false: true;
            party_PartyName = PlayerPrefs.GetString(ConstStringContainer.party_PartyName_Key);
    
            //Check whether in the channel, if in channel mean in party
            if(party_InParty){
                
                ChatChannel chatChannel;
                
                //True then the channel was found, mean player in party
                if(chatManager.GetChannel(ref party_PartyName, out chatChannel)){
    
                    //Set is party leader
                    party_Leader = (PlayerPrefs.GetInt(ConstStringContainer.Party_PartyLeader_Key) == 0)? false : true;
    
                    //Initialize UI party system related function
                    JoinParty_Success();
    
                }else{  //Else then reset the inparty status
    
                    party_PartyName = "";
                    Party_SetInParty(false, ref party_PartyName);
    
                }
    
            }
    
        }
    
        /// <summary>
        /// In Party Function
        /// To quit the party
        /// </summary>
        public void InParty_LeftParty(){
    
            //Unsubscribe from the party chat channel
            string userID = PhotonNetwork.LocalPlayer.UserId;
            chatManager.Party_LeftParty(ref userID);
    
            //Hide In Party UI
            partySystemUIScript.InParty_Dialogue_Show(false);
    
            //Enable the start game related button
            mainMenuScript.GameMode_Buttons_SetActive(true);
    
            //Reset the cache
            Party_ClearCache();
    
            //Set in party
            Party_SetInParty(false, ref party_PartyName);
    
        }
    
        public bool InParty_IsPartyFull(){
            
            ChatChannel cc;
            
            chatManager.GetChannel(ref party_PartyName, out cc);
            
            if(cc.MaxSubscribers == party_TotalPlayer){
            
                return true;
    
            }else{
                
                return false;
    
            }
    
        }
    
        public void InParty_GetPartyLeaderUserID(out string cacheString){
    
            cacheString = party_Leader_UserID;
    
        }
    
        /// <summary>
        /// To get the others player ID without the party leader
        /// </summary>
        /// <param name="targetList"></param>
        public void InParty_GetOthersPlayer(out HashSet<string> targetList){
    
            targetList = party_List_PlayerList;
    
            targetList.Remove(party_Leader_UserID);
    
        }
    
        /// <summary>
        /// Check is player in party, if in party then call to switch the panel
        /// </summary>
        public void InParty_SwitchToCharacterSelectionCheck(int modeNo, ref string map){
            
            if(party_InParty){
    
                //Send Message to switch the scene
                MessageContainer mc;
                mc.messageType = ChatManagerScript.MessageType.Game_CharacterSelection;
                mc.message = modeNo.ToString();
                mc.data = map;
                mc.senderUserIGN = "";
    
                chatManager.PublicChatChannel_Send(ref party_PartyName, ref mc);
    
            }
    
        }
    
        /// <summary>
        /// To inform others member the host quite the character selection
        /// </summary>
        public void InParty_ReturnCharacterSelectionToMainMenu(){
    
            if (party_InParty) {
    
                //Send Message to switch the scene
                MessageContainer mc;
                mc.messageType = ChatManagerScript.MessageType.Game_ReturnFromCharacterSelection;
                mc.message = "";
                mc.data = "";
                mc.senderUserIGN = "";
    
                chatManager.PublicChatChannel_Send(ref party_PartyName, ref mc);
    
            }
    
        }
    
        /// <summary>
        /// To inform others about match making start
        /// </summary>
        public void InParty_StartMatchMaking(){
    
            if (party_InParty) {
    
                //Send Message to switch the scene
                MessageContainer mc;
                mc.messageType = ChatManagerScript.MessageType.Party_MatchMaking;
                mc.message = "";
                mc.data = "";    //This the mode ID
                mc.senderUserIGN = "";
    
                chatManager.PublicChatChannel_Send(ref party_PartyName, ref mc);
    
            }
    
        }
    
        /// <summary>
        /// To inform others about match making cancel
        /// </summary>
        public void InParty_CancelMatchMaking(){
    
            if (party_InParty) {
    
                //Send Message to switch the scene
                MessageContainer mc;
                mc.messageType = ChatManagerScript.MessageType.Party_MatchMaking_Cancel;
                mc.message = "";
                mc.data = "";    //This the mode ID
                mc.senderUserIGN = "";
    
                chatManager.PublicChatChannel_Send(ref party_PartyName, ref mc);
    
            }
    
        }
    
        #endregion
    
        
    
        #region Helper
    
        /// <summary>
        /// To update party count
        /// This will call on everyone
        /// </summary>
        /// <param name="count">Players Count</param>
        /// <param name="userIDs">Player List</param>
        /// <param name="relatedUser">The related user for this callback</param>
        public void Party_UpdateInPartyCount(int count, bool isUnsubscribe, ref HashSet<string> userIDs, ref string relatedUser){
            
            party_TotalPlayer = count;
    
            //Update party user list
            party_List_PlayerList = userIDs;
    
            //If is unsubscribe then need to remove that player information
            if(isUnsubscribe){
    
                //Check is host still in list, if not mean need to disband party and etc
                if (party_List_PlayerList.Contains(party_Leader_UserID) == false) {
    
                    InParty_LeftParty();
    
                } else{
    
                    //If left 1 player in party then disband party also
                    if (party_TotalPlayer == 1) {
    
                        InParty_LeftParty();
    
                    }else{
                        
                        //if leader still exists and not 1 players left, then remove the related user info
                        for(int i = 0; i < count; i++){
                            
                            if(party_List_PlayerInfo[i].id.Equals(relatedUser)){
    
                                party_List_PlayerInfo.RemoveAt(i);
                                break;
    
                            }
    
                        }
    
                    }
    
                }
    
            }else{  //This is subscribe event
    
            }
    
        }
    
        void Party_ClearCache(){
    
            party_PartyName = "";
            Party_SetPartyLeader(false);
            party_List_PlayerInfo = null;
    
        }
    
        /// <summary>
        /// To handle the received party message
        /// </summary>
        /// <param name="message"></param>
        public void Party_ReceiveMessage(ref MessageContainer message){
    
            switch(message.messageType){
                case ChatManagerScript.MessageType.Game_CharacterSelection: //which mean switch character selection menu
                    if(!party_Leader){  //Only call if u are not leader
                        int mode = Int32.Parse(message.message);
                        mainMenuScript.Panel_Party_MainMenuToHeroSelection(mode, ref message.data);    //Message.Data contain the map name
                    }
                    break;
                case ChatManagerScript.MessageType.Game_ReturnFromCharacterSelection:
                    if (!party_Leader) {
                        mainMenuScript.Panel_StartGameHeroSelection_To_MainMenu();
                    }
                    break;
                case ChatManagerScript.MessageType.Party_MatchMaking:
                    if(!party_Leader){
                        mainMenuScript.MatchMaking_Party_OtherClientsStart();
                    }
                    break;
                case ChatManagerScript.MessageType.Party_MatchMaking_Cancel:
                    if(!party_Leader){
                        mainMenuScript.MatchMaking_Party_CancelMatchMaking();
                    }
                    break;
            }
    
        }
    
        /// <summary>
        /// To handle the received party message
        /// </summary>
        /// <param name="senderID"></param>
        /// <param name="message"></param>
        public void Party_ReceiveMessage(ref string senderID, ref MessageContainer message) {
    
            switch (message.messageType) {
                case ChatManagerScript.MessageType.Game_CharacterSelection: //which mean switch character selection menu
                case ChatManagerScript.MessageType.Game_ReturnFromCharacterSelection:
                case ChatManagerScript.MessageType.Party_MatchMaking:
                case ChatManagerScript.MessageType.Party_MatchMaking_Cancel:
                    Party_ReceiveMessage(ref message);
                    break;
            }
    
        }
    
        /// <summary>
        /// To set is player in party
        /// </summary>
        /// <param name="inParty"></param>
        /// <param name="partyID"></param>
        void Party_SetInParty(bool inParty, ref string partyID){
            
            party_InParty = inParty;
    
            //Update on local storage
            PlayerPrefs.SetInt(ConstStringContainer.party_InParty_Key, (party_InParty)?1:0);
    
            if(inParty){
    
                PlayerPrefs.SetString(ConstStringContainer.party_PartyName_Key, partyID);
    
            }else{
    
                PlayerPrefs.SetString(ConstStringContainer.party_PartyName_Key, "");
    
            }
    
        }
    
        /// <summary>
        /// To set is player in party
        /// </summary>
        /// <param name="inParty"></param>
        void Party_SetInParty(bool inParty){
            
            party_InParty = inParty;
    
            //Update on local storage
            PlayerPrefs.SetInt(ConstStringContainer.party_InParty_Key, (party_InParty)?1:0);
    
            if(inParty){
    
                PlayerPrefs.SetString(ConstStringContainer.party_PartyName_Key, "");
    
            }else{
    
                PlayerPrefs.SetString(ConstStringContainer.party_PartyName_Key, "");
    
            }
    
        }
    
        void Party_SetPartyLeader(bool partyLeader){
            
            party_Leader = partyLeader;
            PlayerPrefs.SetInt(ConstStringContainer.Party_PartyLeader_Key, (party_Leader)?1:0);
        
        }
    
        #endregion
    
    }
    

    Photon Custom Serialization
    using System.Text;
    using ExitGames.Client.Photon;
    using Photon.Pun;
    using Photon.Realtime;
    using UnityEngine;
    
    public enum PhotonEventCode : byte { Respawn = 0, PortalGemDropIn = 1, GameStart = 2 }
    
    public class PhotonGeneralManager : MonoBehaviourPunCallbacks, IPunOwnershipCallbacks{
    
        //Singleton
        public static PhotonGeneralManager instance;
    
    
        // Start is called before the first frame update
        void Start(){
    
            instance = this;
    
            PhotonPeer.RegisterType(typeof(MessageContainer), (byte)'A', SerializeChatMessage, DeserializeChatMessage);
    
        }
    
        #region Se-DeSerialize Chat Message
    
        public static short SerializeChatMessage(StreamBuffer outStream, object playerScoreType) {
    
            MessageContainer messageC = (MessageContainer)playerScoreType;
    
            short messageLength = (short)Encoding.UTF8.GetBytes(messageC.message).Length;
            short dataLength = (short)Encoding.UTF8.GetBytes(messageC.data).Length;
            short senderNameLength = (short)Encoding.UTF8.GetBytes(messageC.senderUserIGN).Length;
    
            int byteLength = 2 + 2 + 2 + messageLength + dataLength + senderNameLength + 4;
    
            byte[] bytes = new byte[byteLength];
            int index = 0;
    
            Protocol.Serialize(messageLength, bytes, ref index);    //Length of the message
            Protocol.Serialize(dataLength, bytes, ref index);    //Length of the data
            Protocol.Serialize(senderNameLength, bytes, ref index); //Length of sender name
    
            System.Array.Copy(Encoding.UTF8.GetBytes(messageC.message), 0, bytes, index, messageLength); //Message
            index += messageLength;
    
            System.Array.Copy(Encoding.UTF8.GetBytes(messageC.data), 0, bytes, index, dataLength); //Data
            index += dataLength;
    
            System.Array.Copy(Encoding.UTF8.GetBytes(messageC.senderUserIGN), 0, bytes, index, senderNameLength); //sender name
            index += senderNameLength;
    
            Protocol.Serialize((int)messageC.messageType, bytes, ref index);    //Bot hero ID
    
            outStream.Write(bytes, 0, byteLength);  //write stream
    
            return (short)(byteLength);
    
        }
    
        public static object DeserializeChatMessage(StreamBuffer inStream, short length) {
    
            MessageContainer messageC;
    
            int index = 0;
    
            //Get the length of the string data
            byte[] strLenByte = new byte[2];
            short strLenMessage, strLenData, strLenSenderName;
    
            //Read the size of message length
            inStream.Read(strLenByte, 0, 2);
            Protocol.Deserialize(out strLenMessage, strLenByte, ref index);
    
            //Read the size of the data
            strLenByte = new byte[2];
            inStream.Read(strLenByte, 0, 2);
            index = 0;
            Protocol.Deserialize(out strLenData, strLenByte, ref index);
    
            //Read the size of the sender name
            strLenByte = new byte[2];
            inStream.Read(strLenByte, 0, 2);
            index = 0;
            Protocol.Deserialize(out strLenSenderName, strLenByte, ref index);
    
            //Initialize the bytes to receive the rest
            int byteLength = strLenMessage + strLenData + strLenSenderName + 4;
            byte[] bytes = new byte[byteLength];
    
            inStream.Read(bytes, 0, byteLength);
            index = 0;
    
            //Receive Message
            var userIDBytes = new byte[strLenMessage];
            System.Array.Copy(bytes, index, userIDBytes, 0, strLenMessage);
            messageC.message = Encoding.UTF8.GetString(bytes, index, strLenMessage);
            index += userIDBytes.Length;
    
            //Receive data
            var dataBytes = new byte[strLenData];
            System.Array.Copy(bytes, index, dataBytes, 0, strLenData);
            messageC.data = Encoding.UTF8.GetString(bytes, index, strLenData);
            index += dataBytes.Length;
    
            //Receive the sender name
            var senderBytes = new byte[strLenSenderName];
            System.Array.Copy(bytes, index, senderBytes, 0, strLenSenderName);
            messageC.senderUserIGN = Encoding.UTF8.GetString(bytes, index, strLenSenderName);
            index += senderBytes.Length;
    
            //Receive Message Type
            int messageType;
            Protocol.Deserialize(out messageType, bytes, ref index); //Get the no of the message type
            messageC.messageType = (ChatManagerScript.MessageType)messageType;
    
            return messageC;
    
        }
    
        #endregion
    
    }
    
    
  • TeohTc
    Bro You just save me from headache about party system in photon. This is the best way a to create party system.

    Friends, there are three types you may create a party system in photon
    1. This method (best) but need to pay for photon chat also.
    2. Creating a playfab group and constantly update group info for every 4 seconds to not to reach the client api limit. (Performance consuming method)
    3. Creating a " staging room" in lobby and invite friend friends to room, when everyone ready masterclient leaves room and search for game room with exepected user and then send roomname toteammates and then they can join with him. ( This method hard to implement and may acquire some error while implementing)