OnClientExitRoom function is called twice - unity3d

I'm using NetworkRoomManager and NetworkDiscovery and when a player exits a room scene I call the NetworkManager.singleton.StopClient() in networkdiscoveryhud then you will found out that its calling the OnClientExitRoom function in networkroomplayerext script twice.
Or I should not use NetworkManager.singleton.StopClient() when client exit a room scene? Below is my script for client or server exiting a room.
public void StopHost()
{
if (NetworkServer.active && NetworkClient.isConnected)
{
NetworkManager.singleton.StopHost();
}
else if (NetworkClient.isConnected)
{
NetworkManager.singleton.StopClient();
}
else if (NetworkServer.active)
{
NetworkManager.singleton.StopServer();
}
networkDiscovery.StopDiscovery();
}

I recommend Lobby and Worlds. It's much more flexible than NetworkRoomManager and has a ton of built in features.
More info on it here. https://trello.com/c/0jT4kZ6O

Related

Unity - Mirror Networking : Player client disconnect after scene changed

I'm currently using Mirror Networking to make a multiplayer game. I have a scene when players are all connected, they can choose their characters and set the ready. If all players are ready, I change current scene to arena scene using MyNetworkManager.ServerChangeScene(arenaSceneName). This method sets all player clients as not ready. But After the scene was loaded, my player client is no longer connected to my host and I don't know why.
Can you help me please ?
Thanks a lot for answers.
Clients that connect to this server will automatically switch to this scene. This is called automatically if onlineScene or offlineScene are set, but it can be called from user code to switch scenes again while the game is in progress. This automatically sets clients to be not-ready. The clients must call NetworkClient.Ready again to participate in the new scene."
if its not solve problem can you give more information about your project
Are you using MatchInterestManager ? This can be lead some problems like yours
Edit 1 -
Maybe the problem was host
can you run your code on server not host
Edit 2 -
i belive your command function runs on client not server because you miss "Cmd" prefix on your command function change it CmdOnAllPlayersReady()
public class ReadyPlayerChecker : NetworkBehaviour
{
public List<PlayerBehaviour> activePlayers;
public List<PlayerBehaviour> GetActivePlayers()
{
return activePlayers;
}
// Start is called before the first frame update
void Start()
{
activePlayers = new List<PlayerBehaviour>();
}
// Update is called once per frame
void Update()
{
foreach (PlayerBehaviour player in FindObjectsOfType<PlayerBehaviour>
(true))
{
if(!activePlayers.Contains(player))
{
activePlayers.Add(player);
}
}
bool allPlayersReady = true;
foreach (PlayerBehaviour player in activePlayers)
{
if (!player.IsReady())
{
allPlayersReady = false;
}
}
if (allPlayersReady && activePlayers.Count > 0)
{
OnAllPLayersReady();
}
}
[Command]
public void OnAllPLayersReady()
{
GameObject.Find("SceneManager").GetComponent<SceneChanger>
().LoadScene("SimpleArena");
}
}
This is my Network Manager settings :
I succeed to make the playerclient connected when the scene change. Now, the player prefab for my client is in host view but not in the corresponding client view. Do someone can tell me why ?

How to generate a random number and make sure it is the same for everyone using Photon in Unity?

