Player Object

In the HLAPI of the network system players are special kinds of objects. They represent the player on the server and so have the ability to run commands (which are secure client-to-server remote procedure calls) from the player’s client. In this server authoritative system, other non-player server side objects do not have the capability to receive commands directly from objects on clients. This is both for security and to reduce the complexity of working in a distributed environment. Routing all incoming commands from users through the player object ensures that these messages came from the right place, the right client, and can be handling in a central location. 在网络高层API系统中,player是一个特殊的object。它表征了在服务端中的玩家对象。所以它可以在玩家的客户端中,执行Commands调用。在这个服务端授权的系统的中,其他的非玩家的服务端object不能直接地接受客户端中的object发送的Commands操作。这种设计是为了提高安全性和降低分布式环境的工作复杂度。通过player object,对所有的从用户发送过来的Commands进行路由,可以确保这些消息是来自于正确的地方,正确的客户端。同时也可以在一个集散中心(central location)进行消息操作。
When using the NetworkManager, a player is added by default when a client connects to the server. In some situations though, adding players should be deferred until some input event happens, so this behaviour can be turned off with the AutoCreatePlayer checkbox on the NetworkManager. When a player is added, the NetworkManager will instantiate an object from the PlayerPrefab and associate it with the connection. This is actually done by the NetworkManager calling NetworkServer.AddPlayerForConnection. This behaviour can be modified by overriding NetworkManager.OnServerAddPlayer. The default implementation of OnServerAddPlayer instantiates a new player instance from the PlayerPrefab and calls NetworkServer.AddPlayerForConnection to spawn the new player instance. A custom implementation of OnServerAddPlayer must also call NetworkServer.AddPlayerForConnection, but is free to perform any other initialization it requires. The example below customizes the color of a player: 当使用NetworkManager时,当一个客户端连接上服务器时,一个player将会默认地被添加进来。在一些情况下,这种“添加player”的操作应该延迟至有一些输入信息发生时才进行。所以要达到这个目的,在NetworkManager组件面板中,“AutoCreatePlayer”的checkbox选项应该被取消掉。当一个player被添加时,NetworkManager将会从对应的player的预设体中实例化一个player object出来。这个操作实质上是由NetworkManager内部调用了NetworkServer.AddPlayerForConnection方法去完成的。通过重载NetworkManager.OnServerAddPlayer方法,可以修改这个操作。OnServerAddPlayer方法的默认实现是:从player预设体中实例化出一个player实例,然后调用NetworkServer.AddPlayerForConnection去产生它。如果重载实现OnServerAddPlayer方法的话,也应该要调用NetworkServer.AddPlayerForConnection。重载实现OnServerAddPlayer方法的好处在于可以定义一些需要的初始化操作,以下代码演示了如何设置一个player的颜色:
class Player : NetworkBehaviour
    public Color color;

class MyManager : NetworkManager
    public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
        GameObject player = (GameObject)Instantiate(playerPrefab, Vector3.Zero, Quaternion.Identity);
        player.GetComponent<Player>().color = Color.Red;
        NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
