3
votes

Im having trouble sending data across my network when more than one player connects to my server. I'm getting the error:

Trying to send command for object without authority.
UnityEngine.Networking.NetworkBehaviour:SendCommandInternal(NetworkWriter, Int32, String)
Network_Transmitter:CallCmdPrepareToReceiveBytes(Int32, Int32)
<DoSendBytes>c__Iterator0:MoveNext() (at Assets/Scripts/Networking/Network_Transmitter.cs:63)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
Player_Data:PrepareServerData() (at Assets/Scripts/Player/Player_Data.cs:81)
Player_Data:OnStartLocalPlayer() (at Assets/Scripts/Player/Player_Data.cs:44)
UnityEngine.Networking.NetworkIdentity:UNetStaticUpdate()

The error is occuring at the line CmdPrepareToReceiveBytes(transmissionId, data.Length); in the function DoSendBytes in my Player_Controller script. I have this script attached along with my Network_Transmitter script to a GameObject with a NetworkIdentity assigned as Local Player Authority. Once this byte array is completely received at the server I then instantiate a prefab (serverAvatar) on the server based on the data sent like so:

public class Server_InstantiatePrefab : NetworkBehaviour
{
    [Server]
    public void InstantiatePlayerOnServer(PlayerObject playerObj)
    {
        GameObject go = Instantiate(Player_Controller.instance.serverAvatar, new Vector3(playerObj.tranX, playerObj.tranY, playerObj.tranZ), Quaternion.identity) as GameObject;

        StartCoroutine(DoLoadRawTextureData(go, playerObj.texBytes, playerObj.texWidth, playerObj.texHeight, playerObj.texFormat));
    }

    [Server]
    IEnumerator DoLoadRawTextureData(GameObject go, byte[] texBytes, int texWidth, int texHeight, TextureFormat texFormat)
    {
        Texture2D tex = new Texture2D(texWidth, texHeight, texFormat, false);
        byte[] decompressedTexBytes = lzip.decompressBuffer(texBytes);          // decompress texture byte array
        tex.LoadRawTextureData(decompressedTexBytes);
        tex.Apply();
        yield return new WaitForEndOfFrame();
        go.GetComponent<Renderer>().material.mainTexture = tex;
    }
}

So like I said, everything works when there is only one client connected but as soon as I connect more than one client I get this client authority error. How do I resolve this?

PlayerController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;

public class Network_Transmitter : NetworkBehaviour {

    public static Network_Transmitter instance = null;

    private static readonly string LOG_PREFIX = "[" + typeof(Network_Transmitter).Name + "]: "; 
    private static int defaultBufferSize = 1400; //max ethernet MTU is ~1400

    private class TransmissionData
    {
        public int curDataIndex; //current position in the array of data already received.
        public byte[] data;

        public TransmissionData(byte[] _data)
        {
            curDataIndex = 0;
            data = _data;
        }
    }

    // list of transmissions currently going on. a transmission id is used to
    // uniquely identify to which transmission a received byte[] belongs to.
    List<int> clientTransmissionIds = new List<int>();

    //maps the transmission id to the data being received.
    Dictionary<int, TransmissionData> serverTransmissionData = new Dictionary<int, TransmissionData>();

    //callbacks which are invoked on the respective events. int = transmissionId. byte[] = data sent or received.
    public event UnityAction<int, byte[]> OnDataComepletelySent;
    public event UnityAction<int, byte[]> OnDataFragmentSent;
    public event UnityAction<int, byte[]> OnDataFragmentReceived;
    public event UnityAction<int, byte[]> OnDataCompletelyReceived;

    private void Awake()
    {
        if (instance == null)
            instance = this;
        else if (instance != this)
            Destroy(gameObject);
    }

    // SEND BYTE[] IN MULTIPLE PACKETS

    [Client]
    public void SendBytes(int transmissionId, byte[] data)
    {
        Debug.Assert(!clientTransmissionIds.Contains(transmissionId));
        StartCoroutine(DoSendBytes(transmissionId, data));
    }

