public void FlushSendBuffer() { //lock (ClientState.SendLock) //{ try { while (ClientState.SendBufferTake(out ServerPacket packet)) { Task.Run(async() => { if (packet == null) { return; } if (packet.ShouldEncrypt) { ++ServerOrdinal; packet.Ordinal = ServerOrdinal; packet.GenerateFooter(); packet.Encrypt(this); } if (packet.TransmitDelay != 0) { await Task.Delay(packet.TransmitDelay); } var buffer = packet.ToArray(); try { Socket.BeginSend(buffer, 0, buffer.Length, 0, SendCallback, ClientState); } catch (ObjectDisposedException e) { GameLog.Warning($"FlushSendBuffer: {e.Message}"); } }); } } catch (ObjectDisposedException) { // Socket is gone, peace out ClientState.Dispose(); } catch (Exception e) { GameLog.Error($"HALP: {e}"); } //} }
public Server(int port) { Clients = new ConcurrentDictionary <IntPtr, Client>(); Port = port; PacketHandlers = new WorldPacketHandler[256]; ControlMessageHandlers = new ControlMessageHandler[64]; Throttles = new Dictionary <byte, IPacketThrottle>(); ExpectedConnections = new ConcurrentDictionary <uint, Redirect>(); for (int i = 0; i < 256; ++i) { PacketHandlers[i] = (c, p) => GameLog.Warning($"{GetType().Name}: Unhandled opcode 0x{p.Opcode:X2}"); } Task.Run(ProcessOutbound); }
/// <summary> /// Calculate loot for a given spawn. /// </summary> /// <param name="spawn">The spawn we will use to calculate Loot.</param> /// <returns>A Loot struct with XP, gold and items, if any</returns> public static Loot CalculateLoot(Xml.Spawn spawn) { // Loot calculations are not particularly complex but have a lot of components: // Spawns can have loot sets, loot tables, or both. // We resolve sets first, then tables. We keep a running tab of gold drops. // Lastly, we return a Loot struct with our calculations. var loot = new Loot(0, 0); var tables = new List <Xml.LootTable>(); // Assign base XP loot.Xp = spawn.Loot.Xp; // Sets foreach (var set in spawn.Loot?.Set ?? new List <Xml.LootImport>()) { // Is the set present? GameLog.SpawnInfo("Processing loot set {Name}", set.Name); if (Game.World.WorldData.TryGetValueByIndex(set.Name, out Xml.LootSet lootset)) { // Set is present, does it fire? // Chance is implemented as a decimalized percentage, e.g. 0.08 = 8% chance // If rolls == 0, all tables fire if (set.Rolls == 0) { tables.AddRange(lootset.Table); GameLog.SpawnInfo("Processing loot set {Name}: set rolls == 0, looting", set.Name); continue; } for (var x = 0; x < set.Rolls; x++) { if (Roll() <= set.Chance) { GameLog.SpawnInfo("Processing loot set {Name}: set hit, looting", set.Name); // Ok, the set fired. Check the subtables, which can have independent chances. // If no chance is present, we simply award something from each table in the set. // Note that we do table processing last! We just find the tables that fire here. foreach (var setTable in lootset.Table) { // Rolls == 0 (default) means the table is automatically used. if (setTable.Rolls == 0) { tables.Add(setTable); GameLog.SpawnInfo("Processing loot set {Name}: setTable rolls == 0, looting", set.Name); continue; } // Did the subtable hit? for (var y = 0; y < setTable.Rolls; y++) { if (Roll() <= setTable.Chance) { tables.Add(setTable); GameLog.SpawnInfo("Processing loot set {Name}: set subtable hit, looting ", set.Name); } } } } else { GameLog.SpawnInfo("Processing loot set {Name}: Set subtable missed", set.Name); } } } else { GameLog.Warning("Spawn {name}: Loot set {name} missing", spawn.Base, set.Name); } } // Now, calculate loot for any tables attached to the spawn foreach (var table in spawn.Loot?.Table ?? new List <Xml.LootTable>()) { if (table.Rolls == 0) { tables.Add(table); GameLog.SpawnInfo("Processing loot: spawn table for {Name}, rolls == 0, looting", spawn.Base); continue; } for (var z = 0; z <= table.Rolls; z++) { if (Roll() <= table.Chance) { GameLog.SpawnInfo("Processing loot: spawn table for {Name} hit, looting", spawn.Base); tables.Add(table); } else { GameLog.SpawnInfo("Processing loot set {Name}: Spawn subtable missed", spawn.Base); } } } // Now that we have all tables that fired, we need to calculate actual loot GameLog.SpawnInfo("Loot for {Name}: tables: {Count}", spawn.Base, tables.Count()); foreach (var table in tables) { loot += CalculateTable(table); } GameLog.SpawnInfo("Final loot for {Name}: {Xp} xp, {Gold} gold, items [{items}]", spawn.Base, loot.Xp, loot.Gold, string.Join(",", loot.Items)); return(loot); }
public void OnDisconnectThreshold(IClientTrigger trigger) { trigger.Client.SendMessage("You have been automatically disconnected due to server abuse. Goodbye!", MessageTypes.SYSTEM_WITH_OVERHEAD); GameLog.Warning($"Client {trigger.Id}: disconnected due to packet spam"); trigger.Client.Disconnect(); }
public ThrottleResult ProcessThrottle(Client client, ClientPacket packet) { ThrottleInfo info; ThrottleResult result = ThrottleResult.Error; if (!client.ThrottleState.TryGetValue(packet.Opcode, out info)) { client.ThrottleState[packet.Opcode] = new ThrottleInfo(); info = client.ThrottleState[packet.Opcode]; } Monitor.Enter(info); try { DateTime rightnow = DateTime.Now; GameLog.Warning($"Right now is {rightnow}"); var transmitInterval = (rightnow - info.LastReceived); var acceptedInterval = (rightnow - info.LastAccepted); info.PreviousReceived = info.LastReceived; info.LastReceived = rightnow; info.TotalReceived++; GameLog.Debug($"Begin: PA: {info.PreviousAccepted} LA: {info.LastAccepted} AInterval is {acceptedInterval.TotalMilliseconds} TInterval {transmitInterval.TotalMilliseconds}"); if (info.Throttled) { result = ThrottleResult.Throttled; if (acceptedInterval.TotalMilliseconds >= ThrottleDuration && acceptedInterval.TotalMilliseconds >= Interval) { GameLog.Error($"Unthrottled: {acceptedInterval.TotalMilliseconds} > {ThrottleDuration} and {Interval}"); info.Throttled = false; info.TotalThrottled = 0; result = ThrottleResult.ThrottleEnd; info.PreviousAccepted = info.LastAccepted; info.LastAccepted = rightnow; OnThrottleStop(new ClientTrigger(client)); } else { info.TotalThrottled++; GameLog.Error($"Throttled, count is {info.TotalThrottled}"); result = ThrottleResult.Throttled; if (ThrottleDisconnectThreshold > 0 && info.TotalThrottled > ThrottleDisconnectThreshold) { result = ThrottleResult.Disconnect; OnDisconnectThreshold(new ClientTrigger(client)); } } } else { if (acceptedInterval.TotalMilliseconds <= Interval && info.LastAccepted != DateTime.MinValue) { GameLog.Debug($"TInterval {transmitInterval}, AInterval {acceptedInterval} - maximum is {Interval}, throttled"); info.Throttled = true; OnThrottleStart(new ClientTrigger(client)); result = ThrottleResult.Throttled; } else { info.PreviousAccepted = info.LastAccepted; info.LastAccepted = rightnow; info.TotalAccepted++; result = ThrottleResult.OK; GameLog.Debug($"Packet accepted, PA: {info.PreviousAccepted} LA: {info.LastAccepted}"); } } } finally { Monitor.Exit(info); } return(result); }