LoadBalancing.Room..ctor throws NullReferenceException

itminb
itminb
edited July 2016 in DotNet
Hi~ I reviewed ParticleDemo project using Photon realtime sdk on Unity 5.3.5f1.

First of all, one of clients created a room by CreateParticleDemoRoom function and another client tried to join that room by OpJoinRandomRoom function.

one client use this code,
case ClientState.ConnectedToMaster:
// when that's done, this demo asks the Master for any game. the result is handled below
// this.OpJoinRandomRoom(null, 0);
this.CreateParticleDemoRoom(DemoConstants.MapType.Forest, 16);
break;
another client like this :
case ClientState.ConnectedToMaster:
// when that's done, this demo asks the Master for any game. the result is handled below
 this.OpJoinRandomRoom(null, 0);
//this.CreateParticleDemoRoom(DemoConstants.MapType.Forest, 16);
break;
My Intention is the first client make a room, and second client joined the room not using specific room name.
So, I called OpJoinRandomRoom() function on the second client.
But, When the second client found a room and tried to join, I got a NullReferenceException below.
NullReferenceException: Object reference not set to an instance of an object
ExitGames.Client.Photon.LoadBalancing.Room..ctor (System.String roomName, ExitGames.Client.Photon.LoadBalancing.RoomOptions options)
ExitGames.Client.DemoParticle.ParticleRoom..ctor (System.String roomName, ExitGames.Client.Photon.LoadBalancing.RoomOptions opt)
ExitGames.Client.DemoParticle.GameLogic.CreateRoom (System.String roomName, ExitGames.Client.Photon.LoadBalancing.RoomOptions opt) (at Assets/Photon/DemoCode/GameLogic.cs:242)
ExitGames.Client.Photon.LoadBalancing.LoadBalancingClient.GameEnteredOnGameServer (ExitGames.Client.Photon.OperationResponse operationResponse) (at Assets/PhotonLoadbalancingApi/LoadBalancingClient.cs:1707)
ExitGames.Client.Photon.LoadBalancing.LoadBalancingClient.OnOperationResponse (ExitGames.Client.Photon.OperationResponse operationResponse) (at Assets/PhotonLoadbalancingApi/LoadBalancingClient.cs:1959)
ExitGames.Client.DemoParticle.GameLogic.OnOperationResponse (ExitGames.Client.Photon.OperationResponse operationResponse) (at Assets/Photon/DemoCode/GameLogic.cs:424)
ExitGames.Client.Photon.PeerBase.DeserializeMessageAndCallback (System.Byte[] inBuff)
ExitGames.Client.Photon.EnetPeer.DispatchIncomingCommands ()
ExitGames.Client.Photon.PhotonPeer.DispatchIncomingCommands ()
ExitGames.Client.DemoParticle.GameLogic.UpdateLoop () (at Assets/Photon/DemoCode/GameLogic.cs:252)
Logic.UpdateLocal () (at Assets/Scripts/Logic.cs:124)
DemoGUI.Update () (at Assets/Scripts/DemoGUI.cs:109)
I ran demo project from Photon sdk , I just changed one line for joining random room.
Please let me know what is a problem.