    [Client]
    public IEnumerator DoSendBytes(int transmissionId, byte[] data)
    {
        Debug.Assert(!clientTransmissionIds.Contains(transmissionId));
        Debug.Log(LOG_PREFIX + "SendBytesToClients processId=" + transmissionId + " | datasize=" + data.Length);

        //tell client that he is going to receive some data and tell him how much it will be.
        CmdPrepareToReceiveBytes(transmissionId, data.Length);
        yield return null;

        //begin transmission of data. send chunks of 'bufferSize' until completely transmitted.
        clientTransmissionIds.Add(transmissionId);
        TransmissionData dataToTransmit = new TransmissionData(data);
        int bufferSize = defaultBufferSize;
        while (dataToTransmit.curDataIndex < dataToTransmit.data.Length - 1)
        {
            //determine the remaining amount of bytes, still need to be sent.
            int remaining = dataToTransmit.data.Length - dataToTransmit.curDataIndex;
            if (remaining < bufferSize)
                bufferSize = remaining;

            //prepare the chunk of data which will be sent in this iteration
            byte[] buffer = new byte[bufferSize];
            System.Array.Copy(dataToTransmit.data, dataToTransmit.curDataIndex, buffer, 0, bufferSize);

            //send the chunk
            CmdReceiveBytes(transmissionId, buffer);
            dataToTransmit.curDataIndex += bufferSize;

            yield return null;

            if (null != OnDataFragmentSent)
                OnDataFragmentSent.Invoke(transmissionId, buffer);
        }

        //transmission complete.
        clientTransmissionIds.Remove(transmissionId);

        if (null != OnDataComepletelySent)
            OnDataComepletelySent.Invoke(transmissionId, dataToTransmit.data);
    }

    [Command]
    private void CmdPrepareToReceiveBytes(int transmissionId, int expectedSize)
    {
        if (serverTransmissionData.ContainsKey(transmissionId))
            return;

        //prepare data array which will be filled chunk by chunk by the received data
        TransmissionData receivingData = new TransmissionData(new byte[expectedSize]);
        serverTransmissionData.Add(transmissionId, receivingData);
    }

    //use reliable sequenced channel to ensure bytes are sent in correct order
    [Command(channel = 1)]
    private void CmdReceiveBytes(int transmissionId, byte[] recBuffer)
    {
        //already completely received or not prepared?
        if (!serverTransmissionData.ContainsKey(transmissionId))
            return;

        //copy received data into prepared array and remember current dataposition
        TransmissionData dataToReceive = serverTransmissionData[transmissionId];
        System.Array.Copy(recBuffer, 0, dataToReceive.data, dataToReceive.curDataIndex, recBuffer.Length);
        dataToReceive.curDataIndex += recBuffer.Length;

        if (null != OnDataFragmentReceived)
            OnDataFragmentReceived(transmissionId, recBuffer);

        if (dataToReceive.curDataIndex < dataToReceive.data.Length - 1)
            //current data not completely received
            return;

        //current data completely received
        Debug.Log(LOG_PREFIX + "Completely Received Data at transmissionId=" + transmissionId);
        serverTransmissionData.Remove(transmissionId);

        if (null != OnDataCompletelyReceived)
            OnDataCompletelyReceived.Invoke(transmissionId, dataToReceive.data);
    }

Network_Transmitter.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;

public class Network_Transmitter : NetworkBehaviour
{

    public static Network_Transmitter instance = null;

    private static readonly string LOG_PREFIX = "[" + typeof(Network_Transmitter).Name + "]: ";
    private static int defaultBufferSize = 1400; //max ethernet MTU is ~1400

    private class TransmissionData
    {
        public int curDataIndex; //current position in the array of data already received.
        public byte[] data;

        public TransmissionData(byte[] _data)
        {
            curDataIndex = 0;
            data = _data;
        }
    }

