Operation failed

The whole answer can be found below.

Please note: The Photon forum is closed permanently. After many dedicated years of service we have made the decision to retire our forum and switch to read-only: we've saved the best to last! And we offer you support through these channels:

Try Our
Documentation

Please check if you can find an answer in our extensive documentation on PUN.

Join Us
on Discord

Meet and talk to our staff and the entire Photon-Community via Discord.

Read More on
Stack Overflow

Find more information on Stack Overflow (for Circle members only).

Write Us
an E-Mail

Feel free to send your question directly to our developers.

Operation failed: 32764 (Game closed) - doesn't raise OnPhotonRandomJoinFailed

heen
2018-07-06 10:01:19

Photon raises the OnJoinedLobby callback when the following error happens.
"Operation failed: OperationResponse 226: ReturnCode: 32764 (Game closed). Parameters: {} Server: GameServer"

I would expect to get the OnPhotonRandomJoinFailed callback in these cases.


A bit longer explanation:

Or games first tries to JoinRandomRoom. If that fails we call CreateRoom. The room will be closed by the master-client when 32 players have joined or after 30 seconds, whichever comes first.

Sometimes the master client closes the room at the exact same time as an other client joins. At this point, the late client will be kicked out and given a "ReturnCode: 32764 (Game closed)", after which the OnJoinedLobby is raised. What I would like to happen is to get the OnPhotonRandomJoinFailed callback. That way I could keep same pattern for create/join calls. Naturally I can not redirect my OnJoinedLobby to CreateRoom calls, that would break the logic.

Can I change this myself in PUN or would this be a feature-request for the dev team?

Best, He

Comments

Tobias
2018-07-06 10:10:43

Which version of PUN are you using? As far as I can see, this exact case should result in OnPhotonJoinRoomFailed on the GameServer.
The client will also automatically return to the MasterServer and join the lobby again, if that automation is enabled. Matchmaking is only possible when you're back on the MasterServer.

In my opinion you should be able to just restart the same matchmaking process as before: Try to join random once more and in doubt, create a new room.

Our servers should be fast enough to sync that you closed the room and that gets it off the "available for JoinRandomList". So either you find another room already or you create one and let others join.

heen
2018-07-10 12:09:26

I'll try to create a isolated test case for this. I thought I had one but then noticed it was the -Game does not exist- error that I already solved earlier.

heen
2018-07-10 12:33:14

Ok, I think I have an isolated test case that can help me figure out what the correct behaviour should be.

Steps:

  • Use Mac, Unity 2017.4.5f1
  • Download Pun Free from asset store. Version v1.90 (10. April 2018)
  • Add an AppId and enabled auto-join lobby.
  • Open MarcoPolo-Tutorial
  • Changed RandomMatchmaker.cs to the code attached below.
  • Start one stand-alone client and run another in the editor.
  • After a few seconds the (Game closed)-error appears.

The log from the app is the following:

... some successful connects
OnJoinedLobby
PhotonNetwork.JoinRandomRoom
Operation failed: OperationResponse 226: ReturnCode: 32764 (Game closed). Parameters: {} Server: GameServer
OnJoinedLobby

As mentioned, it is not the (Game closed)-error that puzzles me, but rather the callback that happens after that. It is the last OnJoinedLobby that I'm wonder about. Should that be an OnPhotonRandomJoinFailed?

Best, He

PS. My previous post was originally the wrong test case. This should now be the one that causes the (Game Closed)-error. Sorry if I confused someone about an hour ago :).


RandomMatchmaker.cs:

  
using UnityEngine;  
using System;  
using System.Collections;

public class RandomMatchmaker : Photon.PunBehaviour  
{  
    private PhotonView myPhotonView;  
    private RoomOptions m_roomOptions;

    public void Start()  
    {  
        PhotonNetwork.ConnectUsingSettings("0.1");

        m_roomOptions = new RoomOptions();  
        m_roomOptions.EmptyRoomTtl = 500;  
    }

    public override void OnJoinedLobby()  
    {  
        Debug.Log("OnJoinedLobby");

        Debug.Log("PhotonNetwork.JoinRandomRoom");  
        PhotonNetwork.JoinRandomRoom();  
    }