Hi Guys I am converting a single player Sudoko game into a multiplayer game (right now 2 players) using Photon in Unity.
The basic logic of the Sudoku game is that there are 300 puzzle data already loaded in it. A random number between 1 to 300 is picked up and the corresponding puzzle is loaded.
But the problem I am facing is even though I have made sure that the same number is getting picked up for both the client and the master server, different puzzles are getting loaded.
So basically I script called MultiManager attached to the MultiManager GameObject in the Sudoku screen.The script looks something like this.
void Start()
{
PV = GetComponent<PhotonView>();
if (PhotonNetwork.IsMasterClient)
{
puzzleIndex = Random.Range(0, 300);
PV.RPC("RPC_PuzzleIndex", RpcTarget.Others, puzzleIndex);
}
gameManager = FindObjectOfType(typeof(GameManager)) as GameManager;
gameManager.PlayNewGame("easy");
}
[PunRPC]
void RPC_PuzzleIndex(int puzzleIndexNUmber)
{
puzzleIndex = puzzleIndexNUmber;
}
So in the GameManager script you have these functions:
public void PlayNewGame(string groupId)
{
// Get the PuzzleGroupData for the given groupId
for (int i = 0; i < puzzleGroups.Count; i++)
{
PuzzleGroupData puzzleGroupData = puzzleGroups[i];
if (groupId == puzzleGroupData.groupId)
{
PlayNewGame(puzzleGroupData);
return;
}
}
}
private void PlayNewGame(PuzzleGroupData puzzleGroupData)
{
// Get a puzzle that has not yet been played by the user
PuzzleData puzzleData = puzzleGroupData.GetPuzzle();
// Play the game using the new puzzle data
PlayGame(puzzleData);
}
And in the PuzzleGroupData class you have this function :
public PuzzleData GetPuzzle()
{
return new PuzzleData(puzzleFiles[MultiManager.puzzleIndex], shiftAmount, groupId);
}
I don't quite get as to whats wrong which is happening. I tried to use other variations like keeping that random number outside of the condition inside of PhotonNetwork.isMasterClient and all, but doesn't work.
If anyone can help it would be great. By the way this Sudoku game was purchased and I am trying to convert it to a mutliPlayer game
Since the master is usually the first one in a room so also the first one getting Start called I think what happens is that your other clients are simply not connected yet when the RPC is called.
Further it might also happen (actually pretty likely) that Start is called before the RPC has the chance to be received.
I would rather actually wait until you have the value and do
void Start()
{
PV = GetComponent<PhotonView>();
if (PhotonNetwork.IsMasterClient)
{
PV.RPC(name of(RPC_PuzzleIndex), RpcTarget.AllBuffered, Random.Range(0, 300));
}
}
[PunRPC]
void RPC_PuzzleIndex(int puzzleIndexNUmber)
{
puzzleIndex = puzzleIndexNUmber;
gameManager = FindObjectOfType(typeof(GameManager)) as GameManager;
gameManager.PlayNewGame("easy");
}
This way
the RpcTarget.AllBuffered makes sure that also clients joining later will receive the call
there is no way you start a game without receiving the random value first

WebSocketSharp .onMessage on main thread? / handling in Unity?

