Ejemplo n.º 1
0
        private static void SendCharacterCreateResponse(Session session, uint responseCode, uint guid = 0, string charName = null)
        {
            var charCreateResponse = new ServerPacket(0x0B, PacketHeaderFlags.EncryptedChecksum);
            var charCreateFragment = new ServerPacketFragment(9, FragmentOpcode.CharacterCreateResponse);

            if (responseCode == 1)
            {
                charCreateFragment.Payload.Write(responseCode);
                charCreateFragment.Payload.Write(guid);
                charCreateFragment.Payload.WriteString16L(charName);
                charCreateFragment.Payload.Write(0u);
            }
            else
            {
                charCreateFragment.Payload.Write(responseCode);
            }
            charCreateResponse.Fragments.Add(charCreateFragment);
            NetworkManager.SendPacket(ConnectionType.Login, charCreateResponse, session);
        }
Ejemplo n.º 2
0
        public static void CharacterEnterWorld(ClientPacketFragment fragment, Session session)
        {
            ObjectGuid guid    = fragment.Payload.ReadGuid();
            string     account = fragment.Payload.ReadString16L();

            if (account != session.Account)
            {
                session.SendCharacterError(CharacterError.EnterGameCharacterNotOwned);
                return;
            }

            var cachedCharacter = session.CachedCharacters.SingleOrDefault(c => c.Guid.Full == guid.Full);

            if (cachedCharacter == null)
            {
                session.SendCharacterError(CharacterError.EnterGameCharacterNotOwned);
                return;
            }

            session.CharacterRequested = cachedCharacter;

            // this isn't really that necessary since ACE doesn't split login/world to multiple daemons, handle it anyway
            byte[] connectionKey = new byte[sizeof(ulong)];
            RandomNumberGenerator.Create().GetNonZeroBytes(connectionKey);

            session.WorldConnectionKey = BitConverter.ToUInt64(connectionKey, 0);

            var referralPacket = new ServerPacket(0x0B, PacketHeaderFlags.EncryptedChecksum | PacketHeaderFlags.Referral);

            referralPacket.Payload.Write(session.WorldConnectionKey);
            referralPacket.Payload.Write((ushort)2);
            referralPacket.Payload.WriteUInt16BE((ushort)ConfigManager.Config.Server.Network.WorldPort);
            referralPacket.Payload.Write(ConfigManager.Host);
            referralPacket.Payload.Write(0ul);
            referralPacket.Payload.Write((ushort)0x18);
            referralPacket.Payload.Write((ushort)0);
            referralPacket.Payload.Write(0u);

            NetworkManager.SendPacket(ConnectionType.Login, referralPacket, session);

            session.State = SessionState.WorldLoginRequest;
        }
Ejemplo n.º 3
0
        private static void AccountSelectCallback(Account account, Session session)
        {
            var connectResponse = new ServerPacket(0x0B, PacketHeaderFlags.ConnectRequest);

            connectResponse.Payload.Write(0u);
            connectResponse.Payload.Write(0u);
            connectResponse.Payload.Write(13626398284849559039ul); // some sort of check value?
            connectResponse.Payload.Write((ushort)0);
            connectResponse.Payload.Write((ushort)0);
            connectResponse.Payload.Write(ISAAC.ServerSeed);
            connectResponse.Payload.Write(ISAAC.ClientSeed);
            connectResponse.Payload.Write(0u);

            NetworkManager.SendPacket(ConnectionType.Login, connectResponse, session);

            if (account == null)
            {
                session.SendCharacterError(CharacterError.AccountDoesntExist);
                return;
            }

            if (WorldManager.Find(account.Name) != null)
            {
                session.SendCharacterError(CharacterError.AccountInUse);
                return;
            }

            /*if (glsTicket != digest)
             * {
             * }*/

            /*if (WorldManager.ServerIsFull())
             * {
             *  session.SendCharacterError(CharacterError.LogonServerFull);
             *  return;
             * }*/

            // TODO: check for account bans

            session.SetAccount(account.AccountId, account.Name);
            session.State = SessionState.AuthConnectResponse;
        }
