I am making a multiplayer game and I want to have player to interact with a non-player object (whose transform can be changed by any player). When I interact them with the player who joined first (or the guy who is hosting) its working but if I try to interact it with the another player (the one who joined second) the objects goes back to the location that the first player left him at.
So what I tried is to shift the authority of non-player object but I am having the following errors.
Anyone is having the same issue or knows any other way to do the above task? I am using the following code to change the authority:
[Command]
void Cmd_AssignLocalAuthority(GameObject obj)
{
print("shifting authority successfully");
NetworkInstanceId nIns = obj.GetComponent<NetworkIdentity>().netId;
GameObject client = NetworkServer.FindLocalObject(nIns);
NetworkIdentity ni = client.GetComponent<NetworkIdentity>();
ni.AssignClientAuthority(connectionToClient);
}
[Command]
void Cmd_RemoveLocalAuthority(GameObject obj)
{
print("reverting authority successfully");
NetworkInstanceId nIns = obj.GetComponent<NetworkIdentity>().netId;
GameObject client = NetworkServer.FindLocalObject(nIns);
NetworkIdentity ni = client.GetComponent<NetworkIdentity>();
ni.RemoveClientAuthority(ni.clientAuthorityOwner);
}
And the error I am getting is this
You need to know that the changes SHOULD be called from a player object, not the object itself, as it do not have authority.
For setting authority you should do something like this:
[Command]
public void CmdSetAuth(NetworkInstanceId objectId, NetworkIdentity player)
{
GameObject iObject = NetworkServer.FindLocalObject(objectId);
NetworkIdentity networkIdentity = iObject.GetComponent<NetworkIdentity>();
//Checks if anyone else has authority and removes it and lastly gives the authority to the player who interacts with object
NetworkConnection otherOwner = networkIdentity.clientAuthorityOwner;
if (otherOwner == player.connectionToClient)
{
return;
}
else
{
if (otherOwner != null)
{
networkIdentity.RemoveClientAuthority(otherOwner);
}
networkIdentity.AssignClientAuthority(player.connectionToClient);
}
networkIdentity.AssignClientAuthority(player.connectionToClient);
}
Related
I have been trying to spawn gameObject(Specifically Player gameObject) in Server using this player.GetComponent().Spawn() and then tried to change ownership. It didn't works for me. So frustrated. Can anyone help on this.
I want to spawn two different player in server and client. So I tried to spawn all the players in server only and tried to change the ownership using ownerclientId, LocalClientId. Nothing worked. What happens is that it spawns two players in both server and client screen but only server has the ownership.
Code snippet:
private void Start()
{
var playerSelected = GameObject.Find("PlayerSelected");
int selectedPlayer = playerSelected.GetComponent<PlayerSelected>().selectPlayer;
if (NetworkManager.Singleton.IsServer)
{
PlayerSpawner(selectedPlayer, OwnerClientId);
}
else
{
PlayerSpawnerOnServerRpc(selectedPlayer, OwnerClientId);
}
}
[ServerRpc(RequireOwnership = false)]
void PlayerSpawnerOnServerRpc(int selectedPlayer, ulong clientId)
{
PlayerSpawner(selectedPlayer, clientId);
}
void PlayerSpawner(int selectedPlayer, ulong clientId)
{
GameObject player;
switch(selectedPlayer)
{
case 0:
player = Instantiate(player1Prefab, spawnPositionPlayer1);
player.GetComponent<NetworkObject>().Spawn();
player.GetComponent<NetworkObject>().ChangeOwnership(clientId);
break;
case 1:
player = Instantiate(player2Prefab, spawnPositionPlayer2);
player.GetComponent<NetworkObject>().Spawn();
player.GetComponent<NetworkObject>().ChangeOwnership(clientId);
break;
}
}
The reason this doesn't work is that the server is sending two messages in quick succession.
player.GetComponent<NetworkObject>().Spawn();
player.GetComponent<NetworkObject>().ChangeOwnership(clientId);
The first tells the clients to spawn an object. The second gives ownership to the object. You cannot guarantee (even running locally) that the Spawn() has finished when the ChangeOwnership() is called. So when ChangeOwnership() is called, the client may not have finished initialising the object that's spawned.
Unity were aware of this, and provided you with SpawnWithOwnership (docs). Replace your two lines with one:
player.GetComponent<NetworkObject>().SpawnWithOwnership(clientId);
I'm new to unity multiplayer so just learning it's concepts. I have this script on my player
private void Start()
{
if (isServer)
{
//run when if it's server
} else
{
//run if its client
this.InstantiateBullet();
}
}
[Command]
void InstantiateBullet()
{
Debug.Log("Test Command");
}
I'm getting a warning "Trying to send command for object without authority. ShootHooks.InstantiateBullet" My question is do I have to assign my host player prefab to client an authority so it can call InstantiateBullet() without getting warning? Or can I just ignore the warning
As the error/warning tells you you may only call commands from an object you have the authority over.
Currently it is running on all instances of your player prefab, also the ones that belong to the other connected players and you don't have the authority over those but they do!
So simply make an additional check
private void Start()
{
if(hasAuthority)
{
if(isServer)
{
...
}
else
{
InstantiateBullet();
}
}
}
I am trying to implement skill based matchmaking using Photon in Unity. It seems
I got most of this code from the documentation and it works but not well.
The problem is that you can't use JoinOrCreate() with the sql lobby type so my logic here is try and find a room, if it fails create one.
void init()
{
_client = Game.Context.GetComponent<SocketConnectionManager>().client;
joinRoom();
}
public void joinRoom()
{
TypedLobby sqlLobby = new TypedLobby("skillLobby", LobbyType.SqlLobby);
string sqlLobbyFilter = "C0 BETWEEN 100 AND 200";
_client.OpJoinRandomRoom(null, MatchMaker.MaxPlayers, MatchmakingMode.FillRoom, sqlLobby, sqlLobbyFilter);
}
public void createRoom()
{
RoomOptions o = new RoomOptions();
o.MaxPlayers = MatchMaker.MaxPlayers;
o.CustomRoomProperties = new Hashtable() { { "C0", Game.Me.getInt("trophies") } };
o.CustomRoomPropertiesForLobby = new string[] { "C0" }; // this makes "C0" available in the lobby
TypedLobby sqlLobby = new TypedLobby("skillLobby", LobbyType.SqlLobby);
_client.OpCreateRoom("", o, sqlLobby);
}
private void onEvent(EventData obj)
{
if (_client.CurrentRoom != null)
{
if (_client.CurrentRoom.PlayerCount >= _client.CurrentRoom.MaxPlayers)
{
// sweet I am good to go.
}
}
else
{
createRoom();
}
}
The problem is this is pretty unreliable. Say two players try to find a game at the same time they will both search fail and then both create. Now I have two players sitting in empty rooms instead of playing each other.
Any ideas on a better system?
Thanks all.
Thank you for choosing Photon!
First of all, there are few things that you should understand about Photon:
you can't use JoinOrCreate() with the sql lobby type
This is not correct.
Where did you read such thing?
Did you test this yourself? What did you test exactly?
onEvent (LoadBalancingClient.OnEventAction) callback cannot be used to be notified of a failed join random room operation. Instead, you should make use of the LoadBalancingClient.OnOpResponseAction callback, as follows:
private void OnOpResponse(OperationResponse operationResponse)
{
switch (operationResponse.Code)
{
case OperationCode.JoinRandomGame:
if (operationResponse.ReturnCode == ErrorCode.NoMatchFound)
{
createRoom();
}
break;
}
}
To detect a join event inside a room (local or remote player entered a room):
private void onEvent(EventData eventData)
{
switch (eventData.Code)
{
case EventCode.Join:
int actorNr = (int)eventData[ParameterCode.ActorNr];
PhotonPlayer originatingPlayer = this.GetPlayerWithId(actorNr);
if (originatingPlayer.IsLocal)
{
}
else
{
}
break;
}
}
To answer your question:
Say two players try to find a game at the same time they will both
search fail and then both create.
Any ideas on a better system?
No.
This issue happens only during the development phase where you use a few clients to run some tests. Once you have enough user base you won't notice this issue.
A separate running Thread that are handling Rooms and Users in queue to pick 2 players after client initiates search request
The following is the scenario which I am really concered about and not quite understand wheather or not I am in the right path
lets say Player A and Player B sent search request to Lobby_1 room.
Both User objects are added to ConcurrentLinkedQueue which is
already mapped to Room ( Looby_1 ) so this condition "if (
queueSize >= 2 )" satisfies and its about to call onSearchSuccess
on lobby object sending Player A and Player B as params. Since
this is separate thread running concurrently all the time, Lets say
Player A quits/disconnects just before this function call
"lobby.onSearchSuccess(player1, player2);" since Player A
disconnect is happening before executing onSearchSuccess on Lobby,
Player A recieves onSearchSuccess and wait for the game to begin
forever. If onSearchSuccess execute first and then disconnect, I
could let Player A know that Player B is disconnected
Is adding Room and User objects to Collection objects and handling
them inside a separate Thread is recommended? is there any problem
in doing so ?
I hope I've made my question clear.
How to solve this problem. I don't think introducing synchronization in not the right approach in multiplayer games
public void run() {
if(!isRunning()) {
setRunning(true);
}
while( isRunning() ) {
for(Map.Entry<Room, ConcurrentLinkedQueue<User>> entry : LOBBY_MAP.entrySet()) {
Room room = entry.getKey();
ConcurrentLinkedQueue<User> queue = entry.getValue();
//check queue size
int queueSize = queue.size();
//minimum queue size must be 2 or greater than 2
if ( queueSize >= 2 ) {
logger.debug("QUEUE SIZE: " + queueSize);
User player1 = queue.poll();
User player2 = queue.poll();
//check if both players are not null
if( player1 != null && player2 != null ) {
Lobby lobby = (Lobby) room.getExtension();
lobby.onSearchSuccess(player1, player2);
}
}
}
}
}
public class UserDisconnectHandler extends BaseServerEventHandler {
public void handleServerEvent(ISFSEvent isfse) throws SFSException {
if(isfse.getType() == SFSEventType.USER_DISCONNECT ) {
User user = (User) isfse.getParameter(SFSEventParam.USER);
List<Room> rooms = (List<Room>) isfse.getParameter(SFSEventParam.JOINED_ROOMS);
if( rooms != null ) {
for(Room room : rooms) {
if( !room.isGame() ) {
SearchWorker.getInstance().removeUser(room, user);
//handle users qualified after search success
}
}
}
}
}
}
Why you are using a tread for that ?
I did this thing with requests. I mean , when player A request , a listener in server , add him to room FindMatch , then when player B request , you add him to that room too. but , you must check the room size in each request and find out if size is 2 , then remove them from FindMatch room and join them in game room.
this is how i did it for my game , with this way , you can even create game room for more than 2 players.
I am having an issue with forge networking.I have finished beginner tutorials and now I wanted to create my own project.It is very simple:I create a local game on one of my devices and client connects.Each of them(client and host) enter their number, and then host's number is displayed to client and vice versa.However, client's number never gets saved and host always sees 0 for client's number, while the actual number is different(the host's number is getting saved and synchronized through the network properly and client CAN read it). I took these steps in creating this project: 1) in forge network contract wizard I have created 2 variables for storing numbers 2) i created a simple script for saving and loading those 2 numbers(this script is attached to an empty game object) :
public void UpdateNumber(Text Nmbr)//Button!
{
if (!networkObject.IsServer) {
networkObject.clientNmbr = int.Parse (Nmbr.text);
} else {
networkObject.hostNmbr = int.Parse (Nmbr.text);
}
}
public void ShowText(Text text)
{
if (networkObject.IsServer ) {
text.text = "client:" + networkObject.clientNmbr;
} else {
text.text = "host:" + networkObject.hostNmbr;
}
}
I don't see why this works for host but doesn't work for the client