0

I am building a 3d virtual Auction house in Unity for my semester's project where multiple users can join the host server and interact with the action items in the game and increase their bids. The updated bid values should be synced across all the users. I am able to sync player's movements across the network by adding my "character controller" to the "player prefab" of the network manager. Users are also able to interact with the other game object to increase the item's bid locally. I am facing problems in syncing the updated bids of each auction item across the network for every client.

I am adding each auction item to the "registered spawnable prefabs" list of the network manager.

Registered Spawnable Prefabs

This is the error I am getting


Trying to send command for object without authority. DataSync.CmdIncreaseBidUI
UnityEngine.Debug:LogWarning(Object)
Mirror.NetworkBehaviour:SendCommandInternal(Type, String, NetworkWriter, Int32, Boolean) (at Assets/Mirror/Runtime/NetworkBehaviour.cs:185)
DataSync:CmdIncreaseBidUI(Int32)
DataSync:Update() (at Assets/Scripts/DataSync.cs:38)

This is the script that I placed on my auction item. I am using the text mash pro game object to show the current bid of the auction item.

Game Scene

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;

public class DataSync : NetworkBehaviour
{

    [SyncVar]
    public int num = 100;

    public TMPro.TextMeshPro textObj;

    void Start()
    {
    }

    [Command]
    public void CmdIncreaseBidUI(int num)
    {
        num += 100;
        RpcIncreaseBidUI(num);
    }

    [ClientRpc]
    public void RpcIncreaseBidUI(int num)
    {
        this.num = num;
        GetComponent<TMPro.TextMeshProUGUI>().text = "Current Bid $" + num.ToString();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown("space"))
        {
            CmdIncreaseBidUI(num);
        }
    }
}

3
  • Sounds like your DataSync class is not placed on the player object so you don't have the authority to send a command ... You should put your logic on your player and only reference the NetworkIdentity of that DataSync object
    – derHugo
    Commented Mar 22, 2021 at 6:46
  • Thank you for the comment and help. Your solution kind of worked but I am still not able to do what I intended. I am pretty new to Unity. It seems like, the variable "num" is not syncing across the network. When I update the variable on the host it successfully updates the UI on the client with the updated bid. But when I try to increase the updated bid on the client, the variable "num" takes the old value and not the updated one. The same happens on the host side too :/ Commented Mar 25, 2021 at 0:13
  • And also I am still getting the same message as the warning now, "Trying to send command for object without authority. DataSync.CmdIncreaseBidUI" Commented Mar 25, 2021 at 1:28

1 Answer 1

2

As said by default only the Host/Server has the Network Authority over spawned objects.

enter image description here

Unless you spawn them with a certain client having the authority over the object (using Networking.NetworkServer.SpawnWithClientAuthority) but this also makes no sense if multiple clients shall be able to interact with the object as in your case.


And [SyncVar]

variables will have their values sychronized from the server to clients

.. and only in this direction!


Now there is the option to gain the authority over an object via Networking.NetworkIdentity.AssignClientAuthority so yes, you could send a [Command] to the Host/Server and tell it to assign the authority to you so you can call a [Command] on that object.

However, this has some huge flaws:

  • As this might change very often and quick you always have to make sure first that you currently have the authority and also the Host/Server has to make sure authority is assignable to you
  • You don't know when exactly the Host/Server is done assigning the authority so you would have to send a ClientRpc back to the client so he knows that now he can send a Command back to the host ... you see where this is going: Why not simply tell the Host/Server already with the very first [Command] what shall happen ;)

Instead you have to (should/ I would ^^) put your logic into your player script (or at least a script attached to the player object you have authority over) and rather do something like

using System.Linq;

// This script goes on your player object/prefab
public class DataSyncInteractor : NetworkBehaviour
{
    // configure this interaction range via the Inspector in Unity units
    [SerializeField] private float interactionRange = 1;

    // Update is called once per frame
    void Update()
    {
        //Disable this compoennt if this is not your local player
        if (!isLocalPlayer)
        {
            enabled = false;
            return;
        }

        if (Input.GetKeyDown("space"))
        {
            if(FindClosestDataSyncItemInRange(out var dataSync))
            {
                CmdIncreaseBid(dataSync.gameObject, gameObject);
            }
        }
    }

    private bool FindClosestDataSyncItemInRange(out DataSync closestDataSyncInRange)
    {
        // Find all existing DataSync items in the scene that are currently actuve and enabled
        var allActiveAndEnabledDataSyncs = FindObjectsOfType<DataSync>();

        // Use Linq Where to filter out only those that are in range
        var dataSyncsInRange = allActiveAndEnabledDataSyncs.Where(d => Vector3.Distance(d.transform.position, transform.position) <= interactionRange);

        // Use Linq OrderBy to order them by distance (ascending)
        var dataSyncsOrderedByDistance = dataSyncsInRange.OrderBy(d => Vector3.Distance(d.transform.position, transform.position));

        // Take the first item (the closest one) or null if the list is empty
        closestDataSyncInRange = dataSyncsOrderedByDistance.FirstOrDefault();

        // return true if an item was found (meaning closestDataSyncInRange != null)
        return closestDataSyncInRange;
    }

    // As you can see the CMD is now on the player object so here you HAVE the authority
    // You can pass in a reference to GameObject as long as this GameObject has a
    // NetworkIdentity component attached!
    [Command]
    private void CmdIncreaseBid(GameObject dataSyncObject, GameObject biddingPlayer)
    {
        // This is happening on the host
        // Just as a little extra from my side: I thought it would probably be interesting to store the 
        // last bidding player so when it comes to sell the item you know
        // who was the last one to bid on it ;)
        dataSyncObject.GetComponent<DataSync>().IncreaseBid(biddingPlayer);
    }
}

and then change your DataSync a little

[RequireComponent(typeof(NetworkIdentity))]
public class DataSync : NetworkBehaviour
{
    // This is automatically synced from HOST to CLIENT
    // (and only in this direction)
    // whenever it does the hook method will be executed on all clients
    [SyncVar(hook = nameof(OnNumChanged))]
    public int num = 100;

    public TMPro.TextMeshPro textObj;

    public GameObject LastBiddingPlayer;

    void Start()
    {
        if(!textObj) textObj = GetComponent<TMPro.TextMeshProUGUI>();
    }

    // This method will only be called on the Host/Server
    [Server]
    public void IncreaseBid(GameObject biddingPlayer)
    {
        // increase the value -> will be synced to all clients
        num += 100;

        // store the last bidding player (probably enough to do this on the host)
        LastBiddingPlayer = biddingPlayer;

        // Since the hook is only called on the clients
        // update the display also on the host
        OnNumChanged(num);
    }

    private void OnNumChanged(int newNum)
    {
        textObj.text = "Current Bid $" + num.ToString();
    }
}
1
  • I think I get it now. Earlier I also went with the approach you mentioned where we can assign authority to game objects using "Networking.NetworkIdentity.AssignClientAuthority" but it was also a little confusing for me. The solution that you provided worked like a charm. Thank you for the thorough description. I am still learning Unity and with your solution and a little research of mine, I made it work. Also now I think. Commented Mar 27, 2021 at 22:21

Not the answer you're looking for? Browse other questions tagged or ask your own question.