Ejemplo n.º 4
0
        public static void CharacterListSelectCallback(MySqlResult result, Session session)
        {
            var characterList     = new ServerPacket(0x0B, PacketHeaderFlags.EncryptedChecksum);
            var characterFragment = new ServerPacketFragment(9, FragmentOpcode.CharacterList);

            characterFragment.Payload.Write(0u);
            characterFragment.Payload.Write(result.Count);

            session.CachedCharacters.Clear();
            for (byte i = 0; i < result.Count; i++)
            {
                uint   lowGuid = result.Read <uint>(i, "guid");
                string name    = result.Read <string>(i, "name");

                characterFragment.Payload.Write(lowGuid);
                characterFragment.Payload.WriteString16L(name);

                ulong deleteTime = result.Read <ulong>(i, "deleteTime");
                characterFragment.Payload.Write(deleteTime != 0ul ? (uint)(WorldManager.GetUnixTime() - deleteTime) : 0u);

                session.CachedCharacters.Add(new CachedCharacter(lowGuid, i, name));
            }

            characterFragment.Payload.Write(0u);
            characterFragment.Payload.Write(11u /*slotCount*/);
            characterFragment.Payload.WriteString16L(session.Account);
            characterFragment.Payload.Write(0u /*useTurbineChat*/);
            characterFragment.Payload.Write(0u /*hasThroneOfDestiny*/);
            characterList.Fragments.Add(characterFragment);

            NetworkManager.SendPacket(ConnectionType.Login, characterList, session);

            var serverName         = new ServerPacket(0x0B, PacketHeaderFlags.EncryptedChecksum);
            var serverNameFragment = new ServerPacketFragment(9, FragmentOpcode.ServerName);

            serverNameFragment.Payload.Write(0u);
            serverNameFragment.Payload.Write(0u);
            serverNameFragment.Payload.WriteString16L(ConfigManager.Config.Server.WorldName);
            serverName.Fragments.Add(serverNameFragment);

            NetworkManager.SendPacket(ConnectionType.Login, serverName, session);
        }
Ejemplo n.º 5
0
        private void SendPacketRaw(ServerPacket packet)
        {
            Socket socket = SocketManager.GetSocket();

            if (packet.Header.Sequence == 0)
            {
                socket = SocketManager.GetSocket(0);
            }

            byte[] payload = packet.GetPayload();

            if (packetLog.IsDebugEnabled)
            {
                System.Net.IPEndPoint listenerEndpoint = (System.Net.IPEndPoint)socket.LocalEndPoint;
                StringBuilder         sb = new StringBuilder();
                sb.AppendLine(String.Format("[{5}] Sending Packet (Len: {0}) [{1}:{2}=>{3}:{4}]", payload.Length, listenerEndpoint.Address, listenerEndpoint.Port, session.EndPoint.Address, session.EndPoint.Port, session.SubscriptionId));
                sb.AppendLine(payload.BuildPacketString());
                packetLog.Debug(sb.ToString());
            }
            socket.SendTo(payload, session.EndPoint);
        }
Ejemplo n.º 6
0
        public static void CharacterRestore(ClientPacketFragment fragment, Session session)
        {
            ObjectGuid guid = fragment.Payload.ReadGuid();

            var cachedCharacter = session.CachedCharacters.SingleOrDefault(c => c.Guid.Full == guid.Full);

            if (cachedCharacter == null)
            {
                return;
            }

            DatabaseManager.Character.DeleteOrRestore(0, guid.Low);

            var characterRestore         = new ServerPacket(0x0B, PacketHeaderFlags.EncryptedChecksum);
            var characterRestoreFragment = new ServerPacketFragment(9, FragmentOpcode.CharacterRestoreResponse);

            characterRestoreFragment.Payload.Write(1u /* Verification OK flag */);
            characterRestoreFragment.Payload.WriteGuid(guid);
            characterRestoreFragment.Payload.WriteString16L(cachedCharacter.Name);
            characterRestoreFragment.Payload.Write(0u /* secondsGreyedOut */);
            characterRestore.Fragments.Add(characterRestoreFragment);

            NetworkManager.SendPacket(ConnectionType.Login, characterRestore, session);
        }
Ejemplo n.º 7
0
        private void FlushPackets()
        {
            while (packetQueue.Count > 0)
            {
                ServerPacket packet = packetQueue.Dequeue();

                if (packet.Header.HasFlag(PacketHeaderFlags.EncryptedChecksum) && ConnectionData.PacketSequence < 2)
                {
                    ConnectionData.PacketSequence = 2;
                }

                packet.Header.Sequence = ConnectionData.PacketSequence++;
                packet.Header.Id       = ServerId;
                packet.Header.Table    = 0x14;
                packet.Header.Time     = (ushort)ConnectionData.ServerTime;

                if (packet.Header.Sequence >= 2u)
                {
                    cachedPackets.TryAdd(packet.Header.Sequence, packet);
                }

                SendPacket(packet);
            }
        }
