포톤 룸 리스트 - poton lum liseuteu

PUN2 콜백 : https://doc-api.photonengine.com/en/pun/v2/class_photon_1_1_pun_1_1_mono_behaviour_pun_callbacks.html#a5c82419bda5edcbc20997573b460b9c2

에러코드 : https://doc-api.photonengine.com/en/pun/v1/class_error_code.html

연결상태

Debug.Log($"PhotonNetwork.IsConnected: {PhotonNetwork.IsConnected}"); //false
PhotonNetwork.ConnectUsingSettings(); //접속시도
Debug.Log($"PhotonNetwork.IsConnected: {PhotonNetwork.IsConnected}"); //true
//IsConnected는 접속호출 즉시 True가됨

Debug.Log($"PhotonNetwork.IsConnectedAndReady: {PhotonNetwork.IsConnectedAndReady}"); 
//IsConnectedAndReady로 해야 ConnectedToMaster되었는지 체크가능

RPC 샘플

    public void SendChatMessage(string message)
    {
        photonView.RPC(nameof(ReceiveChatMessage), RpcTarget.Others, message);
    }

    [PunRPC]
    void ReceiveChatMessage(string message)
    {
        Debug.Log($"{nameof(ReceiveChatMessage)}:{message}");
    }

RPC샘플 V2


//브로드캐스트
    [PunRPC]
    public void ChatMessage(string message)
    {
        if (photonView.IsMine)
        {
            var methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
            photonView.RPC(methodName, RpcTarget.Others, message);
        }
        Debug.Log($"{nameof(ChatMessage)}:{message}");
    }
    
    
    
//주인에게 보낼때
    [PunRPC]
    public void ChatMessage(string message)
    {
        if (photonView.IsMine)
        {
            Debug.Log($"{nameof(ChatMessage)}:{message}");
        }
        else
        {
            var methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
            photonView.RPC(methodName, photonView.Controller, message);
        }
    }
    
    
//주인을 제외하고 보낼때
    [PunRPC]
    public void ChatMessage(string message)
    {
        if (photonView.IsMine)
        {
            var methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
            photonView.RPC(methodName, photonView.Controller, message);
        }
        else
        {
            Debug.Log($"{nameof(ChatMessage)}:{message}");
        }
    }

RPC 관련 코드

//오너에게 보낼때(Fixed타입이면 오너가 null이므로 주의)
photonView.RPC(nameof(ReceiveChatMessage), photonView.Owner, message); 

//Controller에게 보낼때(Fixed타입일때 실질적 오너)
photonView.RPC(nameof(ReceiveChatMessage), photonView.Controller, message);

변수동기화 샘플

using Photon.Pun;

[RequireComponent(typeof(PhotonView))]
public class GameSystem : MonoBehaviourPunCallbacks, IPunObservable
{
    float timer = 200;
    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.IsWriting)
        {
            stream.SendNext(timer);
        }
        else
        {
            timer = (float)stream.ReceiveNext();
        }
    }
}


//클래스 교환
    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.IsWriting)
        {
            string[] sampleclassJsons = System.Array.ConvertAll(sampleClass,x=> JsonUtility.ToJson(x));
            stream.SendNext(sampleclassJsons);
        }
        else
        {
            string[] sampleclassJsons = (string[])stream.ReceiveNext();
            sampleClass = System.Array.ConvertAll(sampleclassJsons, x => JsonUtility.FromJson<SampleClass>(x));
        }
    }

타이머 동기화

[RequireComponent(typeof(PhotonView))]
public class GameSystem : MonoBehaviourPunCallbacks, IPunObservable
{
    System.DateTime startTime = System.DateTime.UtcNow;
    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.IsWriting)
        {
            stream.SendNext(startTime.Ticks);
        }
        else
        {
            startTime = new System.DateTime((long)stream.ReceiveNext());
        }
    }
    public void SetTimer()
    {
        timer = oneGameTime - ((System.DateTime.UtcNow.Ticks - startTime.Ticks) / 10000000);
        timer = Mathf.Clamp(0, timer, oneGameTime);
        timerUI.text = timer.ToString();
    }
    static readonly float oneGameTime = 200;
    float timer = oneGameTime;
    public Text timerUI;

로컬타이머

        if (PhotonNetwork.CurrentRoom == null)
        {
            localTimer = 0;
        }
        else
        {
            localTimer += Time.deltaTime;
        }

해당 오브젝트의 오너의 닉네임을 불러옴

textUI.text = photonView.Owner.NickName;

자신이 마스터인지 체크

PhotonNetwork.IsMasterClient

닉네임 설정

Photon.Pun.PhotonNetwork.NickName = "Name";

타임아웃시간 설정

PhotonNetwork.KeepAliveInBackground = 3;

