using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; using Assets.CoreScripts; using Hazel; using Hazel.Udp; using UnityEngine; using UnityEngine.SceneManagement; namespace InnerNet { public abstract class InnerNetClient : MonoBehaviour { // 是否连接成功 private bool AmConnected { get { return this.connection != null; } } // ping public int Ping { get { if (this.connection == null) { return 0; } return (int)this.connection.AveragePingMs; } } // 本机是host public bool AmHost { get { return this.HostId == this.ClientId; } } public bool AmClient { get { return this.ClientId > 0; } } public bool IsGamePublic { get; private set; } public bool IsGameStarted { get { return this.GameState == InnerNetClient.GameStates.Started; } } public bool IsGameOver { get { return this.GameState == InnerNetClient.GameStates.Ended; } } private static readonly DisconnectReasons[] disconnectReasons = new DisconnectReasons[] { DisconnectReasons.Error, DisconnectReasons.GameFull, DisconnectReasons.GameStarted, DisconnectReasons.GameNotFound, DisconnectReasons.IncorrectVersion, DisconnectReasons.Banned, DisconnectReasons.Kicked, DisconnectReasons.ServerFull, DisconnectReasons.Custom }; public const int NoClientId = -1; private string networkAddress = "127.0.0.1"; private int networkPort; // Udp连接 private UdpClientConnection connection; public MatchMakerModes mode; public int GameId = 32; public int HostId; public int ClientId = -1; // 本局内的所有玩家的数据,包括cliendId,playercontrol public List allClients = new List(); public DisconnectReasons LastDisconnectReason; public string LastCustomDisconnect; private readonly List PreSpawnDispatcher = new List(); //c 网络消息队列,网络线程写入,主线程调用 private readonly List Dispatcher = new List(); public InnerNetClient.GameStates GameState; private List TempQueue = new List(); private volatile bool appPaused; public const int CurrentClient = -3; public const int InvalidClient = -2; internal const byte DataFlag = 1; internal const byte RpcFlag = 2; internal const byte SpawnFlag = 4; internal const byte DespawnFlag = 5; internal const byte SceneChangeFlag = 6; internal const byte ReadyFlag = 7; internal const byte ChangeSettingsFlag = 8; // 没用这个类型的消息,采用Rpc实现的 // 每次发送数据的间隔,每过0.1s发送一次 public float MinSendInterval = 0.1f; private uint NetIdCnt = 1U; private float timer; public InnerNetObject[] SpawnableObjects; private bool InOnlineScene; private HashSet DestroyedObjects = new HashSet(); // 所有要同步的数据,包括InnerNetObject的所有派生类,场景内所有的对象的数据 public List allObjects = new List(); private Dictionary allObjectsFast = new Dictionary(); // 2个 private MessageWriter[] Streams; public enum GameStates { NotJoined, // Joined, // Started, // Ended // } // 每次开局的时候设置remote endpoint public void SetEndpoint(string addr, ushort port) { this.networkAddress = addr; this.networkPort = (int)port; } // 初始化 public virtual void Start() { // 场景切换,包含很多场景,EndGame、MainMenuScene、MMOline等场景 SceneManager.activeSceneChanged += delegate(Scene oldScene, Scene scene) { this.SendSceneChange(scene.name); }; this.ClientId = -1; this.GameId = 32; } //c 发送数据,在主线程调用 private void SendOrDisconnect(MessageWriter msg) { try { this.connection.Send(msg); } catch { this.EnqueueDisconnect(DisconnectReasons.Error, "Failed to send message"); } } // 找到作为host的主机数据 public ClientData GetHost() { List obj = this.allClients; lock (obj) { for (int i = 0; i < this.allClients.Count; i++) { ClientData clientData = this.allClients[i]; if (clientData.Id == this.HostId) { return clientData; } } } return null; } // 根据playerControl找到对应的clientId public int GetClientIdFromCharacter(InnerNetObject /*PlayerControl*/ character) { if (!character) { return -1; } List obj = this.allClients; lock (obj) { for (int i = 0; i < this.allClients.Count; i++) { ClientData clientData = this.allClients[i]; if (clientData.Character == character) { return clientData.Id; } } } return -1; } // 销毁的时候断开连接 public virtual void OnDestroy() { if (this.AmConnected) { this.DisconnectInternal(DisconnectReasons.Destroy, null); } } // 只建立“udp连接”,不建房间 public IEnumerator CoConnect() { if (this.AmConnected) { yield break; } DestroyableSingleton.Instance.Close(); this.LastDisconnectReason = DisconnectReasons.ExitGame; this.NetIdCnt = 1U; this.DestroyedObjects.Clear(); // 创建发送流 if (this.Streams == null) { this.Streams = new MessageWriter[2]; // 一个是不可靠的,一个是可靠的 for (int i = 0; i < this.Streams.Length; i++) { this.Streams[i] = MessageWriter.Get((SendOption)i); } } //c 初始化发送流 // SendRpcImmediately和SendRpc的区别是前者每次会发送一个rpc,后者会把rpc拼起来 // 所以这里设置了头5 for (int j = 0; j < this.Streams.Length; j++) { MessageWriter messageWriter = this.Streams[j]; messageWriter.Clear((SendOption)j); messageWriter.StartMessage(5); // GameData messageWriter.Write(this.GameId); } // 建立连接 // 因为是Udp所以实际上socket没有调用connect函数,ConnectAsync的第二个参数timeout只是在第一次建立连接的时候向服务器发送一个 // hello消息,确保连接成功 // Hazel-Networking的Udp连接风格实现了和Tcp一样的方式,这样容易被客户端使用,将游戏过程中共用的数据比如Ip,端口,封装为一个 // Udp连接,方便统一管理 IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(this.networkAddress), this.networkPort); this.connection = new UdpClientConnection(remoteEndPoint, IPMode.IPv4); this.connection.KeepAliveInterval = 1500; this.connection.DisconnectTimeout = 7500; this.connection.ResendPingMultiplier = 2f; this.connection.DataReceived += this.OnMessageReceived; // 注册到网络线程的消息处理函数 this.connection.Disconnected += this.OnDisconnect; // 连接断开时的回调函数 // “建立连接”并尝试向服务器发送一个hello消息,保证连接是通的 this.connection.ConnectAsync(this.GetConnectionData(), 5000); yield return this.WaitWithTimeout( () => this.connection == null || this.connection.State == ConnectionState.Connected ); yield break; } private void Connection_DataReceivedRaw(byte[] data) { Debug.Log("Client Got: " + string.Join(" ", from b in data select b.ToString())); } private void Connection_DataSentRaw(byte[] data, int length) { Debug.Log("Client Sent: " + string.Join(" ", (from b in data select b.ToString()).ToArray(), 0, length)); } public void Connect(MatchMakerModes mode) { base.StartCoroutine(this.CoConnect(mode)); } // 这个协程包括了建立udp连接和建立host服务器(如果是MatchMakerModes.HostAndClient的话)或加入游戏(如果是MatchMakerModes.Client) private IEnumerator CoConnect(MatchMakerModes mode) { if (this.mode != MatchMakerModes.None) { this.DisconnectInternal(DisconnectReasons.NewConnection, null); } this.mode = mode; // “udp连接” yield return this.CoConnect(); if (!this.AmConnected) // 连接失败 { yield break; } // 连接成功 MatchMakerModes matchMakerModes = this.mode; if (matchMakerModes == MatchMakerModes.Client) // 如果在本局只作为client,加入游戏 { this.JoinGame(); // 加入游戏 yield return this.WaitWithTimeout( () => this.ClientId >= 0 // clientId大于0代表加入游戏成功 ); bool amConnected = this.AmConnected; yield break; } // 下面是作为host建立房间 if (matchMakerModes != MatchMakerModes.HostAndClient) { yield break; } this.GameId = 0; // 单局游戏的配置,本玩家作为host建立游戏 PlayerControl.GameOptions = SaveManager.GameHostOptions; // 作为host创建游戏 this.HostGame(PlayerControl.GameOptions); yield return this.WaitWithTimeout( () => this.GameId != 0 // gameId不为0代表建立游戏成功,对应InnerNetServer.cs中的HandleMessage ); if (!this.AmConnected) // 连接失败 { yield break; } this.JoinGame(); // 加入游戏 yield return this.WaitWithTimeout( () => this.ClientId >= 0 // clientId大于0代表加入游戏成功 ); bool amConnected2 = this.AmConnected; yield break; } // 等待连接建立 public IEnumerator WaitForConnectionOrFail() { while (this.AmConnected) { switch (this.mode) { case MatchMakerModes.None: goto IL_5F; case MatchMakerModes.Client: if (this.ClientId >= 0) { yield break; } break; case MatchMakerModes.HostAndClient: if (this.GameId != 0 && this.ClientId >= 0) { yield break; } break; default: goto IL_5F; } yield return null; continue; IL_5F: yield break; } yield break; } // 每帧检查一下success是否达成,并检查timeout,超过timeout限制后fail private IEnumerator WaitWithTimeout(Func success) { bool failed = true; for (float timer = 0f; timer < 5f; timer += Time.deltaTime) { if (success()) { failed = false; break; } if (!this.AmConnected) { yield break; } yield return null; } // 超时,断开连接 if (failed) { this.DisconnectInternal(DisconnectReasons.Error, null); } yield break; } //c 执行从网络线程收到的回调Dispatcher public void Update() { if (Input.GetKeyDown(KeyCode.Return) && (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt))) { ResolutionManager.ToggleFullscreen(); } this.TempQueue.Clear(); List obj = this.Dispatcher; lock (obj) { this.TempQueue.AddAll(this.Dispatcher); this.Dispatcher.Clear(); } for (int i = 0; i < this.TempQueue.Count; i++) { Action action = this.TempQueue[i]; try { action(); } catch (Exception exception) { Debug.LogException(exception); } } if (this.InOnlineScene) { this.TempQueue.Clear(); obj = this.PreSpawnDispatcher; lock (obj) { this.TempQueue.AddAll(this.PreSpawnDispatcher); this.PreSpawnDispatcher.Clear(); } for (int j = 0; j < this.TempQueue.Count; j++) { Action action2 = this.TempQueue[j]; try { action2(); } catch (Exception exception2) { Debug.LogException(exception2); } } } } // 断开连接的回调,在网络线程调用 private void OnDisconnect(object sender, DisconnectedEventArgs e) { if (!e.Reason.Contains("The remote sent a")) { this.LastCustomDisconnect = "You disconnected from the server.\r\n\r\n" + e.Reason; this.EnqueueDisconnect(DisconnectReasons.Custom, e.Reason); return; } this.EnqueueDisconnect(DisconnectReasons.Error, e.Reason); } public void HandleDisconnect(DisconnectReasons reason, string stringReason = null) { base.StopAllCoroutines(); DestroyableSingleton.Instance.WriteDisconnect(this.LastDisconnectReason); this.DisconnectInternal(reason, stringReason); this.OnDisconnected(); } // 将主动断开连接的任务加到dispatcher,待主线程执行 protected void EnqueueDisconnect(DisconnectReasons reason, string stringReason = null) { UdpClientConnection udpClientConnection = this.connection; List dispatcher = this.Dispatcher; lock (dispatcher) { this.Dispatcher.Add(delegate { this.HandleDisconnect(reason, stringReason); }); } } // 主动断开连接 protected void DisconnectInternal(DisconnectReasons reason, string stringReason = null) { if (reason != DisconnectReasons.NewConnection && reason != DisconnectReasons.FocusLostBackground) { this.LastDisconnectReason = reason; if (reason != DisconnectReasons.ExitGame && DestroyableSingleton.InstanceExists) { DestroyableSingleton.Instance.Show(); } } if (this.mode == MatchMakerModes.HostAndClient) { this.GameId = 0; } if (this.mode == MatchMakerModes.Client || this.mode == MatchMakerModes.HostAndClient) { this.ClientId = -1; } this.mode = MatchMakerModes.None; this.GameState = InnerNetClient.GameStates.NotJoined; UdpClientConnection udpClientConnection = this.connection; this.connection = null; if (udpClientConnection != null) { try { udpClientConnection.Dispose(); } catch (Exception exception) { Debug.LogException(exception); } } if (DestroyableSingleton.InstanceExists) { DestroyableSingleton.Instance.StopServer(); } List obj = this.Dispatcher; lock (obj) { this.Dispatcher.Clear(); } obj = this.PreSpawnDispatcher; lock (obj) { this.PreSpawnDispatcher.Clear(); } if (reason != DisconnectReasons.Error) { this.TempQueue.Clear(); } this.allObjects.Clear(); this.allClients.Clear(); this.allObjectsFast.Clear(); } // 作为host建立服务器 // 根据settings配置 // 服务器会返回GameId(根据settings) public void HostGame(IBytesSerializable settings) { this.IsGamePublic = false; MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(0); messageWriter.WriteBytesAndSize(settings.ToBytes()); // 这里当作为InnerNetServer启动时没有用到,中心服务器需要这个字段是因为要显示房间列表 messageWriter.EndMessage(); this.SendOrDisconnect(messageWriter); messageWriter.Recycle(); Debug.Log("Client requesting new game."); } // 加入游戏 public void JoinGame() { this.ClientId = -1; if (!this.AmConnected) { this.HandleDisconnect(DisconnectReasons.Error, null); return; } Debug.Log("Client joining game: " + InnerNetClient.IntToGameName(this.GameId)); MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(1); messageWriter.Write(this.GameId); messageWriter.EndMessage(); this.SendOrDisconnect(messageWriter); messageWriter.Recycle(); } // 是否可以ban其他玩家(只有房主可以) public bool CanBan() { return this.AmHost && !this.IsGameStarted; } // 是否可以踢其他玩家 public bool CanKick() { return this.IsGameStarted || this.AmHost; } // 踢玩家 public void KickPlayer(int clientId, bool ban) { if (!this.AmHost) { return; } MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(11); messageWriter.Write(this.GameId); messageWriter.WritePacked(clientId); messageWriter.Write(ban); messageWriter.EndMessage(); this.SendOrDisconnect(messageWriter); messageWriter.Recycle(); } // 结束游戏 // MsgWrite:EndGame public MessageWriter StartEndGame() { MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(8); // endGame messageWriter.Write(this.GameId); return messageWriter; } public void FinishEndGame(MessageWriter msg) { msg.EndMessage(); this.SendOrDisconnect(msg); msg.Recycle(); } protected void SendLateRejection(int targetId, DisconnectReasons reason) { MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(4); messageWriter.Write(this.GameId); messageWriter.WritePacked(targetId); messageWriter.Write((byte)reason); messageWriter.EndMessage(); this.SendOrDisconnect(messageWriter); messageWriter.Recycle(); } // 发送已经准备好的消息 protected void SendClientReady() { if (!this.AmHost) { MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(5); messageWriter.Write(this.GameId); messageWriter.StartMessage(7); messageWriter.WritePacked(this.ClientId); messageWriter.EndMessage(); messageWriter.EndMessage(); this.SendOrDisconnect(messageWriter); messageWriter.Recycle(); return; } ClientData clientData = this.FindClientById(this.ClientId); if (clientData == null) { this.HandleDisconnect(DisconnectReasons.Error, "Couldn't find self as host"); return; } clientData.IsReady = true; } protected void SendStartGame() { MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(2); messageWriter.Write(this.GameId); messageWriter.EndMessage(); this.SendOrDisconnect(messageWriter); messageWriter.Recycle(); } public void RequestGameList(bool includePrivate, IBytesSerializable settings) { MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(9); messageWriter.Write(includePrivate); messageWriter.WriteBytesAndSize(settings.ToBytes()); messageWriter.EndMessage(); this.SendOrDisconnect(messageWriter); messageWriter.Recycle(); } public void ChangeGamePublic(bool isPublic) { if (this.AmHost) { MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(10); messageWriter.Write(this.GameId); messageWriter.Write(1); messageWriter.Write(isPublic); messageWriter.EndMessage(); this.SendOrDisconnect(messageWriter); messageWriter.Recycle(); this.IsGamePublic = isPublic; } } //c 收到数据,在网络线程 private void OnMessageReceived(DataReceivedEventArgs e) { MessageReader message = e.Message; try { while (message.Position < message.Length) { this.HandleMessage(message.ReadMessage()); } } finally { message.Recycle(); } } //c 从imposter搞过来的 //c 后来发现Tags.cs里有 private static readonly Dictionary TagMap = new Dictionary { {0, "HostGame"}, {1, "JoinGame"}, {2, "StartGame"}, {3, "RemoveGame"}, {4, "RemovePlayer"}, {5, "GameData"}, {6, "GameDataTo"}, {7, "JoinedGame"}, {8, "EndGame"}, {9, "GetGameList"}, {10, "AlterGame"}, {11, "KickPlayer"}, {12, "WaitForHost"}, {13, "Redirect"}, {14, "ReselectServer"}, {16, "GetGameListV2"} }; private enum TagAlias { HostGame = 0, // 创建 StartGame = 2, // 开始游戏 Disconnect = 3, // 断开 GameData = 5, GameDataTo = 6, // JoinGame = 7, // 加入游戏 Gameover = 8, // 游戏结束 } //c 处理收到的数据 private void HandleMessage(MessageReader reader) { List obj; switch (reader.Tag) { case (int)TagAlias.HostGame: this.GameId = reader.ReadInt32(); Debug.Log("Client hosting game: " + InnerNetClient.IntToGameName(this.GameId)); obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { this.OnGameCreated(InnerNetClient.IntToGameName(this.GameId)); }); return; } break; case 1: goto IL_2F5; case (int)TagAlias.StartGame: this.GameState = InnerNetClient.GameStates.Started; obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { this.OnStartGame(); }); return; } goto IL_675; case (int)TagAlias.Disconnect: { DisconnectReasons reason3 = DisconnectReasons.ServerRequest; if (reader.Position < reader.Length) { reason3 = (DisconnectReasons)reader.ReadByte(); } this.EnqueueDisconnect(reason3, null); return; } case Tags.RemovePlayer: break; case (int)TagAlias.GameData: case (int)TagAlias.GameDataTo: // 把这类消息存在队列里,主线程后续调用 { int gameId = reader.ReadInt32(); if (this.GameId == gameId) { if (reader.Tag == 6) { int num2 = reader.ReadPackedInt32(); if (this.ClientId != num2) { Debug.LogWarning(string.Format("Got data meant for {0}", num2)); return; } } MessageReader subReader; if (this.InOnlineScene) { subReader = MessageReader.Get(reader); obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { this.HandleGameData(subReader, 0); // 在主线程调用 }); return; } } Debug.Log("Stored early game data"); subReader = MessageReader.Get(reader); obj = this.PreSpawnDispatcher; lock (obj) { this.PreSpawnDispatcher.Add(delegate { this.HandleGameData(subReader, 0); // 在主线程调用 }); return; } goto IL_517; } return; } case (int)TagAlias.JoinGame: goto IL_235; case (int)TagAlias.Gameover: { int num3 = reader.ReadInt32(); if (this.GameId == num3 && this.GameState != InnerNetClient.GameStates.Ended) { this.GameState = InnerNetClient.GameStates.Ended; List obj2 = this.allClients; lock (obj2) { this.allClients.Clear(); } GameOverReason reason = (GameOverReason)reader.ReadByte(); bool showAd = reader.ReadBoolean(); obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { this.OnGameEnd(reason, showAd); }); return; } goto IL_1DD; } return; } case 9: goto IL_517; case 10: goto IL_5BC; case 11: goto IL_675; case 12: goto IL_1DD; case 13: { uint address = reader.ReadUInt32(); ushort port = reader.ReadUInt16(); AmongUsClient.Instance.SetEndpoint(InnerNetClient.AddressToString(address), port); obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { Debug.Log(string.Format("Redirected to: {0}:{1}", this.networkAddress, this.networkPort)); base.StopAllCoroutines(); this.Connect(this.mode); }); return; } goto IL_70A; } default: goto IL_70A; } int num4 = reader.ReadInt32(); if (this.GameId == num4) { int playerIdThatLeft = reader.ReadInt32(); int hostId = reader.ReadInt32(); DisconnectReasons reason2 = (DisconnectReasons)reader.ReadByte(); if (!this.AmHost) { this.HostId = hostId; if (this.AmHost) { obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { this.OnBecomeHost(); }); } } } this.RemovePlayer(playerIdThatLeft, reason2); return; } return; IL_1DD: int num5 = reader.ReadInt32(); if (this.GameId != num5) { return; } this.ClientId = reader.ReadInt32(); obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { this.OnWaitForHost(InnerNetClient.IntToGameName(this.GameId)); }); return; } IL_235: int num6 = reader.ReadInt32(); if (this.GameId != num6 || this.GameState == InnerNetClient.GameStates.Joined) { return; } this.GameState = InnerNetClient.GameStates.Joined; this.ClientId = reader.ReadInt32(); ClientData myClient = this.GetOrCreateClient(this.ClientId); this.HostId = reader.ReadInt32(); int num7 = reader.ReadPackedInt32(); for (int i = 0; i < num7; i++) { this.GetOrCreateClient(reader.ReadPackedInt32()); } obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { this.OnGameJoined(InnerNetClient.IntToGameName(this.GameId), myClient); }); return; } IL_2F5: // 同时处理join game和disconnect int num8 = reader.ReadInt32(); DisconnectReasons disconnectReasons = (DisconnectReasons)num8; if (InnerNetClient.disconnectReasons.Contains(disconnectReasons)) { if (disconnectReasons == DisconnectReasons.Custom) { this.LastCustomDisconnect = reader.ReadString(); } this.GameId = -1; this.EnqueueDisconnect(disconnectReasons, null); return; } if (this.GameId == num8) { int num9 = reader.ReadInt32(); bool amHost = this.AmHost; this.HostId = reader.ReadInt32(); ClientData client = this.GetOrCreateClient(num9); Debug.Log(string.Format("Player {0} joined", num9)); obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { // 某个玩家加入了本局 this.OnPlayerJoined(client); }); } if (!this.AmHost || amHost) { return; } obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { this.OnBecomeHost(); }); return; } } this.EnqueueDisconnect(DisconnectReasons.IncorrectGame, null); return; IL_517: int totalGames = reader.ReadPackedInt32(); List output = new List(); while (reader.Position < reader.Length) { output.Add(new GameListing(reader.ReadInt32(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadPackedInt32(), reader.ReadString())); } obj = this.Dispatcher; lock (obj) { this.Dispatcher.Add(delegate { this.OnGetGameList(totalGames, output); }); return; } IL_5BC: int num10 = reader.ReadInt32(); if (this.GameId != num10) { return; } byte b = reader.ReadByte(); if (b == 1) { this.IsGamePublic = reader.ReadBoolean(); string str = "Alter Public = "; bool flag = this.IsGamePublic; Debug.Log(str + flag.ToString()); return; } Debug.Log("Alter unknown"); return; IL_675: int num11 = reader.ReadInt32(); if (this.GameId == num11 && reader.ReadPackedInt32() == this.ClientId) { this.EnqueueDisconnect(reader.ReadBoolean() ? DisconnectReasons.Banned : DisconnectReasons.Kicked, null); return; } return; IL_70A: Debug.Log(string.Format("Bad tag {0} at {1}+{2}={3}: ", new object[] { reader.Tag, reader.Offset, reader.Position, reader.Length }) + string.Join(" ", reader.Buffer)); } private static string AddressToString(uint address) { return string.Format("{0}.{1}.{2}.{3}", new object[] { (byte)address, (byte)(address >> 8), (byte)(address >> 16), (byte)(address >> 24) }); } // 根据clientID返回对应的client结构,如果没有的话新建一个并保存 private ClientData GetOrCreateClient(int clientId) { List obj = this.allClients; ClientData clientData; lock (obj) { clientData = this.allClients.FirstOrDefault((ClientData c) => c.Id == clientId); if (clientData == null) { clientData = new ClientData(clientId); this.allClients.Add(clientData); } } return clientData; } //在网络线程调用,删除某个角色gameobject private void RemovePlayer(int playerIdThatLeft, DisconnectReasons reason) { ClientData client = null; List obj = this.allClients; lock (obj) { for (int i = 0; i < this.allClients.Count; i++) { ClientData clientData = this.allClients[i]; if (clientData.Id == playerIdThatLeft) { client = clientData; this.allClients.RemoveAt(i); break; } } } if (client != null) { List dispatcher = this.Dispatcher; lock (dispatcher) { this.Dispatcher.Add(delegate { // 在主线程调用 this.OnPlayerLeft(client, reason); }); } } } // protected virtual void OnApplicationPause(bool pause) { this.appPaused = pause; if (!pause) { Debug.Log("Resumed Game"); if (this.AmHost) { this.RemoveUnownedObjects(); return; } } else if (this.GameState != InnerNetClient.GameStates.Ended && this.AmConnected) { Debug.Log("Lost focus during game"); ThreadPool.QueueUserWorkItem(new WaitCallback(this.WaitToDisconnect)); } } private void WaitToDisconnect(object state) { int num = 0; while (num < 10 && this.appPaused) { Thread.Sleep(1000); num++; } if (this.appPaused && this.GameState != InnerNetClient.GameStates.Ended && this.AmConnected) { this.DisconnectInternal(DisconnectReasons.FocusLostBackground, null); this.EnqueueDisconnect(DisconnectReasons.FocusLost, null); } } // protected void SendInitialData(int clientId) { MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(6); // 6 = scene change flag messageWriter.Write(this.GameId); messageWriter.WritePacked(clientId); List obj = this.allObjects; lock (obj) { HashSet hashSet = new HashSet(); for (int i = 0; i < this.allObjects.Count; i++) { InnerNetObject innerNetObject = this.allObjects[i]; if (innerNetObject && hashSet.Add(innerNetObject.gameObject)) { // 将innerNetObject对象的children都序列化为消息 this.WriteSpawnMessage(innerNetObject, innerNetObject.OwnerId, innerNetObject.SpawnFlags, messageWriter); } } } messageWriter.EndMessage(); this.SendOrDisconnect(messageWriter); messageWriter.Recycle(); } protected abstract void OnGameCreated(string gameIdString); protected abstract void OnGameJoined(string gameIdString, ClientData client); protected abstract void OnWaitForHost(string gameIdString); protected abstract void OnStartGame(); protected abstract void OnGameEnd(GameOverReason reason, bool showAd); protected abstract void OnBecomeHost(); protected abstract void OnPlayerJoined(ClientData client); protected abstract void OnPlayerChangedScene(ClientData client, string targetScene); protected abstract void OnPlayerLeft(ClientData client, DisconnectReasons reason); protected abstract void OnDisconnected(); protected abstract void OnGetGameList(int totalGames, List availableGames); protected abstract byte[] GetConnectionData(); // 根据clientId返回对应的client结构 protected ClientData FindClientById(int id) { if (id < 0) { return null; } List obj = this.allClients; ClientData result; lock (obj) { for (int i = 0; i < this.allClients.Count; i++) { ClientData clientData = this.allClients[i]; if (clientData.Id == id) { return clientData; } } result = null; } return result; } public static string IntToGameName(int gameId) { char[] array = new char[] { (char)(gameId & 255), (char)(gameId >> 8 & 255), (char)(gameId >> 16 & 255), (char)(gameId >> 24 & 255) }; if (array.Any((char c) => c < 'A' || c > 'z')) { return null; } return new string(array); } public static int GameNameToInt(string gameId) { if (gameId.Length != 4) { return -1; } gameId = gameId.ToUpperInvariant(); return (int)(gameId[0] | (int)gameId[1] << 8 | (int)gameId[2] << 16 | (int)gameId[3] << 24); } // 每隔MinSendInterval的时间同步一次标记为脏的数据 private void FixedUpdate() { if (this.mode == MatchMakerModes.None || this.Streams == null) { this.timer = 0f; return; } this.timer += Time.fixedDeltaTime; if (this.timer < this.MinSendInterval) { return; } this.timer = 0f; //c 写入所有场景内包含InnerNetObject派生类的数据 List obj = this.allObjects; lock (obj) { for (int i = 0; i < this.allObjects.Count; i++) { InnerNetObject innerNetObject = this.allObjects[i]; // 如果这个数据是本玩家控制的,作为同步数据写入 if (innerNetObject && innerNetObject.DirtyBits != 0U && (innerNetObject.AmOwner || (innerNetObject.OwnerId == /*-2*/InvalidClient && this.AmHost))) { // MsgWrite:SyncNetObject MessageWriter messageWriter = this.Streams[(int)innerNetObject.sendMode]; // 根据是否是可靠UDP,选择对应的writer messageWriter.StartMessage(1); // 1=DataFlag messageWriter.WritePacked(innerNetObject.NetId); if (innerNetObject.Serialize(messageWriter, false)) { messageWriter.EndMessage(); } else { messageWriter.CancelMessage(); } } } } // 发送数据 for (int j = 0; j < this.Streams.Length; j++) { MessageWriter messageWriter2 = this.Streams[j]; /* public void StartMessage(byte typeFlag) { messageStarts.Push(this.Position); this.Position += 2; // Skip for size this.Write(typeFlag); } public void EndMessage() { var lastMessageStart = messageStarts.Pop(); ushort length = (ushort)(this.Position - lastMessageStart - 3); // Minus length and type byte this.Buffer[lastMessageStart] = (byte)length; this.Buffer[lastMessageStart + 1] = (byte)(length >> 8); } */ // 里面是否有数据,有的话发送。 if (messageWriter2.HasBytes(7)) // 7=length(2B) + DataFlag(1B) + netId(4B) { messageWriter2.EndMessage(); this.SendOrDisconnect(messageWriter2); messageWriter2.Clear((SendOption)j); messageWriter2.StartMessage(5); // 初始化 messageWriter2.Write(this.GameId); } } } // 根据netId返回对应的同步对象 public T FindObjectByNetId(uint netId) where T : InnerNetObject { InnerNetObject innerNetObject; if (this.allObjectsFast.TryGetValue(netId, out innerNetObject)) { return (T)((object)innerNetObject); } return default(T); } // MsgWrite:Rpc 下面是有关Rpc的写入函数 //c 发送RPC public void SendRpcImmediately(uint targetNetId, byte callId, SendOption option) { MessageWriter messageWriter = MessageWriter.Get(option); messageWriter.StartMessage(5); // public const byte GameData = 5; messageWriter.Write(this.GameId); messageWriter.StartMessage(2); // Rpc messageWriter.WritePacked(targetNetId); messageWriter.Write(callId); messageWriter.EndMessage(); messageWriter.EndMessage(); this.connection.Send(messageWriter); messageWriter.Recycle(); } // 封装一个Rpc,配合FinishRpcImmediately public MessageWriter StartRpcImmediately(uint targetNetId, byte callId, SendOption option, int targetClientId = -1) { MessageWriter messageWriter = MessageWriter.Get(option); if (targetClientId < 0) { messageWriter.StartMessage(5); messageWriter.Write(this.GameId); } else { messageWriter.StartMessage(6); messageWriter.Write(this.GameId); messageWriter.WritePacked(targetClientId); } messageWriter.StartMessage(2); messageWriter.WritePacked(targetNetId); messageWriter.Write(callId); return messageWriter; } // 立即发送Rpc public void FinishRpcImmediately(MessageWriter msg) { msg.EndMessage(); msg.EndMessage(); this.SendOrDisconnect(msg); msg.Recycle(); } // 向公用的stream写入Rpc public void SendRpc(uint targetNetId, byte callId, SendOption option = SendOption.Reliable) { this.StartRpc(targetNetId, callId, option) .EndMessage(); } // 准备好Rpc通用数据,包含RpcFlag, netId和callId public MessageWriter StartRpc(uint targetNetId, byte callId, SendOption option = SendOption.Reliable) { MessageWriter messageWriter = this.Streams[(int)option]; // 拿到对应的发送流 messageWriter.StartMessage(2); // 2 = rpc messageWriter.WritePacked(targetNetId); messageWriter.Write(callId); return messageWriter; } // 发送切换场景消息 private void SendSceneChange(string sceneName) { this.InOnlineScene = string.Equals(sceneName, "OnlineGame"); if (!this.AmConnected) { return; } Debug.Log("Changed To " + sceneName); base.StartCoroutine(this.CoSendSceneChange(sceneName)); } // private IEnumerator CoSendSceneChange(string sceneName) { List obj = this.allObjects; lock (obj) { for (int i = this.allObjects.Count - 1; i > -1; i--) { if (!this.allObjects[i]) { this.allObjects.RemoveAt(i); } } goto IL_BF; } IL_A8: yield return null; IL_BF: if (this.AmConnected && this.ClientId < 0) { goto IL_A8; } if (!this.AmConnected) { yield break; } // MsgWrite:SceneChange if (!this.AmHost && this.connection.State == ConnectionState.Connected)//如果是非主机玩家,广播加载完场景的消息 { MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); messageWriter.StartMessage(5); messageWriter.Write(this.GameId); messageWriter.StartMessage(6); messageWriter.WritePacked(this.ClientId); messageWriter.Write(sceneName); messageWriter.EndMessage(); messageWriter.EndMessage(); this.SendOrDisconnect(messageWriter); messageWriter.Recycle(); } ClientData client = this.FindClientById(this.ClientId); if (client != null) { Debug.Log(string.Format("Self changed scene: {0} {1}", this.ClientId, sceneName)); List dispatcher = this.Dispatcher; lock (dispatcher) { this.Dispatcher.Add(delegate { this.OnPlayerChangedScene(client, sceneName); }); yield break; } } Debug.Log(string.Format("Couldn't find self in clients: {0}: ", this.ClientId) + sceneName); yield break; } public void Spawn(InnerNetObject netObjParent, int ownerId = /*-2*/InvalidClient, SpawnFlags flags = SpawnFlags.None) { if (this.AmHost) { ownerId = ((ownerId == /*-3*/CurrentClient) ? this.ClientId : ownerId); MessageWriter msg = this.Streams[1]; this.WriteSpawnMessage(netObjParent, ownerId, flags, msg); return; } if (!this.AmClient) { return; } Debug.LogError("Tried to spawn while not host:" + netObjParent); } // 将netObjParent下的所有InnerNetObject类型都序列化为消息,后续会广播给其他玩家 // MsgWrite:SpawnMsg private void WriteSpawnMessage(InnerNetObject netObjParent, int ownerId, SpawnFlags flags, MessageWriter msg) { msg.StartMessage(4); // 4 = SpawnFlag msg.WritePacked(netObjParent.SpawnId); // spawnId用来指代某个派生类的类型 msg.WritePacked(ownerId); // ownerId是对应玩家的clientId msg.Write((byte)flags); // flags有两种,none 和 IsClientCharacter InnerNetObject[] componentsInChildren = netObjParent.GetComponentsInChildren(); msg.WritePacked(componentsInChildren.Length); foreach (InnerNetObject innerNetObject in componentsInChildren) { innerNetObject.OwnerId = ownerId; innerNetObject.SpawnFlags = flags; if (innerNetObject.NetId == 0U) // 如果没有netId,分配一个新的 { InnerNetObject innerNetObject2 = innerNetObject; uint netIdCnt = this.NetIdCnt; this.NetIdCnt = netIdCnt + 1U; innerNetObject2.NetId = netIdCnt; this.allObjects.Add(innerNetObject); this.allObjectsFast.Add(innerNetObject.NetId, innerNetObject); } msg.WritePacked(innerNetObject.NetId); msg.StartMessage(1); // 1=DataFlag innerNetObject.Serialize(msg, true); msg.EndMessage(); } msg.EndMessage(); } // MsgWrite:DespawnMsg public void Despawn(InnerNetObject objToDespawn) { if (objToDespawn.NetId < 1U) { Debug.LogError("Tried to net destroy: " + objToDespawn); return; } MessageWriter messageWriter = this.Streams[1]; messageWriter.StartMessage(5); // DespawnFlag messageWriter.WritePacked(objToDespawn.NetId); messageWriter.EndMessage(); this.RemoveNetObject(objToDespawn); } private bool AddNetObject(InnerNetObject obj) { uint num = obj.NetId + 1U; if (num > this.NetIdCnt) { this.NetIdCnt = num; } if (!this.allObjectsFast.ContainsKey(obj.NetId)) { this.allObjects.Add(obj); this.allObjectsFast.Add(obj.NetId, obj); return true; } return false; } public void RemoveNetObject(InnerNetObject obj) { int num = this.allObjects.IndexOf(obj); if (num > -1) { this.allObjects.RemoveAt(num); } this.allObjectsFast.Remove(obj.NetId); obj.NetId = uint.MaxValue; } public void RemoveUnownedObjects() { HashSet hashSet = new HashSet(); hashSet.Add(/*-2*/InvalidClient); List obj = this.allClients; lock (obj) { for (int i = 0; i < this.allClients.Count; i++) { ClientData clientData = this.allClients[i]; if (clientData.Character) { hashSet.Add(clientData.Id); } } } List obj2 = this.allObjects; lock (obj2) { for (int j = this.allObjects.Count - 1; j > -1; j--) { InnerNetObject innerNetObject = this.allObjects[j]; if (!innerNetObject) { this.allObjects.RemoveAt(j); } else if (!hashSet.Contains(innerNetObject.OwnerId)) { innerNetObject.OwnerId = this.ClientId; UnityEngine.Object.Destroy(innerNetObject.gameObject); } } } } private void HandleGameData(MessageReader parentReader, int cnt = 0) { try { while (parentReader.Position < parentReader.Length) { this.HandleGameDataInner(parentReader.ReadMessage(), cnt); } } finally { parentReader.Recycle(); } } //c 辅助tag别名 /* internal const byte DataFlag = 1; internal const byte RpcFlag = 2; internal const byte SpawnFlag = 4; internal const byte DespawnFlag = 5; internal const byte SceneChangeFlag = 6; internal const byte ReadyFlag = 7; internal const byte ChangeSettingsFlag = 8; */ private enum SubTagAlias { Normal = 1, // 游戏内的简单数据,比如同步角色位置、动画等 Rpc = 2, // 远程调用Remote Procedure Call SpawnCharacter = 4, // 生成角色 RemoveNetObjectByNetId = 5, // 删除数据 ChangeScene = 6, // 切换场景 PlayerReady = 7, // 角色准备好了 } //c 处理收到的数据,用来同步 //c 这个方法是在主线程调用的 private void HandleGameDataInner(MessageReader reader, int cnt) { switch (reader.Tag) { case DataFlag: // MsgRead:SyncNetObject 隔一段时间同步一次netObject的数据 { uint netId = reader.ReadPackedUInt32(); InnerNetObject innerNetObject; if (this.allObjectsFast.TryGetValue(netId, out innerNetObject)) // 如果有这个netObject { // 反序列化,更新数据 innerNetObject.Deserialize(reader, false); return; } // 如果没有这个netObject且DestroyedObjects里也没有记录这个netId已经被销毁,那么 // 意味着SpawnMsg还没过来,因为Udp没法保证收到包的顺序,所以这里延迟执行这个消息,等到对应的netId收到后执行 // 如果下次执行的时候还是没有,那么继续调用DeferMessage,cnt>10次的时候退出这个递归 if (!this.DestroyedObjects.Contains(netId)) { this.DeferMessage(cnt, reader, "Stored data for " + netId); return; } return; } case RpcFlag: // MsgRead:Rpc , Rpc调用,别的机器发过来的Rpc调用,发给对应的innnerNetObject调用 { // 根据编号找到对应的同步数据 uint netId = reader.ReadPackedUInt32(); InnerNetObject innerNetObject2; if (this.allObjectsFast.TryGetValue(netId, out innerNetObject2)) { byte callId = reader.ReadByte(); //c 调用Rpc的唯一入口 innerNetObject2.HandleRpc(callId, reader); return; } if (!this.DestroyedObjects.Contains(netId)) { this.DeferMessage(cnt, reader, "Stored RPC for " + netId); return; } return; } case SpawnFlag: // MsgRead:SpawnMsg ,收到spawn消息后,实例化这个对象并设置netObjects的参数 { uint spawnId = reader.ReadPackedUInt32(); if ((ulong)spawnId >= (ulong)((long)this.SpawnableObjects.Length)) { Debug.LogError("Couldn't find spawnable prefab: " + spawnId); return; } int clientId = reader.ReadPackedInt32(); ClientData clientData = this.FindClientById(clientId); if (clientId > 0 && clientData == null) { this.DeferMessage(cnt, reader, "Delay spawn for unowned " + spawnId); return; } // 创建角色对象并设置参数 // parentNetObject是父物体 InnerNetObject parentNetObject = UnityEngine.Object.Instantiate(this.SpawnableObjects[(int)spawnId]); parentNetObject.SpawnFlags = (SpawnFlags)reader.ReadByte(); if ((parentNetObject.SpawnFlags & SpawnFlags.IsClientCharacter) != SpawnFlags.None) // 如果是角色的话,parentNetObject一定是playerControl { if (!clientData.Character) { clientData.InScene = true; clientData.Character = (parentNetObject as PlayerControl); } else if (parentNetObject) { Debug.LogWarning(string.Format("Double spawn character: {0} already has {1}", clientData.Id, clientData.Character.NetId)); UnityEngine.Object.Destroy(parentNetObject.gameObject); return; } } int numOfNetObjs = reader.ReadPackedInt32(); InnerNetObject[] componentsInChildren = parentNetObject.GetComponentsInChildren(); if (numOfNetObjs != componentsInChildren.Length) { Debug.LogError("Children didn't match for spawnable " + spawnId); UnityEngine.Object.Destroy(parentNetObject.gameObject); return; } for (int i = 0; i < numOfNetObjs; i++) { InnerNetObject childNetObject = componentsInChildren[i]; childNetObject.NetId = reader.ReadPackedUInt32(); childNetObject.OwnerId = clientId; if (this.DestroyedObjects.Contains(childNetObject.NetId)) // 如果这个netId已经被标记了销毁,表明这是一个错误的生成消息 { parentNetObject.NetId = uint.MaxValue; UnityEngine.Object.Destroy(parentNetObject.gameObject); return; } if (!this.AddNetObject(childNetObject)) { parentNetObject.NetId = uint.MaxValue; UnityEngine.Object.Destroy(parentNetObject.gameObject); return; } MessageReader messageReader = reader.ReadMessage(); // netObject的序列化数据 if (messageReader.Length > 0) { childNetObject.Deserialize(messageReader, true); // 反序列化重建出来结构 } } return; } case DespawnFlag: // MsgRead:DespawnMsg,收到消息后,销毁对应的netObject { uint netId = reader.ReadPackedUInt32(); this.DestroyedObjects.Add(netId); // 回收这个netId InnerNetObject innerNetObject5 = this.FindObjectByNetId(netId); if (innerNetObject5) { this.RemoveNetObject(innerNetObject5); UnityEngine.Object.Destroy(innerNetObject5.gameObject); // 销毁 return; } return; } case SceneChangeFlag: // MsgRead:SceneChange , 收到某个玩家加载完成场景的消息 { int id = reader.ReadPackedInt32(); // clientId ClientData client = this.FindClientById(id); // 找到对应的玩家 string targetScene = reader.ReadString(); if (client != null && !string.IsNullOrWhiteSpace(targetScene)) { Debug.Log(string.Format("Client {0} changed scene to {1}", client.Id, targetScene)); List dispatcher = this.Dispatcher; lock (dispatcher) { this.Dispatcher.Add(delegate { // 切换场景 this.OnPlayerChangedScene(client, targetScene); }); return; } } Debug.Log(string.Format("Couldn't find client {0} to change scene to {1}", id, targetScene)); return; } case ReadyFlag: // MsgRead:PlayerReady , 某个玩家已经准备好了 { ClientData clientData2 = this.FindClientById(reader.ReadPackedInt32()); if (clientData2 != null) { Debug.Log(string.Format("Client {0} ready", clientData2.Id)); clientData2.IsReady = true; return; } return; } } Debug.Log(string.Format("Bad tag {0} at {1}+{2}={3}: ", new object[] { reader.Tag, reader.Offset, reader.Position, reader.Length }) + string.Join(" ", reader.Buffer)); } // 延迟到下一帧调用回调,因为Udp没法保证消息顺序,对于那些需要netObject存在的消息,比如同步netObject内容,可能 // 会出现netObject还没有创建的情况(因为没有受到SpawnMsg),这里会先缓存起来,每帧去检查一下,但超过10次后就放弃了 // cnt是递归次数 private void DeferMessage(int cnt, MessageReader reader, string logMsg) { if (cnt > 10) { Debug.Log("Giving up on: " + logMsg); return; } int cnt2 = cnt; cnt = cnt2 + 1; Debug.Log(logMsg); MessageReader copy = MessageReader.CopyMessageIntoParent(reader); // 拷贝 List preSpawnDispatcher = this.PreSpawnDispatcher; lock (preSpawnDispatcher) { this.PreSpawnDispatcher.Add(delegate { this.HandleGameData(copy, cnt); }); } } } }