Ejemplo n.º 8
0
        public static void CharacterListSelectCallback(List <CachedCharacter> characters, Session session)
        {
            var characterList     = new ServerPacket(0x0B, PacketHeaderFlags.EncryptedChecksum);
            var characterFragment = new ServerPacketFragment(9, FragmentOpcode.CharacterList);

            characterFragment.Payload.Write(0u);
            characterFragment.Payload.Write(characters.Count);

            session.CachedCharacters.Clear();
            foreach (var character in characters)
            {
                characterFragment.Payload.Write(character.LowGuid);
                characterFragment.Payload.WriteString16L(character.Name);
                characterFragment.Payload.Write(character.DeleteTime != 0ul ? (uint)(WorldManager.GetUnixTime() - character.DeleteTime) : 0u);
                session.CachedCharacters.Add(character);
            }

            characterFragment.Payload.Write(0u);
            characterFragment.Payload.Write(11u /*slotCount*/);
            characterFragment.Payload.WriteString16L(session.Account);
            characterFragment.Payload.Write(0u /*useTurbineChat*/);
            characterFragment.Payload.Write(0u /*hasThroneOfDestiny*/);
            characterList.Fragments.Add(characterFragment);

            NetworkManager.SendPacket(ConnectionType.Login, characterList, session);

            var serverName         = new ServerPacket(0x0B, PacketHeaderFlags.EncryptedChecksum);
            var serverNameFragment = new ServerPacketFragment(9, FragmentOpcode.ServerName);

            serverNameFragment.Payload.Write(0u);
            serverNameFragment.Payload.Write(0u);
            serverNameFragment.Payload.WriteString16L(ConfigManager.Config.Server.WorldName);
            serverName.Fragments.Add(serverNameFragment);

            NetworkManager.SendPacket(ConnectionType.Login, serverName, session);
        }
Ejemplo n.º 9
0
        private void FlushPackets()
        {
            while (packetQueue.Count > 0)
            {
                ServerPacket packet = packetQueue.Dequeue();

                if (packet.Header.HasFlag(PacketHeaderFlags.EncryptedChecksum) && ConnectionData.PacketSequence < 2)
                {
                    ConnectionData.PacketSequence = 2;
                }

                packet.Header.Sequence = ConnectionData.PacketSequence++;
                packet.Header.Id       = 0x0B; // This value is currently the hard coded Server ID. It can be something different...
                packet.Header.Table    = 0x14;
                packet.Header.Time     = (ushort)ConnectionData.ServerTime;

                if (packet.Header.Sequence >= 2u)
                {
                    cachedPackets.TryAdd(packet.Header.Sequence, packet);
                }

                SendPacket(packet);
            }
        }
Ejemplo n.º 10
0
 public static void SendPacketDirect(ConnectionType type, ServerPacket packet, Session session)
 {
     GetSocket(type).SendTo(packet.Data.ToArray(), session.EndPoint);
 }