PhotonView들이 OnPhotonSerialize를 초당 몇회 호출할지

PhotonNetwork.SerializationRate=10;

서버시간

var serverTime = PhotonNetwork.Time;

방을 나가도 파괴 안되는 오브젝트

PhotonNetwork.InstantiateRoomObject(prefab.name, pos, rot, 0);

자신이 마스터인지

    public bool CheckRoomMaster()
    {
        return (PhotonNetwork.IsMasterClient) || (PhotonNetwork.CurrentRoom == null);
    }

플레이어 관련

//플레이어 리스트
PhotonNetwork.PlayerList

//플레이어 리스트 (자기자신 제외)
PhotonNetwork.PlayerListOthers

//로컬플레이어 (자기자신)
PhotonNetwork.LocalPlayer

//UserId 설정 (연결전에 설정되어야함)
var auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
PhotonNetwork.AuthValues = new Photon.Realtime.AuthenticationValues(auth.CurrentUser.UserId);

//userId to player
var player = System.Array.Find(PhotonNetwork.PlayerList,x=>x.UserId== userId);
if (player == null)
{
    Debug.LogError($"해당 id의 플레이어가 존재하지 않음{userId}");
    return;
}

//userIdList to playerList
var playerList = PhotonNetwork.PlayerList.ToList().FindAll(x=> userIdList.Contains(x.UserId));

룸관련

//룸옵션
var roomOptions=new Photon.Realtime.RoomOptions();
roomOptions.MaxPlayers = 0;

//방잠그기
PhotonNetwork.CurrentRoom.IsOpen = false;

특정상태의 플레이어 리스트 관리

Ready는 각자호출하고 리스트는 오너가 관리함

이방식 해보니까 오프라인모드일때나 룸이동시에 처리도 번거롭고 해서 그냥 오브젝트 생성식이 나은듯...

데이터는 절감될거 같은데 이건 목업본 말고 v2에서나 적용시키자

    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {

        if (stream.IsWriting)
        {
            stream.SendNext(ReadReadyPlayerList().ConvertAll(x=>x.UserId).ToArray());
            
        }
        else
        {
            var userIdList = ((string[])stream.ReceiveNext()).ToList();
            readyPlayerList = PhotonNetwork.PlayerList.ToList().FindAll(x=> userIdList.Contains(x.UserId));
        }

    }


    List<Photon.Realtime.Player> readyPlayerList = new List<Photon.Realtime.Player>();

    void Ready()
    {
        Debug.Log("Ready()");
        photonView.RPC(nameof(AddReadyPlayerList), photonView.Controller, PhotonNetwork.LocalPlayer.UserId);
    }
    void ReadyOff()
    {
        photonView.RPC(nameof(RemoveReadyPlayerList), photonView.Controller, PhotonNetwork.LocalPlayer.UserId);
    }

    [PunRPC]
    void AddReadyPlayerList(string userId)
    {
        Debug.Log("AddReadyPlayerList()");
        var player = System.Array.Find(PhotonNetwork.PlayerList,x=>x.UserId== userId);
        if (player == null)
        {
            Debug.LogError($"해당 id의 플레이어가 존재하지 않음{userId}");
            return;
        }
        if (readyPlayerList.Find(x => x.UserId == userId) == null)
        {
            readyPlayerList.Add(player);
        }
    }

    [PunRPC]
    void RemoveReadyPlayerList(string userId)
    {
        var player = System.Array.Find(PhotonNetwork.PlayerList, x => x.UserId == userId);
        if (player==null)
        {
            Debug.LogError($"해당 id의 플레이어가 존재하지 않음{userId}");
            return;
        }
        readyPlayerList.Remove(player);
    }

    List<Photon.Realtime.Player> ReadReadyPlayerList()
    {
        //오프라인 유저 제거
        readyPlayerList=readyPlayerList.FindAll(x => PhotonNetwork.PlayerList.Contains(x));
        return readyPlayerList;
    }

커스텀서버

var appSettings = new Photon.Realtime.AppSettings();
PhotonNetwork.PhotonServerSettings.AppSettings.CopyTo(appSettings);
appSettings.AppVersion = serverName + appSettings.AppVersion;
PhotonNetwork.ConnectUsingSettings(appSettings, PhotonNetwork.PhotonServerSettings.StartInOfflineMode);

로비분리

Photon.Realtime.TypedLobby lobby;
public override void OnConnectedToMaster()
{
    Debug.Log("ConnectedToMaster");

    lobby = new Photon.Realtime.TypedLobby("loobyNameSample", Photon.Realtime.LobbyType.Default);
    PhotonNetwork.JoinLobby(lobby); //로비접속
}
public override void OnJoinRandomFailed(short returnCode, string message)
{
    Debug.LogError("PhotonRandomJoinFailed");
    PhotonNetwork.CreateRoom(null,null, lobby);
}

