Unity3D. Попытка отправить команду для объекта без полномочий
У меня есть многопользовательская пошаговая стратегическая игра, в которой нужен игровой менеджер, который контролирует текущее состояние игры (чья очередь и т.д.) Этот менеджер должен быть общим для каждого клиента, его состояние должно быть синхронизировано на сервере.
Вот как я это делаю: Объект диспетчера игры - NetworkBehaviour, и он имеет NetworkIdentity, который не является полномочиями локального игрока или сервера. Я сделал собственный NetworkManager, и он запускает Game Manager при подключении к клиенту, а также проверяет, является ли он сервером. Вот код:
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);
}
Когда я запускаю его на сервере, он работает нормально, он создает экземпляр на клиенте и синхронизирует переменные от сервера к клиенту. Но когда я пытаюсь запустить команды из клиента, он игнорирует их и выдает следующее предупреждение:
Попытка отправить команду для объекта без полномочий. UnityEngine.Networking.NetworkBehaviour:SendCommandInternal(NetworkWriter, Int32, String)
Обратите внимание, что этот игровой менеджер создается раньше, чем любой игрок, потому что он должен отвечать за порождение игроков. Что я делаю неправильно?
3 ответа
Рядом с вашим фрагментом кода, предупреждение
Попытка отправить команду для объекта без полномочий.
Означает, что: вы посылаете команду от объекта, полномочия которого у вашего (игрока) отсутствуют.
Что говорится в документации Unity: Команды от объектов игрока на клиенте отправляются объектам игрока на сервере. В целях безопасности Команды могут отправляться только с объекта ВАШЕГО игрока, поэтому вы не можете управлять объектами других игроков.
НО
Начиная с выпуска Unity 5.2 можно отправлять команды от неигровых объектов, которые имеют полномочия клиента. Эти объекты должны быть созданы с помощью NetworkServer.SpawnWithClientAuthority или иметь полномочия, установленные с помощью NetworkIdentity.AssignClientAuthority. Команды, отправленные из этих объектов, выполняются на экземпляре объекта сервера, а не на связанном объекте проигрывателя для клиента ( подробнее).
Итак, каково решение: Перед отправкой команды (или выполнением функции команды) назначьте полномочия этого объекта вашему проигрывателю. Что-то вроде этого
assignAuthorityObj.GetComponent<NetworkIdentity>().AssignClientAuthority(this.GetComponent<NetworkIdentity>().connectionToClient);
" this " будет представлять ваш объект Player. После выполнения командного вызова вы можете удалить полномочия, используя этот фрагмент кода.
assignAuthorityObj.GetComponent<NetworkIdentity>().RemoveClientAuthority(this.GetComponent<NetworkIdentity>().connectionToClient);
Опять же, " это " будет представлять для вашего объекта Player.
Важное примечание: я обычно назначаю полномочия объекта (если я хочу это использовать), используя OnTriggerEnter, и удаляю полномочия для OnTriggerExit. Это зависит от конкретного сценария, при котором вы хотите получить или удалить объектные права доступа.
Вы не можете отправить команду без местных полномочий.
if (!isLocalPlayer) return;
Но какие данные вы отправляете с помощью этой команды? В общем и как ты сказал
"это состояние должно быть синхронизировано на сервере"
Так что, ИМХО, у вас нет причин отправлять Command
от вашего игрока. Команды должны быть для ввода игрока. Эти входы будут запускать действия в вашей игре. Ваш менеджер будет получать информацию об этих действиях (обновление счета, конец игры...) и отправлять данные и / или инициировать действия на клиентах, используя ClientRpc
Это модель авторитета сервера. Если вы позволите локальному объекту отправлять команды, хакер может легко войти и сказать вашему менеджеру
"Эй, это моя очередь. Эй, это моя очередь снова. Эй, мой счет 9999999, и я выиграл игру за 1 секунду".
Если вы используете зеркало, используйте Bypass Authority, потребовался целый день, чтобы попытаться реализовать вышеуказанные решения, которые не сработали. Вот как это нужно сделать: https://mirror-networking.gitbook.io/docs/guides/communication/remote-actions
[Command(requiresAuthority = false)]
public void Something (){
}