    // list of transmissions currently going on. a transmission id is used to
    // uniquely identify to which transmission a received byte[] belongs to.
    List<int> clientTransmissionIds = new List<int>();

    //maps the transmission id to the data being received.
    Dictionary<int, TransmissionData> serverTransmissionData = new Dictionary<int, TransmissionData>();

    //callbacks which are invoked on the respective events. int = transmissionId. byte[] = data sent or received.
    public event UnityAction<int, byte[]> OnDataComepletelySent;
    public event UnityAction<int, byte[]> OnDataFragmentSent;
    public event UnityAction<int, byte[]> OnDataFragmentReceived;
    public event UnityAction<int, byte[]> OnDataCompletelyReceived;

    private void Awake()
    {
        if (instance == null)
            instance = this;
        else if (instance != this)
            Destroy(gameObject);
    }

    // SEND BYTE[] IN MULTIPLE PACKETS

    public void SendBytes(int transmissionId, byte[] data)
    {
        Debug.Assert(!clientTransmissionIds.Contains(transmissionId));
        StartCoroutine(DoSendBytes(transmissionId, data));
    }

    public IEnumerator DoSendBytes(int transmissionId, byte[] data)
    {
        Debug.Assert(!clientTransmissionIds.Contains(transmissionId));
        Debug.Log(LOG_PREFIX + "SendBytesToClients processId=" + transmissionId + " | datasize=" + data.Length);

        //tell server that he is going to receive some data and tell him how much it will be.
        CmdPrepareToReceiveBytes(transmissionId, data.Length);
        yield return null;

        //begin transmission of data. send chunks of 'bufferSize' until completely transmitted.
        clientTransmissionIds.Add(transmissionId);
        TransmissionData dataToTransmit = new TransmissionData(data);
        int bufferSize = defaultBufferSize;
        while (dataToTransmit.curDataIndex < dataToTransmit.data.Length - 1)
        {
            //determine the remaining amount of bytes, still need to be sent.
            int remaining = dataToTransmit.data.Length - dataToTransmit.curDataIndex;
            if (remaining < bufferSize)
                bufferSize = remaining;

            //prepare the chunk of data which will be sent in this iteration
            byte[] buffer = new byte[bufferSize];
            System.Array.Copy(dataToTransmit.data, dataToTransmit.curDataIndex, buffer, 0, bufferSize);

            //send the chunk
            CmdReceiveBytes(transmissionId, buffer);
            dataToTransmit.curDataIndex += bufferSize;

            yield return null;

            if (null != OnDataFragmentSent)
                OnDataFragmentSent.Invoke(transmissionId, buffer);
        }

        //transmission complete.
        clientTransmissionIds.Remove(transmissionId);

        if (null != OnDataComepletelySent)
            OnDataComepletelySent.Invoke(transmissionId, dataToTransmit.data);
    }

    [Command]
    private void CmdPrepareToReceiveBytes(int transmissionId, int expectedSize)
    {
        if (serverTransmissionData.ContainsKey(transmissionId))
            return;

        //prepare data array which will be filled chunk by chunk by the received data
        TransmissionData receivingData = new TransmissionData(new byte[expectedSize]);
        serverTransmissionData.Add(transmissionId, receivingData);
    }