Ejemplo n.º 11
0
        /// <summary>
        /// This function handles turning a bundle of messages (representing all messages accrued in a timeslice),
        /// into 1 or more packets, combining multiple messages into one packet or spliting large message across
        /// several packets as needed.
        ///
        /// 1 Create Packet
        /// 2 If first packet for this bundle, fill any optional headers
        /// 3 Append messages that will fit
        /// 4 If we have more messages, create additional packets.
        /// 5 If any packet is greater than the max packet size, split it across two fragments
        /// </summary>
        /// <param name="bundle"></param>
        private void SendBundle(NetworkBundle bundle)
        {
            bool firstPacket = true;

            MessageFragment carryOverMessage = null;

            while (firstPacket || carryOverMessage != null || bundle.Messages.Count > 0)
            {
                ServerPacket packet       = new ServerPacket();
                PacketHeader packetHeader = packet.Header;

                if (bundle.EncryptedChecksum)
                {
                    packetHeader.Flags |= PacketHeaderFlags.EncryptedChecksum;
                }

                uint availableSpace = Packet.MaxPacketDataSize;

                if (firstPacket)
                {
                    firstPacket = false;

                    if (bundle.SendAck) //0x4000
                    {
                        packetHeader.Flags |= PacketHeaderFlags.AckSequence;
                        packet.BodyWriter.Write(lastReceivedSequence);
                    }

                    if (bundle.TimeSync) //0x1000000
                    {
                        packetHeader.Flags |= PacketHeaderFlags.TimeSynch;
                        packet.BodyWriter.Write(ConnectionData.ServerTime);
                    }

                    if (bundle.ClientTime != -1f) //0x4000000
                    {
                        packetHeader.Flags |= PacketHeaderFlags.EchoResponse;
                        packet.BodyWriter.Write(bundle.ClientTime);
                        packet.BodyWriter.Write((float)ConnectionData.ServerTime - bundle.ClientTime);
                    }

                    availableSpace -= (uint)packet.Data.Length;
                }

                if (carryOverMessage != null || bundle.Messages.Count > 0)
                {
                    packetHeader.Flags |= PacketHeaderFlags.BlobFragments;
                    int fragmentCount = 0;
                    while (carryOverMessage != null || bundle.Messages.Count > 0)
                    {
                        MessageFragment currentMessageFragment;

                        if (carryOverMessage != null) // If we have a carryOverMessage, use that
                        {
                            currentMessageFragment = carryOverMessage;
                            carryOverMessage       = null;
                        }
                        else // If we don't have a carryOverMessage, go ahead and dequeue next message from the bundle
                        {
                            currentMessageFragment = new MessageFragment(bundle.Messages.Dequeue());
                        }

                        var currentGameMessage = currentMessageFragment.message;

                        availableSpace -= PacketFragmentHeader.HeaderSize; // Account for fragment header

                        // Compute amount of data to send based on the total length and current position
                        uint dataToSend = (uint)currentGameMessage.Data.Length - currentMessageFragment.position;

                        if (dataToSend > availableSpace) // Message is too large to fit in packet
                        {
                            carryOverMessage = currentMessageFragment;
                            if (fragmentCount == 0) // If this is first message in packet, this is just a really large message, so proceed with splitting it
                            {
                                dataToSend = availableSpace;
                            }
                            else // Otherwise there are other messages already, so we'll break and come back and see if the message will fit
                            {
                                break;
                            }
                        }

                        if (currentMessageFragment.count == 0) // Compute number of fragments if we have not already
                        {
                            uint remainingData = (uint)currentGameMessage.Data.Length - dataToSend;
                            currentMessageFragment.count = (ushort)(Math.Ceiling((double)remainingData / PacketFragment.MaxFragmentDataSize) + 1);
                        }

                        // Set sequence, if new, pull next sequence from ConnectionData, if it is a carryOver, reuse that sequence
                        currentMessageFragment.sequence = currentMessageFragment.sequence == 0 ? ConnectionData.FragmentSequence++ : currentMessageFragment.sequence;

                        // Build ServerPacketFragment structure
                        ServerPacketFragment fragment       = new ServerPacketFragment();
                        PacketFragmentHeader fragmentHeader = fragment.Header;
                        fragmentHeader.Sequence = currentMessageFragment.sequence;
                        fragmentHeader.Id       = 0x80000000;
                        fragmentHeader.Count    = currentMessageFragment.count;
                        fragmentHeader.Index    = currentMessageFragment.index;
                        fragmentHeader.Group    = (ushort)currentMessageFragment.message.Group;

                        // Read data starting at current position reading dataToSend bytes
                        currentGameMessage.Data.Seek(currentMessageFragment.position, SeekOrigin.Begin);
                        fragment.Content = new byte[dataToSend];
                        currentGameMessage.Data.Read(fragment.Content, 0, (int)dataToSend);

                        // Increment position and index
                        currentMessageFragment.position = currentMessageFragment.position + dataToSend;
                        currentMessageFragment.index++;

                        // Add fragment to packet
                        packet.AddFragment(fragment);
                        fragmentCount++;

                        // Deduct consumed space
                        availableSpace -= dataToSend;

                        // Smallest message I am aware of requires HeaderSize + 4 bytes, so if we have less space then that, go ahead and break
                        if (availableSpace <= PacketFragmentHeader.HeaderSize + 4)
                        {
                            break;
                        }
                    }
                }

                PacketQueue.Enqueue(packet);
            }
        }
