As said by default only the Host/Server has the Network Authority over spawned objects.
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/kl1S5.png)
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();
}
}
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 thatDataSync
object