Unity:当连接多个客户端时,尝试为对象发送命令而没有权限错误

gre*_*Bow 3 c# networking client unity-game-engine

当多个玩家连接到我的服务器时,我无法通过我的网络发送数据。我收到错误:

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()
Run Code Online (Sandbox Code Playgroud)

错误发生在我的 Player_Controller 脚本CmdPrepareToReceiveBytes(transmissionId, data.Length);中的函数行中DoSendBytes。我将这个脚本与我的 Network_Transmitter 脚本一起附加到一个游戏对象,其中 NetworkIdentity 被分配为本地玩家权限。一旦在服务器上完全接收到这个字节数组,我就会根据发送的数据在服务器上实例化一个预制件(serverAvatar),如下所示:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

所以就像我说的,当只有一个客户端连接时一切正常,但是一旦我连接多个客户端,我就会收到这个客户端权限错误。我该如何解决?

玩家控制器.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);
    }
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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";
    }
}
Run Code Online (Sandbox Code Playgroud)

Muh*_*han 5

嗯,这就是我第一次学习 UNET 时遇到的问题。所以这是一般答案,我相信一旦您了解了 UNET 的实际工作原理,它就会解决您的问题。

在您的代码片段旁边,错误

试图在没有权限的情况下为对象发送命令。

显然意味着您正在从您没有权限的对象发送命令请求

那么这意味着什么以及如何解决它,您可以在此处查看我的答案。

根据您的代码片段, 在发送命令(或执行命令功能)之前,将该对象(您的脚本附加的位置)的权限分配给您的 Player

希望它会有意义并解决问题。快乐的!