1. 程式人生 > >UNet詳解(轉)

UNet詳解(轉)

Unity Networking(UNet)函式時序統計和分析

背景和概述

Unity Networking是官方自Unity5.1以來推出的新網路通訊解決方案。UNet是非官方但更民間更精簡的叫法。

本文需要讀者有基礎的UNet知識。

瞭解UNet時序,可以更好更嚴謹地編寫UNet相關的業務邏輯程式碼。
本文針對UNet的HLAPI進行時序統計和分析。
本文可作為工具文件,需要時可進行時序查閱。

在有時序統計的基礎上,本文再參考Unity Networking 5.3原始碼(Bitbucket網站可能需要翻牆)進行整合分析,可以幫助瞭解底層發生的具體邏輯。

當前將Unity(就算是Headless)執行在Linux伺服器上,會出現一定的效能問題。聯絡諮詢過Unity內部開發同學Ian和一位和藹大胖子,獲知Headless所剔除的功能模組並不多,僅僅是最終不提交(也不能提交)到GPU、DSP而已。原話是“Modulization is hard”、“Should not run Unity on the server”。
所以當前,應避免粗暴地將Unity執行在“關鍵”伺服器上。

  • 應從業務層著手剔除Headless模式下所啟用的業務功能(如模型、渲染、物理、音效等)
  • 將Unity運行於“非關鍵”伺服器(比如用於外掛分析,等)是可能可行的
  • 將Unity無狀態地執行,多“關鍵”伺服器(比如用於戰鬥)共享該Unity伺服器,是有成功案例的
  • 但粗暴地每一局遊戲都在伺服器執行一個Unity程序是欠妥的

可惜的是,UNet的預設思路正是最後一種。由於Ian並非UNet Team的開發同學,所以其並不十分了解將於Unity5.4(但被delay了)的Server Library所完成的功能。但一種推測是,Server Library正是為了避免將Unity運行於伺服器,而是提供UNet、Unity的基礎功能(Math等),讓我們伺服器同學利用UNet介面,重新實現邏輯。

測試方法

測試Unity版本為5.3.1。執行平臺是OSX。
測試NetworkManager通過NBNetworkManager繼承並override掉關鍵函式;測試Player的Prefab名字為NBPlayer。通過在這個Prefab加上測試指令碼TestNetworkBehaviour進行日誌輸出。
通過分析日誌,可以統計UNet函式的時序。

函式時序概括

以下為關鍵函式的羅列,以供快速查詢之用。
如需可細看下一章節的詳細文件及分析。

Dedicated Server情況

NetworkManager NetworkBehaviour
Server初始化階段 Server初始化階段
Awake()
Start()
OnStartServer()
ServerChangeScene()
OnServerSceneChanged()
Client初始化階段 Client初始化階段
OnServerConnect()
OnServerReady()
Player初始化階段 Player初始化階段
OnServerAddPlayer()
Awake()
OnEnable()
OnStartServer()
OnRebuildObservers()
OnSerialize()(多次)
Start()
Player運轉階段 Player運轉階段
FixedUpdate()(多次)
Update()(多次)
OnSerialize()(多次)
Player銷燬階段 Player銷燬階段
OnDisable()
OnDestroy()
OnServerDisconnect
Server銷燬階段 Server銷燬階段
OnStopServer()

Remote Client情況

NetworkManager NetworkBehaviour
Client初始化階段 Client初始化階段
Awake()
Start()
OnStartClient()
OnClientConnect()
OnClientSceneChanged()
Player初始化階段 Player初始化階段
Awake()
OnEnable()
OnDeserialize()
PreStartClient()
OnStartClient()
OnStartLocalPlayer()
OnStartAuthority()
(後面運轉階段也可能調到)
OnDeserialize()(多次)
Start()
Player運轉階段 Player運轉階段
FixedUpdate()(多次)
Update()(多次)
OnDeserialize()(多次)
Player銷燬階段 Player銷燬階段
OnNetworkDestroy()
OnDisable()
OnDestroy()
Client銷燬階段 Client銷燬階段
OnStopClient()

Host情況

