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); }
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; }
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; }
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); }
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); }
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); }
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); } }
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); }
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); } }
public static void SendPacketDirect(ConnectionType type, ServerPacket packet, Session session) { GetSocket(type).SendTo(packet.Data.ToArray(), session.EndPoint); }
/// <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); } }
/// <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); } }
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); } } } }
// 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); } } }
/// <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); } }
public CachedPacket(uint issacXor, ServerPacket packet) { IssacXor = issacXor; Packet = packet; }
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); } } } } }