using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Xna.Framework; using Terraria; using TerrariaAPI; using TerrariaAPI.Hooks; namespace TShockAPI { public class PacketBufferer : IDisposable { /// /// Maximum number of bytes to send per update per socket /// public int BytesPerUpdate { get; set; } PacketBuffer[] buffers = new PacketBuffer[Netplay.serverSock.Length]; int[] Bytes = new int[52]; int[] Packets = new int[52]; Command dump; Command flush; public PacketBufferer() { BytesPerUpdate = 0xFFFF; for (int i = 0; i < buffers.Length; i++) buffers[i] = new PacketBuffer(); dump = new Command("superadmin", Dump, "netdump"); flush = new Command("superadmin", Flush, "netflush"); Commands.ChatCommands.Add(dump); Commands.ChatCommands.Add(flush); ServerHooks.SendBytes += ServerHooks_SendBytes; ServerHooks.SocketReset += ServerHooks_SocketReset; GameHooks.PostUpdate += GameHooks_Update; } public void Dispose() { Commands.ChatCommands.Remove(dump); Commands.ChatCommands.Remove(flush); ServerHooks.SendBytes -= ServerHooks_SendBytes; ServerHooks.SocketReset -= ServerHooks_SocketReset; GameHooks.PostUpdate -= GameHooks_Update; } void Dump(CommandArgs args) { var sb = new StringBuilder(); sb.AppendLine("{0}{1}{2}".SFormat("Name:".PadRight(25, ' '), "Packets".PadRight(10, ' '), "Bytes")); for (int i = 1; i < Bytes.Length; i++) { sb.AppendLine("{0}{1}{2}".SFormat((Enum.GetName(typeof(PacketTypes), i) + ":").PadRight(25, ' '), Packets[i].ToString().PadRight(10, ' '), Bytes[i])); } File.WriteAllText(Path.Combine(TShock.SavePath, "dmp.txt"), sb.ToString()); } void Flush(CommandArgs args) { Bytes = new int[52]; Packets = new int[52]; } void GameHooks_Update(GameTime obj) { for (int i = 0; i < Netplay.serverSock.Length; i++) { if (Netplay.serverSock[i] == null || !Netplay.serverSock[i].active) continue; if (!Netplay.serverSock[i].tcpClient.Client.Poll(0, SelectMode.SelectWrite)) continue; byte[] buff = buffers[i].GetBytes(BytesPerUpdate); if (buff == null) continue; try { Netplay.serverSock[i].tcpClient.Client.Send(buff); } catch (ObjectDisposedException) { } catch (SocketException) { } } } void ServerHooks_SocketReset(ServerSock socket) { buffers[socket.whoAmI] = new PacketBuffer(); } void ServerHooks_SendBytes(ServerSock socket, byte[] buffer, int offset, int count, HandledEventArgs e) { e.Handled = true; lock (buffers[socket.whoAmI]) { var pt = buffer[offset + 4]; Packets[pt]++; Bytes[pt] += (count - offset); buffers[socket.whoAmI].AddRange(new MemoryStream(buffer, offset, count).ToArray()); } } } public class PacketBuffer : List { public byte[] GetBytes(int max) { lock (this) { if (this.Count < 1) return null; var ret = new byte[Math.Min(max, this.Count)]; this.CopyTo(0, ret, 0, ret.Length); this.RemoveRange(0, ret.Length); return ret; } } } }