NetworkManager NetworkBehaviour
Host初始化階段 Host初始化階段
Awake()
Start()
OnStartHost()
OnStartServer()
ServerChangeScene()
OnServerConnect()
(LocalClient混雜進來的Server函式)
OnStartClient()
(LocalClient混雜進來的Client函式)
OnClientConnect()
(LocalClient混雜進來的Client函式)
OnServerSceneChanged()
OnClientSceneChanged()
(LocalClient混雜進來的Client函式)
OnServerReady()
(LocalClient混雜進來的Server函式)
OnServerAddPlayer()
(LocalClient混雜進來的Server函式)
OnServerConnect()
OnServerReady()
Player初始化階段 Player初始化階段
OnServerAddPlayer()
Awake()
OnEnable()
OnStartServer()
PreStartClient()
OnStartClient()
OnRebuildObservers()
OnSerialize()(多次)
OnSetLocalVisibility()
Start()
Player運轉階段 Player運轉階段
FixedUpdate()(多次)
Update()(多次)
OnSerialize()(多次)
Player銷燬階段 Player銷燬階段
OnNetworkDestroy()
OnDisable()
OnDestroy()
OnServerDisconnect
Host銷燬階段 Host銷燬階段
OnStopHost()
OnStopServer()
ServerChangeScene()
(LocalClient混雜進來的Server函式)
OnStopClient()
(LocalClient混雜進來的Client函式)

函式時序的詳細文件及分析

以下為嚴格按照時間次序進行羅列的UNet函式時序,附上官方文件。重要地方也結合原始碼進行解釋。

Dedicated Server情況

Dedicated Server的Server初始化階段

NetworkManager.Awake()
NetworkManager目前的Awake()(被不好地設計)為非virtual的私有方法。所以子類應注意不能再定義Awake(),否則將hide掉基類的Awake()。

NetworkManager.Start()

NetworkManager (NewBorn.NBNetworkManager).Start()

public void OnStartServer();
Description
This hook is invoked when a server is started - including when a host is started.
StartServer has multiple signatures, but they all cause this hook to be called.

Server初始化函式。呼叫肯定比看似相似的OnStartClient()早。
注意在OnStartServer()之後,才進行網路Connect的初始化、才進行場景的切換。

NetworkManager (NewBorn.NBNetworkManager).OnStartServer()

public void ServerChangeScene(string newSceneName);
Parameters
newSceneName The name of the scene to change to. The server will change scene immediately, and a message will be sent to connected clients to ask them to change scene also.
Description
This causes the server to switch scenes and sets the networkSceneName.
Clients that connect to this server will automatically switch to this scene. This is called autmatically if onlineScene or offlineScene are set, but it can be called from user code to switch scenes again while the game is in progress. This automatically sets clients to be not-ready. The clients must call NetworkClient.Ready() again to participate in the new scene.

StartServer()裡、OnStartServer()之後,呼叫ServerChangeScene()進行場景切換。之後在任意時刻,也可以手動呼叫它進行中途的場景切換。
在ServerChangeScene()裡,會發出MsgType.Scene通知當前已連線上的Client也進行場景的切換。

  1. Battle_Demo_Official

  2. NetworkManager (NewBorn.NBNetworkManager).ServerChangeScene()

public void OnServerSceneChanged(string sceneName);
Parameters
sceneName The name of the new scene.
Description
Called on the server when a scene is completed loaded, when the scene load was initiated by the server with ServerChangeScene().

Server完成場景切換後的一個回撥。
在本回調之前,Server會收集場景所有已有NetworkIdentity的GameObject,併發出Spawn的Message,從而通知已連線上的Client進行Spawn。

  1. Battle_Demo_Official

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerSceneChanged()

  3. 2016-01-01T10:57:28.8472060+08:00

至此,Server的初始化階段結束。之後(通過上面的時間10:57:28和下面的時間11:04:10就可以看出時間差),當有Client連線進Server的時候,函式流程就進入該Client的初始化階段。

Dedicated Server的Client初始化階段

public void OnServerConnect(Networking.NetworkConnection conn);
Parameters
conn Connection from client.
Description
Called on the server when a new client connects.

新玩家新Client和Server建立連線後的回撥函式。
Client剛連線上來,第一個問題肯定是“我現在在什麼場景?”。所以在本回調之前,Server會發出MsgType.Scene,通知客戶端進行場景載入。

  1. hostId: 0 connectionId: 1 isReady: False channel count: 2

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerConnect()

  3. 2016-01-01T11:04:10.7621350+08:00

public void OnServerReady(Networking.NetworkConnection conn);
Parameters
conn Connection from client.
Description
Called on the server when a client is ready.
The default implementation of this function calls NetworkServer.SetClientReady() to continue the network setup process