    public override void OnConnectedToMaster()  
    {  
        Debug.Log("OnConnectedToMaster");  
        // Using auto join lobby  
    }

    public void OnPhotonRandomJoinFailed()  
    {  
        Debug.Log("OnPhotonRandomJoinFailed");

        Debug.Log("PhotonNetwork.CreateRoom");  
        PhotonNetwork.CreateRoom( null, m_roomOptions, null );   
    }

    public override void OnJoinedRoom()  
    {  
        Debug.Log("OnJoinedRoom");  
        GameObject monster = PhotonNetwork.Instantiate("monsterprefab", Vector3.zero, Quaternion.identity, 0);  
        monster.GetComponent().isControllable = true;  
        myPhotonView = monster.GetComponent();

        this.StartCoroutine( this.CloseAndLeaveRoomRoutine() );  
    }

    public override void OnLeftRoom()  
    {  
        Debug.Log("OnLeftRoom");  
    }

    private IEnumerator CloseAndLeaveRoomRoutine()  
    {  
        var wait = new WaitForSeconds( 0.1f );

        yield return wait;  
          
        Debug.Log("PhotonNetwork.room.IsOpen = false");  
        PhotonNetwork.room.IsOpen = false;  
        yield return wait;

        Debug.Log("PhotonNetwork.LeaveRoom");  
        PhotonNetwork.LeaveRoom();  
    }

    public void OnGUI()  
    {  
        GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString());

        if (PhotonNetwork.inRoom)  
        {  
            bool shoutMarco = GameLogic.playerWhoIsIt == PhotonNetwork.player.ID;

            if (shoutMarco && GUILayout.Button("Marco!"))  
            {  
                myPhotonView.RPC("Marco", PhotonTargets.All);  
            }  
            if (!shoutMarco && GUILayout.Button("Polo!"))  
            {  
                myPhotonView.RPC("Polo", PhotonTargets.All);  
            }  
        }  
    }  
}  

JohnTube
2018-07-10 12:42:28

Hi @heen,

Thank you for choosing Photon!

Could you implement the callback void OnPhotonJoinRoomFailed(object[] codeAndMsg) ? and log error code and error message?

If it is triggered with the GameClosed error than that is the expected behaviour in this case.
A random room has been found but client failed to join it since it was closed meanwhile.

heen
2018-07-10 13:43:30

Thanks John,

The OnPhotonRandomJoinFailed( object[] codeAndMsg ) is never call after (Game Closed)-error.

EDIT: To demonstrate this even better: I made the execution stop when an unexpected flow happens. In the correct case the MarcoPolo sample should run infinitely. However, the current code below stops after a while.

  
using UnityEngine;  
using System;  
using System.Collections;

public class RandomMatchmaker : Photon.PunBehaviour  
{  
	private PhotonView myPhotonView;  
	private RoomOptions m_roomOptions;  
	private bool m_joinRoom = true;

	public void Start()  
	{  
		PhotonNetwork.ConnectUsingSettings("0.1");

		m_roomOptions = new RoomOptions();  
		m_roomOptions.EmptyRoomTtl = 500;  
	}

	public override void OnJoinedLobby()  
	{  
		Debug.Log("OnJoinedLobby");

		// In a real game there would be a button to start the game - this button would only be pressed once  
		if( m_joinRoom )  
		{  
			Debug.Log("PhotonNetwork.JoinRandomRoom");  
			PhotonNetwork.JoinRandomRoom();  
		}

		#if UNITY_EDITOR  
		m_joinRoom = false;  
		#endif  
	}

	public override void OnConnectedToMaster()  
	{  
		Debug.Log("OnConnectedToMaster");  
		// Using auto join lobby  
	}

	public override void OnPhotonRandomJoinFailed( object[] codeAndMsg )  
	{  
		Debug.Log("OnPhotonRandomJoinFailed( object[] codeAndMsg )");

		Debug.Log("PhotonNetwork.CreateRoom");  
		PhotonNetwork.CreateRoom( null, m_roomOptions, null );   
	}