The function NetworkServer.AddPlayerForConnection does not have to be called from within OnServerAddPlayer. As long as the correct connection object and playerControllerId are passed in, it can be called after OnServerAddPlayer has returned. This allows asynchronous steps to happen in between, such as loading player data from a remote data source. 在上述代码中,函数NetworkServer.AddPlayerForConnection不是一定要在重载的OnServerAddPlayer函数中被调用的。只要参数conn和playerControllerId被传递进来。它可以在OnServerAddPlayer函数返回后才被调用。这样子可以允许在OnServerAddPlayer返回之后,AddPlayerForConnection方法被调用之前,进行若干异步操作,比如从一些远程数据中装载player的相关数据。
The HLAPI treats players and clients as separate objects. In most cases there is a single player for each client. But, in some situations - such as when there are multiple controllers connected to a console system, they could be multiple player objects for a single connection. When there are multiple players for a connection, the playerControllerId property is used to tell them apart. This is an identifier that is scoped to the connection - so that it literally maps to the id of the controller associated with the player on that client. 网络高层API是把player和客户端分开对待的。在大多数情况下,每一个客户端只有一个player。但是,在一些情况下,比如在一些允许有多人同时操作的控制台游戏中,在一个单独的客户端连接中,会有多个player object的存在。当一个连接有多个player存在的话。参数playerControllerId可以告诉他们要分别处理。这个playerControllerId参数是一个在这个连接中的一个ID值。所以这个参数可以近似地认为是对于一个客户端链接的不同玩家控制的player。
The player object passed to NetworkServer.AddPlayerForConnection on the server is automatically spawned by the system, so there is no need to call NetworkServer.Spawn for the player. Once a player is ready, the active NetworkIdentity objects in the scene will be spawned on the player’s client. So all networked objects in the game will be created on that client with their latest state, so they are in sync with the other participants of the game. 作为参数传递给NetworkServer.AddPlayerForConnection的player object。是自动地由系统产生的,所以在代码出就不用显式地去调用NetworkServer.Spawn去生成player了。一旦一个player准备好了。在场景中的,处于活跃状态的,挂接有NetworkIdentity组件的object,就会在玩家的客户端中被生成出来。所以所有的网络化对象(networked object)将会伴随着他们最新的状态,在客户端中被创建出来。所以他们是和游戏中的其他的参与者(participants)同步的。
The playerPrefab on the NetworkManager does not have to be used to create player objects. You could use different methods of creating different players. 注册在NetworkManager的player的预设体不需要用来创建player object。你可以用不同的方法来创建不同的player。
The function AddPlayerForConnection does not have to be called from within OnServerAddPlayer. It could be called asynchronously, such as when a request to another service like a database returns information on what kind of player to created. AddPlayerForConnection函数不一定非得在OnServerAddPlayer方法中调用。它可以被异步地调用。这样子可以在等请求其他服务器的操作完成之后,比如请求一个数据库返回信息之后,再去创建对应的player object
Ready State
In addition to players, client connections also have a “ready” state. A client that is ready is sent spawned objects and state synchronization updates; a client that is not ready, is not sent these updates. When a client initially connects to a server it is not ready. While in this state, the client is able to do things that don’t require real-time interactions with the server simulation, such as load scenes, choose avatars or fill out login boxes. Once a client has all their pre-game work done, and all their assets loaded, they can call ClientScene.Ready to enter the ready state. The simple example above also works, as adding a player with NetworkServer.AddPlayerForConnection also puts the client into the ready state if it is not already in that state. 除player之外,其他的客户端连接也拥有一个“ready”状态。所谓的“一个客户端处于ready状态”,是指可以被发送spawned object,以及状态同步更新。一个客户端尚未ready,则不能发送update。当一个client初始地连接一个服务器时,它还未ready好。当在这个未ready好的状态时,客户端可以去做一些不需要和服务端实时交互的操作。比如装载场景;选择装备等等。一旦一个客户端准备好所有的游戏前的工作(pre-game work)。所有的资源都已经被加载完毕。它可以调用ClientScene.Ready去进入ready状态。上面的示例代码也是如此,因为调用NetworkServer.AddPlayerForConnection去添加一个player,其实也是会把客户端设置为ready状态的。
Clients can send and receive network messages without being ready, which also means without having an active player. So a client at a menu or selection screen can be connected to the game and interact with it, even though they have no player object. There is a section later in this document about sending messages without using commands and RPC calls. 客户端在没有处于ready状态时,也是可以发送和接受网络消息。但这也意味着当前没有一个处于活跃状态的player。所以当客户端处于一个开始菜单或者选择屏幕的时候,它也是连上了游戏并且和发生交互。尽管这时候没有player object。这也是下面章节中会提及到的,不使用Commands和RPC calls去发送消息的机制。
Switching Players
The player object for a connection can be replaced with NetworkServer.ReplacePlayerForConnection. This can be useful to restrict the commands that can be issued by players at certain times, such as in a pre-game lobby screen. This function takes the same arguments as AddPlayerForConnection, but allows there to already be a player for that connection. The old player object does not have to be destroyed. The NetworkLobbyManager uses this technique to switch from the LobbyPlayer to a game-play player when all the players in the lobby are ready. 通过使用NetworkServer.ReplacePlayerForConnection方法。可以在保持本次连接的情况下,替换掉当前的player object。这对于在一些特定的场合,由player去发布的命令约束,是很有用的。比如在一个游戏前的大厅屏幕。和AddPlayerForConnection函数一样,这个ReplacePlayerForConnection也需要一些参数。但是允许当前连接中,player已经存在。旧的player object可以不被删除。NetworkLobbyManager则是通过这个技巧,在当所有的在大厅的player已经准备好的情况下,去把一个LobbyPlayer切换到游戏player。
This can also be used to respawn a player after their object is destroyed. In some cases it is better to just disable an object and reset its game attributes on respawn, but to actually replace it with a new object you could use code like: 同样的,这个函数也可以用来在旧的player object被销毁后,重生一个新的player object。在一些情况下这种做法要优于“把旧player object给disable掉然后重置它的属性”的做法。如下代码所示:
class GameManager
    public void PlayerWasKilled(Player player)
        var conn = oldPlayer.connectionToClient;
        var newPlayer = Instantiate<GameObject>(playerPrefab);
        NetworkServer.ReplacePlayerForConnection(conn, newPlayer, 0);
If the player object for a connection is destroyed, then that client will be unable to execute Commands. They can however still send network messages. 当一个连接中的player object被销毁了,这个客户端就不能执行Commands操作,但依然可以进行网络消息的发送。
To use ReplacePlayerForConnection you must have the NetworkConnection object for the player’s client to establish the relationship between the object and the client. This is usually the property connectionToClient on the NetworkBehaviour class, but if the old player has already been destroyed, then that may not be readily available. 要使用ReplacePlayerForConnection方法,你必须有一个对应于玩家的客户端的NetworkConnection对象,这个对象是用来建立player object和客户端之间的联系的。通过NetworkBehaviour组件的connectionToClient属性可以获取到这个对象。但如果旧的old player object已经被销毁的话,这个属性是不能简单地获取到的。
To find the connection, there are some lists available. If using the NetworkLobbyManager, then the lobby players are available in lobbySlots. Also, the NetworkServer has lists of connections and localConnections 所以要得到这个NetworkConnection对象,可以有一些可用的办法。如果使用NetworkLobbyManager的话,lobby player将在NetworkLobbyManager的lobbySlots中获取。同时,NetworkServer类也维护有一系列的connection和localConnections