    //use reliable sequenced channel to ensure bytes are sent in correct order
    [Command(channel = 1)]
    private void CmdReceiveBytes(int transmissionId, byte[] recBuffer)
    {
        //already completely received or not prepared?
        if (!serverTransmissionData.ContainsKey(transmissionId))
            return;

        //copy received data into prepared array and remember current dataposition
        TransmissionData dataToReceive = serverTransmissionData[transmissionId];
        System.Array.Copy(recBuffer, 0, dataToReceive.data, dataToReceive.curDataIndex, recBuffer.Length);
        dataToReceive.curDataIndex += recBuffer.Length;

        if (null != OnDataFragmentReceived)
            OnDataFragmentReceived(transmissionId, recBuffer);

        if (dataToReceive.curDataIndex < dataToReceive.data.Length - 1)
            //current data not completely received
            return;

        //current data completely received
        Debug.Log(LOG_PREFIX + "Completely Received Data at transmissionId=" + transmissionId);
        serverTransmissionData.Remove(transmissionId);

        if (null != OnDataCompletelyReceived)
            OnDataCompletelyReceived.Invoke(transmissionId, dataToReceive.data);
    }
}

Player_Data.cs

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using VacuumShaders.TextureExtensions;

[Serializable]
public class PlayerObject
{
    public byte[] texBytes;
    public int texWidth;
    public int texHeight;
    public TextureFormat texFormat;

    public float tranX;
    public float tranY;
    public float tranZ;
    public string type;
    public string id;
    public int strength;
    public int hitpoints;
}

public class Player_Data : NetworkBehaviour
{
    PlayerObject playerObj = new PlayerObject(); 

    public Texture2D texToSend;
    private Texture2D texResized;

    private string typeToSend = "Deer";
    public Vector3 tran;
    private int strengthToSend = 80;
    private int hitPointsToSend = 2;

    private void Start()
    {
        if (!isLocalPlayer)
            return;

        PrepareServerData();
    }

    [Client]
    public void PrepareServerData()
    {
        Player_Spawn spawn = GetComponent<Player_Spawn>();
        tran = spawn.GetPlayerPos();

        StartCoroutine(DoResizeTexture(texToSend));
        StartCoroutine(DoGetRawTextureData(texResized));

        playerObj.texWidth = texResized.width;
        playerObj.texHeight = texResized.height;
        playerObj.texFormat = texResized.format;
        playerObj.tranX = tran.x;
        playerObj.tranY = tran.y;
        playerObj.tranZ = tran.z;
        playerObj.type = typeToSend;
        Player_ID id = GetComponent<Player_ID>();
        playerObj.id = id.MakeUniqueIdentity();
        playerObj.strength = strengthToSend;
        playerObj.hitpoints = hitPointsToSend;

        Network_Serializer serialize = GetComponent<Network_Serializer>();

        // Send Data from Client to Server as many small sequenced packets
        byte[] bytes = serialize.ObjectToByteArray(playerObj);

        Network_Transmitter networkTransmitter = GetComponent<Network_Transmitter>();
        StartCoroutine(networkTransmitter.DoSendBytes(0, bytes));
    }

    IEnumerator DoResizeTexture(Texture2D tex)
    {
        tex.ResizePro(16, 16, out texResized);
        yield return new WaitForEndOfFrame();
    }

    IEnumerator DoGetRawTextureData(Texture2D tex)
    {
        byte[] texBytes = tex.GetRawTextureData();                      // comvert texture to raw bytes
        byte[] compressedTexBytes = lzip.compressBuffer(texBytes, 1);   // compress texture byte array
        playerObj.texBytes = compressedTexBytes;                        // set compressed bytes to player object

        yield return new WaitForEndOfFrame();

        GameObject infoDisplayText = GameObject.Find("InfoDisplay");
        infoDisplayText.GetComponent<Text>().text += "Bytes to send : " + playerObj.texBytes.Length + "\n";
    }
}
1
Have you seen this response on a similar question? stackoverflow.com/a/42689614/1395607mushcraft

1 Answers

5
votes

Well this is the problem which I faced when first time I was learning UNET. So here is the general answer and I am sure it will solve your problem once you get that How UNET actually works.

Beside your code fragment, the error

Trying to send command for object without authority.

Clearly means that you are sending Command request from an object whose authority you don't have.

So What it means and how to resolve it you can see my answer Here.

According to your code fragment, Before sending the command (or executing command function), Assign authority of that object (where your script attached) to your Player.

Hope that it will make sense and solve the problem. Happy!