Unity:当连接多个客户端时,尝试在没有权限错误的情况下发送对象命令

时间:2017-11-10 17:55:45

标签: c# unity3d networking client

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

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()

我的Player_Controller脚本中函数CmdPrepareToReceiveBytes(transmissionId, data.Length);中的行DoSendBytes发生错误。我将此脚本与我的Network_Transmitter脚本一起附加到GameObject,并将NetworkIdentity指定为Local Player Authority。一旦在服务器上完全接收到该字节数组,我就会根据发送的数据在服务器上实例化一个预制(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;
    }
}

就像我说的那样,当只有一个客户端连接时,一切正常,但是当我连接多个客户端时,我得到了这个客户端权限错误。我该如何解决这个问题?

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 个答案:

答案 0 :(得分:4)

这是我第一次学习UNET时遇到的问题。所以这是一般的答案,我相信一旦你得到UNET的实际工作,它将解决你的问题。

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

  

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

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

那么它意味着什么以及如何解决它你可以看到我的答案Here

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

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