private void WriteOptionalHeaders(NetworkBundle bundle, ServerPacket packet) { PacketHeader packetHeader = packet.Header; if (bundle.SendAck) // 0x4000 { packetHeader.Flags |= PacketHeaderFlags.AckSequence; packetLog.DebugFormat("[{0}] Outgoing AckSeq: {1}", session.LoggingIdentifier, lastReceivedPacketSequence); packet.InitializeBodyWriter(); packet.BodyWriter.Write(lastReceivedPacketSequence); } if (bundle.TimeSync) // 0x1000000 { packetHeader.Flags |= PacketHeaderFlags.TimeSync; packetLog.DebugFormat("[{0}] Outgoing TimeSync TS: {1}", session.LoggingIdentifier, Timers.PortalYearTicks); packet.InitializeBodyWriter(); packet.BodyWriter.Write(Timers.PortalYearTicks); } if (bundle.ClientTime != -1f) // 0x4000000 { packetHeader.Flags |= PacketHeaderFlags.EchoResponse; packetLog.DebugFormat("[{0}] Outgoing EchoResponse: {1}", session.LoggingIdentifier, bundle.ClientTime); packet.InitializeBodyWriter(); packet.BodyWriter.Write(bundle.ClientTime); packet.BodyWriter.Write((float)Timers.PortalYearTicks - bundle.ClientTime); } }
// It is assumed that this will only be called from a single thread.WorldManager.UpdateWorld()->Session.Update(lastTick)->This /// <summary> /// Checks if we should send the current bundle and then flushes all pending packets. /// </summary> /// <param name="lastTick">Amount of time that has passed for the last cycle.</param> public void Update(double lastTick) { ConnectionData.ServerTime += lastTick; currentBundles.Keys.ToList().ForEach(group => { var currentBundleLock = currentBundleLocks[group]; var currentBundle = currentBundles[group]; NetworkBundle bundleToSend = null; lock (currentBundleLock) { if (group == GameMessageGroup.InvalidQueue) { if (sendResync && !currentBundle.TimeSync && DateTime.UtcNow > nextResync) { log.DebugFormat("[{0}] Setting to send TimeSync packet", session.LoggingIdentifier); currentBundle.TimeSync = true; currentBundle.EncryptedChecksum = true; nextResync = DateTime.UtcNow.AddMilliseconds(timeBetweenTimeSync); } if (sendAck && !currentBundle.SendAck && DateTime.UtcNow > nextAck) { log.DebugFormat("[{0}] Setting to send ACK packet", session.LoggingIdentifier); currentBundle.SendAck = true; nextAck = DateTime.UtcNow.AddMilliseconds(timeBetweenAck); } if (currentBundle.NeedsSending && DateTime.UtcNow > nextSend) { log.DebugFormat("[{0}] Swaping bundle", session.LoggingIdentifier); // Swap out bundle so we can process it bundleToSend = currentBundle; currentBundles[group] = new NetworkBundle(); } } else { if (currentBundle.NeedsSending && DateTime.UtcNow > nextSend) { log.DebugFormat("[{0}] Swaping bundle", session.LoggingIdentifier); // Swap out bundle so we can process it bundleToSend = currentBundle; currentBundles[group] = new NetworkBundle(); } } } // Send our bundle if we have one // We should be able to execute this outside the lock as Sending is single threaded // and all future writes from other threads will go to the new bundle if (bundleToSend != null) { SendBundle(bundleToSend, group); nextSend = DateTime.UtcNow.AddMilliseconds(minimumTimeBetweenBundles); } }); FlushPackets(); }
private void WriteOptionalHeaders(NetworkBundle bundle, ServerPacket packet) { PacketHeader packetHeader = packet.Header; if (bundle.SendAck) // 0x4000 { packetHeader.Flags |= PacketHeaderFlags.AckSequence; log.DebugFormat("[{0}] Outgoing AckSeq: {1}", session.LoggingIdentifier, lastReceivedPacketSequence); packet.BodyWriter.Write(lastReceivedPacketSequence); } if (bundle.TimeSync) // 0x1000000 { packetHeader.Flags |= PacketHeaderFlags.TimeSynch; log.DebugFormat("[{0}] Outgoing TimeSync TS: {1}", session.LoggingIdentifier, ConnectionData.ServerTime); packet.BodyWriter.Write(ConnectionData.ServerTime); } if (bundle.ClientTime != -1f) // 0x4000000 { packetHeader.Flags |= PacketHeaderFlags.EchoResponse; log.DebugFormat("[{0}] Outgoing EchoResponse: {1}", session.LoggingIdentifier, bundle.ClientTime); packet.BodyWriter.Write(bundle.ClientTime); packet.BodyWriter.Write((float)ConnectionData.ServerTime - bundle.ClientTime); } }
public NetworkSession(Session session, ushort clientId, ushort serverId) { this.session = session; ClientId = clientId; ServerId = serverId; // New network auth session timeouts will always be low. TimeoutTick = DateTime.UtcNow.AddSeconds(AuthenticationHandler.DefaultAuthTimeout).Ticks; for (int i = 0; i < currentBundles.Length; i++) { currentBundleLocks[i] = new object(); currentBundles[i] = new NetworkBundle(); } }
public NetworkSession(Session session, ushort clientId, ushort serverId) { this.session = session; ClientId = clientId; ServerId = serverId; // New network auth session timeouts will always be low. TimeoutTick = DateTime.UtcNow.AddSeconds(AuthenticationHandler.DefaultAuthTimeout).Ticks; foreach (var gmg in System.Enum.GetValues(typeof(GameMessageGroup))) { var group = (GameMessageGroup)gmg; currentBundleLocks[group] = new object(); currentBundles[group] = new NetworkBundle(); } }
public NetworkSession(Session session, ushort clientId, ushort serverId) { this.session = session; ClientId = clientId; ServerId = serverId; // New network auth session timeouts will always be low. TimeoutTick = DateTime.UtcNow.AddSeconds(AuthenticationHandler.DefaultAuthTimeout).Ticks; for (int i = 0; i < currentBundles.Length; i++) { currentBundleLocks[i] = new object(); currentBundles[i] = new NetworkBundle(); } ConnectionData.CryptoClient.OnCryptoSystemCatastrophicFailure += (sender, e) => { session.State = Enum.SessionState.ClientConnectionFailure; }; }
/// <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, GameMessageGroup group) { packetLog.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); } packetLog.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; } int availableSpace = ServerPacket.MaxPacketSize; // 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) { packetLog.DebugFormat("[{0}] Sending large fragment", session.LoggingIdentifier); ServerPacketFragment spf = firstMessage.GetNextFragment(); packet.Fragments.Add(spf); availableSpace -= 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); if (packet.Data != null) { availableSpace -= (int)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) { packetLog.DebugFormat("[{0}] Sending tail fragment", session.LoggingIdentifier); ServerPacketFragment spf = fragment.GetTailFragment(); packet.Fragments.Add(spf); availableSpace -= spf.Length; } // Otherwise will this message fit in the remaining space? else if (availableSpace >= fragment.NextSize) { packetLog.DebugFormat("[{0}] Sending small message", session.LoggingIdentifier); ServerPacketFragment spf = fragment.GetNextFragment(); packet.Fragments.Add(spf); availableSpace -= 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 { packetLog.DebugFormat("[{0}] No messages, just sending optional headers", session.LoggingIdentifier); if (writeOptionalHeaders) { writeOptionalHeaders = false; WriteOptionalHeaders(bundle, packet); if (packet.Data != null) { availableSpace -= (int)packet.Data.Length; } } } EnqueueSend(packet); } }
/// <summary> /// Prunes the cachedPackets dictionary /// Checks if we should send the current bundle and then flushes all pending packets. /// </summary> public void Update() { if (isReleased) // Session has been removed { return; } if (DateTime.UtcNow - lastCachedPacketPruneTime > cachedPacketPruneInterval) { PruneCachedPackets(); } for (int i = 0; i < currentBundles.Length; i++) { NetworkBundle bundleToSend = null; var group = (GameMessageGroup)i; var currentBundleLock = currentBundleLocks[i]; lock (currentBundleLock) { var currentBundle = currentBundles[i]; if (group == GameMessageGroup.InvalidQueue) { if (sendResync && !currentBundle.TimeSync && DateTime.UtcNow > nextResync) { packetLog.DebugFormat("[{0}] Setting to send TimeSync packet", session.LoggingIdentifier); currentBundle.TimeSync = true; currentBundle.EncryptedChecksum = true; nextResync = DateTime.UtcNow.AddMilliseconds(timeBetweenTimeSync); } if (sendAck && !currentBundle.SendAck && DateTime.UtcNow > nextAck) { packetLog.DebugFormat("[{0}] Setting to send ACK packet", session.LoggingIdentifier); currentBundle.SendAck = true; nextAck = DateTime.UtcNow.AddMilliseconds(timeBetweenAck); } if (currentBundle.NeedsSending && DateTime.UtcNow >= nextSend) { packetLog.DebugFormat("[{0}] Swapping bundle", session.LoggingIdentifier); // Swap out bundle so we can process it bundleToSend = currentBundle; currentBundles[i] = new NetworkBundle(); } } else { if (currentBundle.NeedsSending && DateTime.UtcNow >= nextSend) { packetLog.DebugFormat("[{0}] Swapping bundle", session.LoggingIdentifier); // Swap out bundle so we can process it bundleToSend = currentBundle; currentBundles[i] = new NetworkBundle(); } } } // Send our bundle if we have one // We should be able to execute this outside the lock as Sending is single threaded // and all future writes from other threads will go to the new bundle if (bundleToSend != null) { SendBundle(bundleToSend, group); nextSend = DateTime.UtcNow.AddMilliseconds(minimumTimeBetweenBundles); } } FlushPackets(); }