/// <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 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); }