Answers

  • I'm sorry for the late reply, this got lost somehow.

    What you are doing is quite complex, if you think about it: You build one version to join and one to create rooms? How does a player know which one to start??

    The workflow in the demo is to attempt to join the room "Demo". CreateParticleDemoRoom uses OpJoinOrCreateRoom to achieve this.

    The best workflow is to attempt to join a random room and to only create one when random join fails.
    See: https://doc.photonengine.com/en-us/realtime/current/reference/matchmaking-and-lobby

    The place where you changed code is not the place you should change.


    Do this:
    In GameLogic.OnStateChanged(), case ClientState.ConnectedToMaster, call JoinRandomRoom.
    In OnOperationResponse, the result of JoinRandomRoom is handled. Instead of calling CreateParticleDemoRoom, you can do your OpCreateRoom call.

    Hope that helps.
  • itminb
    itminb
    edited July 2016
    Thank you for your reply.

    The reason why I built one version to join and one to create rooms is for test.
    I think it gave you some confusion.

    As you mentioned,
    OpJoinRandomRoom is called in GameLogic.OnStateChanged(), case ClientState.ConnectedToMaster,
    I did at the first.
    
    private void OnStateChanged(ClientState clientState)
    {
        switch (clientState)
        {
        case ClientState.ConnectedToNameServer:
            if (string.IsNullOrEmpty(this.CloudRegion))
            {
                this.OpGetRegions();
            }
            break;
        case ClientState.ConnectedToGameserver:
            break;
        case ClientState.ConnectedToMaster:
            // when that's done, this demo asks the Master for any game. the result is handled below
            this.OpJoinRandomRoom (null, 0);
            break;
        }
    }
    
    If player can't find match, call OpCreateRoom in OnOperationResponse.
    (I use your demo project itself)
    
    public override void OnOperationResponse(Photon.OperationResponse operationResponse)
    {
        base.OnOperationResponse(operationResponse);  // important to call, to keep state up to date
    
        if (operationResponse.ReturnCode != ErrorCode.Ok)
        {
            //this.DebugReturn(DebugLevel.ERROR, operationResponse.ToStringFull() + " " + this.State);
        }
    
        // this demo connects when you call start and then it automatically executes a certain operation workflow to get you in a room
        switch (operationResponse.OperationCode)
        {
        case OperationCode.Authenticate:
            // Unlike before, the game-joining can now be triggered by the simpler OnStateChangeAction delegate: OnStateChanged(ClientState newState)
            break;
    
        case OperationCode.JoinRandomGame:
            // OpJoinRandomRoom is called above. the response to that is handled here
            // if the Master Server didn't find a room, simply create one. the result is handled below
            if (this.JoinRandomGame && operationResponse.ReturnCode != ErrorCode.Ok)
            {
                Hashtable roomPropsForCreation = new Hashtable() { { DemoConstants.MapProp, DemoConstants.MapType.Forest.ToString() }, { DemoConstants.GridSizeProp, 4 } };
                this.OpCreateRoom(null, new RoomOptions() { CustomRoomProperties = roomPropsForCreation, CustomRoomPropertiesForLobby = RoomPropsInLobby }, null);
            }
            break;
    
        case OperationCode.JoinGame:
        case OperationCode.CreateGame:
            // the master server will respond to join and create but this is handled in the base class
            if (this.State == ClientState.Joined)
            {
                // no matter if we joined or created a game, when we arrived in state "Joined", we are on the game server in a room and
                // this client could start moving and update others of it's color
                this.LocalPlayer.RandomizePosition();
                //this.loadBalancingPeer.OpRaiseEvent(DemoConstants.EvColor, this.LocalPlayer.WriteEvColor(), true, 0, null, EventCaching.AddToRoomCache);
                this.loadBalancingPeer.OpRaiseEvent(DemoConstants.EvColor, this.LocalPlayer.WriteEvColor(), this.SendReliable, new RaiseEventOptions() { CachingOption = EventCaching.AddToRoomCache });
            }
            break;
        }
    
        UpdateVisuals = true;
    }
    
    If there is no room, successfully created room.
    If there is a ROOM, finally Photon found match, called GameEnteredOnGameServer function internally
    and then throw exception at
    this.CurrentRoom = this.CreateRoom(this.enterRoomParamsCache.RoomName, this.enterRoomParamsCache.RoomOptions);


    I used your demo, so i didn't change many thing.
    in your demo, OpJoinOrCreateRoom is only used for join and create room.

    I just called JoinRandomRoom in GameLogic.OnStateChanged(), case ClientState.ConnectedToMaster.
    to use your join random function. when join random room fails, call OpCreateRoom.
    I just replaced OpJoinOrCreate with OpJoinRandomRoom.
    Because I wanted to use your random join function. That's it. This was all I did.
    So, I don't think that I did try complex thing.


    if there are some part I didn't understand, please let me know.
    Thank you in advance.

  • itminb
    itminb
    edited July 2016
    I found that when extending room class, the problem is happened.

    I looked at another demo which works well with OpJoinRandomRoom.
    This project doesn't use extended room class.

    So, I tried not to use ParticleRoom class which is extended room class(derived class of Room)
    now, OpJoinRandomRoom worked well.

    it is better not to derived room class.
    even derived room class, ParticleRoom.cs in this project, doesn't have any methods, the problem is always happened.

    So, I decide not to extend room class.

    please check this point.

    Thank you for your helping.
  • itminb
    itminb
    edited August 2016
    I also found one more thing.

    GameLogic.cs in your demo project.

    you override CreateRoom function like below.
    protected internal override Room CreateRoom(string roomName, RoomOptions opt)
    {
        return new ParticleRoom(roomName, opt);
    }
    and this is base function in LoadBalancingClient.cs
    protected internal virtual Room CreateRoom(string roomName, RoomOptions opt)
    {
        if (opt == null)
        {
            opt = new RoomOptions();
        }
        Room r = new Room(roomName, opt);
        return r;
    }
    So, If we want to inherit LoadBalancing class and Room class for our own logic,
    We have to do like this.
    protected internal override Room CreateRoom(string roomName, RoomOptions opt)
    {
    	if (opt == null)
    	{
    		opt = new RoomOptions();
    	}
    	ParticleRoom r = new ParticleRoom(roomName, opt);
    	return r;
    }
  • JohnTube
    JohnTube ✭✭✭✭✭
    Hi @itminb,

    Thank you for your detailed reports. We will take a look at this.
    I hope there is nothing blocking you meanwhile.