當Client“準備好”(載入好場景,一些自定義的初始化)後,需要傳送MsgType.Ready給Server。
Server收到這個Message了之後,就會呼叫本OnServerReady()函式。
Client準備好了之後,接著問題是“我Client當前場景有什麼網路物件可見和需要同步?”
所以在OnServerReady()裡會呼叫NetworkServer.SetClientReady(),進行該Client的可見性檢測,然後在NetworkServer.SendSpawnMessage()裡下發MsgType.ObjectSpawn進行Spawn。

  1. hostId: 0 connectionId: 1 isReady: False channel count: 2

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerReady()

至此,Client已經連線好、載入好場景、同步好已有的網路物件。
所以Server將進入Player初始化階段。

Dedicated Server的Player初始化階段

public void OnServerAddPlayer(Networking.NetworkConnection conn, short playerControllerId);
Parameters
conn Connection from client.
playerControllerId Id of the new player.
extraMessageReader An extra message object passed for the new player.
Description
Called on the server when a client adds a new player with ClientScene.AddPlayer.
The default implementation for this function creates a new player object from the playerPrefab.

新連線上來的Client連線好了、場景準備好了、其他有NetworkIdentity的GameObject同步好了,接下來準備為該Client準備屬於它自己的Player了。
通過呼叫ClientScene.AddPlayer()發出MsgType.AddPlayer可以通知伺服器新增屬於該connection的Player,然後Server就響應該Message會呼叫OnServerAddPlayer()。
使用者可以在OnServerAddPlayer()自定義新建Player的邏輯,包括直接Instantiate新Player、或者從自己的Spawn機制裡取出Player、給Player修改初始化屬性等。
然後,在OnServerAddPlayer()裡就會呼叫NetworkServer.AddPlayerForConnection(),繼而一系列初始化Player邏輯(生成netId、決定Observer、收集SyncVar、傳送MsgType.Spawn給Client),通知Client真正去建立Player。

  1. hostId: 0 connectionId: 1 isReady: True channel count: 2, 0

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerAddPlayer()

至此,Player的Prefab在Server已被Instantiate出來,繼而呼叫

等函式,正式開始NetworkIdentity/NetworkBehaviour的函式流程。
NetworkBehaviour和NetworkIdentity是相互依存的。許多NetworkBehaviour的UNet相關函式事實上都是在其配對的NetworkIdentity中被NetworkIdentity所觸發呼叫的。

NetworkBehaviour.Awake()
留意到這次測試Player的GameObject的instanceID是-10746。

只有localPlayerAuthority=True
這個UNet配置變數是合法的。其他UNet變數都是非法的。

  1. go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=0

  3. playerControllerId=-1

  4. connectionToClient=

  5. connectionToServer=

  6. isClient=False

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).Awake()

NetworkBehaviour.OnEnable()

  1. go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=0

  3. playerControllerId=-1

  4. connectionToClient=

  5. connectionToServer=

  6. isClient=False

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnEnable()

public void OnStartServer();
Description
This hook is invoked when a server is started - including when a host is started.
StartServer has multiple signatures, but they all cause this hook to be called.

在NetworkIdentity.OnStartServer()裡,

  • 會cache住NetworkIdentity所同GameObject的所有NetworkBehaviour
  • 會生成netId給自己
  • 通過NetworkServer.instance.SetLocalObjectOnServer(),更新isServer的標誌位
  • 呼叫這些NetworkBehaviour的OnStartServer()函式。

所以這個時候,netIdisServer合法了。

  1. go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=False

  7. isServer=True

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnStartServer()

public bool OnRebuildObservers(HashSet<NetworkConnection> observers, bool initialize);
Parameters
observers The new set of observers for this object.
initialize True if the set of observers is being built for the first time.
Returns
bool Return true if this function did work.
Description
Callback used by the visibility system to (re)construct the set of observers that can see this object.
Implementations of this callback should add network connections of players that can see this object to the observers set.

這個新的NetworkIdentity在Server建立了,但哪些Client是其真正的“觀察者”(Observer)呢?只有這些觀察者Client,才需要在他們的執行時裡建立這個新NetworkIdentity及其GameObject。

