Unity3D。试图在未经授权的情况下为对象发送命令

Unity3D. Trying to send command for object without authority

我有一个多人回合制策略游戏,需要一个游戏管理器来控制当前游戏状态(轮到谁等等)。这个管理器应该对每个客户端都是通用的,它的状态应该在服务器上同步。

我是这样操作的: 游戏管理器对象是 NetworkBehaviour,它具有 NetworkIdentity,它既不是本地玩家权限也不是服务器权限。我制作了一个自定义 NetworkManager,它在客户端连接时生成游戏管理器,同时测试它是否是服务器。这是一个代码:

public override void OnClientConnect(NetworkConnection conn)
    {
        ClientScene.Ready(conn);
        if (NetworkServer.active)
        {
            var manager = Instantiate(MultiplayerManagerPrefab, Vector3.zero, Quaternion.identity) as GameObject;
            var tacticsManager = manager.GetComponent<MultiplayerManagerModel>();
            NetworkServer.RegisterHandler(MsgType.AddPlayer, tacticsManager.CreatePlayerOnServer);
            NetworkServer.Spawn(manager);
        }
        ClientScene.AddPlayer(0);
    }

当我 运行 它在服务器上工作时它工作正常,它在客户端上创建一个实例并将变量从服务器同步到客户端。但是当我尝试从客户端执行 运行 命令时,它会忽略它们,并抛出此警告:

Trying to send command for object without authority. UnityEngine.Networking.NetworkBehaviour:SendCommandInternal(NetworkWriter, Int32, String)

请注意,此游戏管理器在任何玩家之前生成,因为它必须负责生成玩家。我做错了什么?

没有本地权限不能发送命令。

if (!isLocalPlayer) return;

但是,你用这个命令发送什么样的数据? 一般来说,正如你所说:

"it's state should be synchronized on server"

所以,恕我直言,您没有理由从您的播放器发送 Command。 命令应该用于玩家输入。 这些输入将触发您游戏中的动作。 您的经理将从这些操作中获取信息(比分更新、游戏结束...)并使用 ClientRpc

向客户端发送数据 and/or 触发操作

这是服务器权限模型。如果你让本地对象发送命令,黑客很容易进来并告诉你的经理

"Hey, it's my turn. Hey, it's my turn again. Hey, my score is 9999999 and I won the game in 1 second.".

在您的代码片段旁边,警告

Trying to send command for object without authority.

意味着: 您正在从一个对象发送 命令,该对象的 authority,您的 (播放器)没有。

Unity 文档说明了什么: 命令 从客户端上的播放器对象发送到服务器上的播放器对象。为了安全起见,命令只能从你的玩家对象发送,所以你不能控制其他玩家的对象。

但是

Starting with Unity release 5.2 it is possible to send commands from non-player objects that have client authority. These objects must have been spawned with NetworkServer.SpawnWithClientAuthority or have authority set with NetworkIdentity.AssignClientAuthority. Commands sent from these object are run on the server instance of the object, not on the associated player object for the client(more).

那么解决方法是什么: 在发送Command(或执行command函数)之前,分配那个权限反对你的玩家。像这样

assignAuthorityObj.GetComponent<NetworkIdentity>().AssignClientAuthority(this.GetComponent<NetworkIdentity>().connectionToClient);

"this" 将代表您的 Player 对象。在进行 Command 调用后,您可以使用此代码段删除权限。

 assignAuthorityObj.GetComponent<NetworkIdentity>().RemoveClientAuthority(this.GetComponent<NetworkIdentity>().connectionToClient);

同样,“this”将代表您的 Player 对象。

重要说明:我通常使用 OnTriggerEnter 分配对象的权限(如果我想使用它)并取消对 OnTriggerExit 的授权。需要在什么情况下获取或移除对象权限,要看具体场景。

如果您使用的是镜像,那么请使用绕过权限,花了一整天时间尝试实施上述解决方案,但没有奏效。 应该怎么做:https://mirror-networking.gitbook.io/docs/guides/communications/remote-actions

[Command(requiresAuthority = false)]
public void Something (){
   
}