diff options
Diffstat (limited to 'Client/Assembly-CSharp/InnerNet/InnerNetClient.cs')
-rw-r--r-- | Client/Assembly-CSharp/InnerNet/InnerNetClient.cs | 1726 |
1 files changed, 1726 insertions, 0 deletions
diff --git a/Client/Assembly-CSharp/InnerNet/InnerNetClient.cs b/Client/Assembly-CSharp/InnerNet/InnerNetClient.cs new file mode 100644 index 0000000..7cff87c --- /dev/null +++ b/Client/Assembly-CSharp/InnerNet/InnerNetClient.cs @@ -0,0 +1,1726 @@ +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; + } + } + + public int Ping + { + get + { + if (this.connection == null) + { + return 0; + } + return (int)this.connection.AveragePingMs; + } + } + + 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; + + private UdpClientConnection connection; + + public MatchMakerModes mode; + + public int GameId = 32; + + public int HostId; + + public int ClientId = -1; + + public List<ClientData> allClients = new List<ClientData>(); + + public DisconnectReasons LastDisconnectReason; + + public string LastCustomDisconnect; + + private readonly List<Action> PreSpawnDispatcher = new List<Action>(); + + //c 网络消息队列,网络线程写入,主线程调用 + private readonly List<Action> Dispatcher = new List<Action>(); + + public InnerNetClient.GameStates GameState; + + private List<Action> TempQueue = new List<Action>(); + + 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; + + // 每次发送数据的间隔,每过0.1s发送一次 + public float MinSendInterval = 0.1f; + + private uint NetIdCnt = 1U; + + private float timer; + + public InnerNetObject[] SpawnableObjects; + + private bool InOnlineScene; + + private HashSet<uint> DestroyedObjects = new HashSet<uint>(); + + // 所有要同步的数据,包括InnerNetObject的所有派生类,场景内所有的对象的数据 + public List<InnerNetObject> allObjects = new List<InnerNetObject>(); + + private Dictionary<uint, InnerNetObject> allObjectsFast = new Dictionary<uint, InnerNetObject>(); + + private MessageWriter[] Streams; + + public enum GameStates + { + NotJoined, + Joined, + Started, + Ended + } + + public void SetEndpoint(string addr, ushort port) + { + this.networkAddress = addr; + this.networkPort = (int)port; + } + + public virtual void Start() + { + SceneManager.activeSceneChanged += delegate(Scene oldScene, Scene scene) + { + this.SendSceneChange(scene.name); + }; + this.ClientId = -1; + this.GameId = 32; + } + + private void SendOrDisconnect(MessageWriter msg) + { + try + { + this.connection.Send(msg); + } + catch + { + this.EnqueueDisconnect(DisconnectReasons.Error, "Failed to send message"); + } + } + + public ClientData GetHost() + { + List<ClientData> 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; + } + + public int GetClientIdFromCharacter(InnerNetObject character) + { + if (!character) + { + return -1; + } + List<ClientData> 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); + } + } + + public IEnumerator CoConnect() + { + if (this.AmConnected) + { + yield break; + } + DestroyableSingleton<DisconnectPopup>.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); + } + } + for (int j = 0; j < this.Streams.Length; j++) + { + MessageWriter messageWriter = this.Streams[j]; + messageWriter.Clear((SendOption)j); + messageWriter.StartMessage(5); + messageWriter.Write(this.GameId); + } + 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; // 连接断开时的回调函数 + 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<string>(), 0, length)); + } + + public void Connect(MatchMakerModes mode) + { + base.StartCoroutine(this.CoConnect(mode)); + } + + private IEnumerator CoConnect(MatchMakerModes mode) + { + if (this.mode != MatchMakerModes.None) + { + this.DisconnectInternal(DisconnectReasons.NewConnection, null); + } + this.mode = mode; + yield return this.CoConnect(); + if (!this.AmConnected) + { + yield break; + } + MatchMakerModes matchMakerModes = this.mode; + if (matchMakerModes == MatchMakerModes.Client) + { + this.JoinGame(); + yield return this.WaitWithTimeout(() => this.ClientId >= 0); + bool amConnected = this.AmConnected; + yield break; + } + if (matchMakerModes != MatchMakerModes.HostAndClient) + { + yield break; + } + this.GameId = 0; + PlayerControl.GameOptions = SaveManager.GameHostOptions; + this.HostGame(PlayerControl.GameOptions); + yield return this.WaitWithTimeout(() => this.GameId != 0); + if (!this.AmConnected) + { + yield break; + } + this.JoinGame(); + yield return this.WaitWithTimeout(() => this.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; + } + + private IEnumerator WaitWithTimeout(Func<bool> 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<Action> 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<Telemetry>.Instance.WriteDisconnect(this.LastDisconnectReason); + this.DisconnectInternal(reason, stringReason); + this.OnDisconnected(); + } + + protected void EnqueueDisconnect(DisconnectReasons reason, string stringReason = null) + { + UdpClientConnection udpClientConnection = this.connection; + List<Action> 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<DisconnectPopup>.InstanceExists) + { + DestroyableSingleton<DisconnectPopup>.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<InnerNetServer>.InstanceExists) + { + DestroyableSingleton<InnerNetServer>.Instance.StopServer(); + } + List<Action> 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(); + } + + public void HostGame(IBytesSerializable settings) + { + this.IsGamePublic = false; + MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); + messageWriter.StartMessage(0); + messageWriter.WriteBytesAndSize(settings.ToBytes()); + 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(); + } + + 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(); + } + + public MessageWriter StartEndGame() + { + MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable); + messageWriter.StartMessage(8); + 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<byte, string> TagMap = new Dictionary<byte, string> + { + {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, // 断开 + SubMessage = 6, // + JoinGame = 7, // 加入游戏 + Gameover = 8, // 游戏结束 + } + + //c 处理收到的数据 + private void HandleMessage(MessageReader reader) + { + List<Action> 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 4: + break; + case 5: + case (int)TagAlias.SubMessage: // 把这类消息存在队列里,主线程后续调用 + { + int num = reader.ReadInt32(); + if (this.GameId == num) + { + 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<ClientData> 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: + 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<GameListing> output = new List<GameListing>(); + 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<byte>(" ", 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) + }); + } + + private ClientData GetOrCreateClient(int clientId) + { + List<ClientData> 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; + } + + private void RemovePlayer(int playerIdThatLeft, DisconnectReasons reason) + { + ClientData client = null; + List<ClientData> 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<Action> 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); + messageWriter.Write(this.GameId); + messageWriter.WritePacked(clientId); + List<InnerNetObject> obj = this.allObjects; + lock (obj) + { + HashSet<GameObject> hashSet = new HashSet<GameObject>(); + for (int i = 0; i < this.allObjects.Count; i++) + { + InnerNetObject innerNetObject = this.allObjects[i]; + if (innerNetObject && hashSet.Add(innerNetObject.gameObject)) + { + 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<GameListing> availableGames); + + protected abstract byte[] GetConnectionData(); + + protected ClientData FindClientById(int id) + { + if (id < 0) + { + return null; + } + List<ClientData> 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); + } + + 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<InnerNetObject> 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 && this.AmHost))) + { + MessageWriter messageWriter = this.Streams[(int)innerNetObject.sendMode]; + messageWriter.StartMessage(1); + 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]; + if (messageWriter2.HasBytes(7)) + { + messageWriter2.EndMessage(); + this.SendOrDisconnect(messageWriter2); + messageWriter2.Clear((SendOption)j); + messageWriter2.StartMessage(5); + messageWriter2.Write(this.GameId); + } + } + } + + public T FindObjectByNetId<T>(uint netId) where T : InnerNetObject + { + InnerNetObject innerNetObject; + if (this.allObjectsFast.TryGetValue(netId, out innerNetObject)) + { + return (T)((object)innerNetObject); + } + return default(T); + } + + public void SendRpcImmediately(uint targetNetId, byte callId, SendOption option) + { + MessageWriter messageWriter = MessageWriter.Get(option); + messageWriter.StartMessage(5); + messageWriter.Write(this.GameId); + messageWriter.StartMessage(2); + messageWriter.WritePacked(targetNetId); + messageWriter.Write(callId); + messageWriter.EndMessage(); + messageWriter.EndMessage(); + this.connection.Send(messageWriter); + messageWriter.Recycle(); + } + + 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; + } + + public void FinishRpcImmediately(MessageWriter msg) + { + msg.EndMessage(); + msg.EndMessage(); + this.SendOrDisconnect(msg); + msg.Recycle(); + } + + public void SendRpc(uint targetNetId, byte callId, SendOption option = SendOption.Reliable) + { + this.StartRpc(targetNetId, callId, option).EndMessage(); + } + + public MessageWriter StartRpc(uint targetNetId, byte callId, SendOption option = SendOption.Reliable) + { + MessageWriter messageWriter = this.Streams[(int)option]; + messageWriter.StartMessage(2); + 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<InnerNetObject> 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; + } + 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<Action> 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, SpawnFlags flags = SpawnFlags.None) + { + if (this.AmHost) + { + ownerId = ((ownerId == -3) ? 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); + } + + private void WriteSpawnMessage(InnerNetObject netObjParent, int ownerId, SpawnFlags flags, MessageWriter msg) + { + msg.StartMessage(4); + msg.WritePacked(netObjParent.SpawnId); + msg.WritePacked(ownerId); + msg.Write((byte)flags); + InnerNetObject[] componentsInChildren = netObjParent.GetComponentsInChildren<InnerNetObject>(); + msg.WritePacked(componentsInChildren.Length); + foreach (InnerNetObject innerNetObject in componentsInChildren) + { + innerNetObject.OwnerId = ownerId; + innerNetObject.SpawnFlags = flags; + if (innerNetObject.NetId == 0U) + { + 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); + innerNetObject.Serialize(msg, true); + msg.EndMessage(); + } + msg.EndMessage(); + } + + 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); + 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<int> hashSet = new HashSet<int>(); + hashSet.Add(-2); + List<ClientData> 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<InnerNetObject> 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别名 + 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 (int)SubTagAlias.Normal: + { + uint num = reader.ReadPackedUInt32(); + InnerNetObject innerNetObject; + if (this.allObjectsFast.TryGetValue(num, out innerNetObject)) + { + innerNetObject.Deserialize(reader, false); + return; + } + if (!this.DestroyedObjects.Contains(num)) + { + this.DeferMessage(cnt, reader, "Stored data for " + num); + return; + } + return; + } + case (int)SubTagAlias.Rpc: + { + // 根据编号找到对应的同步数据 + uint index = reader.ReadPackedUInt32(); + InnerNetObject innerNetObject2; + if (this.allObjectsFast.TryGetValue(index, out innerNetObject2)) + { + byte callId = reader.ReadByte(); + innerNetObject2.HandleRpc(callId, reader); + return; + } + if (!this.DestroyedObjects.Contains(index)) + { + this.DeferMessage(cnt, reader, "Stored RPC for " + index); + return; + } + return; + } + case (int)SubTagAlias.SpawnCharacter: + { + uint num3 = reader.ReadPackedUInt32(); + if ((ulong)num3 >= (ulong)((long)this.SpawnableObjects.Length)) + { + Debug.LogError("Couldn't find spawnable prefab: " + num3); + return; + } + int num4 = reader.ReadPackedInt32(); + ClientData clientData = this.FindClientById(num4); + if (num4 > 0 && clientData == null) + { + this.DeferMessage(cnt, reader, "Delay spawn for unowned " + num3); + return; + } + // 创建角色对象并设置参数 + InnerNetObject innerNetObject3 = UnityEngine.Object.Instantiate<InnerNetObject>(this.SpawnableObjects[(int)num3]); + innerNetObject3.SpawnFlags = (SpawnFlags)reader.ReadByte(); + if ((innerNetObject3.SpawnFlags & SpawnFlags.IsClientCharacter) != SpawnFlags.None) + { + if (!clientData.Character) + { + clientData.InScene = true; + clientData.Character = (innerNetObject3 as PlayerControl); + } + else if (innerNetObject3) + { + Debug.LogWarning(string.Format("Double spawn character: {0} already has {1}", clientData.Id, clientData.Character.NetId)); + UnityEngine.Object.Destroy(innerNetObject3.gameObject); + return; + } + } + int num5 = reader.ReadPackedInt32(); + InnerNetObject[] componentsInChildren = innerNetObject3.GetComponentsInChildren<InnerNetObject>(); + if (num5 != componentsInChildren.Length) + { + Debug.LogError("Children didn't match for spawnable " + num3); + UnityEngine.Object.Destroy(innerNetObject3.gameObject); + return; + } + for (int i = 0; i < num5; i++) + { + InnerNetObject innerNetObject4 = componentsInChildren[i]; + innerNetObject4.NetId = reader.ReadPackedUInt32(); + innerNetObject4.OwnerId = num4; + if (this.DestroyedObjects.Contains(innerNetObject4.NetId)) + { + innerNetObject3.NetId = uint.MaxValue; + UnityEngine.Object.Destroy(innerNetObject3.gameObject); + return; + } + if (!this.AddNetObject(innerNetObject4)) + { + innerNetObject3.NetId = uint.MaxValue; + UnityEngine.Object.Destroy(innerNetObject3.gameObject); + return; + } + MessageReader messageReader = reader.ReadMessage(); + if (messageReader.Length > 0) + { + innerNetObject4.Deserialize(messageReader, true); + } + } + return; + } + case (int)SubTagAlias.RemoveNetObjectByNetId: + { + uint netId = reader.ReadPackedUInt32(); + this.DestroyedObjects.Add(netId); + InnerNetObject innerNetObject5 = this.FindObjectByNetId<InnerNetObject>(netId); + if (innerNetObject5) + { + this.RemoveNetObject(innerNetObject5); + UnityEngine.Object.Destroy(innerNetObject5.gameObject); + return; + } + return; + } + case (int)SubTagAlias.ChangeScene: + { + int id = reader.ReadPackedInt32(); + 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<Action> 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 (int)SubTagAlias.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<byte>(" ", reader.Buffer)); + } + + 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<Action> preSpawnDispatcher = this.PreSpawnDispatcher; + lock (preSpawnDispatcher) + { + this.PreSpawnDispatcher.Add(delegate + { + this.HandleGameData(copy, cnt); + }); + } + } + } +} |