	public override void OnJoinedRoom()  
	{  
		Debug.Log("OnJoinedRoom");  
		GameObject monster = PhotonNetwork.Instantiate("monsterprefab", Vector3.zero, Quaternion.identity, 0);  
		monster.GetComponent().isControllable = true;  
		myPhotonView = monster.GetComponent();

		m_joinRoom = true;  
		this.StartCoroutine( this.CloseAndLeaveRoomRoutine() );  
	}

	public override void OnLeftRoom()  
	{  
		Debug.Log("OnLeftRoom");  
	}

	private IEnumerator CloseAndLeaveRoomRoutine()  
	{  
		var wait = new WaitForSeconds( 0.1f );

		yield return wait;  
		  
		Debug.Log("PhotonNetwork.room.IsOpen = false");  
		PhotonNetwork.room.IsOpen = false;  
		yield return wait;

		Debug.Log("PhotonNetwork.LeaveRoom");  
		PhotonNetwork.LeaveRoom();  
	}

	public void OnGUI()  
	{  
		GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString());

		if (PhotonNetwork.inRoom)  
		{  
			bool shoutMarco = GameLogic.playerWhoIsIt == PhotonNetwork.player.ID;

			if (shoutMarco && GUILayout.Button("Marco!"))  
			{  
				myPhotonView.RPC("Marco", PhotonTargets.All);  
			}  
			if (!shoutMarco && GUILayout.Button("Polo!"))  
			{  
				myPhotonView.RPC("Polo", PhotonTargets.All);  
			}  
		}  
	}  
}  

JohnTube
2018-07-10 13:54:44

Hi @heen,

pay attention to the callback names.
we're talking about OnPhotonJoinRoomFailed and not OnPhotonRandomJoinFailed.

heen
2018-07-11 07:59:54

Thanks John, my apologies. I didn't pay enough attention to the code in your message. You are perfectly right, with the OnPhotonJoinRoomFailed, I will now get an error callback for this. Just like so:

OnJoinedLobby
PhotonNetwork.JoinRandomRoom
Operation failed: OperationResponse 226: ReturnCode: 32764 (Game closed). Parameters: {} Server: GameServer
OnPhotonJoinRoomFailed( object[] codeAndMsg ) 32764Game closed
OnJoinedLobby

With this info, I could probably start digging through the PUN code and see if I can change the behaviour of OnJoinedLobby/OnPhotonRandomJoinFailed when this error happens. Or alternatively implement a custom work around in my room joining logic.

If it is not too impolite I would like to keep this as a feature request to Photon. I.e. to route this error to OnPhotonRandomJoinFailed instead of eventually going to OnJoinedLobby.

Many thanks, He

heen
2018-07-11 08:03:17

And now I see that Tobias also mentioned the OnPhotonJoinRoomFailed callback. Anyway, I hope my mistake is forgiven as the names are so similar :).

/He

JohnTube
2018-07-11 09:30:03

Hi @heen,

no worries!
As @Tobias already stated, this behaviour is by design and won't change so I suggest you adapt your code and I think it's pretty straightforward.

heen
2018-10-09 12:07:22

Here is an update on our approach on how we handle these events.

My first assumption was that if we get this error rooms are probably getting full. Also, with our current code base it was easier to add a patch for creating a new room instead of trying to rejoin a random room.

It turned out that this was a mistake from my side. Getting this error doesn't mean your rooms are full, it only means players are trying to get the last spot in the first free room. There can still be a lot of space left in other rooms. The end result in our case was that players tried to squeeze into the first room and failed. After that these players created their own room. The issue went unnoticed during testing but in production with 10k CCU it rapidly escalated. Practically generating one new room for each player. This was something the servers did not like and players started to have major difficulties connecting.

So if you wanna learn from my mistake - don't generate new rooms if getting this callback. You should try re-join instead. I also probably could recommend doing a light weight load balancing client side. So instead of all clients trying to join with same room config, add a random number to the properties. As CCU increases you should increment the random range. Hence, having less clients trying get into the exact same room should reduce the likelihood of getting this callback.

/He.

JohnTube
2018-10-09 13:08:19

hi @heen,

Thanks a lot for the updates!
I hope your game is on our showcase already if not please add it.

Back to top