/// <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> /// Create a new buffered remote function call. /// </summary> public void CreateRFC(uint inID, string funcName, Buffer buffer) { if (closed || buffer == null) { return; } Buffer b = Buffer.Create(); b.BeginWriting(false).Write(buffer.buffer, buffer.position, buffer.size); b.BeginReading(); for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.uid == inID && r.functionName == funcName) { if (r.buffer != null) { r.buffer.Recycle(); } r.buffer = b; return; } } RFC rfc = new RFC(); rfc.uid = inID; rfc.buffer = b; rfc.functionName = funcName; rfcs.Add(rfc); }
/// <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> /// 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> /// Verify the connection. /// </summary> public bool VerifyRequestID(Buffer buffer, bool uniqueID) { BinaryReader reader = buffer.BeginReading(); Packet request = (Packet)reader.ReadByte(); if (request == Packet.RequestID) { if (reader.ReadInt32() == version) { lock (mLock) { id = uniqueID ? ++mPlayerCounter : 0; } name = reader.ReadString(); if (buffer.size > 1) { #if STANDALONE data = reader.ReadBytes(buffer.size); #else data = reader.ReadObject(); #endif } else { data = null; } stage = TcpProtocol.Stage.Connected; BinaryWriter writer = BeginSend(Packet.ResponseID); writer.Write(version); writer.Write(id); writer.Write((Int64)(System.DateTime.UtcNow.Ticks / 10000)); EndSend(); return(true); } else { BinaryWriter writer = BeginSend(Packet.ResponseID); writer.Write(0); EndSend(); Close(false); } } return(false); }
/// <summary> /// Receive incoming data. /// </summary> void OnReceive(IAsyncResult result) { if (!isActive) { return; } int bytes = 0; try { bytes = mSocket.EndReceiveFrom(result, ref mEndPoint); } catch (System.Exception ex) { Error(new IPEndPoint(Tools.localAddress, 0), ex.Message); } if (bytes > 4) { // This datagram is now ready to be processed Buffer buffer = Buffer.Create(); buffer.BeginWriting(false).Write(mTemp, 0, bytes); buffer.BeginReading(4); // The 'endPoint', gets reassigned rather than updated. Datagram dg = new Datagram(); dg.buffer = buffer; dg.ip = (IPEndPoint)mEndPoint; lock (mIn) mIn.Enqueue(dg); } // Queue up the next receive operation if (mSocket != null) { mEndPoint = mDefaultEndPoint; try { mSocket.BeginReceiveFrom(mTemp, 0, mTemp.Length, SocketFlags.None, ref mEndPoint, OnReceive, null); } catch (System.Exception ex) { Error(new IPEndPoint(Tools.localAddress, 0), ex.Message); } } }
/// <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 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> /// See if the received packet can be processed and split it up into different ones. /// </summary> bool ProcessBuffer(int bytes) { if (mReceiveBuffer == null) { // Create a new packet buffer mReceiveBuffer = Buffer.Create(); mReceiveBuffer.BeginWriting(false).Write(mTemp, 0, bytes); mExpected = 0; mOffset = 0; } else { // Append this data to the end of the last used buffer mReceiveBuffer.BeginWriting(true).Write(mTemp, 0, bytes); } for (int available = mReceiveBuffer.size - mOffset; available >= 4;) { // Figure out the expected size of the packet if (mExpected == 0) { mExpected = mReceiveBuffer.PeekInt(mOffset); // "GET " -- HTTP GET request sent by a web browser if (mExpected == 542393671) { if (httpGetSupport) { if (stage == Stage.Verifying || stage == Stage.WebBrowser) { stage = Stage.WebBrowser; string request = Encoding.ASCII.GetString(mReceiveBuffer.buffer, mOffset, available); mReceiveBuffer.BeginPacket(Packet.RequestHTTPGet).Write(request); mReceiveBuffer.EndPacket(); mReceiveBuffer.BeginReading(4); lock (mIn) mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; } return(true); } mReceiveBuffer.Recycle(); mReceiveBuffer = null; mExpected = 0; mOffset = 0; Disconnect(); return(false); } else if (mExpected < 0 || mExpected > 16777216) { #if UNITY_EDITOR UnityEngine.Debug.LogError("Malformed data packet: " + mOffset + ", " + available + " / " + mExpected); #else Tools.LogError("Malformed data packet: " + mOffset + ", " + available + " / " + mExpected); #endif mReceiveBuffer.Recycle(); mReceiveBuffer = null; mExpected = 0; mOffset = 0; Disconnect(); return(false); } } // The first 4 bytes of any packet always contain the number of bytes in that packet available -= 4; // If the entire packet is present if (available == mExpected) { // Reset the position to the beginning of the packet mReceiveBuffer.BeginReading(mOffset + 4); // This packet is now ready to be processed lock (mIn) mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; break; } else if (available > mExpected) { // There is more than one packet. Extract this packet fully. int realSize = mExpected + 4; Buffer temp = Buffer.Create(); // Extract the packet and move past its size component BinaryWriter bw = temp.BeginWriting(false); bw.Write(mReceiveBuffer.buffer, mOffset, realSize); temp.BeginReading(4); // This packet is now ready to be processed lock (mIn) mIn.Enqueue(temp); // Skip this packet available -= mExpected; mOffset += realSize; mExpected = 0; } else { break; } } return(true); }
/// <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(); }
/// <summary> /// Process an incoming packet. /// </summary> bool ProcessPacket(Buffer buffer, IPEndPoint ip) { BinaryReader reader = buffer.BeginReading(); Packet request = (Packet)reader.ReadByte(); switch (request) { case Packet.RequestAddServer: { if (reader.ReadUInt16() != GameServer.gameID) return false; ServerList.Entry ent = new ServerList.Entry(); ent.ReadFrom(reader); if (ent.externalAddress.Address.Equals(IPAddress.None)) ent.externalAddress = ip; mList.Add(ent, mTime); #if STANDALONE Console.WriteLine(ip + " added a server (" + ent.internalAddress + ", " + ent.externalAddress + ")"); #endif return true; } case Packet.RequestRemoveServer: { if (reader.ReadUInt16() != GameServer.gameID) return false; IPEndPoint internalAddress, externalAddress; Tools.Serialize(reader, out internalAddress); Tools.Serialize(reader, out externalAddress); if (externalAddress.Address.Equals(IPAddress.None)) externalAddress = ip; RemoveServer(internalAddress, externalAddress); #if STANDALONE Console.WriteLine(ip + " removed a server (" + internalAddress + ", " + externalAddress + ")"); #endif return true; } case Packet.RequestServerList: { if (reader.ReadUInt16() != GameServer.gameID) return false; mList.WriteTo(BeginSend(Packet.ResponseServerList)); EndSend(ip); return true; } } return false; }
/// <summary> /// Process a single incoming packet. Returns whether we should keep processing packets or not. /// </summary> bool ProcessPacket(Buffer buffer, IPEndPoint ip) { mPacketSource = ip; BinaryReader reader = buffer.BeginReading(); if (buffer.size == 0) { return(true); } int packetID = reader.ReadByte(); Packet response = (Packet)packetID; // Verification step must be passed first if (response == Packet.ResponseID || mTcp.stage == TcpProtocol.Stage.Verifying) { if (mTcp.VerifyResponseID(response, reader)) { mTimeDifference = reader.ReadInt64() - (System.DateTime.UtcNow.Ticks / 10000); #if !UNITY_WEBPLAYER if (mUdp.isActive) { // If we have a UDP listener active, tell the server BeginSend(Packet.RequestSetUDP).Write((ushort)mUdp.listeningPort); EndSend(); } #endif mCanPing = true; if (onConnect != null) { onConnect(true, null); } } return(true); } //#if !UNITY_EDITOR // DEBUG // if (response != Packet.ResponsePing) Console.WriteLine("Client: " + response + " " + buffer.position + " of " + buffer.size + ((ip == null) ? " (TCP)" : " (UDP)")); //#else // if (response != Packet.ResponsePing) UnityEngine.Debug.Log("Client: " + response + " " + buffer.position + " of " + buffer.size + ((ip == null) ? " (TCP)" : " (UDP)")); //#endif OnPacket callback; if (packetHandlers.TryGetValue((byte)response, out callback) && callback != null) { callback(response, reader, ip); return(true); } switch (response) { case Packet.Empty: break; case Packet.ForwardToAll: case Packet.ForwardToOthers: case Packet.ForwardToAllSaved: case Packet.ForwardToOthersSaved: case Packet.ForwardToHost: case Packet.Broadcast: { if (onForwardedPacket != null) { onForwardedPacket(reader); } break; } case Packet.ForwardToPlayer: { // Skip the player ID reader.ReadInt32(); if (onForwardedPacket != null) { onForwardedPacket(reader); } break; } case Packet.ForwardByName: { // Skip the player name reader.ReadString(); if (onForwardedPacket != null) { onForwardedPacket(reader); } break; } case Packet.SyncPlayerData: { Player target = GetPlayer(reader.ReadInt32()); if (target != null) { target.data = reader.ReadObject(); if (onPlayerSync != null) { onPlayerSync(target); } } break; } case Packet.ResponsePing: { int ping = (int)(mMyTime - mPingTime); if (ip != null) { if (onPing != null && ip != null) { onPing(ip, ping); } } else { mCanPing = true; mPing = ping; } break; } case Packet.ResponseSetUDP: { #if !UNITY_WEBPLAYER // The server has a new port for UDP traffic ushort port = reader.ReadUInt16(); if (port != 0) { IPAddress ipa = new IPAddress(mTcp.tcpEndPoint.Address.GetAddressBytes()); mServerUdpEndPoint = new IPEndPoint(ipa, port); // Send the first UDP packet to the server if (mUdp.isActive) { mBuffer = Buffer.Create(); mBuffer.BeginPacket(Packet.RequestActivateUDP).Write(playerID); mBuffer.EndPacket(); mUdp.Send(mBuffer, mServerUdpEndPoint); mBuffer.Recycle(); mBuffer = null; } } else { mServerUdpEndPoint = null; } #endif break; } case Packet.ResponseJoiningChannel: { mIsInChannel = true; mDictionary.Clear(); players.Clear(); mChannelID = reader.ReadInt32(); int count = reader.ReadInt16(); for (int i = 0; i < count; ++i) { Player p = new Player(); p.id = reader.ReadInt32(); p.name = reader.ReadString(); p.data = reader.ReadObject(); mDictionary.Add(p.id, p); players.Add(p); } break; } case Packet.ResponseLoadLevel: { // Purposely return after loading a level, ensuring that all future callbacks happen after loading if (onLoadLevel != null) { onLoadLevel(reader.ReadString()); } return(false); } case Packet.ResponsePlayerLeft: { Player p = GetPlayer(reader.ReadInt32()); if (p != null) { mDictionary.Remove(p.id); } players.Remove(p); if (onPlayerLeft != null) { onPlayerLeft(p); } break; } case Packet.ResponsePlayerJoined: { Player p = new Player(); p.id = reader.ReadInt32(); p.name = reader.ReadString(); p.data = reader.ReadObject(); mDictionary.Add(p.id, p); players.Add(p); if (onPlayerJoined != null) { onPlayerJoined(p); } break; } case Packet.ResponseSetHost: { mHost = reader.ReadInt32(); if (onSetHost != null) { onSetHost(isHosting); } break; } case Packet.ResponseSetChannelData: { mData = reader.ReadString(); if (onSetChannelData != null) { onSetChannelData(mData); } break; } case Packet.ResponseJoinChannel: { mIsInChannel = reader.ReadBoolean(); if (onJoinChannel != null) { onJoinChannel(mIsInChannel, mIsInChannel ? null : reader.ReadString()); } break; } case Packet.ResponseLeaveChannel: { mData = ""; mChannelID = 0; mIsInChannel = false; mDictionary.Clear(); players.Clear(); if (onLeftChannel != null) { onLeftChannel(); } break; } case Packet.ResponseRenamePlayer: { Player p = GetPlayer(reader.ReadInt32()); string oldName = p.name; if (p != null) { p.name = reader.ReadString(); } if (onRenamePlayer != null) { onRenamePlayer(p, oldName); } break; } case Packet.ResponseCreate: { if (onCreate != null) { int playerID = reader.ReadInt32(); ushort index = reader.ReadUInt16(); uint objID = reader.ReadUInt32(); onCreate(playerID, index, objID, reader); } break; } case Packet.ResponseDestroy: { if (onDestroy != null) { int count = reader.ReadUInt16(); for (int i = 0; i < count; ++i) { onDestroy(reader.ReadUInt32()); } } break; } case Packet.Error: { if (mTcp.stage != TcpProtocol.Stage.Connected && onConnect != null) { onConnect(false, reader.ReadString()); } else if (onError != null) { onError(reader.ReadString()); } break; } case Packet.Disconnect: { mData = ""; if (isInChannel && onLeftChannel != null) { onLeftChannel(); } players.Clear(); mDictionary.Clear(); mTcp.Close(false); mLoadFiles.Clear(); if (onDisconnect != null) { onDisconnect(); } break; } case Packet.ResponseLoadFile: { string filename = reader.ReadString(); int size = reader.ReadInt32(); byte[] data = reader.ReadBytes(size); OnLoadFile lfc = mLoadFiles.Pop(); if (lfc != null) { lfc(filename, data); } break; } } return(true); }
/// <summary> /// Receive and process a single incoming packet. /// Returns 'true' if a packet was received, 'false' otherwise. /// </summary> bool ProcessPlayerPacket(Buffer buffer, TcpPlayer player, bool reliable) { BinaryReader reader = buffer.BeginReading(); Packet request = (Packet)reader.ReadByte(); //#if UNITY_EDITOR // DEBUG // if (request != Packet.RequestPing) UnityEngine.Debug.Log("Server: " + request + " " + buffer.position + " " + buffer.size); //#else // if (request != Packet.RequestPing) Console.WriteLine("Server: " + request + " " + buffer.position + " " + buffer.size); //#endif // If the player has not yet been verified, the first packet must be an ID request if (player.stage == TcpProtocol.Stage.Verifying) { if (player.VerifyRequestID(request, reader, true)) { mDictionaryID.Add(player.id, player); if (lobbyLink != null) lobbyLink.SendUpdate(this); if (onPlayerConnect != null) onPlayerConnect(player); return true; } #if STANDALONE Console.WriteLine(player.address + " has failed the verification step"); #endif RemovePlayer(player); return false; } switch (request) { case Packet.Empty: { break; } case Packet.Error: { Error(player, reader.ReadString()); break; } case Packet.Disconnect: { RemovePlayer(player); break; } case Packet.RequestPing: { // Respond with a ping back BeginSend(Packet.ResponsePing); EndSend(true, player); break; } case Packet.RequestSetUDP: { int port = reader.ReadUInt16(); if (port != 0) { IPAddress ip = new IPAddress(player.tcpEndPoint.Address.GetAddressBytes()); SetPlayerUdpEndPoint(player, new IPEndPoint(ip, port)); } else SetPlayerUdpEndPoint(player, null); // Let the player know if we are hosting an active UDP connection ushort udp = mUdp.isActive ? (ushort)mUdp.listeningPort : (ushort)0; BeginSend(Packet.ResponseSetUDP).Write(udp); EndSend(true, player); // Send an empty packet to the target player to open up UDP for communication if (player.udpEndPoint != null) mUdp.SendEmptyPacket(player.udpEndPoint); break; } case Packet.RequestJoinChannel: { // Join the specified channel int channelID = reader.ReadInt32(); string pass = reader.ReadString(); string levelName = reader.ReadString(); bool persist = reader.ReadBoolean(); ushort playerLimit = reader.ReadUInt16(); // Join a random existing channel or create a new one if (channelID == -2) { bool randomLevel = string.IsNullOrEmpty(levelName); channelID = -1; for (int i = 0; i < mChannels.size; ++i) { Channel ch = mChannels[i]; if (ch.isOpen && (randomLevel || levelName.Equals(ch.level)) && (string.IsNullOrEmpty(ch.password) || (ch.password == pass))) { channelID = ch.id; break; } } // If no level name has been specified and no channels were found, we're done if (randomLevel && channelID == -1) { BinaryWriter writer = BeginSend(Packet.ResponseJoinChannel); writer.Write(false); writer.Write("No suitable channels found"); EndSend(true, player); break; } } // Join a random new channel if (channelID == -1) { channelID = mRandom.Next(100000000); for (int i = 0; i < 1000; ++i) { if (!ChannelExists(channelID)) break; channelID = mRandom.Next(100000000); } } if (player.channel == null || player.channel.id != channelID) { bool isNew; Channel channel = CreateChannel(channelID, out isNew); if (channel == null || !channel.isOpen) { BinaryWriter writer = BeginSend(Packet.ResponseJoinChannel); writer.Write(false); writer.Write("The requested channel is closed"); EndSend(true, player); } else if (isNew) { channel.password = pass; channel.persistent = persist; channel.level = levelName; channel.playerLimit = playerLimit; SendLeaveChannel(player, false); SendJoinChannel(player, channel); } else if (string.IsNullOrEmpty(channel.password) || (channel.password == pass)) { SendLeaveChannel(player, false); SendJoinChannel(player, channel); } else { BinaryWriter writer = BeginSend(Packet.ResponseJoinChannel); writer.Write(false); writer.Write("Wrong password"); EndSend(true, player); } } break; } case Packet.RequestSetName: { // Change the player's name player.name = reader.ReadString(); BinaryWriter writer = BeginSend(Packet.ResponseRenamePlayer); writer.Write(player.id); writer.Write(player.name); if (player.channel != null) { EndSend(true, player.channel, null); } else { EndSend(true, player); } break; } case Packet.RequestSaveFile: { string fileName = reader.ReadString(); byte[] data = reader.ReadBytes(reader.ReadInt32()); SaveFile(fileName, data); break; } case Packet.RequestLoadFile: { string fn = reader.ReadString(); byte[] data = LoadFile(fn); BinaryWriter writer = BeginSend(Packet.ResponseLoadFile); writer.Write(fn); if (data != null) { writer.Write(data.Length); writer.Write(data); } else { writer.Write(0); } EndSend(true, player); break; } case Packet.RequestDeleteFile: { DeleteFile(reader.ReadString()); break; } case Packet.RequestNoDelay: { player.noDelay = reader.ReadBoolean(); break; } case Packet.RequestChannelList: { BinaryWriter writer = BeginSend(Packet.ResponseChannelList); int count = 0; for (int i = 0; i < mChannels.size; ++i) if (!mChannels[i].closed) ++count; writer.Write(count); for (int i = 0; i < mChannels.size; ++i) { Channel ch = mChannels[i]; if (!ch.closed) { writer.Write(ch.id); writer.Write((ushort)ch.players.size); writer.Write(ch.playerLimit); writer.Write(!string.IsNullOrEmpty(ch.password)); writer.Write(ch.persistent); writer.Write(ch.level); writer.Write(ch.data); } } EndSend(true, player); break; } case Packet.RequestSetTimeout: { // The passed value is in seconds, but the stored value is in milliseconds (to avoid a math operation) player.timeoutTime = reader.ReadInt32() * 1000; break; } case Packet.ForwardToPlayer: { // Forward this packet to the specified player TcpPlayer target = GetPlayer(reader.ReadInt32()); if (target != null && target.isConnected) { // Reset the position back to the beginning (4 bytes for size, 1 byte for ID, 4 bytes for player) buffer.position = buffer.position - 9; target.SendTcpPacket(buffer); } break; } case Packet.Broadcast: { // 4 bytes for size, 1 byte for ID buffer.position = buffer.position - 5; // Forward the packet to everyone connected to the server for (int i = 0; i < mPlayers.size; ++i) { TcpPlayer tp = mPlayers[i]; if (reliable || tp.udpEndPoint == null || !mAllowUdp) { tp.SendTcpPacket(buffer); } else mUdp.Send(buffer, tp.udpEndPoint); } break; } default: { if (player.channel != null && (int)request <= (int)Packet.ForwardToPlayerBuffered) { // Other packets can only be processed while in a channel if ((int)request >= (int)Packet.ForwardToAll) { ProcessForwardPacket(player, buffer, reader, request, reliable); } else { ProcessChannelPacket(player, buffer, reader, request); } } else if (onCustomPacket != null) { onCustomPacket(player, buffer, reader, request, reliable); } break; } } return true; }
/// <summary> /// Process an incoming packet. /// </summary> bool ProcessPacket(Buffer buffer, IPEndPoint ip) { if (mBan.Count != 0 && mBan.Contains(ip.Address.ToString())) { return(false); } var reader = buffer.BeginReading(); var request = (Packet)reader.ReadByte(); switch (request) { case Packet.RequestPing: { var writer = BeginSend(Packet.ResponsePing); writer.Write(mTime); writer.Write((ushort)mList.list.size); EndSend(ip); break; } case Packet.RequestAddServer: { if (reader.ReadUInt16() != GameServer.gameID) { return(false); } var ent = new ServerList.Entry(); ent.ReadFrom(reader); if (mBan.Count != 0 && (mBan.Contains(ent.externalAddress.Address.ToString()) || IsBanned(ent.name))) { return(false); } if (ent.externalAddress.Address.Equals(IPAddress.None) || ent.externalAddress.Address.Equals(IPAddress.IPv6None)) { ent.externalAddress = ip; } mList.Add(ent, mTime); #if STANDALONE Tools.Print(ip + " added a server (" + ent.internalAddress + ", " + ent.externalAddress + ")"); #endif return(true); } case Packet.RequestRemoveServer: { if (reader.ReadUInt16() != GameServer.gameID) { return(false); } IPEndPoint internalAddress, externalAddress; Tools.Serialize(reader, out internalAddress); Tools.Serialize(reader, out externalAddress); if (externalAddress.Address.Equals(IPAddress.None) || externalAddress.Address.Equals(IPAddress.IPv6None)) { externalAddress = ip; } RemoveServer(internalAddress, externalAddress); #if STANDALONE Tools.Print(ip + " removed a server (" + internalAddress + ", " + externalAddress + ")"); #endif return(true); } case Packet.RequestServerList: { if (reader.ReadUInt16() != GameServer.gameID) { return(false); } mList.WriteTo(BeginSend(Packet.ResponseServerList)); EndSend(ip); return(true); } } return(false); }
/// <summary> /// Process a single incoming packet. Returns whether we should keep processing packets or not. /// </summary> bool ProcessPacket(Buffer buffer, IPEndPoint ip) { mPacketSource = ip; BinaryReader reader = buffer.BeginReading(); if (buffer.size == 0) { return(true); } int packetID = reader.ReadByte(); Packet response = (Packet)packetID; // Verification step must be passed first if (mTcp.stage == TcpProtocol.Stage.Verifying) { if (mTcp.VerifyResponseID(response, reader)) { #if !UNITY_WEBPLAYER if (mUdp.isActive) { // If we have a UDP listener active, tell the server BeginSend(Packet.RequestSetUDP).Write(mUdp.isActive ? (ushort)mUdp.listeningPort : (ushort)0); EndSend(); } #endif mCanPing = true; if (onConnect != null) { onConnect(true, null); } } else if (onConnect != null) { onConnect(false, "Protocol version mismatch!"); } return(true); } //#if !UNITY_EDITOR // DEBUG // if (response != Packet.ResponsePing) Console.WriteLine("Client: " + response + " " + buffer.position + " of " + buffer.size + ((ip == null) ? " (TCP)" : " (UDP)")); //#else // if (response != Packet.ResponsePing) UnityEngine.Debug.Log("Client: " + response + " " + buffer.position + " of " + buffer.size + ((ip == null) ? " (TCP)" : " (UDP)")); //#endif switch (response) { case Packet.Empty: break; case Packet.ForwardToAll: case Packet.ForwardToOthers: case Packet.ForwardToAllSaved: case Packet.ForwardToOthersSaved: case Packet.ForwardToHost: { if (onForwardedPacket != null) { onForwardedPacket(reader); } break; } case Packet.ForwardToPlayer: { // Skip the player ID reader.ReadInt32(); if (onForwardedPacket != null) { onForwardedPacket(reader); } break; } case Packet.ResponsePing: { mPing = (int)(mTime - mPingTime); mCanPing = true; break; } case Packet.ResponseSetUDP: { #if !UNITY_WEBPLAYER // The server has a new port for UDP traffic ushort port = reader.ReadUInt16(); if (port != 0) { IPAddress ipa = new IPAddress(mTcp.tcpEndPoint.Address.GetAddressBytes()); mServerUdpEndPoint = new IPEndPoint(ipa, port); // Send an empty packet to the server, opening up the communication channel if (mUdp.isActive) { mUdp.SendEmptyPacket(mServerUdpEndPoint); } } else { mServerUdpEndPoint = null; } #endif break; } case Packet.ResponseJoiningChannel: { mIsInChannel = true; mDictionary.Clear(); players.Clear(); mChannelID = reader.ReadInt32(); int count = reader.ReadInt16(); for (int i = 0; i < count; ++i) { Player p = new Player(); p.id = reader.ReadInt32(); p.name = reader.ReadString(); mDictionary.Add(p.id, p); players.Add(p); } break; } case Packet.ResponseLoadLevel: { // Purposely return after loading a level, ensuring that all future callbacks happen after loading if (onLoadLevel != null) { onLoadLevel(reader.ReadString()); } return(false); } case Packet.ResponsePlayerLeft: { Player p = GetPlayer(reader.ReadInt32()); if (p != null) { mDictionary.Remove(p.id); } players.Remove(p); if (onPlayerLeft != null) { onPlayerLeft(p); } break; } case Packet.ResponsePlayerJoined: { Player p = new Player(); p.id = reader.ReadInt32(); p.name = reader.ReadString(); mDictionary.Add(p.id, p); players.Add(p); if (onPlayerJoined != null) { onPlayerJoined(p); } break; } case Packet.ResponseSetHost: { mHost = reader.ReadInt32(); if (onSetHost != null) { onSetHost(isHosting); } break; } case Packet.ResponseSetChannelData: { mData = reader.ReadString(); if (onSetChannelData != null) { onSetChannelData(mData); } break; } case Packet.ResponseJoinChannel: { mIsInChannel = reader.ReadBoolean(); if (onJoinChannel != null) { onJoinChannel(mIsInChannel, mIsInChannel ? null : reader.ReadString()); } break; } case Packet.ResponseLeaveChannel: { mData = ""; mChannelID = 0; mIsInChannel = false; mDictionary.Clear(); players.Clear(); if (onLeftChannel != null) { onLeftChannel(); } break; } case Packet.ResponseRenamePlayer: { Player p = GetPlayer(reader.ReadInt32()); string oldName = p.name; if (p != null) { p.name = reader.ReadString(); } if (onRenamePlayer != null) { onRenamePlayer(p, oldName); } break; } case Packet.ResponseCreate: { if (onCreate != null) { int playerID = reader.ReadInt32(); short index = reader.ReadInt16(); uint objID = reader.ReadUInt32(); onCreate(playerID, index, objID, reader); } break; } case Packet.ResponseDestroy: { if (onDestroy != null) { int count = reader.ReadUInt16(); for (int i = 0; i < count; ++i) { onDestroy(reader.ReadUInt32()); } } break; } case Packet.Error: { if (mTcp.stage != TcpProtocol.Stage.Connected && onConnect != null) { onConnect(false, reader.ReadString()); } else if (onError != null) { onError(reader.ReadString()); } break; } case Packet.Disconnect: { mData = ""; if (isInChannel && onLeftChannel != null) { onLeftChannel(); } players.Clear(); mDictionary.Clear(); mTcp.Close(false); if (onDisconnect != null) { onDisconnect(); } break; } default: { OnPacket callback; if (packetHandlers.TryGetValue((byte)response, out callback)) { if (callback != null) { callback(response, reader, ip); } } break; } } return(true); }
/// <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> /// Load the channel's data from the specified file. /// </summary> public bool LoadFrom(BinaryReader reader) { int version = reader.ReadInt32(); if (version != Player.version) { return(false); } // Clear all RFCs, just in case for (int i = 0; i < rfcs.size; ++i) { RFC r = rfcs[i]; if (r.buffer != null) { r.buffer.Recycle(); } } rfcs.Clear(); created.Clear(); destroyed.Clear(); level = reader.ReadString(); data = reader.ReadString(); objectCounter = reader.ReadUInt32(); password = reader.ReadString(); persistent = reader.ReadBoolean(); playerLimit = reader.ReadUInt16(); int size = reader.ReadInt32(); for (int i = 0; i < size; ++i) { RFC rfc = new RFC(); rfc.uid = reader.ReadUInt32(); if (rfc.functionID == 0) { rfc.functionName = reader.ReadString(); } Buffer b = Buffer.Create(); b.BeginWriting(false).Write(reader.ReadBytes(reader.ReadInt32())); rfc.buffer = b; rfcs.Add(rfc); } size = reader.ReadInt32(); for (int i = 0; i < size; ++i) { CreatedObject co = new CreatedObject(); co.playerID = reader.ReadInt32(); co.uniqueID = reader.ReadUInt32(); co.objectID = reader.ReadUInt16(); co.type = 1; Buffer b = Buffer.Create(); b.BeginWriting(false).Write(reader.ReadBytes(reader.ReadInt32())); b.BeginReading(); co.buffer = b; created.Add(co); } size = reader.ReadInt32(); for (int i = 0; i < size; ++i) { destroyed.Add(reader.ReadUInt32()); } return(true); }
/// <summary> /// Process an incoming packet. /// </summary> bool ProcessPacket(Buffer buffer, IPEndPoint ip) { BinaryReader reader = buffer.BeginReading(); Packet request = (Packet)reader.ReadByte(); switch (request) { case Packet.RequestPing: { BeginSend(Packet.ResponsePing); EndSend(ip); break; } case Packet.RequestAddServer: { if (reader.ReadUInt16() != GameServer.gameID) { return(false); } ServerList.Entry ent = new ServerList.Entry(); ent.ReadFrom(reader); if (ent.externalAddress.Address.Equals(IPAddress.None)) { ent.externalAddress = ip; } mList.Add(ent, mTime); #if STANDALONE Tools.Print(ip + " added a server (" + ent.internalAddress + ", " + ent.externalAddress + ")"); #endif return(true); } case Packet.RequestRemoveServer: { if (reader.ReadUInt16() != GameServer.gameID) { return(false); } IPEndPoint internalAddress, externalAddress; Tools.Serialize(reader, out internalAddress); Tools.Serialize(reader, out externalAddress); if (externalAddress.Address.Equals(IPAddress.None)) { externalAddress = ip; } RemoveServer(internalAddress, externalAddress); #if STANDALONE Tools.Print(ip + " removed a server (" + internalAddress + ", " + externalAddress + ")"); #endif return(true); } case Packet.RequestServerList: { if (reader.ReadUInt16() != GameServer.gameID) { return(false); } mList.WriteTo(BeginSend(Packet.ResponseServerList)); EndSend(ip); return(true); } } return(false); }
/// <summary> /// Receive and process a single incoming packet. /// Returns 'true' if a packet was received, 'false' otherwise. /// </summary> bool ProcessPlayerPacket(Buffer buffer, TcpPlayer player, bool reliable) { BinaryReader reader = buffer.BeginReading(); Packet request = (Packet)reader.ReadByte(); //#if UNITY_EDITOR // DEBUG // if (request != Packet.RequestPing) UnityEngine.Debug.Log("Server: " + request + " " + buffer.position + " " + buffer.size); //#else // if (request != Packet.RequestPing) Console.WriteLine("Server: " + request + " " + buffer.position + " " + buffer.size); //#endif // If the player has not yet been verified, the first packet must be an ID request if (player.stage == TcpProtocol.Stage.Verifying) { if (player.VerifyRequestID(request, reader, true)) { mDictionaryID.Add(player.id, player); if (lobbyLink != null) { lobbyLink.SendUpdate(this); } if (onPlayerConnect != null) { onPlayerConnect(player); } return(true); } #if STANDALONE Console.WriteLine(player.address + " has failed the verification step"); #endif RemovePlayer(player); return(false); } switch (request) { case Packet.Empty: { break; } case Packet.Error: { Error(player, reader.ReadString()); break; } case Packet.Disconnect: { RemovePlayer(player); break; } case Packet.RequestPing: { // Respond with a ping back BeginSend(Packet.ResponsePing); EndSend(true, player); break; } case Packet.RequestSetUDP: { int port = reader.ReadUInt16(); if (port != 0) { IPAddress ip = new IPAddress(player.tcpEndPoint.Address.GetAddressBytes()); SetPlayerUdpEndPoint(player, new IPEndPoint(ip, port)); } else { SetPlayerUdpEndPoint(player, null); } // Let the player know if we are hosting an active UDP connection ushort udp = mUdp.isActive ? (ushort)mUdp.listeningPort : (ushort)0; BeginSend(Packet.ResponseSetUDP).Write(udp); EndSend(true, player); // Send an empty packet to the target player to open up UDP for communication if (player.udpEndPoint != null) { mUdp.SendEmptyPacket(player.udpEndPoint); } break; } case Packet.RequestJoinChannel: { // Join the specified channel int channelID = reader.ReadInt32(); string pass = reader.ReadString(); string levelName = reader.ReadString(); bool persist = reader.ReadBoolean(); ushort playerLimit = reader.ReadUInt16(); // Join a random existing channel or create a new one if (channelID == -2) { bool randomLevel = string.IsNullOrEmpty(levelName); channelID = -1; for (int i = 0; i < mChannels.size; ++i) { Channel ch = mChannels[i]; if (ch.isOpen && (randomLevel || levelName.Equals(ch.level)) && (string.IsNullOrEmpty(ch.password) || (ch.password == pass))) { channelID = ch.id; break; } } // If no level name has been specified and no channels were found, we're done if (randomLevel && channelID == -1) { BinaryWriter writer = BeginSend(Packet.ResponseJoinChannel); writer.Write(false); writer.Write("No suitable channels found"); EndSend(true, player); break; } } // Join a random new channel if (channelID == -1) { channelID = mRandom.Next(100000000); for (int i = 0; i < 1000; ++i) { if (!ChannelExists(channelID)) { break; } channelID = mRandom.Next(100000000); } } if (player.channel == null || player.channel.id != channelID) { bool isNew; Channel channel = CreateChannel(channelID, out isNew); if (channel == null || !channel.isOpen) { BinaryWriter writer = BeginSend(Packet.ResponseJoinChannel); writer.Write(false); writer.Write("The requested channel is closed"); EndSend(true, player); } else if (isNew) { channel.password = pass; channel.persistent = persist; channel.level = levelName; channel.playerLimit = playerLimit; SendLeaveChannel(player, false); SendJoinChannel(player, channel); } else if (string.IsNullOrEmpty(channel.password) || (channel.password == pass)) { SendLeaveChannel(player, false); SendJoinChannel(player, channel); } else { BinaryWriter writer = BeginSend(Packet.ResponseJoinChannel); writer.Write(false); writer.Write("Wrong password"); EndSend(true, player); } } break; } case Packet.RequestSetName: { // Change the player's name player.name = reader.ReadString(); BinaryWriter writer = BeginSend(Packet.ResponseRenamePlayer); writer.Write(player.id); writer.Write(player.name); if (player.channel != null) { EndSend(true, player.channel, null); } else { EndSend(true, player); } break; } case Packet.RequestSaveFile: { string fileName = reader.ReadString(); byte[] data = reader.ReadBytes(reader.ReadInt32()); SaveFile(fileName, data); break; } case Packet.RequestLoadFile: { string fn = reader.ReadString(); byte[] data = LoadFile(fn); BinaryWriter writer = BeginSend(Packet.ResponseLoadFile); writer.Write(fn); if (data != null) { writer.Write(data.Length); writer.Write(data); } else { writer.Write(0); } EndSend(true, player); break; } case Packet.RequestDeleteFile: { DeleteFile(reader.ReadString()); break; } case Packet.RequestNoDelay: { player.noDelay = reader.ReadBoolean(); break; } case Packet.RequestChannelList: { BinaryWriter writer = BeginSend(Packet.ResponseChannelList); int count = 0; for (int i = 0; i < mChannels.size; ++i) { if (!mChannels[i].closed) { ++count; } } writer.Write(count); for (int i = 0; i < mChannels.size; ++i) { Channel ch = mChannels[i]; if (!ch.closed) { writer.Write(ch.id); writer.Write((ushort)ch.players.size); writer.Write(ch.playerLimit); writer.Write(!string.IsNullOrEmpty(ch.password)); writer.Write(ch.persistent); writer.Write(ch.level); writer.Write(ch.data); } } EndSend(true, player); break; } case Packet.ForwardToPlayer: { // Forward this packet to the specified player TcpPlayer target = GetPlayer(reader.ReadInt32()); if (target != null && target.isConnected) { // Reset the position back to the beginning (4 bytes for size, 1 byte for ID, 4 bytes for player) buffer.position = buffer.position - 9; target.SendTcpPacket(buffer); } break; } default: { if (player.channel != null && (int)request <= (int)Packet.ForwardToPlayerBuffered) { // Other packets can only be processed while in a channel if ((int)request >= (int)Packet.ForwardToAll) { ProcessForwardPacket(player, buffer, reader, request, reliable); } else { ProcessChannelPacket(player, buffer, reader, request); } } else if (onCustomPacket != null) { onCustomPacket(player, buffer, reader, request, reliable); } break; } } return(true); }
/// <summary> /// Process an incoming packet. /// </summary> bool ProcessPacket(Buffer buffer, TcpProtocol tc) { BinaryReader reader = buffer.BeginReading(); Packet request = (Packet)reader.ReadByte(); // TCP connections must be verified first to ensure that they are using the correct protocol if (tc.stage == TcpProtocol.Stage.Verifying) { if (tc.VerifyRequestID(request, reader, false)) return true; #if STANDALONE Console.WriteLine(tc.address + " has failed the verification step"); #endif return false; } switch (request) { case Packet.RequestServerList: { if (reader.ReadUInt16() != GameServer.gameID) return false; tc.data = (long)0; return true; } case Packet.RequestAddServer: { if (reader.ReadUInt16() != GameServer.gameID) return false; ServerList.Entry ent = new ServerList.Entry(); ent.ReadFrom(reader); if (ent.externalAddress.Address.Equals(IPAddress.None)) ent.externalAddress = tc.tcpEndPoint; mList.Add(ent, mTime).data = tc; mLastChange = mTime; #if STANDALONE Console.WriteLine(tc.address + " added a server (" + ent.internalAddress + ", " + ent.externalAddress + ")"); #endif return true; } case Packet.RequestRemoveServer: { if (reader.ReadUInt16() != GameServer.gameID) return false; IPEndPoint internalAddress, externalAddress; Tools.Serialize(reader, out internalAddress); Tools.Serialize(reader, out externalAddress); if (externalAddress.Address.Equals(IPAddress.None)) externalAddress = tc.tcpEndPoint; RemoveServer(internalAddress, externalAddress); #if STANDALONE Console.WriteLine(tc.address + " removed a server (" + internalAddress + ", " + externalAddress + ")"); #endif return true; } case Packet.Disconnect: { #if STANDALONE if (RemoveServer(tc)) Console.WriteLine(tc.address + " has disconnected"); #else RemoveServer(tc); #endif mTcp.Remove(tc); return true; } case Packet.RequestSaveFile: { string fileName = reader.ReadString(); byte[] data = reader.ReadBytes(reader.ReadInt32()); SaveFile(fileName, data); break; } case Packet.RequestLoadFile: { string fn = reader.ReadString(); byte[] data = LoadFile(fn); BinaryWriter writer = BeginSend(Packet.ResponseLoadFile); writer.Write(fn); if (data != null) { writer.Write(data.Length); writer.Write(data); } else writer.Write(0); EndSend(tc); break; } case Packet.RequestDeleteFile: { DeleteFile(reader.ReadString()); break; } case Packet.Error: { #if STANDALONE Console.WriteLine(tc.address + " error: " + reader.ReadString()); #endif return false; } } #if STANDALONE Console.WriteLine(tc.address + " sent a packet not handled by the lobby server: " + request); #endif return false; }
/// <summary> /// Process a single incoming packet. Returns whether we should keep processing packets or not. /// </summary> bool ProcessPacket(Buffer buffer, IPEndPoint ip) { mPacketSource = ip; BinaryReader reader = buffer.BeginReading(); if (buffer.size == 0) { return(true); } int packetID = reader.ReadByte(); Packet response = (Packet)packetID; #if DEBUG_PACKETS && !STANDALONE if (response != Packet.ResponsePing && response != Packet.Broadcast) { UnityEngine.Debug.Log("Client: " + response + " (" + buffer.size + " bytes) " + ((ip == null) ? "(TCP)" : "(UDP)")); } #endif // Verification step must be passed first if (response == Packet.ResponseID || mTcp.stage == TcpProtocol.Stage.Verifying) { if (mTcp.VerifyResponseID(response, reader)) { mTimeDifference = reader.ReadInt64() - (System.DateTime.UtcNow.Ticks / 10000); #if !UNITY_WEBPLAYER if (mUdp.isActive) { // If we have a UDP listener active, tell the server BeginSend(Packet.RequestSetUDP).Write((ushort)mUdp.listeningPort); EndSend(); } #endif mCanPing = true; if (onConnect != null) { onConnect(true, null); } } return(true); } OnPacket callback; if (packetHandlers.TryGetValue((byte)response, out callback) && callback != null) { callback(response, reader, ip); return(true); } switch (response) { case Packet.Empty: break; case Packet.ForwardToAll: case Packet.ForwardToOthers: case Packet.ForwardToAllSaved: case Packet.ForwardToOthersSaved: case Packet.ForwardToHost: case Packet.BroadcastAdmin: case Packet.Broadcast: { packetSourceID = reader.ReadInt32(); int channelID = reader.ReadInt32(); if (onForwardedPacket != null) { onForwardedPacket(channelID, reader); } break; } case Packet.ForwardToPlayer: { packetSourceID = reader.ReadInt32(); reader.ReadInt32(); // Skip the target player ID int channelID = reader.ReadInt32(); if (onForwardedPacket != null) { onForwardedPacket(channelID, reader); } break; } case Packet.ForwardByName: { packetSourceID = reader.ReadInt32(); reader.ReadString(); // Skip the player name int channelID = reader.ReadInt32(); if (onForwardedPacket != null) { onForwardedPacket(channelID, reader); } break; } case Packet.ResponseSetPlayerData: { int pid = reader.ReadInt32(); Player target = GetPlayer(pid); if (target != null) { string path = reader.ReadString(); DataNode node = target.Set(path, reader.ReadObject()); if (onSetPlayerData != null) { onSetPlayerData(target, path, node); } } else { UnityEngine.Debug.LogError("Not found: " + pid); } break; } case Packet.ResponsePing: { int ping = (int)(mMyTime - mPingTime); if (ip != null) { if (onPing != null && ip != null) { onPing(ip, ping); } } else { mCanPing = true; mPing = ping; } break; } case Packet.ResponseSetUDP: { #if !UNITY_WEBPLAYER // The server has a new port for UDP traffic ushort port = reader.ReadUInt16(); if (port != 0 && mTcp.tcpEndPoint != null) { IPAddress ipa = new IPAddress(mTcp.tcpEndPoint.Address.GetAddressBytes()); mServerUdpEndPoint = new IPEndPoint(ipa, port); // Send the first UDP packet to the server if (mUdp.isActive) { mBuffer = Buffer.Create(); mBuffer.BeginPacket(Packet.RequestActivateUDP).Write(playerID); mBuffer.EndPacket(); mUdp.Send(mBuffer, mServerUdpEndPoint); mBuffer.Recycle(); mBuffer = null; } } else { mServerUdpEndPoint = null; } #endif break; } case Packet.ResponseJoiningChannel: { int channelID = reader.ReadInt32(); int count = reader.ReadInt16(); Channel ch = GetChannel(channelID, true); for (int i = 0; i < count; ++i) { int pid = reader.ReadInt32(); Player p = GetPlayer(pid, true); if (reader.ReadBoolean()) { p.name = reader.ReadString(); p.dataNode = reader.ReadDataNode(); } ch.players.Add(p); } break; } case Packet.ResponseLoadLevel: { // Purposely return after loading a level, ensuring that all future callbacks happen after loading int channelID = reader.ReadInt32(); string scene = reader.ReadString(); if (onLoadLevel != null) { onLoadLevel(channelID, scene); } return(false); } case Packet.ResponsePlayerJoined: { int channelID = reader.ReadInt32(); Channel ch = GetChannel(channelID); if (ch != null) { Player p = GetPlayer(reader.ReadInt32(), true); if (reader.ReadBoolean()) { p.name = reader.ReadString(); p.dataNode = reader.ReadDataNode(); } ch.players.Add(p); if (onPlayerJoin != null) { onPlayerJoin(channelID, p); } } break; } case Packet.ResponsePlayerLeft: { int channelID = reader.ReadInt32(); int playerID = reader.ReadInt32(); Channel ch = GetChannel(channelID); if (ch != null) { Player p = ch.GetPlayer(playerID); ch.players.Remove(p); RebuildPlayerDictionary(); if (onPlayerLeave != null) { onPlayerLeave(channelID, p); } } break; } case Packet.ResponseSetHost: { int channelID = reader.ReadInt32(); int hostID = reader.ReadInt32(); for (int i = 0; i < mChannels.size; ++i) { Channel ch = mChannels[i]; if (ch.id == channelID) { ch.host = GetPlayer(hostID); if (onHostChanged != null) { onHostChanged(ch); } break; } } break; } case Packet.ResponseSetChannelData: { int channelID = reader.ReadInt32(); Channel ch = GetChannel(channelID); if (ch != null) { string path = reader.ReadString(); DataNode node = ch.Set(path, reader.ReadObject()); if (onSetChannelData != null) { onSetChannelData(ch, path, node); } } break; } case Packet.ResponseJoinChannel: { int channelID = reader.ReadInt32(); bool success = reader.ReadBoolean(); string msg = success ? null : reader.ReadString(); // mJoining can contain -2 and -1 when joining random channels if (!mJoining.Remove(channelID)) { for (int i = 0; i < mJoining.size; ++i) { int id = mJoining[i]; if (id < 0) { mJoining.RemoveAt(i); break; } } } #if UNITY_EDITOR if (!success) { UnityEngine.Debug.LogError("ResponseJoinChannel: " + success + ", " + msg); } #endif if (onJoinChannel != null) { onJoinChannel(channelID, success, msg); } break; } case Packet.ResponseLeaveChannel: { int channelID = reader.ReadInt32(); for (int i = 0; i < mChannels.size; ++i) { Channel ch = mChannels[i]; if (ch.id == channelID) { mChannels.RemoveAt(i); break; } } RebuildPlayerDictionary(); if (onLeaveChannel != null) { onLeaveChannel(channelID); } // Purposely exit after receiving a "left channel" notification so that other packets get handled in the next frame. return(false); } case Packet.ResponseRenamePlayer: { Player p = GetPlayer(reader.ReadInt32()); string oldName = p.name; if (p != null) { p.name = reader.ReadString(); } if (onRenamePlayer != null) { onRenamePlayer(p, oldName); } break; } case Packet.ResponseCreateObject: { if (onCreate != null) { int playerID = reader.ReadInt32(); int channelID = reader.ReadInt32(); uint objID = reader.ReadUInt32(); onCreate(channelID, playerID, objID, reader); } break; } case Packet.ResponseDestroyObject: { if (onDestroy != null) { int channelID = reader.ReadInt32(); int count = reader.ReadUInt16(); for (int i = 0; i < count; ++i) { uint val = reader.ReadUInt32(); onDestroy(channelID, val); } } break; } case Packet.ResponseTransferObject: { if (onTransfer != null) { int from = reader.ReadInt32(); int to = reader.ReadInt32(); uint id0 = reader.ReadUInt32(); uint id1 = reader.ReadUInt32(); onTransfer(from, to, id0, id1); } break; } case Packet.Error: { string err = reader.ReadString(); if (onError != null) { onError(err); } if (mTcp.stage != TcpProtocol.Stage.Connected && onConnect != null) { onConnect(false, err); } break; } case Packet.Disconnect: { if (onLeaveChannel != null) { while (mChannels.size > 0) { int index = mChannels.size - 1; Channel ch = mChannels[index]; mChannels.RemoveAt(index); onLeaveChannel(ch.id); } } mChannels.Clear(); mGetChannelsCallbacks.Clear(); mDictionary.Clear(); mTcp.Close(false); mLoadFiles.Clear(); mGetFiles.Clear(); mJoining.Clear(); mIsAdmin = false; if (mLocalServer != null) { mLocalServer.localClient = null; mLocalServer = null; } if (onDisconnect != null) { onDisconnect(); } mConfig = new DataNode("Version", Player.version); break; } case Packet.ResponseGetFileList: { string filename = reader.ReadString(); int size = reader.ReadInt32(); string[] files = null; if (size > 0) { files = new string[size]; for (int i = 0; i < size; ++i) { files[i] = reader.ReadString(); } } OnGetFiles cb = null; if (mGetFiles.TryGetValue(filename, out cb)) { mGetFiles.Remove(filename); } if (cb != null) { try { cb(filename, files); } #if UNITY_EDITOR catch (System.Exception ex) { Debug.LogError(ex.Message + ex.StackTrace); } #else catch (System.Exception) {} #endif } break; } case Packet.ResponseLoadFile: { string filename = reader.ReadString(); int size = reader.ReadInt32(); byte[] data = reader.ReadBytes(size); OnLoadFile cb = null; if (mLoadFiles.TryGetValue(filename, out cb)) { mLoadFiles.Remove(filename); } if (cb != null) { try { cb(filename, data); } #if UNITY_EDITOR catch (System.Exception ex) { Debug.LogError(ex.Message + ex.StackTrace); } #else catch (System.Exception) {} #endif } break; } case Packet.ResponseVerifyAdmin: { int pid = reader.ReadInt32(); Player p = GetPlayer(pid); if (p == player) { mIsAdmin = true; } if (onSetAdmin != null) { onSetAdmin(p); } break; } case Packet.ResponseSetServerData: { string path = reader.ReadString(); object obj = reader.ReadObject(); if (obj != null) { DataNode node = mConfig.SetHierarchy(path, obj); if (onSetServerData != null) { onSetServerData(path, node); } } else { DataNode node = mConfig.RemoveHierarchy(path); if (onSetServerData != null) { onSetServerData(path, node); } } break; } case Packet.ResponseChannelList: { if (mGetChannelsCallbacks.Count != 0) { OnGetChannels cb = mGetChannelsCallbacks.Dequeue(); List <Channel.Info> channels = new List <Channel.Info>(); int count = reader.ReadInt32(); for (int i = 0; i < count; ++i) { Channel.Info info = new Channel.Info(); info.id = reader.ReadInt32(); info.players = reader.ReadUInt16(); info.limit = reader.ReadUInt16(); info.hasPassword = reader.ReadBoolean(); info.isPersistent = reader.ReadBoolean(); info.level = reader.ReadString(); info.data = reader.ReadDataNode(); channels.Add(info); } if (cb != null) { cb(channels); } } break; } case Packet.ResponseLockChannel: { int channelID = reader.ReadInt32(); bool isLocked = reader.ReadBoolean(); Channel ch = GetChannel(channelID); if (ch != null) { ch.isLocked = isLocked; } if (onLockChannel != null) { onLockChannel(channelID, isLocked); } break; } } return(true); }
/// <summary> /// See if the received packet can be processed and split it up into different ones. /// </summary> bool ProcessBuffer(byte[] bytes, int offset, int byteCount) { if (offset + byteCount > bytes.Length) { LogError("ProcessBuffer(" + bytes.Length + " bytes, offset " + offset + ", count " + byteCount); return(false); } if (mReceiveBuffer == null) { // Create a new packet buffer mReceiveBuffer = Buffer.Create(); mReceiveBuffer.BeginWriting(false).Write(bytes, offset, byteCount); mExpected = 0; mOffset = 0; } else { // Append this data to the end of the last used buffer mReceiveBuffer.BeginWriting(true).Write(bytes, offset, byteCount); } for (mAvailable = mReceiveBuffer.size - mOffset; mAvailable > 4;) { // Figure out the expected size of the packet if (mExpected == 0) { mExpected = mReceiveBuffer.PeekInt(mOffset); // "GET " -- HTTP GET request sent by a web browser if (mExpected == 542393671) { if (httpGetSupport) { if (stage == Stage.Verifying || stage == Stage.WebBrowser) { stage = Stage.WebBrowser; string request = Encoding.ASCII.GetString(mReceiveBuffer.buffer, mOffset, mAvailable); mReceiveBuffer.BeginPacket(Packet.RequestHTTPGet).Write(request); mReceiveBuffer.EndPacket(); mReceiveBuffer.BeginReading(4); lock (mIn) { mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; } } return(true); } mReceiveBuffer.Recycle(); mReceiveBuffer = null; mExpected = 0; mOffset = 0; Disconnect(); return(false); } else if (mExpected < 0 || mExpected > 16777216) { #if UNITY_EDITOR LogError("Malformed data packet: " + mOffset + ", " + mAvailable + " / " + mExpected); var temp = new byte[mReceiveBuffer.size]; for (int i = 0; i < byteCount; ++i) { temp[i] = mReceiveBuffer.buffer[i]; } var fn = "error_" + lastReceivedTime + ".full"; Tools.WriteFile(fn, temp); Debug.Log("Packet saved as " + fn); #else LogError("Malformed data packet: " + mOffset + ", " + mAvailable + " / " + mExpected); #endif mReceiveBuffer.Recycle(); mReceiveBuffer = null; mExpected = 0; mOffset = 0; Disconnect(); return(false); } } // The first 4 bytes of any packet always contain the number of bytes in that packet mAvailable -= 4; // If the entire packet is present if (mAvailable == mExpected) { // Reset the position to the beginning of the packet mReceiveBuffer.BeginReading(mOffset + 4); // This packet is now ready to be processed lock (mIn) { mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; } break; } else if (mAvailable > mExpected) { // There is more than one packet. Extract this packet fully. int realSize = mExpected + 4; var temp = Buffer.Create(); // Extract the packet and move past its size component var bw = temp.BeginWriting(); bw.Write(mReceiveBuffer.buffer, mOffset, realSize); temp.BeginReading(4); // This packet is now ready to be processed lock (mIn) { mIn.Enqueue(temp); // Skip this packet mAvailable -= mExpected; mOffset += realSize; mExpected = 0; } } else { break; } } return(true); }
/// <summary> /// Send the specified packet. Marks the buffer as used. /// </summary> public void SendTcpPacket(Buffer buffer, bool instant = false) { #if !MODDING buffer.MarkAsUsed(); var reader = buffer.BeginReading(); if (buffer.size == 0) { #if UNITY_EDITOR Debug.LogError("Trying to send a zero packet! " + buffer.position + " " + buffer.isWriting + " " + id); #endif buffer.Recycle(); return; } #if DEBUG_PACKETS && !STANDALONE var 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 (custom != null) { if (!custom.SendPacket(buffer)) { buffer.Recycle(); Disconnect(); } else { buffer.Recycle(); } return; } if (mSocket != null && mSocket.Connected) { lock (mOut) { #if UNITY_WINRT mSocket.Send(buffer.buffer, buffer.size, SocketFlags.None); #else if (instant) { try { var before = mSocket.NoDelay; if (!before) { mSocket.NoDelay = true; } mSocket.Send(buffer.buffer, buffer.position, buffer.size, SocketFlags.None); if (!before) { mSocket.NoDelay = false; } buffer.Recycle(); return; } catch { } } if (mSending) { // Simply add this packet to the outgoing queue mOut.Enqueue(buffer); } else { // If it's the first packet, let's begin the send process mSending = true; try { mSocket.BeginSend(buffer.buffer, buffer.position, buffer.size, SocketFlags.None, OnSend, buffer); } catch (System.Exception ex) { mOut.Clear(); buffer.Recycle(); AddError(ex); CloseNotThreadSafe(false); mSending = false; } } #endif } 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) { lock (sendQueue) sendQueue.Enqueue(buffer); return; } // Multi-part packet -- split it up into separate ones lock (sendQueue) { for (;;) { var bytes = reader.ReadBytes(size); var temp = Buffer.Create(); var writer = temp.BeginWriting(); writer.Write(size); writer.Write(bytes); temp.BeginReading(4); sendQueue.Enqueue(temp); if (buffer.size > 0) { size = reader.ReadInt32(); } else { break; } } } } #endif buffer.Recycle(); }
/// <summary> /// See if the received packet can be processed and split it up into different ones. /// </summary> bool ProcessBuffer(int bytes) { if (mReceiveBuffer == null) { // Create a new packet buffer mReceiveBuffer = Buffer.Create(); mReceiveBuffer.BeginWriting(false).Write(mTemp, 0, bytes); } else { // Append this data to the end of the last used buffer mReceiveBuffer.BeginWriting(true).Write(mTemp, 0, bytes); } for (int available = mReceiveBuffer.size - mOffset; available >= 4;) { // Figure out the expected size of the packet if (mExpected == 0) { mExpected = mReceiveBuffer.PeekInt(mOffset); if (mExpected < 0 || mExpected > 16777216) { Close(true); return(false); } } // The first 4 bytes of any packet always contain the number of bytes in that packet available -= 4; // If the entire packet is present if (available == mExpected) { // Reset the position to the beginning of the packet mReceiveBuffer.BeginReading(mOffset + 4); // This packet is now ready to be processed lock (mIn) mIn.Enqueue(mReceiveBuffer); mReceiveBuffer = null; mExpected = 0; mOffset = 0; break; } else if (available > mExpected) { // There is more than one packet. Extract this packet fully. int realSize = mExpected + 4; Buffer temp = Buffer.Create(); // Extract the packet and move past its size component temp.BeginWriting(false).Write(mReceiveBuffer.buffer, mOffset, realSize); temp.BeginReading(4); // This packet is now ready to be processed lock (mIn) mIn.Enqueue(temp); // Skip this packet available -= mExpected; mOffset += realSize; mExpected = 0; } else { break; } } return(true); }
/// <summary> /// Process a single incoming packet. Returns whether we should keep processing packets or not. /// </summary> bool ProcessPacket(Buffer buffer, IPEndPoint ip) { mPacketSource = ip; BinaryReader reader = buffer.BeginReading(); if (buffer.size == 0) return true; int packetID = reader.ReadByte(); Packet response = (Packet)packetID; // Verification step must be passed first if (mTcp.stage == TcpProtocol.Stage.Verifying) { if (mTcp.VerifyResponseID(response, reader)) { #if !UNITY_WEBPLAYER if (mUdp.isActive) { // If we have a UDP listener active, tell the server BeginSend(Packet.RequestSetUDP).Write(mUdp.isActive ? (ushort)mUdp.listeningPort : (ushort)0); EndSend(); } #endif mCanPing = true; if (onConnect != null) onConnect(true, null); } else if (onConnect != null) { onConnect(false, "Protocol version mismatch!"); } return true; } //#if !UNITY_EDITOR // DEBUG // if (response != Packet.ResponsePing) Console.WriteLine("Client: " + response + " " + buffer.position + " of " + buffer.size + ((ip == null) ? " (TCP)" : " (UDP)")); //#else // if (response != Packet.ResponsePing) UnityEngine.Debug.Log("Client: " + response + " " + buffer.position + " of " + buffer.size + ((ip == null) ? " (TCP)" : " (UDP)")); //#endif switch (response) { case Packet.Empty: break; case Packet.ForwardToAll: case Packet.ForwardToOthers: case Packet.ForwardToAllSaved: case Packet.ForwardToOthersSaved: case Packet.ForwardToHost: case Packet.Broadcast: { if (onForwardedPacket != null) onForwardedPacket(reader); break; } case Packet.ForwardToPlayer: { // Skip the player ID reader.ReadInt32(); if (onForwardedPacket != null) onForwardedPacket(reader); break; } case Packet.ResponsePing: { mPing = (int)(mTime - mPingTime); mCanPing = true; break; } case Packet.ResponseSetUDP: { #if !UNITY_WEBPLAYER // The server has a new port for UDP traffic ushort port = reader.ReadUInt16(); if (port != 0) { IPAddress ipa = new IPAddress(mTcp.tcpEndPoint.Address.GetAddressBytes()); mServerUdpEndPoint = new IPEndPoint(ipa, port); // Send an empty packet to the server, opening up the communication channel if (mUdp.isActive) mUdp.SendEmptyPacket(mServerUdpEndPoint); } else mServerUdpEndPoint = null; #endif break; } case Packet.ResponseJoiningChannel: { mIsInChannel = true; mDictionary.Clear(); players.Clear(); mChannelID = reader.ReadInt32(); int count = reader.ReadInt16(); for (int i = 0; i < count; ++i) { Player p = new Player(); p.id = reader.ReadInt32(); p.name = reader.ReadString(); mDictionary.Add(p.id, p); players.Add(p); } break; } case Packet.ResponseLoadLevel: { // Purposely return after loading a level, ensuring that all future callbacks happen after loading if (onLoadLevel != null) onLoadLevel(reader.ReadString()); return false; } case Packet.ResponsePlayerLeft: { Player p = GetPlayer(reader.ReadInt32()); if (p != null) mDictionary.Remove(p.id); players.Remove(p); if (onPlayerLeft != null) onPlayerLeft(p); break; } case Packet.ResponsePlayerJoined: { Player p = new Player(); p.id = reader.ReadInt32(); p.name = reader.ReadString(); mDictionary.Add(p.id, p); players.Add(p); if (onPlayerJoined != null) onPlayerJoined(p); break; } case Packet.ResponseSetHost: { mHost = reader.ReadInt32(); if (onSetHost != null) onSetHost(isHosting); break; } case Packet.ResponseSetChannelData: { mData = reader.ReadString(); if (onSetChannelData != null) onSetChannelData(mData); break; } case Packet.ResponseJoinChannel: { mIsInChannel = reader.ReadBoolean(); if (onJoinChannel != null) onJoinChannel(mIsInChannel, mIsInChannel ? null : reader.ReadString()); break; } case Packet.ResponseLeaveChannel: { mData = ""; mChannelID = 0; mIsInChannel = false; mDictionary.Clear(); players.Clear(); if (onLeftChannel != null) onLeftChannel(); break; } case Packet.ResponseRenamePlayer: { Player p = GetPlayer(reader.ReadInt32()); string oldName = p.name; if (p != null) p.name = reader.ReadString(); if (onRenamePlayer != null) onRenamePlayer(p, oldName); break; } case Packet.ResponseCreate: { if (onCreate != null) { int playerID = reader.ReadInt32(); ushort index = reader.ReadUInt16(); uint objID = reader.ReadUInt32(); onCreate(playerID, index, objID, reader); } break; } case Packet.ResponseDestroy: { if (onDestroy != null) { int count = reader.ReadUInt16(); for (int i = 0; i < count; ++i) onDestroy(reader.ReadUInt32()); } break; } case Packet.Error: { if (mTcp.stage != TcpProtocol.Stage.Connected && onConnect != null) { onConnect(false, reader.ReadString()); } else if (onError != null) { onError(reader.ReadString()); } break; } case Packet.Disconnect: { mData = ""; if (isInChannel && onLeftChannel != null) onLeftChannel(); players.Clear(); mDictionary.Clear(); mTcp.Close(false); if (onDisconnect != null) onDisconnect(); break; } default: { OnPacket callback; if (packetHandlers.TryGetValue((byte)response, out callback)) { if (callback != null) { callback(response, reader, ip); } } break; } } return true; }