Ejemplo n.º 12
0
        /// <summary>
        /// This function handles turning a bundle of messages (representing all messages accrued in a timeslice),
        /// into 1 or more packets, combining multiple messages into one packet or spliting large message across
        /// several packets as needed.
        /// </summary>
        /// <param name="bundle"></param>
        private void SendBundle(NetworkBundle bundle)
        {
            log.DebugFormat("[{0}] Sending Bundle", session.LoggingIdentifier);
            bool writeOptionalHeaders = true;

            List <MessageFragment> fragments = new List <MessageFragment>();

            // Pull all messages out and create MessageFragment objects
            while (bundle.HasMoreMessages)
            {
                var message  = bundle.Dequeue();
                var fragment = new MessageFragment(message, ConnectionData.FragmentSequence++);
                fragments.Add(fragment);
            }

            log.DebugFormat("[{0}] Bundle Fragment Count: {1}", session.LoggingIdentifier, fragments.Count);

            // Loop through while we have fragements
            while (fragments.Count > 0 || writeOptionalHeaders)
            {
                ServerPacket packet       = new ServerPacket();
                PacketHeader packetHeader = packet.Header;

                if (fragments.Count > 0)
                {
                    packetHeader.Flags |= PacketHeaderFlags.BlobFragments;
                }

                if (bundle.EncryptedChecksum)
                {
                    packetHeader.Flags |= PacketHeaderFlags.EncryptedChecksum;
                }

                uint availableSpace = Packet.MaxPacketDataSize;

                // Pull first message and see if it is a large one
                var firstMessage = fragments.FirstOrDefault();
                if (firstMessage != null)
                {
                    // If a large message send only this one, filling the whole packet
                    if (firstMessage.DataRemaining >= availableSpace)
                    {
                        log.DebugFormat("[{0}] Sending large fragment", session.LoggingIdentifier);
                        ServerPacketFragment spf = firstMessage.GetNextFragment();
                        packet.Fragments.Add(spf);
                        availableSpace -= (uint)spf.Length;
                        if (firstMessage.DataRemaining <= 0)
                        {
                            fragments.Remove(firstMessage);
                        }
                    }
                    // Otherwise we'll write any optional headers and process any small messages that will fit
                    else
                    {
                        if (writeOptionalHeaders)
                        {
                            writeOptionalHeaders = false;
                            WriteOptionalHeaders(bundle, packet);
                            availableSpace -= (uint)packet.Data.Length;
                        }

                        // Create a list to remove completed messages after iterator
                        List <MessageFragment> removeList = new List <MessageFragment>();

                        foreach (MessageFragment fragment in fragments)
                        {
                            // Is this a large fragment and does it have a tail that needs sending?
                            if (!fragment.TailSent && availableSpace >= fragment.TailSize)
                            {
                                log.DebugFormat("[{0}] Sending tail fragment", session.LoggingIdentifier);
                                ServerPacketFragment spf = fragment.GetTailFragment();
                                packet.Fragments.Add(spf);
                                availableSpace -= (uint)spf.Length;
                            }
                            // Otherwise will this message fit in the remaining space?
                            else if (availableSpace >= fragment.NextSize)
                            {
                                log.DebugFormat("[{0}] Sending small message", session.LoggingIdentifier);
                                ServerPacketFragment spf = fragment.GetNextFragment();
                                packet.Fragments.Add(spf);
                                availableSpace -= (uint)spf.Length;
                            }
                            // If message is out of data, set to remove it
                            if (fragment.DataRemaining <= 0)
                            {
                                removeList.Add(fragment);
                            }
                        }

                        // Remove all completed messages
                        fragments.RemoveAll(x => removeList.Contains(x));
                    }
                }
                // If no messages, write optional headers
                else
                {
                    log.DebugFormat("[{0}] No messages, just sending optional headers", session.LoggingIdentifier);
                    if (writeOptionalHeaders)
                    {
                        writeOptionalHeaders = false;
                        WriteOptionalHeaders(bundle, packet);
                        availableSpace -= (uint)packet.Data.Length;
                    }
                }

                EnqueueSend(packet);
            }
        }
Ejemplo n.º 13
0
        public static void HandlePacket(ConnectionType type, ClientPacket packet, Session session)
        {
            // CLinkStatusAverages::OnPingResponse
            if (packet.Header.HasFlag(PacketHeaderFlags.EchoRequest))
            {
                var connectionData = (type == ConnectionType.Login ? session.LoginConnection : session.WorldConnection);

                // used to calculate round trip time (ping)
                // client calculates: currentTime - requestTime - serverDrift
                var echoResponse = new ServerPacket((ushort)(type == ConnectionType.Login ? 0x0B : 0x18), PacketHeaderFlags.EncryptedChecksum | PacketHeaderFlags.EchoResponse);
                echoResponse.Payload.Write(packet.HeaderOptional.ClientTime);
                echoResponse.Payload.Write((float)connectionData.ServerTime - packet.HeaderOptional.ClientTime);

                NetworkManager.SendPacket(type, echoResponse, session);
            }

            // ClientNet::HandleTimeSynch
            if (packet.Header.HasFlag(PacketHeaderFlags.TimeSynch))
            {
                var connectionData = (type == ConnectionType.Login ? session.LoginConnection : session.WorldConnection);

                // used to update time at client and check for overspeed (60s desync and client will disconenct with speed hack warning)
                var timeSynchResponse = new ServerPacket((ushort)(type == ConnectionType.Login ? 0x0B : 0x18), PacketHeaderFlags.EncryptedChecksum | PacketHeaderFlags.TimeSynch);
                timeSynchResponse.Payload.Write(connectionData.ServerTime);

                NetworkManager.SendPacket(type, timeSynchResponse, session);
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.RequestRetransmit))
            {
                foreach (uint sequence in packet.HeaderOptional.RetransmitData)
                {
                    CachedPacket cachedPacket;
                    if (session.CachedPackets.TryGetValue(sequence, out cachedPacket))
                    {
                        if (!cachedPacket.Packet.Header.HasFlag(PacketHeaderFlags.Retransmission))
                        {
                            cachedPacket.Packet.Header.Flags |= PacketHeaderFlags.Retransmission;

                            uint issacXor;
                            cachedPacket.Packet.Header.Checksum = packet.CalculateChecksum(session, ConnectionType.World, cachedPacket.IssacXor, out issacXor);
                        }

                        NetworkManager.SendPacketDirect(ConnectionType.World, cachedPacket.Packet, session);
                    }
                }
            }

            if (type == ConnectionType.Login)
            {
                if (packet.Header.HasFlag(PacketHeaderFlags.LoginRequest))
                {
                    AuthenticationHandler.HandleLoginRequest(packet, session);
                    return;
                }

                if (packet.Header.HasFlag(PacketHeaderFlags.ConnectResponse) && session.Authenticated)
                {
                    AuthenticationHandler.HandleConnectResponse(packet, session);
                    return;
                }
            }
            else
            {
                if (packet.Header.HasFlag(PacketHeaderFlags.WorldLoginRequest))
                {
                    AuthenticationHandler.HandleWorldLoginRequest(packet, session);
                    return;
                }

                if (packet.Header.HasFlag(PacketHeaderFlags.ConnectResponse))
                {
                    AuthenticationHandler.HandleWorldConnectResponse(packet, session);
                    return;
                }
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.Disconnect))
            {
                HandleDisconnectResponse(packet, session);
            }

            foreach (ClientPacketFragment fragment in packet.Fragments)
            {
                var opcode = (FragmentOpcode)fragment.Payload.ReadUInt32();
                if (!fragmentHandlers.ContainsKey(opcode))
                {
                    Console.WriteLine($"Received unhandled fragment opcode: 0x{((uint)opcode).ToString("X4")}");
                }
                else
                {
                    FragmentHandler fragmentHandler;
                    if (fragmentHandlers.TryGetValue(opcode, out fragmentHandler))
                    {
                        fragmentHandler.Invoke(fragment, session);
                    }
                }
            }
        }
