#if UNITY_WEBGL || WEBSOCKET || (UNITY_XBOXONE && UNITY_EDITOR) // -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) Exit Games GmbH. All rights reserved. // // // Internal class to encapsulate the network i/o functionality for the realtime library. // // developer@exitgames.com // -------------------------------------------------------------------------------------------------------------------- namespace ExitGames.Client.Photon { using System; using System.Collections; using UnityEngine; using SupportClassPun = ExitGames.Client.Photon.SupportClass; /// /// Yield Instruction to Wait for real seconds. Very important to keep connection working if Time.TimeScale is altered, we still want accurate network events /// public sealed class WaitForRealSeconds : CustomYieldInstruction { private readonly float _endTime; public override bool keepWaiting { get { return this._endTime > Time.realtimeSinceStartup; } } public WaitForRealSeconds(float seconds) { this._endTime = Time.realtimeSinceStartup + seconds; } } /// /// Internal class to encapsulate the network i/o functionality for the realtime libary. /// public class SocketWebTcp : IPhotonSocket, IDisposable { private WebSocket sock; private readonly object syncer = new object(); public SocketWebTcp(PeerBase npeer) : base(npeer) { this.ServerAddress = npeer.ServerAddress; if (this.ReportDebugOfLevel(DebugLevel.INFO)) { this.Listener.DebugReturn(DebugLevel.INFO, "new SocketWebTcp() for Unity. Server: " + this.ServerAddress); } //this.Protocol = ConnectionProtocol.WebSocket; this.PollReceive = false; } public void Dispose() { this.State = PhotonSocketState.Disconnecting; if (this.sock != null) { try { if (this.sock.Connected) this.sock.Close(); } catch (Exception ex) { this.EnqueueDebugReturn(DebugLevel.INFO, "Exception in Dispose(): " + ex); } } this.sock = null; this.State = PhotonSocketState.Disconnected; } GameObject websocketConnectionObject; public override bool Connect() { //bool baseOk = base.Connect(); //if (!baseOk) //{ // return false; //} this.State = PhotonSocketState.Connecting; if (this.websocketConnectionObject != null) { UnityEngine.Object.Destroy(this.websocketConnectionObject); } this.websocketConnectionObject = new GameObject("websocketConnectionObject"); MonoBehaviour mb = this.websocketConnectionObject.AddComponent(); this.websocketConnectionObject.hideFlags = HideFlags.HideInHierarchy; UnityEngine.Object.DontDestroyOnLoad(this.websocketConnectionObject); this.sock = new WebSocket(new Uri(this.ConnectAddress), this.SerializationProtocol); this.sock.Connect(); mb.StartCoroutine(this.ReceiveLoop()); return true; } public override bool Disconnect() { if (this.ReportDebugOfLevel(DebugLevel.INFO)) { this.Listener.DebugReturn(DebugLevel.INFO, "SocketWebTcp.Disconnect()"); } this.State = PhotonSocketState.Disconnecting; lock (this.syncer) { if (this.sock != null) { try { this.sock.Close(); } catch (Exception ex) { this.Listener.DebugReturn(DebugLevel.ERROR, "Exception in Disconnect(): " + ex); } this.sock = null; } } if (this.websocketConnectionObject != null) { UnityEngine.Object.Destroy(this.websocketConnectionObject); } this.State = PhotonSocketState.Disconnected; return true; } /// /// used by TPeer* /// public override PhotonSocketError Send(byte[] data, int length) { if (this.State != PhotonSocketState.Connected) { return PhotonSocketError.Skipped; } try { if (data.Length > length) { byte[] trimmedData = new byte[length]; Buffer.BlockCopy(data, 0, trimmedData, 0, length); data = trimmedData; } if (this.ReportDebugOfLevel(DebugLevel.ALL)) { this.Listener.DebugReturn(DebugLevel.ALL, "Sending: " + SupportClassPun.ByteArrayToString(data)); } if (this.sock != null) { this.sock.Send(data); } } catch (Exception e) { this.Listener.DebugReturn(DebugLevel.ERROR, "Cannot send to: " + this.ServerAddress + ". " + e.Message); this.HandleException(StatusCode.Exception); return PhotonSocketError.Exception; } return PhotonSocketError.Success; } public override PhotonSocketError Receive(out byte[] data) { data = null; return PhotonSocketError.NoData; } internal const int ALL_HEADER_BYTES = 9; internal const int TCP_HEADER_BYTES = 7; internal const int MSG_HEADER_BYTES = 2; public IEnumerator ReceiveLoop() { //this.Listener.DebugReturn(DebugLevel.INFO, "ReceiveLoop()"); if (this.sock != null) { while (this.sock != null && !this.sock.Connected && this.sock.Error == null) { yield return new WaitForRealSeconds(0.1f); } if (this.sock != null) { if (this.sock.Error != null) { this.Listener.DebugReturn(DebugLevel.ERROR, "Exiting receive thread. Server: " + this.ServerAddress + ":" + this.ServerPort + " Error: " + this.sock.Error); this.HandleException(StatusCode.ExceptionOnConnect); } else { // connected if (this.ReportDebugOfLevel(DebugLevel.ALL)) { this.Listener.DebugReturn(DebugLevel.ALL, "Receiving by websocket. this.State: " + this.State); } this.State = PhotonSocketState.Connected; this.peerBase.OnConnect(); while (this.State == PhotonSocketState.Connected) { if (this.sock != null) { if (this.sock.Error != null) { this.Listener.DebugReturn(DebugLevel.ERROR, "Exiting receive thread (inside loop). Server: " + this.ServerAddress + ":" + this.ServerPort + " Error: " + this.sock.Error); this.HandleException(StatusCode.ExceptionOnReceive); break; } else { byte[] inBuff = this.sock.Recv(); if (inBuff == null || inBuff.Length == 0) { // nothing received. wait a bit, try again yield return new WaitForRealSeconds(0.02f); continue; } if (this.ReportDebugOfLevel(DebugLevel.ALL)) { this.Listener.DebugReturn(DebugLevel.ALL, "TCP << " + inBuff.Length + " = " + SupportClassPun.ByteArrayToString(inBuff)); } if (inBuff.Length > 0) { try { this.HandleReceivedDatagram(inBuff, inBuff.Length, false); } catch (Exception e) { if (this.State != PhotonSocketState.Disconnecting && this.State != PhotonSocketState.Disconnected) { if (this.ReportDebugOfLevel(DebugLevel.ERROR)) { this.EnqueueDebugReturn(DebugLevel.ERROR, "Receive issue. State: " + this.State + ". Server: '" + this.ServerAddress + "' Exception: " + e); } this.HandleException(StatusCode.ExceptionOnReceive); } } } } } } } } } this.Disconnect(); } private class MonoBehaviourExt : MonoBehaviour { } } } #endif