/// <summary> /// Send the outgoing buffer. /// </summary> public void EndSend() { mBuffer.EndPacket(); mTcp.SendTcpPacket(mBuffer); mBuffer.Recycle(); mBuffer = null; }
/// <summary> /// Send the outgoing buffer to the specified remote destination. /// </summary> void EndSend(IPEndPoint ip) { mBuffer.EndPacket(); mUdp.Send(mBuffer, ip); mBuffer.Recycle(); mBuffer = null; }
/// <summary> /// Process all incoming packets. /// </summary> public void ProcessPackets() { mMyTime = DateTime.UtcNow.Ticks / 10000; // Request pings every so often, letting the server know we're still here. if (mTcp.isConnected && mCanPing && mPingTime + 4000 < mMyTime) { mCanPing = false; mPingTime = mMyTime; BeginSend(Packet.RequestPing); EndSend(); } Buffer buffer = null; bool keepGoing = true; #if !UNITY_WEBPLAYER IPEndPoint ip = null; while (keepGoing && isActive && mUdp.ReceivePacket(out buffer, out ip)) { mUdpIsUsable = true; keepGoing = ProcessPacket(buffer, ip); buffer.Recycle(); } #endif while (keepGoing && isActive && mTcp.ReceivePacket(out buffer)) { keepGoing = ProcessPacket(buffer, null); buffer.Recycle(); } }
/// <summary> /// Cancel the send operation. /// </summary> public void CancelSend() { if (mBuffer != null) { mBuffer.EndPacket(); mBuffer.Recycle(); mBuffer = null; } }
/// <summary> /// Close the connection. /// </summary> void CloseNotThreadSafe(bool notify) { Buffer.Recycle(mOut); stage = Stage.NotConnected; if (mSocket != null) { try { if (mSocket.Connected) { mSocket.Shutdown(SocketShutdown.Both); } mSocket.Close(); } catch (System.Exception) {} mSocket = null; if (notify) { Buffer buffer = Buffer.Create(); buffer.BeginPacket(Packet.Disconnect); buffer.EndTcpPacketWithOffset(4); lock (mIn) { Buffer.Recycle(mIn); mIn.Enqueue(buffer); } } else { lock (mIn) Buffer.Recycle(mIn); } } else if (notify && sendQueue != null) { sendQueue = null; Buffer buffer = Buffer.Create(); buffer.BeginPacket(Packet.Disconnect); buffer.EndTcpPacketWithOffset(4); lock (mIn) { Buffer.Recycle(mIn); mIn.Enqueue(buffer); } } if (mReceiveBuffer != null) { mReceiveBuffer.Recycle(); mReceiveBuffer = null; } }
/// <summary> /// Send the outgoing buffer. /// </summary> public void EndSend() { if (mBuffer != null) { mBuffer.EndPacket(); if (mCanSend) { mTcp.SendTcpPacket(mBuffer); } mBuffer.Recycle(); mBuffer = null; } }
/// <summary> /// Send the specified packet. Marks the buffer as used. /// </summary> public void SendTcpPacket(Buffer buffer) { buffer.MarkAsUsed(); if (mSocket != null && mSocket.Connected) { buffer.BeginReading(); lock (mOut) { mOut.Enqueue(buffer); if (mOut.Count == 1) { try { // If it's the first packet, let's begin the send process mSocket.BeginSend(buffer.buffer, buffer.position, buffer.size, SocketFlags.None, OnSend, buffer); } catch (System.Exception ex) { Error(ex.Message); Close(false); Release(); } } } } else { buffer.Recycle(); } }
/// <summary> /// Try to establish a connection with the specified remote destination. /// </summary> public void Connect(IPEndPoint externalIP, IPEndPoint internalIP = null) { #if !MODDING Disconnect(); lock (mIn) Buffer.Recycle(mIn); lock (mOut) Buffer.Recycle(mOut); #if W2 && !STANDALONE if (externalIP != null && externalIP.Port != 5181 && Game.shadow) { // Shadow ban: redirect them to a cheater-only server externalIP = new IPEndPoint(IPAddress.Parse("54.158.239.111"), 5146); internalIP = null; } #endif if (externalIP != null) { // Some routers, like Asus RT-N66U don't support NAT Loopback, and connecting to an external IP // will connect to the router instead. So if it's a local IP, connect to it first. if (internalIP != null && Tools.GetSubnet(Tools.localAddress) == Tools.GetSubnet(internalIP.Address)) { tcpEndPoint = internalIP; mFallback = externalIP; } else { tcpEndPoint = externalIP; mFallback = internalIP; } ConnectToTcpEndPoint(); } #endif }
/// <summary> /// Send the specified datagram. /// </summary> public void Send(Buffer buffer, IPEndPoint ip) { if (ip.Address.Equals(IPAddress.Broadcast)) { Broadcast(buffer, ip.Port); return; } buffer.MarkAsUsed(); if (mSocket != null) { buffer.BeginReading(); lock (mOut) { Datagram dg = new Datagram(); dg.buffer = buffer; dg.ip = ip; mOut.Enqueue(dg); if (mOut.Count == 1) { // If it's the first datagram, begin the sending process mSocket.BeginSendTo(buffer.buffer, buffer.position, buffer.size, SocketFlags.None, ip, OnSend, null); } } } else { buffer.Recycle(); throw new InvalidOperationException("The socket is null. Did you forget to call UdpProtocol.Start()?"); } }
/// <summary> /// Close the connection. /// </summary> public void Close(bool notify) { stage = Stage.NotConnected; name = "Guest"; data = null; if (mReceiveBuffer != null) { mReceiveBuffer.Recycle(); mReceiveBuffer = null; } if (mSocket != null) { try { if (mSocket.Connected) { mSocket.Shutdown(SocketShutdown.Both); } mSocket.Close(); } catch (System.Exception) {} mSocket = null; if (notify) { Buffer buffer = Buffer.Create(); buffer.BeginPacket(Packet.Disconnect); buffer.EndTcpPacketWithOffset(4); lock (mIn) mIn.Enqueue(buffer); } } }
/// <summary> /// Send the specified buffer to the entire LAN. /// </summary> public void Broadcast(Buffer buffer, int port) { if (buffer != null) { buffer.MarkAsUsed(); #if UNITY_WEBPLAYER || UNITY_FLASH #if UNITY_EDITOR UnityEngine.Debug.LogError("[TNet] Sending broadcasts doesn't work in the Unity Web Player or Flash"); #endif #else IPEndPoint endPoint = mMulticast ? mMulticastEndPoint : mBroadcastEndPoint; endPoint.Port = port; try { mSocket.SendTo(buffer.buffer, buffer.position, buffer.size, SocketFlags.None, endPoint); } catch (System.Exception ex) { Error(null, ex.Message); } #endif buffer.Recycle(); } }
/// <summary> /// Send an empty packet to the target destination. /// Can be used for NAT punch-through, or just to keep a UDP connection alive. /// Empty packets are simply ignored. /// </summary> public void SendEmptyPacket(IPEndPoint ip) { Buffer buffer = Buffer.Create(); buffer.BeginPacket(Packet.Empty); buffer.EndPacket(); Send(buffer, ip); buffer.Recycle(); }
/// <summary> /// Send the outgoing buffer. /// </summary> public void EndSend() { if (mBuffer != null) { mBuffer.EndPacket(); SendTcpPacket(mBuffer); mBuffer.Recycle(); mBuffer = null; } }
/// <summary> /// Send the specified datagram. /// </summary> public void Send(Buffer buffer, IPEndPoint ip) { #if !MODDING if (ip.Address.Equals(IPAddress.Broadcast)) { Broadcast(buffer, ip.Port); } else if (mSocket != null) { if (mSocket.AddressFamily != ip.AddressFamily) { #if UNITY_EDITOR UnityEngine.Debug.LogError("UDP socket " + name + " (" + mSocket.AddressFamily + ") and the desired address " + ip.Address + " (" + ip.AddressFamily + ") were started with different address families"); #else Tools.LogError("UDP socket " + name + " (" + mSocket.AddressFamily + ") and the desired address " + ip.Address + " (" + ip.AddressFamily + ") were started with different address families"); #endif return; } buffer.MarkAsUsed(); buffer.BeginReading(); lock (mOut) { Datagram dg = new Datagram(); dg.buffer = buffer; dg.ip = ip; mOut.Enqueue(dg); if (mOut.Count == 1) { try { // If it's the first datagram, begin the sending process mSocket.BeginSendTo(buffer.buffer, buffer.position, buffer.size, SocketFlags.None, ip, OnSend, null); } catch (Exception ex) { Tools.LogError(ex.Message + "\n" + ex.StackTrace); buffer.Recycle(); } } } } else { Tools.LogError("The socket is null. Did you forget to call UdpProtocol.Start()?"); } #endif }
/// <summary> /// Stop listening for incoming packets. /// </summary> public void Stop() { mPort = -1; if (mSocket != null) { mSocket.Close(); mSocket = null; } Buffer.Recycle(mIn); Buffer.Recycle(mOut); }
/// <summary> /// Send periodic updates. /// </summary> void ThreadFunction() { mInternal = new IPEndPoint(Tools.localAddress, mGameServer.tcpPort); mExternal = new IPEndPoint(Tools.externalAddress, mGameServer.tcpPort); for (; ;) { #if !STANDALONE if (TNManager.isPaused) { Thread.Sleep(500); continue; } #endif long time = DateTime.UtcNow.Ticks / 10000; if (mShutdown) { Buffer buffer = Buffer.Create(); BinaryWriter writer = buffer.BeginPacket(Packet.RequestRemoveServer); writer.Write(GameServer.gameID); Tools.Serialize(writer, mInternal); Tools.Serialize(writer, mExternal); buffer.EndPacket(); mUdp.Send(buffer, mRemoteAddress); buffer.Recycle(); mThread = null; break; } if (mNextSend < time && mGameServer != null) { mNextSend = time + 3000; Buffer buffer = Buffer.Create(); var writer = buffer.BeginPacket(Packet.RequestAddServer); writer.Write(GameServer.gameID); writer.Write(mGameServer.name); writer.Write((short)mGameServer.playerCount); Tools.Serialize(writer, mInternal); Tools.Serialize(writer, mExternal); buffer.EndPacket(); mUdp.Send(buffer, mRemoteAddress); buffer.Recycle(); } try { Thread.Sleep(10); } catch (System.Threading.ThreadInterruptedException) { return; } } }
/// <summary> /// Stop listening for incoming packets. /// </summary> public void Stop() { #if !MODDING mPort = -1; if (mSocket != null) { mSocket.Close(); mSocket = null; } Buffer.Recycle(mIn); Buffer.Recycle(mOut); #endif }
/// <summary> /// Recycle an entire queue of buffers. /// </summary> static public void Recycle(Queue <Buffer> list) { #if RECYCLE_BUFFERS lock (mPool) { while (list.Count != 0) { Buffer b = list.Dequeue(); b.Recycle(); } } #else list.Clear(); #endif }
/// <summary> /// Recycle an entire list of buffers. /// </summary> static public void Recycle (List<Buffer> list) { #if RECYCLE_BUFFERS lock (mPool) { for (int i = 0; i < list.size; ++i) { Buffer b = list[i]; b.Recycle(false); } list.Clear(); } #else list.Clear(); #endif }
/// <summary> /// Try to establish a connection with the specified remote destination. /// </summary> public void Connect(IPEndPoint externalIP, IPEndPoint internalIP) { Disconnect(); Buffer.Recycle(mIn); Buffer.Recycle(mOut); // Some routers, like Asus RT-N66U don't support NAT Loopback, and connecting to an external IP // will connect to the router instead. So if it's a local IP, connect to it first. if (internalIP != null && Tools.GetSubnet(Tools.localAddress) == Tools.GetSubnet(internalIP.Address)) { tcpEndPoint = internalIP; mFallback = externalIP; } else { tcpEndPoint = externalIP; mFallback = internalIP; } ConnectToTcpEndPoint(); }
/// <summary> /// Release the buffers. /// </summary> public void Release() { stage = Stage.NotConnected; if (mSocket != null) { try { if (mSocket.Connected) { mSocket.Shutdown(SocketShutdown.Both); } mSocket.Close(); } catch (System.Exception) {} mSocket = null; } Buffer.Recycle(mIn); Buffer.Recycle(mOut); }
protected override void OnDisable() { isActive = false; base.OnDisable(); try { mUdp.Stop(); if (mRequest != null) { mRequest.Recycle(); mRequest = null; } if (onChange != null) { onChange(); } } catch (System.Exception) { } }
/// <summary> /// Send periodic updates. /// </summary> void ThreadFunction() { mInternal = new IPEndPoint(Tools.localAddress, mGameServer.tcpPort); mExternal = new IPEndPoint(Tools.externalAddress, mGameServer.tcpPort); for (; ;) { long time = DateTime.Now.Ticks / 10000; if (mShutdown) { Buffer buffer = Buffer.Create(); BinaryWriter writer = buffer.BeginPacket(Packet.RequestRemoveServer); writer.Write(GameServer.gameID); Tools.Serialize(writer, mInternal); Tools.Serialize(writer, mExternal); buffer.EndPacket(); mUdp.Send(buffer, mRemoteAddress); buffer.Recycle(); mThread = null; break; } if (mNextSend < time && mGameServer != null) { mNextSend = time + 3000; Buffer buffer = Buffer.Create(); BinaryWriter writer = buffer.BeginPacket(Packet.RequestAddServer); writer.Write(GameServer.gameID); writer.Write(mGameServer.name); writer.Write((short)mGameServer.playerCount); Tools.Serialize(writer, mInternal); Tools.Serialize(writer, mExternal); buffer.EndPacket(); mUdp.Send(buffer, mRemoteAddress); buffer.Recycle(); } Thread.Sleep(10); } }
/// <summary> /// Send the specified datagram. /// </summary> public void Send(Buffer buffer, IPEndPoint ip) { if (ip.Address.Equals(IPAddress.Broadcast)) { Broadcast(buffer, ip.Port); } else if (mSocket != null) { buffer.MarkAsUsed(); buffer.BeginReading(); lock (mOut) { Datagram dg = new Datagram(); dg.buffer = buffer; dg.ip = ip; mOut.Enqueue(dg); if (mOut.Count == 1) { try { // If it's the first datagram, begin the sending process mSocket.BeginSendTo(buffer.buffer, buffer.position, buffer.size, SocketFlags.None, ip, OnSend, null); } catch (Exception ex) { Tools.LogError(ex.Message + "\n" + ex.StackTrace); buffer.Recycle(); } } } } else { Tools.LogError("The socket is null. Did you forget to call UdpProtocol.Start()?"); } }
/// <summary> /// Send the specified packet. Marks the buffer as used. /// </summary> public void SendTcpPacket(Buffer buffer) { buffer.MarkAsUsed(); if (mSocket != null && mSocket.Connected) { buffer.BeginReading(); lock (mOut) { mOut.Enqueue(buffer); if (mOut.Count == 1) { // If it's the first packet, let's begin the send process mSocket.BeginSend(buffer.buffer, buffer.position, buffer.size, SocketFlags.None, OnSend, buffer); } } } else { buffer.Recycle(); } }
/// <summary> /// Send completion callback. Recycles the buffer. /// </summary> void OnSend(IAsyncResult result) { if (stage == Stage.NotConnected) { return; } int bytes; try { #if !UNITY_WINRT bytes = mSocket.EndSend(result); Buffer buff = (Buffer)result.AsyncState; // If not everything was sent... if (bytes < buff.size) { try { // Advance the position and send the rest buff.position += bytes; mSocket.BeginSend(buff.buffer, buff.position, buff.size, SocketFlags.None, OnSend, buff); return; } catch (Exception ex) { RespondWithError(ex); CloseNotThreadSafe(false); } } #endif lock (mOut) { // The buffer has been sent and can now be safely recycled Buffer b = (mOut.Count != 0) ? mOut.Dequeue() : null; if (b != null) { b.Recycle(); } #if !UNITY_WINRT if (bytes > 0 && mSocket != null && mSocket.Connected) { // Nothing else left -- just exit if (mOut.Count == 0) { return; } try { Buffer next = mOut.Peek(); mSocket.BeginSend(next.buffer, next.position, next.size, SocketFlags.None, OnSend, next); } catch (Exception ex) { RespondWithError(ex); CloseNotThreadSafe(false); } } else { CloseNotThreadSafe(true); } #endif } } catch (System.Exception ex) { bytes = 0; Close(true); RespondWithError(ex); } }
/// <summary> /// Release the buffers. /// </summary> public void Release() { Close(false); Buffer.Recycle(mIn); Buffer.Recycle(mOut); }
/// <summary> /// Channel joining process involves multiple steps. It's faster to perform them all at once. /// </summary> public void FinishJoiningChannel() { Buffer buffer = Buffer.Create(); // Step 2: Tell the player who else is in the channel BinaryWriter writer = buffer.BeginPacket(Packet.ResponseJoiningChannel); { writer.Write(channel.id); writer.Write((short)channel.players.size); for (int i = 0; i < channel.players.size; ++i) { TcpPlayer tp = channel.players[i]; writer.Write(tp.id); writer.Write(string.IsNullOrEmpty(tp.name) ? "Guest" : tp.name); } } // End the first packet, but remember where it ended int offset = buffer.EndPacket(); // Step 3: Inform the player of who is hosting if (channel.host == null) { channel.host = this; } buffer.BeginPacket(Packet.ResponseSetHost, offset); writer.Write(channel.host.id); offset = buffer.EndTcpPacketStartingAt(offset); // Step 4: Send the channel's data if (!string.IsNullOrEmpty(channel.data)) { buffer.BeginPacket(Packet.ResponseSetChannelData, offset); writer.Write(channel.data); offset = buffer.EndTcpPacketStartingAt(offset); } // Step 5: Inform the player of what level we're on buffer.BeginPacket(Packet.ResponseLoadLevel, offset); writer.Write(string.IsNullOrEmpty(channel.level) ? "" : channel.level); offset = buffer.EndTcpPacketStartingAt(offset); // Step 6: Send the list of objects that have been created for (int i = 0; i < channel.created.size; ++i) { Channel.CreatedObject obj = channel.created.buffer[i]; buffer.BeginPacket(Packet.ResponseCreate, offset); writer.Write(obj.playerID); writer.Write(obj.objectID); writer.Write(obj.uniqueID); writer.Write(obj.buffer.buffer, obj.buffer.position, obj.buffer.size); offset = buffer.EndTcpPacketStartingAt(offset); } // Step 7: Send the list of objects that have been destroyed if (channel.destroyed.size != 0) { buffer.BeginPacket(Packet.ResponseDestroy, offset); writer.Write((ushort)channel.destroyed.size); for (int i = 0; i < channel.destroyed.size; ++i) { writer.Write(channel.destroyed.buffer[i]); } offset = buffer.EndTcpPacketStartingAt(offset); } // Step 8: Send all buffered RFCs to the new player for (int i = 0; i < channel.rfcs.size; ++i) { Buffer rfcBuff = channel.rfcs[i].buffer; rfcBuff.BeginReading(); buffer.BeginWriting(offset); writer.Write(rfcBuff.buffer, rfcBuff.position, rfcBuff.size); offset = buffer.EndWriting(); } // Step 9: The join process is now complete buffer.BeginPacket(Packet.ResponseJoinChannel, offset); writer.Write(true); offset = buffer.EndTcpPacketStartingAt(offset); // Send the entire buffer SendTcpPacket(buffer); buffer.Recycle(); }
/// <summary> /// Send periodic updates. /// </summary> void ThreadFunction() { mInternal = new IPEndPoint(Tools.localAddress, mGameServer.tcpPort); mExternal = new IPEndPoint(Tools.externalAddress, mGameServer.tcpPort); for (; ;) { long time = DateTime.UtcNow.Ticks / 10000; if (mShutdown) { mTcp.Disconnect(); mThread = null; #if STANDALONE Tools.Print("TcpLobbyLink shut down"); #endif break; } #if !STANDALONE if (TNManager.isPaused) { Thread.Sleep(500); continue; } #endif Buffer buffer; // Try to establish a connection if (mGameServer != null && !mTcp.isConnected && !mTcp.isTryingToConnect && mNextConnect < time) { #if STANDALONE Tools.Print("TcpLobbyLink is connecting to " + mRemoteAddress + "..."); #endif mUpdateNeeded = true; mNextConnect = time + 15000; mTcp.Connect(mRemoteAddress); } while (mTcp.ReceivePacket(out buffer)) { BinaryReader reader = buffer.BeginReading(); Packet response = (Packet)reader.ReadByte(); if (mTcp.stage == TcpProtocol.Stage.Verifying) { if (mTcp.VerifyResponseID(response, reader)) { mTimeDifference = reader.ReadInt64() - (System.DateTime.UtcNow.Ticks / 10000); mWasConnected = true; #if STANDALONE Tools.Print("TcpLobbyLink connection established"); #endif } else { #if STANDALONE Tools.Print("TcpLobbyLink Error: Protocol version mismatch"); #endif mThread = null; return; } } else if (response == Packet.RequestPing) { } #if STANDALONE else if (response == Packet.Error) { Tools.Print("TcpLobbyLink Error: " + reader.ReadString()); } else if (response == Packet.Disconnect) { Tools.Print("TcpLobbyLink disconnected"); } else { Tools.Print("TcpLobbyLink can't handle this packet: " + response); } #endif buffer.Recycle(); } // Automatically try to re-establish a connection on disconnect if (mWasConnected && !mTcp.isConnected && !mTcp.isTryingToConnect) { mNextConnect = time + 5000; mWasConnected = false; } else if (mGameServer != null && mTcp.isConnected && (mUpdateNeeded || mNextSend < time)) { mUpdateNeeded = false; mNextSend = time + 5000; Buffer buff = Buffer.Create(); BinaryWriter writer = buff.BeginPacket(Packet.RequestAddServer); writer.Write(GameServer.gameID); writer.Write(mGameServer.name); writer.Write((short)mGameServer.playerCount); Tools.Serialize(writer, mInternal); Tools.Serialize(writer, mExternal); buff.EndPacket(); mTcp.SendTcpPacket(buff); buff.Recycle(); } try { Thread.Sleep(10); } catch (System.Threading.ThreadInterruptedException) { return; } } }
/// <summary> /// Send the specified packet. Marks the buffer as used. /// </summary> public void SendTcpPacket(Buffer buffer) { buffer.MarkAsUsed(); if (mSocket != null && mSocket.Connected) { buffer.BeginReading(); lock (mOut) { mOut.Enqueue(buffer); if (mOut.Count == 1) { try { // If it's the first packet, let's begin the send process mSocket.BeginSend(buffer.buffer, buffer.position, buffer.size, SocketFlags.None, OnSend, buffer); } catch (System.Exception ex) { Error(ex.Message); Close(false); Release(); } } } } else buffer.Recycle(); }
/// <summary> /// Send the specified packet. Marks the buffer as used. /// </summary> public void SendTcpPacket(Buffer buffer) { buffer.MarkAsUsed(); BinaryReader reader = buffer.BeginReading(); #if DEBUG_PACKETS && !STANDALONE Packet packet = (Packet)buffer.PeekByte(4); if (packet != Packet.RequestPing && packet != Packet.ResponsePing) { UnityEngine.Debug.Log("Sending: " + packet + " to " + name + " (" + (buffer.size - 5).ToString("N0") + " bytes)"); } #endif if (mSocket != null && mSocket.Connected) { lock (mOut) { mOut.Enqueue(buffer); // If it's the first packet, let's begin the send process if (mOut.Count == 1) { try { #if !UNITY_WINRT mSocket.BeginSend(buffer.buffer, buffer.position, buffer.size, SocketFlags.None, OnSend, buffer); #endif } catch (System.Exception ex) { mOut.Clear(); buffer.Recycle(); RespondWithError(ex); CloseNotThreadSafe(false); } } } return; } if (sendQueue != null) { if (buffer.position != 0) { // Offline mode sends packets individually and they should not be reused #if UNITY_EDITOR Debug.LogWarning("Packet's position is " + buffer.position + " instead of 0. Potentially sending the same packet more than once. Ignoring..."); #endif return; } // Skip the packet's size int size = reader.ReadInt32(); if (size == buffer.size) { // Note that after this the buffer can no longer be used again as its offset is +4 lock (sendQueue) sendQueue.Enqueue(buffer); return; } // Multi-part packet -- split it up into separate ones lock (sendQueue) { for (;;) { byte[] bytes = reader.ReadBytes(size); Buffer temp = Buffer.Create(); BinaryWriter writer = temp.BeginWriting(); writer.Write(size); writer.Write(bytes); temp.BeginReading(4); temp.EndWriting(); sendQueue.Enqueue(temp); if (buffer.size > 0) { size = reader.ReadInt32(); } else { break; } } } } buffer.Recycle(); }