룸리스트

    List<RoomInfo> roomList= new List<RoomInfo>();
    public override void OnRoomListUpdate(List<RoomInfo> roomList)
    {
        this.roomList.RemoveAll(x=>roomList.FindIndex(y=>y.Name== x.Name)>=0);
        this.roomList.AddRange(roomList);
        this.roomList.RemoveAll(x => x.MaxPlayers == 0 || x.PlayerCount == 0);
        
        textUI.text = string.Join("",roomList.ConvertAll(room => $"{room.Name}:{room.CustomProperties["RoomName"]}"));
    }

연결상태

    [System.Serializable]
    public class BoolEvent : UnityEngine.Events.UnityEvent<bool> { }
    public BoolEvent OnChangedConnectedEvent;
    
    
    public override void OnConnectedToMaster()
    {
        Debug.Log("ConnectedToMaster");
        OnChangedConnectedEvent.Invoke(true);
    }
    public override void OnDisconnected(DisconnectCause cause)
    {
        Debug.Log("OnDisconnected");
        OnChangedConnectedEvent.Invoke(false);
    }

연결오류 모음

    public override void OnDisconnected(DisconnectCause cause)
    {
        switch (cause)
        {
            case DisconnectCause.None:
                break;
            case DisconnectCause.ExceptionOnConnect:
                break;
            case DisconnectCause.DnsExceptionOnConnect:
                break;
            case DisconnectCause.ServerAddressInvalid:
                break;
            case DisconnectCause.Exception:
                break;
            case DisconnectCause.ServerTimeout:
                break;
            case DisconnectCause.ClientTimeout:
                Debug.LogError($"포톤 서버가 터짐. Dev Region과 Fixed Region을 공백으로 하세요");
                break;
            case DisconnectCause.DisconnectByServerLogic:
                break;
            case DisconnectCause.DisconnectByServerReasonUnknown:
                break;
            case DisconnectCause.InvalidAuthentication:
                break;
            case DisconnectCause.CustomAuthenticationFailed:
                break;
            case DisconnectCause.AuthenticationTicketExpired:
                break;
            case DisconnectCause.MaxCcuReached:
                break;
            case DisconnectCause.InvalidRegion:
                break;
            case DisconnectCause.OperationNotAllowedInCurrentState:
                break;
            case DisconnectCause.DisconnectByClientLogic:
                break;
            case DisconnectCause.DisconnectByOperationLimit:
                break;
            case DisconnectCause.DisconnectByDisconnectMessage:
                break;
            default:
                break;
        }
        Debug.LogError($"{cause}");
    }

포톤 프로퍼티


    //생성
    public override void OnJoinRoomFailed(short returnCode, string message)
    {
        Debug.LogWarning("JoinRoomFailed");
        Debug.LogWarning("CreateRoom");
        //PhotonNetwork.CreateRoom(roomName);


        RoomOptions roomOptions = new RoomOptions();
        var hashtable = new ExitGames.Client.Photon.Hashtable();
        hashtable.Add(key:"mapName", value: "testMap"); //mapName 프로퍼티
        hashtable.Add(key: "password", value: "testPW"); //testPW 프로퍼티
        roomOptions.CustomRoomProperties = hashtable;
        roomOptions.CustomRoomPropertiesForLobby = System.Array.ConvertAll(roomOptions.CustomRoomProperties.Keys.ToArray(), x => x.ToString());
        Debug.Log($"Properties : {string.Join(", ", roomOptions.CustomRoomPropertiesForLobby)}");

        TypedLobby sqlLobby = new TypedLobby("myLobby", LobbyType.SqlLobby);
        PhotonNetwork.CreateRoom(null, roomOptions,sqlLobby);
    }
    
    
    //수정
    public override void OnCreatedRoom()
    {
        var hashtable = PhotonNetwork.CurrentRoom.CustomProperties;
        hashtable["mapName"] = "testMap2"; //수정
        Debug.Log($"mapName : {PhotonNetwork.CurrentRoom.CustomProperties["mapName"]}");

        Debug.Log("CreatedRoom");
    }
    
    //접속
    public override void OnConnectedToMaster()
    {
        Debug.Log("ConnectedToMaster");

        TypedLobby sqlLobby = new TypedLobby("myLobby", LobbyType.SqlLobby); 
        string sqlLobbyFilter = "mapName = \"testMap\""; 
        // "C0 = 1"
        // "C0 = 1 AND C2 > 50"
        // "C5 = \"Map2\" AND C2 > 10 AND C2 < 20"
        PhotonNetwork.JoinRandomRoom(null, 2, MatchmakingMode.FillRoom, sqlLobby, sqlLobbyFilter);
        PhotonNetwork.JoinRoom(roomName);
    }