Ejemplo n.º 14
0
        // TODO: packet pipe really needs a rework...
        private static bool ConstructPacket(out byte[] buffer, ConnectionType type, ServerPacket packet, Session session, bool useHeaders = false)
        {
            var connectionData = (type == ConnectionType.Login ? session.LoginConnection : session.WorldConnection);

            using (var packetStream = new MemoryStream())
            {
                using (var packetWriter = new BinaryWriter(packetStream))
                {
                    packetWriter.Seek((int)PacketHeader.HeaderSize, SeekOrigin.Begin);
                    packetWriter.Write(packet.Data.ToArray());

                    if (packet.Fragments.Count > 0)
                    {
                        packet.Header.Flags |= PacketHeaderFlags.BlobFragments;

                        for (int i = 0; i < packet.Fragments.Count; i++)
                        {
                            var fragment = (ServerPacketFragment)packet.Fragments[i];

                            uint fragmentsRequired = ((uint)fragment.Data.Length / PacketFragment.MaxFragmentDataSize);
                            if ((fragment.Data.Length % PacketFragment.MaxFragmentDataSize) != 0)
                            {
                                fragmentsRequired++;
                            }

                            if (fragmentsRequired > 1u)
                            {
                                fragment.Data.Seek(0L, SeekOrigin.Begin);

                                uint dataToSend  = (uint)fragment.Data.Length;
                                uint fragmentSeq = connectionData.FragmentSequence++;

                                for (uint j = 0u; j < fragmentsRequired; j++)
                                {
                                    uint fragmentSize = dataToSend > PacketFragment.MaxFragmentDataSize ? PacketFragment.MaxFragmentDataSize : dataToSend;

                                    byte[] data = new byte[fragmentSize];
                                    fragment.Data.Read(data, 0, (int)fragmentSize);

                                    var newPacket   = new ServerPacket(packet.Header.Id, PacketHeaderFlags.EncryptedChecksum | PacketHeaderFlags.BlobFragments);
                                    var newFragment = new ServerPacketFragment(fragment.Header.Group);
                                    newFragment.Header.Sequence = fragmentSeq;
                                    newFragment.Header.Id       = 0x80000000;
                                    newFragment.Header.Count    = (ushort)fragmentsRequired;
                                    newFragment.Header.Index    = (ushort)j;
                                    newFragment.Data.Write(data, 0, (int)fragmentSize);
                                    newPacket.Fragments.Add(newFragment);

                                    SendPacket(type, newPacket, session, true);

                                    dataToSend -= fragmentSize;
                                }

                                buffer = null;
                                return(false);
                            }

                            fragment.Header.Size = (ushort)(PacketFragmentHeader.HeaderSize + fragment.Payload.BaseStream.Length);

                            if (!useHeaders)
                            {
                                fragment.Header.Sequence = connectionData.FragmentSequence++;
                                fragment.Header.Id       = 0x80000000; // this seems to be a global incremental value
                                fragment.Header.Count    = 1;
                                fragment.Header.Index    = (ushort)i;
                            }

                            packetWriter.Write(fragment.Header.GetRaw());
                            packetWriter.Write(fragment.Data.ToArray());
                        }
                    }

                    if (packet.Header.HasFlag(PacketHeaderFlags.EncryptedChecksum) && connectionData.PacketSequence < 2)
                    {
                        connectionData.PacketSequence = 2;
                    }

                    packet.Header.Sequence = connectionData.PacketSequence++;
                    packet.Header.Size     = (ushort)(packetWriter.BaseStream.Length - PacketHeader.HeaderSize);
                    packet.Header.Table    = 0x14;
                    packet.Header.Time     = (ushort)connectionData.ServerTime;

                    uint issacXor;
                    packet.Header.Checksum = packet.CalculateChecksum(session, type, 0u, out issacXor);

                    packetWriter.Seek(0, SeekOrigin.Begin);
                    packetWriter.Write(packet.Header.GetRaw());

                    if (type == ConnectionType.World && packet.Header.Sequence >= 2u)
                    {
                        session.CachedPackets.TryAdd(packet.Header.Sequence, new CachedPacket(issacXor, packet));
                    }

                    buffer = packetStream.ToArray();
                    return(true);
                }
            }
        }