I find it very difficult to find a simple reliable library to use, in Unity, for a simple websocket client. WebSocketSharp (ie, https://github.com/sta/websocket-sharp ) seems fine but many items are unexplained.
When incoming messages arrive,
wssharp_connection.OnMessage += (sender, e) =>
{
if (!e.IsText) { return; }
Debug.Log(">> OnMessage, " + e.Data);
Handle(e.Data);
// but wait, not the main Unity thread??
};
void Handle(string msg)
{
.. your conventional Unity call, in a monobehavior
}
Testing shows it seems to arrive on (always? sometimes?) not the main Unity thread.
Does anyone know
In fact is it always not the main thread, or can it vary?
If always another thread, in fact is it always the same one thread, or does it vary/many?
Since every single Unity app that uses a websocket client has to do this, I'm surprised that there are not 100s of discussion about how to handle incoming messages, including on the WebSocketSharp page,
but here's what I would do, just in the usual way you handle "threading" in a frame-based engine like Unity:
wssharp_connection.OnMessage += (sender, e) =>
{
if (!e.IsText) { return; }
incoming_messages.Enqueue(e.Data);
};
// and ...
ConcurrentQueue<string> incoming_messages = new ConcurrentQueue<string>();
void Update()
{
if (incoming_messages.TryDequeue(out var message))
{
Handle(message);
}
}
So then
Is that indeed the usual way to handle the threading problem in WebSocketSharp, when using inside Unity?
Given how many billion Unity apps there are with websockets clients, this must be a pretty well-trodden path.

Can't get RunTransaction with Firebase in Unity to work

I'm working on a game in Unity, using Firebase for user and game data.
Everything is fine saving the game to Unity, but in Multiplayer I have the challenge to pair an open game to a unique player 2.
The flow is:
1. You look for a game with a state of "open"
2. If no game is found within X seconds, a new one is created with the state of "open"
I have a query that returns open games (those with state "open")
This query is added a Firebase eventhandler
public void OnButtonLookForOpenMPGames()
{
this.OpenFirebaseGamesAsHost = this.LiveGamereference.OrderByChild("State").EqualTo(WYMSettings.MP_GAME_STATE_OPEN_TO_PLAYER2)
.LimitToFirst(3);
this.OpenFirebaseGamesAsHost.ChildAdded += this.OnOpenMPGameFound;
}
This fires fine in the handler:
private void OnOpenMPGameFound(object sender, ChildChangedEventArgs args)
{
Debug.Log("Looking for open games");
if (args.DatabaseError != null)
{
// handle errors
}
else
{
Debug.Log("Open game found (it may be my own)");
// remove event listener because we got a hit
// it will fire as many times as it's true!
this.OpenFirebaseGamesAsHost.ChildAdded -= this.OnOpenMPGameFound;
if (args.Snapshot.Child("HostId").Value.ToString() != FirebaseAuth.DefaultInstance.CurrentUser.UserId)
{
Debug.Log("Other game than mine found open");
// this.AddDelayedUpdateAction(() =>
// {
// this.MPGameLockInTransaction(args.Snapshot.Reference);
// });
this.MPGameLockInTransaction(args.Snapshot.Reference);
}
}
}
The problem: The handler passes this on to the transaction function, but it fails with an inner exception (Internal Task Faulted) - I've tried many different variations, but can't figure this one out from the official Firebase example.
Problem code:
What I want to achieve is to lock only that game to those two players, the host and the players 2 in this example querying for open games. The State is an int, but here a string for clarity.
private void MPGameLockInTransaction(DatabaseReference mpreference)
{
mpreference.RunTransaction(mutableData =>
{
MultiPlayerGame transactionMPG = mutableData.Value as MultiPlayerGame;
if (transactionMPG == null)
{
return TransactionResult.Abort();
}
if (transactionMPG.State != "open")
{
// game is taken, abort
Debug.Log("transaction aborted");
return TransactionResult.Abort();
}
transactionMPG.State = "game started";
mutableData.Value = transactionMPG;
return TransactionResult.Success(mutableData);
}).ContinueWith(task =>
{
if (task.Exception != null)
{
Debug.Log("Transactionlock to game failed" + task.Exception.ToString());
// Look over again
// not implemented yet
}
else
{
Debug.Log("starting game immediately");
this.AddDelayedUpdateAction(() => this.StartMPGameImmediatelyFromSearch());
}
});
}
This is just a guess but it might be related to threading.
In newer .Net version ContinueWith might end on a thread where most of the Unity API may not be called (including Debug.Log).
Instead you could try and use ContinueWithOnMainThread from the extensions instead which was created exactly for this reason.
With the .NET 4.x runtime, continuations may often execute in the background, whilst this extension method pushes them onto the main thread.
The .NET 3.x runtime didn't have this issue as continuations were contained within the Parse library, which did this implicitly.

Photon Unity Turn based Multiplayer game

I am trying to create a simple 2D Turn based multiplayer game using Photon Unity Networking.
It is just a simple turn based game where a player 1 (host) presses his button and it adds his score and changes its turn to player 2 (client) who presses his button to add score and change turn to player 1. It continues to infinite.
I was able to connect two players in the game using the basic Photon documentation. Now I need to add the networking logic of taking turns and changing them.
I searched the internet but I can't understand the RPC and SerializeView of Photon. I am really confused with that. Please Help me. Thank you in future. Here is my GameManager Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GameManager : Photon.PunBehaviour
{
public Text roomName;
public Text player1Name,player2Name;
public List<string> playersConnected = new List<string>();
int scoreP1 = 0, scoreP2 = 0;
public Text scoreTextP1, scoreTextP2;
public Button p1Btn, p2Btn;
int playerTurn = 1;
void Start()
{
roomName.text = SceneManager.GetActiveScene().name;
p2Btn.gameObject.SetActive(false);
p1Btn.gameObject.SetActive(true);
}
public void AddScoreP1()
{
scoreP1++;
scoreTextP1.text = scoreP1.ToString();
ChangePlayerTurn();
}
public void AddScoreP2()
{
scoreP2++;
scoreTextP2.text = scoreP2.ToString();
ChangePlayerTurn();
}
void ChangePlayerTurn()
{
if (playerTurn == 1)
{
playerTurn = 2;
p2Btn.gameObject.SetActive(true);
p1Btn.gameObject.SetActive(false);
}
else
{
playerTurn = 1;
p1Btn.gameObject.SetActive(true);
p2Btn.gameObject.SetActive(false);
}
print("Player Turn: P" + playerTurn);
}
void LoadArena()
{
if (!PhotonNetwork.isMasterClient)
{
Debug.LogError("PhotonNetwork : Trying to Load a level but we are not the master Client");
}
Debug.Log("PhotonNetwork : Loading Level : " + PhotonNetwork.room.PlayerCount);
PhotonNetwork.LoadLevel("Room for " + PhotonNetwork.room.PlayerCount);
}
public override void OnLeftRoom()
{
SceneManager.LoadScene(0);
}
public void LeaveRoom()
{
PhotonNetwork.LeaveRoom();
}
public override void OnPhotonPlayerConnected(PhotonPlayer other)
{
Debug.Log("OnPhotonPlayerConnected() " + other.NickName); // not seen if you're the player connecting
foreach (PhotonPlayer _player in PhotonNetwork.playerList)
{
playersConnected.Add(other.NickName);
}
if (PhotonNetwork.isMasterClient)
{
Debug.Log("OnPhotonPlayerConnected isMasterClient " + PhotonNetwork.isMasterClient); // called before OnPhotonPlayerDisconnected
LoadArena();
}
}
public override void OnPhotonPlayerDisconnected(PhotonPlayer other)
{
Debug.Log("OnPhotonPlayerDisconnected() " + other.NickName); // seen when other disconnects
foreach (PhotonPlayer _player in PhotonNetwork.playerList)
{
playersConnected.Remove(other.NickName);
}
if (PhotonNetwork.isMasterClient)
{
Debug.Log("OnPhotonPlayerDisonnected isMasterClient " + PhotonNetwork.isMasterClient); // called before OnPhotonPlayerDisconnected
LoadArena();
}
}
}
RPC is basically a way of invoking a function on a remote client.
Usually in a multiplayer setup you'd want your MasterClient to control the flow of the game. So, when two players join a room, what you need to do is from your MasterClient, decide which player goes first and then call a RPC function from MasterClient telling both client whose the first turn is. Then whichever player's turn it is, just activate its button and let them add score (Send RPC to MasterClient as well for that, so that everyone can stay in sync.) and the update turn and tell everyone via another RPC and so on.
though you can also use Events for such cases, they need less preparation to be used.
Both in a nutshell:
RPC, Remote Procedural calls, is used to call a certain method to all or certain clients/users in the same room/level. Example like this, you might want to use RCP to update the teams score if a team scored a goal.
SerializeView is used by PUN to synchronize and read data a few times per second, depending on the serialization rate. Example like this, you will use this to get and read to see each other's data like how much points another player have in realtime.
both of these functions are practically similar, but their use is entirely different.
There is more information about RCP and SerializeView on the Photon Website Link