NetworkIdentity會呼叫其所有NetworkBehaviour的OnRebuildObservers()。
預設情況下,是當前已連線的所有Client都能觀察到這個新NetworkIdentity。
但如果有NetworkBehaviour的OnRebuildObservers()返回了true,則以HashSet<NetworkConnection> observers裡存在的連線作為Observer。

  1. observers=System.Collections.Generic.HashSet`1[UnityEngine.Networking.NetworkConnection], initialize=True,

  2. go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  3. netId=7

  4. playerControllerId=0

  5. connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2

  6. connectionToServer=

  7. isClient=False

  8. isServer=True

  9. localPlayerAuthority=True

  10. hasAuthority=False

  11. isLocalPlayer=False

  12. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnRebuildObservers()

public bool OnSerialize(Networking.NetworkWriter writer, bool initialState);
Parameters
writer Writer to use to write to the stream.
initialState If this is being called to send initial state.
Returns
bool True if data was written.
Description
Virtual function to override to send custom serialization data.

決定了Observer後、給這些Observer傳送MsgType.Spawn之前,需要在Server把這個新NetworkIdentity的GameObject的所有同步屬性進行序列化

OnSerialize()和OnDeserialize()
是用於自定義NetworkBehaviour中變數的序列化和反序列化的虛擬函式。前者必然是隻在Server被呼叫、後者必然是隻在Client被呼叫。
事實上,[SyncVar]修飾的變數和SyncList變數都是通過編譯時UNet將這些變數的序列化反序列化邏輯自動生成在OnSerialize()和OnDeserialize()中的。
所以要注意,如果你在NetworkBehaviour中顯式override掉了這兩個函式,則該NetworkBehaviour的[SyncVar]修飾的變數和SyncList變數都需要你自行編寫程式碼實現序列化反序列化。

  1. serializeCount=1, writer=UnityEngine.Networking.NetworkWriter, initialState=True,

  2. go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  3. netId=7

  4. playerControllerId=0

  5. connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2

  6. connectionToServer=

  7. isClient=False

  8. isServer=True

  9. localPlayerAuthority=True

  10. hasAuthority=False

  11. isLocalPlayer=False

  12. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnSerialize()

NetworkBehaviour.Start()

一幀真正開始。

  1. go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=False

  7. isServer=True

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).Start()

至此,在Dedicated Server的Player初始化階段已結束。接下來是Player運轉階段。

Dedicated Server的Player運轉階段

NetworkBehaviour.FixedUpdate()(多次)

  1. fixedUpdateCount=22, go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=False

  7. isServer=True

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).FixedUpdate()

NetworkBehaviour.Update()(多次)

  1. updateCount=22, go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=False

  7. isServer=True

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).Update()

NetworkBehaviour.OnSerialize()(多次)

  1. serializeCount=16, writer=UnityEngine.Networking.NetworkWriter, initialState=False,

  2. go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  3. netId=7

  4. playerControllerId=0

  5. connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2

  6. connectionToServer=

  7. isClient=False

  8. isServer=True

  9. localPlayerAuthority=True

  10. hasAuthority=False

  11. isLocalPlayer=False

  12. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnSerialize()

Dedicated Server的Player銷燬階段

通過呼叫NetworkServer.Destroy(gameObject);,gameObject進入銷燬階段。

NetworkBehaviour.OnDisable()

  1. go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=False

  7. isServer=True

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnDisable()

NetworkBehaviour.OnDestroy()
留意到所有變數皆已非法。
留意到在Server並不會呼叫OnNetworkDestroy()。

  1. go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=0

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=False

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnDestroy()

至此,Player的NetworkIdentity/NetworkBehaviour流程結束。

NetworkManager.OnServerDisconnect()

public void OnServerDisconnect(Networking.NetworkConnection conn);
Parameters
conn Connection from client.
Description Called on the server when a client disconnects.

  1. hostId: 0 connectionId: 2 isReady: False channel count: 2

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerDisconnect()

Dedicated Server的Server銷燬階段

略。因為在Server kill掉Unity不能及時輸出日誌。

至此,Player的整個Dedicated Server流程結束。

Remote Client情況

在上面已有Dedicated Server情況的前提下,Remote Client情況將適度從簡,僅針對差異性進行描述。

Remote Client的Client初始化階段

NetworkManager.Awake()
NetworkManager目前的Awake()(被不好地設計)為非virtual的私有方法。所以子類應注意不能再定義Awake(),否則將hide掉基類的Awake()。

NetworkManager.Start()

NetworkManager (NewBorn.NBNetworkManager).Start()

public void OnStartClient(Networking.NetworkClient client);
Parameters
client The NetworkClient object that was started.
Description
This is a hook that is invoked when the client is started.
StartClient has multiple signatures, but they all cause this hook to be called.

  • Client註冊各種Client相關的MsgType監聽(MsgType.Connect/Disconnect/Scene/
  • 連線,
  • 然後會呼叫OnStartClient()
  1. UnityEngine.Networking.NetworkClient

  2. NetworkManager (NewBorn.NBNetworkManager).OnStartClient()

Client連線成功後,第一個問題肯定是“我現在在什麼場景?”。通過之前Dedicated Server情況的分析可知,Server會在Client連線成功後、OnServerConnect()之前通過MsgType.Scene通知客戶端切換場景
所以此時之後,Client將進行場景載入併成功。

BattleStarter.Awake()(場景中本就有的GameObject)

BattleStarter (NewBorn.BattleStarter).Awake()

GlobalObject.OnLevelWasLoaded()(載入場景前就DontDestroyOnLoad的GameObject)

GlobalObject (MoreFun.GlobalObjectComponent).OnLevelWasLoaded()

public void OnClientConnect(Networking.NetworkConnection conn);
Parameters
conn Connection to the server.
Description
Called on the client when connected to a server.
The default implementation of this function sets the client as ready and adds a player.

當場景載入成功後,才在NetworkManager.FinishLoadScene()裡呼叫OnClientConnect()
在OnClientConnect()裡,當沒有OnlineScene或當前就是OnlineScene時,就會立刻呼叫ClientScene.Ready()告訴Server本Client已準備好。
所以此時的isReady是False。

  1. hostId: 0 connectionId: 1 isReady: False channel count: 2

  2. NetworkManager (NewBorn.NBNetworkManager).OnClientConnect()

public void OnClientSceneChanged(Networking.NetworkConnection conn);
Parameters
conn The network connection that the scene change message arrived on.
Description
Called on clients when a scene has completed loaded, when the scene load was initiated by the server.
Scene changes can cause player objects to be destroyed. The default implementation of OnClientSceneChanged in the NetworkManager is to add a player object for the connection if no player object exists.

OnClientSceneChanged()必然會呼叫ClientScene.Ready()告訴Server本Client已準備好。所以根據之前的Dedicated Server情況分析可知,Server會在OnServerReady()裡會呼叫NetworkServer.SetClientReady(),進行該Client的可見性檢測、並進行已在Server的NetworkIdentity的GameObject進行反序列化和Spawn。
然後,如果NetworkManager配置成AutoCreatePlayer為true,則OnClientSceneChanged()還會在本Client找不到LocalPlayer時呼叫ClientScene.AddPlayer(0)通知Server生成本Client的玩家。

  1. hostId: 0 connectionId: 1 isReady: False channel count: 2

  2. NetworkManager (NewBorn.NBNetworkManager).OnClientSceneChanged()

其他Player的GameObject的其他指令碼的Awake()
因此,其他Player就會比LocalPlayer先行在本Client被Spawn出來。

  1. go.instanceID=-64522

  2. NBPlayer(Clone) (NewBorn.PlayerController).Awake()

BattleStarter.OnStartClient()
本身就在場景裡的有NetworkIdentity的GameObject也比LocalPlayer先行被Spawn出來。

BattleStarter (NewBorn.BattleStarter).OnStartClient()

至此,Remote Client的Client本身就初始化好了。
接下來,由於ClientScene.OnObjectSpawn()監聽了MsgType.ObjectSpawn,所以當Server生成本Client的LocalPlayer(或者Spawn其他任意GameObject時),本Client都會進入NetworkIdentity/NetworkBehaviour的函式流程。

Remote Client的Player初始化階段

NetworkBehaviour.Awake()
只有localPlayerAuthority=True這個UNet配置變數是合法的。其他UNet變數都是非法的。

  1. go.instanceID=-65642,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=0

  3. playerControllerId=-1

  4. connectionToClient=

  5. connectionToServer=

  6. isClient=False

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).Awake()

NetworkBehaviour.OnEnable()

  1. go.instanceID=-65642,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=0

  3. playerControllerId=-1

  4. connectionToClient=

  5. connectionToServer=

  6. isClient=False

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnEnable()

public void OnDeserialize([Networking.NetworkReader reader, bool initialState);
Parameters
reader Reader to read from the stream.
initialState True if being sent initial state.
Description
Virtual function to override to receive custom serialization data.

Server把這個新NetworkIdentity的GameObject的所有同步屬性進行序列化會連同傳送MsgType.Spawn一併下發。
所以Client接受Server的資料後也通過OnDeserialize()在本地進行反序列化。留意到這是第一次反序列化initialState=True

OnSerialize()和OnDeserialize()
是用於自定義NetworkBehaviour中變數的序列化和反序列化的虛擬函式。前者必然是隻在Server被呼叫、後者必然是隻在Client被呼叫。
事實上,[SyncVar]修飾的變數和SyncList變數都是通過編譯時UNet將這些變數的序列化反序列化邏輯自動生成在OnSerialize()和OnDeserialize()中的。
所以要注意,如果你在NetworkBehaviour中顯式override掉了這兩個函式,則該NetworkBehaviour的[SyncVar]修飾的變數和SyncList變數都需要你自行編寫程式碼實現序列化反序列化。

  1. deserializeCount=1, reader=NetBuf sz:87 pos:87, initialState=True,

  2. go.instanceID=-65642,go=NBPlayer(Clone) (UnityEngine.GameObject)

  3. netId=0

  4. playerControllerId=-1

  5. connectionToClient=

  6. connectionToServer=

  7. isClient=False

  8. isServer=False

  9. localPlayerAuthority=True

  10. hasAuthority=False

  11. isLocalPlayer=False

  12. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnDeserialize()

public void PreStartClient();
Description
An internal method called on client objects to resolve GameObject references.

留意到經過上一步的OnDeserialize()之後,合法變數為

  • netId=7
  • isClient=True
  • isServer=False
  • gameObject.name已經改變為跟server所給予的名字(NBPlayer7)。
  • 事實上,所有SyncVar此時皆已合法。
  1. go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=-1

  4. connectionToClient=

  5. connectionToServer=

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).PreStartClient()

public void OnStartClient();
Description
Called on every NetworkBehaviour when it is activated on a client.
Objects on the host have this function called, as there is a local client on the host. The values of SyncVars on object are guaranteed to be initialized correctly with the latest state from the server when this function is called on the client.

和PreStartClient()沒什麼區別。當然,SyncVar已經合法。

  1. go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=-1

  4. connectionToClient=

  5. connectionToServer=

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).OnStartClient()

public void OnStartLocalPlayer();
Description
Called when the local player object has been set up.
This happens after OnStartClient(), as it is triggered by an ownership message from the server. This is an appropriate place to activate components or functionality that should only be active for the local player, such as cameras and input.

在一個Client執行時中,只有一個Connection,有很多個Player。眾多Player中,只有和這個Connection繫結起來的Player,才“提拔為”LocalPlayer,代表的是本Client玩家的“MyPlayer”。所以,此時合法的變數就比較好理解了。
合法變數:

  • playerControllerId=0
  • connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
  • isLocalPlayer=True

另,應注意,LocalPlayer的“Local”,和Host模式下的LocalClient的“Local”可不是同一個概念,應分清區別:

  • LocalClient的Local可理解為“同機器的”:是Host模式下,和Server同處於一部物理機器上的一種特殊的Client。LocalClient的並列相反概念是RemoteClient,RemoteClient是指和Server處於不同物理機器上的常見Client。
  • LocalPlayer的Local可理解為“我自己的”,不管是RemoteClient,還是LocalClient,它們都會有本客戶端的自己的LocalPlayer。LocalPlayer的並列相反概念是DumbPlayer(作者本人喜歡的叫法),DumbPlayer是指本客戶端代表其他玩家的Player。
  1. go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=

  5. connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=True

  10. isLocalPlayer=True

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).OnStartLocalPlayer()

public void OnStartAuthority();
Description
This is invoked on behaviours that have authority, based on context and the LocalPlayerAuthority value on the NetworkIdentity.
This is called after OnStartServer and OnStartClient.When NetworkIdentity.AssignClientAuthority() is called on the server, this will be called on the client that owns the object. When an object is spawned with NetworkServer.SpawnWithClientAuthority(), this will be called on the client that owns the object.

當一個NetworkIdentity配置有LocalPlayerAuthority時,此NetworkIdentity認為是可以授權給Client的。只有一個Client真正有Authority的時候,才可以在該GameObject的NetworkBehaviour中傳送Command給Server。
什麼時候Client才真正有Authority呢?LocalPlayer都是有Authority的。另自Unity5.2開始,也允許非Player在執行時通過在Server呼叫NetworkIdentity.AssignClientAuthority()NetworkServer.SpawnWithClientAuthority()將Authority賦予特定的Connection,即指定該Connection的Client也擁有該非Player的Authority,即允許該Client也可以在該非Player的NetworkBehaviour中發Command給Server。此時OnStartAuthority()這個函式是可以在那個時候再被呼叫到的。

Authority要麼只有Server擁有,要麼只有Client擁有。

合法變數:

  • hasAuthority=True
  1. go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=

  5. connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=True

  10. isLocalPlayer=True

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).OnStartAuthority()

NetworkBehaviour.Start()
一幀真正開始。

  1. go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=

  5. connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=True

  10. isLocalPlayer=True

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).Start()

至此,NetworkBehaviour的初始化階段已結束。接下來是正常運轉階段。

Remote Client的Player運轉階段

NetworkBehaviour.FixedUpdate()(多次)

  1. fixedUpdateCount=15, go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=

  5. connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=True

  10. isLocalPlayer=True

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).FixedUpdate()

NetworkBehaviour.Update()(多次)

  1. updateCount=4, go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=

  5. connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=True

  10. isLocalPlayer=True

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).Update()

NetworkBehaviour.OnDeserialize()(多次)

  1. deserializeCount=5, reader=NetBuf sz:28 pos:28, initialState=False, go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=

  5. connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=True

  10. isLocalPlayer=True

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).OnDeserialize()

Remote Client的Player銷燬階段

通過呼叫NetworkServer.Destroy(gameObject);,gameObject進入銷燬階段。

NetworkBehaviour.OnNetworkDestroy()

public void OnNetworkDestroy();
Description
This is invoked on clients when the server has caused this object to be destroyed.
This can be used as a hook to invoke effects or do client specific cleanup.

留意只有客戶端才會被呼叫OnNetworkDestroy()。

  1. go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=

  5. connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=True

  10. isLocalPlayer=True

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).OnNetworkDestroy()

NetworkBehaviour.OnDisable()

  1. go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=

  5. connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=True

  10. isLocalPlayer=True

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).OnDisable()

NetworkBehaviour.OnDestroy()

  1. go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=

  5. connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2

  6. isClient=True

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=True

  10. isLocalPlayer=True

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).OnDestroy()

Remote Client的Client銷燬階段

通過點選NetworkManagerHUD的Stop按鈕,停止Remote Client。

NetworkManager.OnStopHost()

public void OnStopHost();
Description
This hook is called when a host is stopped.

NetworkManagerHUD有bug。Remote Client情況點選它也呼叫StopHost(),所以OnStopHost()也會被錯誤地呼叫。
正式情況應忽略。

NetworkManager (NewBorn.NBNetworkManager).OnStopHost()

public void OnStopClient();
Description
This hook is called when a client is stopped.

在NetworkManager.StopClient()呼叫時,先呼叫NetworkManager.OnStopClient(),然後再斷連線、清除GameObject、跳轉到OfflineScene。

NetworkManager (NewBorn.NBNetworkManager).OnStopClient()

至此,Remote Client整個流程結束。

Host情況

在已有上面Dedicated Server、Remote Client的情況,Host情況也將適當從略。

Host初始化階段

NetworkManager.Start()

NetworkManager (NewBorn.NBNetworkManager).Start()

NetworkManager.StartHost()

public Networking.NetworkClient StartHost();
Returns
NetworkClient The client object created - this is a "local client".
Description
This starts a network "host" - a server and client in the same application.
The client returned from StartHost() is a special "local" client that communicates to the in-process server using a message queue instead of the real network. But in almost all other cases, it can be treated as a normal client.

NetworkManager (NewBorn.NBNetworkManager).StartHost()

NetworkManager.OnStartHost()

NetworkManager (NewBorn.NBNetworkManager).OnStartHost()

NetworkManager.OnStartServer()

NetworkManager (NewBorn.NBNetworkManager).OnStartServer()

NetworkManager.ServerChangeScene()

  1. Battle_Demo_Official

  2. NetworkManager (NewBorn.NBNetworkManager).ServerChangeScene()

NetworkManager.OnServerConnect()(2次)(LocalClient混雜進來的Server函式)

  1. hostId: -1 connectionId: 0 isReady: False channel count: 0

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerConnect()

NetworkManager.OnStartClient()(LocalClient混雜進來的Client函式)

  1. UnityEngine.Networking.LocalClient

  2. NetworkManager (NewBorn.NBNetworkManager).OnStartClient()

NetworkManager.OnClientConnect()(LocalClient混雜進來的Client函式)

  1. hostId: -1 connectionId: 0 isReady: False channel count: 0

  2. NetworkManager (NewBorn.NBNetworkManager).OnClientConnect()

NetworkManager.OnServerSceneChanged()

  1. Battle_Demo_Official

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerSceneChanged()

NetworkManager.OnClientSceneChanged()(LocalClient混雜進來的Client函式)

  1. hostId: -1 connectionId: 0 isReady: False channel count: 0

  2. NetworkManager (NewBorn.NBNetworkManager).OnClientSceneChanged()

NetworkManager.OnServerReady()(LocalClient混雜進來的Server函式)

  1. hostId: -1 connectionId: 0 isReady: False channel count: 0

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerReady()

NetworkManager.OnServerAddPlayer()(LocalClient混雜進來的Server函式)

  1. hostId: -1 connectionId: 0 isReady: True channel count: 0, 0

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerAddPlayer()

至此,Host的Server初始化邏輯(混雜著LocalClient的初始化邏輯)結束。
進入Player初始化階段。

Host情況的Player初始化階段

NetworkManager.OnServerConnect()

  1. hostId: 0 connectionId: 1 isReady: False channel count: 2

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerConnect()

NetworkManager.OnServerReady()

  1. hostId: 0 connectionId: 1 isReady: False channel count: 2

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerReady()

NetworkManager.OnServerAddPlayer()

  1. hostId: 0 connectionId: 1 isReady: True channel count: 2, 0

  2. NetworkManager (NewBorn.NBNetworkManager).OnServerAddPlayer()

至此,開始Player的Prefab在Host已被Instantiate出來。正式開始NetworkIdentity/NetworkBehaviour的函式流程。

NetworkBehaviour.Awake()

  1. go.instanceID=-78256,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=0

  3. playerControllerId=-1

  4. connectionToClient=

  5. connectionToServer=

  6. isClient=False

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).Awake()

NetworkBehaviour.OnEnable()

  1. go.instanceID=-78256,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=0

  3. playerControllerId=-1

  4. connectionToClient=

  5. connectionToServer=

  6. isClient=False

  7. isServer=False

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnEnable()

NetworkBehaviour.OnStartServer()
合法變數:

  • netId=2。由於是Host,所以不需呼叫OnDeserialize()。立刻確定了netId。
  • isServer=True
  1. go.instanceID=-78256,go=NBPlayer(Clone) (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=False

  7. isServer=True

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnStartServer()

NetworkBehaviour.PreStartClient()

  1. go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=True

  7. isServer=True

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).PreStartClient()

NetworkBehaviour.OnStartClient()

  1. go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=True

  7. isServer=True

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).OnStartClient()

NetworkBehaviour.OnRebuildObservers()

  1. observers=System.Collections.Generic.HashSet`1[UnityEngine.Networking.NetworkConnection], initialize=True,

  2. go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)

  3. netId=7

  4. playerControllerId=0

  5. connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2

  6. connectionToServer=

  7. isClient=True

  8. isServer=True

  9. localPlayerAuthority=True

  10. hasAuthority=False

  11. isLocalPlayer=False

  12. NBPlayer7 (MoreFun.TestNetworkBehaviour).OnRebuildObservers()

NetworkBehaviour.OnSerialize()(多次)

  1. serializeCount=1, writer=UnityEngine.Networking.NetworkWriter, initialState=True, go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=True

  7. isServer=True

  8. localPlayerAuthority=True

  9. hasAuthority=False

  10. isLocalPlayer=False

  11. NBPlayer7 (MoreFun.TestNetworkBehaviour).OnSerialize()

NetworkBehaviour.OnSetLocalVisibility()

public void OnSetLocalVisibility(bool vis);
Parameters
vis New visibility state.
Description
Callback used by the visibility system for objects on a host.
Objects on a host (with a local client) cannot be disabled or destroyed when they are not visibile to the local client. So this function is called to allow custom code to hide these objects. A typical implementation will disable renderer components on the object. This is only called on local clients on a host.

  1. vis=True, go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)

  2. netId=7

  3. playerControllerId=0

  4. connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2

  5. connectionToServer=

  6. isClient=True

  7. isServer=True

  8. localPlayerAuthority=True