Ejemplo n.º 15
0
        /// <summary>
        /// This function handles turning a bundle of messages (representing all messages accrued in a timeslice),
        /// into 1 or more packets, combining multiple messages into one packet or spliting large message across
        /// several packets as needed.
        ///
        /// 1 Create Packet
        /// 2 If first packet for this bundle, fill any optional headers
        /// 3 Append fragments that will fit
        /// 3.1 Check each fragment to see if it is a partial fit
        /// 4 If we have a partial message remaining or more messages, create additional packets.
        /// </summary>
        /// <param name="bundle"></param>
        private void SendBundle(NetworkBundle bundle)
        {
            bool firstPacket = true;

            MessageFragment carryOverMessage = null;

            while (firstPacket || carryOverMessage != null || bundle.Messages.Count > 0)
            {
                ServerPacket packet       = new ServerPacket();
                PacketHeader packetHeader = packet.Header;
                if (bundle.encryptedChecksum)
                {
                    packetHeader.Flags |= PacketHeaderFlags.EncryptedChecksum;
                }

                uint availableSpace = Packet.MaxPacketDataSize;

                if (firstPacket)
                {
                    firstPacket = false;
                    if (bundle.SendAck) //0x4000
                    {
                        packetHeader.Flags |= PacketHeaderFlags.AckSequence;
                        packet.BodyWriter.Write(lastReceivedSequence);
                    }
                    if (bundle.TimeSync) //0x1000000
                    {
                        packetHeader.Flags |= PacketHeaderFlags.TimeSynch;
                        packet.BodyWriter.Write(ConnectionData.ServerTime);
                    }
                    if (bundle.ClientTime != -1f) //0x4000000
                    {
                        packetHeader.Flags |= PacketHeaderFlags.EchoResponse;
                        packet.BodyWriter.Write(bundle.ClientTime);
                        packet.BodyWriter.Write((float)ConnectionData.ServerTime - bundle.ClientTime);
                    }
                    availableSpace -= (uint)packet.Data.Length;
                }

                if (carryOverMessage != null || bundle.Messages.Count > 0)
                {
                    packetHeader.Flags |= PacketHeaderFlags.BlobFragments;

                    while (carryOverMessage != null || bundle.Messages.Count > 0)
                    {
                        if (availableSpace <= PacketFragmentHeader.HeaderSize + 4)
                        {
                            break;
                        }

                        MessageFragment currentMessageFragment;

                        if (carryOverMessage != null)
                        {
                            currentMessageFragment = carryOverMessage;
                            carryOverMessage       = null;
                            currentMessageFragment.index++;
                        }
                        else
                        {
                            currentMessageFragment = new MessageFragment(bundle.Messages.Dequeue());
                        }

                        var currentGameMessage = currentMessageFragment.message;

                        ServerPacketFragment fragment       = new ServerPacketFragment();
                        PacketFragmentHeader fragmentHeader = fragment.Header;
                        availableSpace -= PacketFragmentHeader.HeaderSize;

                        currentMessageFragment.sequence = currentMessageFragment.sequence == 0 ? ConnectionData.FragmentSequence++ : currentMessageFragment.sequence;

                        uint dataToSend = (uint)currentGameMessage.Data.Length - currentMessageFragment.position;
                        if (dataToSend > availableSpace)
                        {
                            dataToSend       = availableSpace;
                            carryOverMessage = currentMessageFragment;
                        }

                        if (currentMessageFragment.count == 0)
                        {
                            uint remainingData = (uint)currentGameMessage.Data.Length - dataToSend;
                            currentMessageFragment.count = (ushort)(Math.Ceiling((double)remainingData / PacketFragment.MaxFragmentDataSize) + 1);
                        }

                        fragmentHeader.Sequence = currentMessageFragment.sequence;
                        fragmentHeader.Id       = 0x80000000;
                        fragmentHeader.Count    = currentMessageFragment.count;
                        fragmentHeader.Index    = currentMessageFragment.index;
                        fragmentHeader.Group    = (ushort)currentMessageFragment.message.Group;

                        currentGameMessage.Data.Seek(currentMessageFragment.position, SeekOrigin.Begin);
                        fragment.Content = new byte[dataToSend];
                        currentGameMessage.Data.Read(fragment.Content, 0, (int)dataToSend);

                        currentMessageFragment.position = currentMessageFragment.position + dataToSend;

                        availableSpace -= dataToSend;

                        packet.AddFragment(fragment);
                        //break;
                    }
                }

                PacketQueue.Enqueue(packet);
            }
        }
Ejemplo n.º 16
0
 public CachedPacket(uint issacXor, ServerPacket packet)
 {
     IssacXor = issacXor;
     Packet   = packet;
 }
Ejemplo n.º 17
0
        public static void HandlePacket(ConnectionType type, ClientPacket packet, Session session)
        {
            if (!CheckPacketHeader(packet, session))
            {
                // server treats all packets sent during the first 30 seconds as invalid packets due to server crash, this will move clients to the disconnect screen
                if (DateTime.Now < WorldManager.WorldStartTime.AddSeconds(30d))
                {
                    session.SendCharacterError(CharacterError.ServerCrash);
                }
                return;
            }

            // CLinkStatusAverages::OnPingResponse
            if (packet.Header.HasFlag(PacketHeaderFlags.EchoRequest))
            {
                var connectionData = (type == ConnectionType.Login ? session.LoginConnection : session.WorldConnection);

                // used to calculate round trip time (ping)
                // client calculates: currentTime - requestTime - serverDrift
                var echoResponse = new ServerPacket((ushort)(type == ConnectionType.Login ? 0x0B : 0x18), PacketHeaderFlags.EncryptedChecksum | PacketHeaderFlags.EchoResponse);
                echoResponse.Payload.Write(packet.HeaderOptional.ClientTime);
                echoResponse.Payload.Write((float)connectionData.ServerTime - packet.HeaderOptional.ClientTime);

                NetworkManager.SendPacket(type, echoResponse, session);
            }

            // ClientNet::HandleTimeSynch
            if (packet.Header.HasFlag(PacketHeaderFlags.TimeSynch))
            {
                var connectionData = (type == ConnectionType.Login ? session.LoginConnection : session.WorldConnection);

                // used to update time at client and check for overspeed (60s desync and client will disconenct with speed hack warning)
                var timeSynchResponse = new ServerPacket((ushort)(type == ConnectionType.Login ? 0x0B : 0x18), PacketHeaderFlags.EncryptedChecksum | PacketHeaderFlags.TimeSynch);
                timeSynchResponse.Payload.Write(connectionData.ServerTime);

                NetworkManager.SendPacket(type, timeSynchResponse, session);
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.RequestRetransmit))
            {
                foreach (uint sequence in packet.HeaderOptional.RetransmitData)
                {
                    CachedPacket cachedPacket;
                    if (session.CachedPackets.TryGetValue(sequence, out cachedPacket))
                    {
                        if (!cachedPacket.Packet.Header.HasFlag(PacketHeaderFlags.Retransmission))
                        {
                            cachedPacket.Packet.Header.Flags |= PacketHeaderFlags.Retransmission;

                            uint issacXor;
                            cachedPacket.Packet.Header.Checksum = packet.CalculateChecksum(session, ConnectionType.World, cachedPacket.IssacXor, out issacXor);
                        }

                        NetworkManager.SendPacketDirect(ConnectionType.World, cachedPacket.Packet, session);
                    }
                }
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.LoginRequest))
            {
                AuthenticationHandler.HandleLoginRequest(packet, session);
                return;
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.WorldLoginRequest))
            {
                AuthenticationHandler.HandleWorldLoginRequest(packet, session);
                return;
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.ConnectResponse))
            {
                if (type == ConnectionType.Login)
                {
                    AuthenticationHandler.HandleConnectResponse(packet, session);
                    return;
                }

                AuthenticationHandler.HandleWorldConnectResponse(packet, session);
                return;
            }

            if (packet.Header.HasFlag(PacketHeaderFlags.Disconnect))
            {
                HandleDisconnectResponse(packet, session);
            }

            foreach (ClientPacketFragment fragment in packet.Fragments)
            {
                var opcode = (FragmentOpcode)fragment.Payload.ReadUInt32();
                if (!fragmentHandlers.ContainsKey(opcode))
                {
                    Console.WriteLine($"Received unhandled fragment opcode: 0x{(uint)opcode:X4}");
                }
                else
                {
                    FragmentHandlerInfo fragmentHandlerInfo;
                    if (fragmentHandlers.TryGetValue(opcode, out fragmentHandlerInfo))
                    {
                        if (fragmentHandlerInfo.Attribute.State == session.State)
                        {
                            fragmentHandlerInfo.Handler.Invoke(fragment, session);
                        }
                    }
                }
            }
        }