fix: Removes profiling. Streamlines core tick count. (#1948)

### Summary
- Removes `Profiling` - Recommend using Visual Studio Performance Profiler,  [dotnet-trace](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace) or [JetBrains dotTrace](https://www.jetbrains.com/profiler/)
- Cleans up the core tick count, sampling, etc.
This commit is contained in:
Kamron Batman 2024-09-11 00:55:18 -07:00 committed by GitHub
parent 65bc264402
commit 2e6ddcd31a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 80 additions and 949 deletions

View file

@ -7,16 +7,6 @@ public class Application
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
bool profiling = false; Core.Setup(Assembly.GetEntryAssembly(), Process.GetCurrentProcess());
foreach (var a in args)
{
if (a.InsensitiveEquals("-profile"))
{
profiling = true;
}
}
Core.Setup(profiling, Assembly.GetEntryAssembly(), Process.GetCurrentProcess());
} }
} }

View file

@ -2,7 +2,6 @@ using System;
using System.Buffers; using System.Buffers;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using Server.Diagnostics;
namespace Server.Network namespace Server.Network
{ {
@ -19,12 +18,6 @@ namespace Server.Network
protected Packet(int packetID) protected Packet(int packetID)
{ {
PacketID = packetID; PacketID = packetID;
if (Core.Profiling)
{
var prof = PacketSendProfile.Acquire(PacketID);
prof.Increment();
}
} }
protected Packet(int packetID, int length) protected Packet(int packetID, int length)
@ -34,12 +27,6 @@ namespace Server.Network
Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length ); Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length );
Stream.Write((byte)packetID); Stream.Write((byte)packetID);
if (Core.Profiling)
{
var prof = PacketSendProfile.Acquire(PacketID);
prof.Increment();
}
} }
public int PacketID { get; } public int PacketID { get; }

View file

@ -1,79 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Server.Diagnostics;
public abstract class BaseProfile
{
private readonly Stopwatch _stopwatch;
protected BaseProfile(string name)
{
Name = name;
_stopwatch = new Stopwatch();
}
public string Name { get; }
public long Count { get; private set; }
public TimeSpan AverageTime => TimeSpan.FromTicks(TotalTime.Ticks / Math.Max(Count, 1));
public TimeSpan PeakTime { get; private set; }
public TimeSpan TotalTime { get; private set; }
public static void WriteAll<T>(TextWriter op, IEnumerable<T> profiles) where T : BaseProfile
{
var list = new List<T>(profiles);
list.Sort((a, b) => -a.TotalTime.CompareTo(b.TotalTime));
foreach (var prof in list)
{
prof.WriteTo(op);
op.WriteLine();
}
}
public virtual void Start()
{
if (_stopwatch.IsRunning)
{
_stopwatch.Reset();
}
_stopwatch.Start();
}
public virtual void Finish()
{
var elapsed = _stopwatch.Elapsed;
TotalTime += elapsed;
if (elapsed > PeakTime)
{
PeakTime = elapsed;
}
Count++;
_stopwatch.Reset();
}
public virtual void WriteTo(TextWriter op)
{
op.Write(
"{0,-100} {1,12:N0} {2,12:F5} {3,-12:F5} {4,12:F5}",
Name,
Count,
AverageTime.TotalSeconds,
PeakTime.TotalSeconds,
TotalTime.TotalSeconds
);
}
}

View file

@ -1,88 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace Server.Diagnostics;
public abstract class BasePacketProfile : BaseProfile
{
protected BasePacketProfile(string name) : base(name)
{
}
public long TotalLength { get; private set; }
public double AverageLength => (double)TotalLength / Math.Max(Count, 1);
public void Finish(long length)
{
Finish();
TotalLength += length;
}
public override void WriteTo(TextWriter op)
{
base.WriteTo(op);
op.Write("\t{0,12:F2} {1,-12:N0}", AverageLength, TotalLength);
}
}
public class PacketSendProfile : BasePacketProfile
{
private static readonly Dictionary<int, PacketSendProfile> _profiles = new();
private long _created;
public PacketSendProfile(int packetId) : base($"0x{packetId:X2}")
{
}
public static IEnumerable<PacketSendProfile> Profiles => _profiles.Values;
public static PacketSendProfile Acquire(int packetId)
{
if (!_profiles.TryGetValue(packetId, out var prof))
{
_profiles.Add(packetId, prof = new PacketSendProfile(packetId));
}
return prof;
}
public void Increment()
{
Interlocked.Increment(ref _created);
}
public override void WriteTo(TextWriter op)
{
base.WriteTo(op);
op.Write("\t{0,12:N0}", _created);
}
}
public class PacketReceiveProfile : BasePacketProfile
{
private static readonly Dictionary<int, PacketReceiveProfile>
_profiles = new();
public PacketReceiveProfile(int packetId) : base($"0x{packetId:X2}")
{
}
public static IEnumerable<PacketReceiveProfile> Profiles => _profiles.Values;
public static PacketReceiveProfile Acquire(int packetId)
{
if (!_profiles.TryGetValue(packetId, out var prof))
{
_profiles.Add(packetId, prof = new PacketReceiveProfile(packetId));
}
return prof;
}
}

View file

@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
namespace Server.Diagnostics;
public class TargetProfile : BaseProfile
{
private static readonly Dictionary<Type, TargetProfile> _profiles = new();
public TargetProfile(Type type)
: base(type.FullName)
{
}
public static IEnumerable<TargetProfile> Profiles => _profiles.Values;
public static TargetProfile Acquire(Type type)
{
if (!Core.Profiling)
{
return null;
}
if (!_profiles.TryGetValue(type, out var prof))
{
_profiles.Add(type, prof = new TargetProfile(type));
}
return prof;
}
}

View file

@ -1,44 +0,0 @@
using System.Collections.Generic;
using System.IO;
namespace Server.Diagnostics;
public class TimerProfile : BaseProfile
{
private static readonly Dictionary<string, TimerProfile> _profiles = new();
public TimerProfile(string name)
: base(name)
{
}
public static IEnumerable<TimerProfile> Profiles => _profiles.Values;
public long Created { get; set; }
public long Started { get; set; }
public long Stopped { get; set; }
public static TimerProfile Acquire(string name)
{
if (!Core.Profiling)
{
return null;
}
if (!_profiles.TryGetValue(name, out var prof))
{
_profiles.Add(name, prof = new TimerProfile(name));
}
return prof;
}
public override void WriteTo(TextWriter op)
{
base.WriteTo(op);
op.Write("\t{0,12:N0} {1,12:N0} {2,-12:N0}", Created, Started, Stopped);
}
}

View file

@ -44,9 +44,6 @@ public static class Core
private static bool _crashed; private static bool _crashed;
private static string _baseDirectory; private static string _baseDirectory;
private static bool _profiling;
private static long _profileStart;
private static long _profileTime;
private static bool? _isRunningFromXUnit; private static bool? _isRunningFromXUnit;
private static int _itemCount; private static int _itemCount;
@ -90,30 +87,6 @@ public static class Core
} }
#nullable restore #nullable restore
public static bool Profiling
{
get => _profiling;
set
{
if (_profiling == value)
{
return;
}
_profiling = value;
if (_profileStart > 0)
{
_profileTime += Stopwatch.GetTimestamp() - _profileStart;
}
_profileStart = _profiling ? Stopwatch.GetTimestamp() : 0;
}
}
public static TimeSpan ProfileTime =>
TimeSpan.FromTicks(_profileStart > 0 ? _profileTime + (Stopwatch.GetTimestamp() - _profileStart) : _profileTime);
public static Assembly ApplicationAssembly { get; set; } public static Assembly ApplicationAssembly { get; set; }
public static Assembly Assembly { get; set; } public static Assembly Assembly { get; set; }
@ -126,50 +99,15 @@ public static class Core
private static long _firstTick; private static long _firstTick;
[ThreadStatic]
private static long _tickCount; private static long _tickCount;
// Don't access this from other threads than the game thread.
private static DateTime _now; private static DateTime _now;
// For Unix Stopwatch.Frequency is normalized to 1ns public static long TickCount => _tickCount;
// We don't anticipate needing this for Windows/OSX
private const long _maxTickCountBeforePrecisionLoss = long.MaxValue / 1000L;
private static readonly long _ticksPerMillisecond = Stopwatch.Frequency / 1000L;
public static long TickCount public static DateTime Now => _now;
{
get
{
if (_tickCount != 0)
{
return _tickCount;
}
var timestamp = Stopwatch.GetTimestamp(); public static long Uptime => TickCount - _firstTick;
return timestamp > _maxTickCountBeforePrecisionLoss
? timestamp / _ticksPerMillisecond
// No precision loss
: 1000L * timestamp / Stopwatch.Frequency;
}
// Setting this to a value lower than the previous is bad. Timers will become delayed
// until time catches up.
set => _tickCount = value;
}
public static DateTime Now
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
// See notes above for _now and why this is a volatile variable.
var now = _now;
return now == DateTime.MinValue ? DateTime.UtcNow : now;
}
}
public static long Uptime => Thread.CurrentThread != Thread ? 0 : TickCount - _firstTick;
private static long _cycleIndex; private static long _cycleIndex;
private static readonly double[] _cyclesPerSecond = new double[128]; private static readonly double[] _cyclesPerSecond = new double[128];
@ -207,21 +145,6 @@ public static class Core
public static bool Closing => ClosingTokenSource.IsCancellationRequested; public static bool Closing => ClosingTokenSource.IsCancellationRequested;
public static string Arguments
{
get
{
var sb = new StringBuilder();
if (_profiling)
{
Utility.Separate(sb, "-profile", " ");
}
return sb.ToString();
}
}
public static int GlobalUpdateRange { get; set; } = 18; public static int GlobalUpdateRange { get; set; } = 18;
public static int GlobalMaxUpdateRange { get; set; } = 24; public static int GlobalMaxUpdateRange { get; set; } = 24;
@ -380,7 +303,7 @@ public static class Core
logger.Information("Restarting"); logger.Information("Restarting");
if (IsWindows) if (IsWindows)
{ {
using var process = Process.Start("dotnet", $"{ApplicationAssembly.Location} {Arguments}"); using var process = Process.Start("dotnet", $"{ApplicationAssembly.Location}");
} }
else else
{ {
@ -388,7 +311,7 @@ public static class Core
process.StartInfo = new ProcessStartInfo process.StartInfo = new ProcessStartInfo
{ {
FileName = "dotnet", FileName = "dotnet",
Arguments = $"{ApplicationAssembly.Location} {Arguments}", Arguments = $"{ApplicationAssembly.Location}",
UseShellExecute = true UseShellExecute = true
}; };
@ -420,7 +343,10 @@ public static class Core
} }
} }
public static void Setup(bool profiling, Assembly applicationAssembly, Process process) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long GetTimestamp() => 1000L * Stopwatch.GetTimestamp() / Stopwatch.Frequency;
public static void Setup(Assembly applicationAssembly, Process process)
{ {
Process = process; Process = process;
ApplicationAssembly = applicationAssembly; ApplicationAssembly = applicationAssembly;
@ -428,7 +354,6 @@ public static class Core
Thread = Thread.CurrentThread; Thread = Thread.CurrentThread;
LoopContext = new EventLoopContext(); LoopContext = new EventLoopContext();
SynchronizationContext.SetSynchronizationContext(LoopContext); SynchronizationContext.SetSynchronizationContext(LoopContext);
Profiling = profiling;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
@ -472,13 +397,6 @@ public static class Core
logger.Information("Running on {Framework}", RuntimeInformation.FrameworkDescription); logger.Information("Running on {Framework}", RuntimeInformation.FrameworkDescription);
var s = Arguments;
if (s.Length > 0)
{
logger.Information("Running with arguments: {Args}", s);
}
var assemblyPath = Path.Join(BaseDirectory, AssembliesConfiguration); var assemblyPath = Path.Join(BaseDirectory, AssembliesConfiguration);
// Load UOContent.dll // Load UOContent.dll
@ -497,7 +415,10 @@ public static class Core
VerifySerialization(); VerifySerialization();
Timer.Init(TickCount); _now = DateTime.UtcNow;
_firstTick = _tickCount = GetTimestamp();
Timer.Init(_tickCount);
AssemblyHandler.Invoke("Configure"); AssemblyHandler.Invoke("Configure");
@ -511,7 +432,6 @@ public static class Core
TcpServer.Start(); TcpServer.Start();
PingServer.Start(); PingServer.Start();
EventSink.InvokeServerStarted(); EventSink.InvokeServerStarted();
_firstTick = TickCount;
RunEventLoop(); RunEventLoop();
} }
@ -526,7 +446,7 @@ public static class Core
#endif #endif
var cycleCount = _cyclesPerSecond.Length; var cycleCount = _cyclesPerSecond.Length;
long last = Stopwatch.GetTimestamp(); long last = _tickCount;
const int interval = 100; const int interval = 100;
double frequency = Stopwatch.Frequency * interval; double frequency = Stopwatch.Frequency * interval;
@ -534,7 +454,7 @@ public static class Core
while (!Closing) while (!Closing)
{ {
_tickCount = TickCount; _tickCount = GetTimestamp();
_now = DateTime.UtcNow; _now = DateTime.UtcNow;
Mobile.ProcessDeltaQueue(); Mobile.ProcessDeltaQueue();
@ -566,9 +486,10 @@ public static class Core
_tickCount = 0; _tickCount = 0;
_now = DateTime.MinValue; _now = DateTime.MinValue;
if (++sample % interval == 0) if (sample++ == interval)
{ {
var now = Stopwatch.GetTimestamp(); sample = 0;
var now = GetTimestamp();
var cyclesPerSecond = frequency / (now - last); var cyclesPerSecond = frequency / (now - last);
_cyclesPerSecond[_cycleIndex++] = cyclesPerSecond; _cyclesPerSecond[_cycleIndex++] = cyclesPerSecond;

View file

@ -15,7 +15,6 @@
using Server.Accounting; using Server.Accounting;
using Server.Collections; using Server.Collections;
using Server.Diagnostics;
using Server.HuePickers; using Server.HuePickers;
using Server.Items; using Server.Items;
using Server.Logging; using Server.Logging;
@ -455,14 +454,6 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
try try
{ {
PacketSendProfile prof = null;
if (Core.Profiling)
{
prof = PacketSendProfile.Acquire(span[0]);
prof.Start();
}
if (_packetEncoder != null) if (_packetEncoder != null)
{ {
length = _packetEncoder(span, buffer); length = _packetEncoder(span, buffer);
@ -484,8 +475,6 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
_flushPending.Enqueue(this); _flushPending.Enqueue(this);
_flushQueued = true; _flushQueued = true;
} }
prof?.Finish();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -804,14 +793,6 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
SetPacketTime(packetId); SetPacketTime(packetId);
} }
PacketReceiveProfile prof = null;
if (Core.Profiling)
{
prof = PacketReceiveProfile.Acquire(packetId);
prof?.Start();
}
UpdatePacketCount(packetId); UpdatePacketCount(packetId);
if (PacketLogging) if (PacketLogging)
@ -826,8 +807,6 @@ public partial class NetState : IComparable<NetState>, IValueLinkListNode<NetSta
handler.OnReceive(this, new SpanReader(packetReader.Buffer.Slice(start, remainingLength))); handler.OnReceive(this, new SpanReader(packetReader.Buffer.Slice(start, remainingLength)));
prof?.Finish(packetLength);
return ParserState.AwaitingNextPacket; return ParserState.AwaitingNextPacket;
} }

View file

@ -136,9 +136,6 @@ public partial class Timer
{ {
var finished = timer.Count != 0 && timer.Index + 1 >= timer.Count; var finished = timer.Count != 0 && timer.Index + 1 >= timer.Count;
var prof = timer.GetProfile();
prof?.Start();
// Stop the timer from running so that way if Start() is called in OnTick, the timer will be started. // Stop the timer from running so that way if Start() is called in OnTick, the timer will be started.
if (finished) if (finished)
{ {
@ -149,7 +146,6 @@ public partial class Timer
var version = timer.Version; var version = timer.Version;
timer.OnTick(); timer.OnTick();
prof?.Finish();
// Starting doesn't change the timer version, so we need to check if it's finished and if it's still running. // Starting doesn't change the timer version, so we need to check if it's finished and if it's still running.
if (timer.Version != version || finished && timer.Running) if (timer.Version != version || finished && timer.Running)

View file

@ -15,7 +15,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using Server.Diagnostics;
using Server.Logging; using Server.Logging;
namespace Server; namespace Server;
@ -54,13 +53,6 @@ public partial class Timer
Next = Core.Now + Delay; Next = Core.Now + Delay;
_ring = -1; _ring = -1;
_slot = -1; _slot = -1;
var prof = GetProfile();
if (prof != null)
{
prof.Created++;
}
} }
protected int Version { get; set; } // Used to determine if a timer was altered and we should abandon it. protected int Version { get; set; } // Used to determine if a timer was altered and we should abandon it.
@ -73,8 +65,6 @@ public partial class Timer
public int RemainingCount => Count == 0 ? int.MaxValue : Count - Index; public int RemainingCount => Count == 0 ? int.MaxValue : Count - Index;
public bool Running { get; private set; } public bool Running { get; private set; }
public TimerProfile GetProfile() => !Core.Profiling ? null : TimerProfile.Acquire(ToString() ?? "null");
public override string ToString() => GetType().FullName; public override string ToString() => GetType().FullName;
public Timer Start() public Timer Start()
@ -111,13 +101,6 @@ public partial class Timer
Running = true; Running = true;
AddTimer(this, (long)Delay.TotalMilliseconds); AddTimer(this, (long)Delay.TotalMilliseconds);
var prof = GetProfile();
if (prof != null)
{
prof.Started++;
}
return this; return this;
} }
@ -174,12 +157,6 @@ public partial class Timer
{ {
_executingRings[_ring] = _nextTimer; _executingRings[_ring] = _nextTimer;
} }
var prof = GetProfile();
if (prof != null)
{
prof.Stopped++;
}
} }
protected virtual void OnTick() protected virtual void OnTick()

View file

@ -1,427 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Server.Diagnostics;
namespace Server.Commands
{
public static class Profiling
{
public static void Configure()
{
CommandSystem.Register("DumpTimers", AccessLevel.Administrator, DumpTimers_OnCommand);
CommandSystem.Register("CountObjects", AccessLevel.Administrator, CountObjects_OnCommand);
CommandSystem.Register("ProfileWorld", AccessLevel.Administrator, ProfileWorld_OnCommand);
CommandSystem.Register("TraceInternal", AccessLevel.Administrator, TraceInternal_OnCommand);
CommandSystem.Register("TraceExpanded", AccessLevel.Administrator, TraceExpanded_OnCommand);
CommandSystem.Register("WriteProfiles", AccessLevel.Administrator, WriteProfiles_OnCommand);
CommandSystem.Register("SetProfiles", AccessLevel.Administrator, SetProfiles_OnCommand);
}
[Usage("WriteProfiles")]
[Description("Generates a log files containing performance diagnostic information.")]
public static void WriteProfiles_OnCommand(CommandEventArgs e)
{
try
{
using var sw = new StreamWriter("profiles.log", true);
sw.WriteLine("# Dump on {0:f}", Core.Now);
sw.WriteLine($"# Core profiling for {Core.ProfileTime}");
sw.WriteLine("# Packet send");
BaseProfile.WriteAll(sw, PacketSendProfile.Profiles);
sw.WriteLine();
sw.WriteLine("# Packet receive");
BaseProfile.WriteAll(sw, PacketReceiveProfile.Profiles);
sw.WriteLine();
sw.WriteLine("# Timer");
BaseProfile.WriteAll(sw, TimerProfile.Profiles);
sw.WriteLine();
sw.WriteLine("# Gump response");
BaseProfile.WriteAll(sw, GumpProfile.Profiles);
sw.WriteLine();
sw.WriteLine("# Target response");
BaseProfile.WriteAll(sw, TargetProfile.Profiles);
sw.WriteLine();
}
catch
{
// ignored
}
}
[Usage("SetProfiles [true | false]"),
Description("Enables, disables, or toggles the state of core packet and timer profiling.")]
public static void SetProfiles_OnCommand(CommandEventArgs e)
{
if (e.Length == 1)
{
Core.Profiling = e.GetBoolean(0);
}
else
{
Core.Profiling = !Core.Profiling;
}
if (Core.Profiling)
{
e.Mobile.SendMessage($"Profiling has been enabled.");
}
else
{
e.Mobile.SendMessage($"Profiling has been disabled.");
}
}
[Usage("DumpTimers"),
Description("Generates a log file of all currently executing timers. Used for tracing timer leaks.")]
public static void DumpTimers_OnCommand(CommandEventArgs e)
{
try
{
using var sw = new StreamWriter("timerdump.log", true);
Timer.DumpInfo(sw);
e.Mobile.SendMessage("Timers dumped to timerdump.log");
}
catch
{
// ignored
}
}
[Usage("CountObjects")]
[Description("Generates a log file detailing all item and mobile types in the world.")]
public static void CountObjects_OnCommand(CommandEventArgs e)
{
using (var op = new StreamWriter("objects.log"))
{
var table = new Dictionary<Type, int>();
foreach (var item in World.Items.Values)
{
var type = item.GetType();
table[type] = (table.TryGetValue(type, out var value) ? value : 0) + 1;
}
var items = table.ToList();
table.Clear();
foreach (var m in World.Mobiles.Values)
{
var type = m.GetType();
table[type] = (table.TryGetValue(type, out var value) ? value : 0) + 1;
}
var mobiles = table.ToList();
items.Sort(new CountSorter());
mobiles.Sort(new CountSorter());
op.WriteLine("# Object count table generated on {0}", Core.Now);
op.WriteLine();
op.WriteLine();
op.WriteLine("# Items:");
items.ForEach(
kvp =>
op.WriteLine("{0}\t{1:F2}%\t{2}", kvp.Value, 100.0 * kvp.Value / World.Items.Count, kvp.Key)
);
op.WriteLine();
op.WriteLine();
op.WriteLine("#Mobiles:");
mobiles.ForEach(
kvp =>
op.WriteLine("{0}\t{1:F2}%\t{2}", kvp.Value, 100.0 * kvp.Value / World.Mobiles.Count, kvp.Key)
);
}
e.Mobile.SendMessage("Object table has been generated. See the file : objects.log");
}
[Usage("TraceExpanded")]
[Description("Generates a log file describing all items using expanded memory.")]
public static void TraceExpanded_OnCommand(CommandEventArgs e)
{
var typeTable = new Dictionary<Type, int[]>();
foreach (var item in World.Items.Values)
{
var flags = item.GetExpandFlags();
if ((flags & ~(ExpandFlag.TempFlag | ExpandFlag.SaveFlag)) == 0)
{
continue;
}
var itemType = item.GetType();
do
{
if (!typeTable.TryGetValue(itemType, out var countTable))
{
typeTable[itemType] = countTable = new int[9];
}
if ((flags & ExpandFlag.Name) != 0)
{
++countTable[0];
}
if ((flags & ExpandFlag.Items) != 0)
{
++countTable[1];
}
if ((flags & ExpandFlag.Bounce) != 0)
{
++countTable[2];
}
if ((flags & ExpandFlag.Holder) != 0)
{
++countTable[3];
}
if ((flags & ExpandFlag.Blessed) != 0)
{
++countTable[4];
}
/*if (( flags & ExpandFlag.TempFlag ) != 0)
++countTable[5];
if (( flags & ExpandFlag.SaveFlag ) != 0)
++countTable[6];*/
if ((flags & ExpandFlag.Weight) != 0)
{
++countTable[7];
}
if ((flags & ExpandFlag.Spawner) != 0)
{
++countTable[8];
}
itemType = itemType.BaseType;
} while (itemType != typeof(object));
}
try
{
using var op = new StreamWriter("expandedItems.log", true);
string[] names =
{
"Name",
"Items",
"Bounce",
"Holder",
"Blessed",
"TempFlag",
"SaveFlag",
"Weight",
"Spawner"
};
var list = typeTable.ToList();
list.Sort(new CountsSorter());
foreach (var kvp in list)
{
var countTable = kvp.Value;
op.WriteLine("# {0}", kvp.Key.FullName);
for (var i = 0; i < countTable.Length; ++i)
{
if (countTable[i] > 0)
{
op.WriteLine("{0}\t{1:N0}", names[i], countTable[i]);
}
}
op.WriteLine();
}
}
catch
{
// ignored
}
}
[Usage("TraceInternal")]
[Description("Generates a log file describing all items in the 'internal' map.")]
public static void TraceInternal_OnCommand(CommandEventArgs e)
{
var totalCount = 0;
var table = new Dictionary<Type, int[]>();
foreach (var item in World.Items.Values)
{
if (item.Parent != null || item.Map != Map.Internal)
{
continue;
}
++totalCount;
var type = item.GetType();
if (table.TryGetValue(type, out var parms))
{
parms[0]++;
parms[1] += item.Amount;
}
else
{
table[type] = new[] { 1, item.Amount };
}
}
using var op = new StreamWriter("internal.log");
op.WriteLine("# {0} items found", totalCount);
op.WriteLine("# {0} different types", table.Count);
op.WriteLine();
op.WriteLine();
op.WriteLine("Type\t\tCount\t\tAmount\t\tAvg. Amount");
foreach (var de in table)
{
var parms = de.Value;
op.WriteLine("{0}\t\t{1}\t\t{2}\t\t{3:F2}", de.Key.Name, parms[0], parms[1], (double)parms[1] / parms[0]);
}
}
[Usage("ProfileWorld"),
Description("Prints the amount of data serialized for every object type in your world file.")]
public static void ProfileWorld_OnCommand(CommandEventArgs e)
{
ProfileWorld("items", "worldprofile_items.log");
ProfileWorld("mobiles", "worldprofile_mobiles.log");
}
public static void ProfileWorld(string type, string opFile)
{
try
{
var types = new List<Type>();
using (var bin = new BinaryReader(
new FileStream(
string.Format("Saves/{0}/{0}.tdb", type),
FileMode.Open,
FileAccess.Read,
FileShare.Read
)
))
{
var count = bin.ReadInt32();
for (var i = 0; i < count; ++i)
{
types.Add(AssemblyHandler.FindTypeByFullName(bin.ReadString()));
}
}
long total = 0;
var table = new Dictionary<Type, int>();
using (var bin = new BinaryReader(
new FileStream(
string.Format("Saves/{0}/{0}.idx", type),
FileMode.Open,
FileAccess.Read,
FileShare.Read
)
))
{
var count = bin.ReadInt32();
for (var i = 0; i < count; ++i)
{
var typeID = bin.ReadInt32();
var serial = bin.ReadInt32();
var pos = bin.ReadInt64();
var length = bin.ReadInt32();
var objType = types[typeID];
while (objType != null && objType != typeof(object))
{
table[objType] = length + (table.TryGetValue(objType, out var value) ? value : 0);
objType = objType.BaseType;
total += length;
}
}
}
var list = table.ToList();
list.Sort(new CountSorter());
using var op = new StreamWriter(opFile);
op.WriteLine("# Profile of world {0}", type);
op.WriteLine("# Generated on {0}", Core.Now);
op.WriteLine();
op.WriteLine();
list.ForEach(
kvp =>
op.WriteLine("{0}\t{1:F2}%\t{2}", kvp.Value, 100.0 * kvp.Value / total, kvp.Key)
);
}
catch
{
// ignored
}
}
private class CountSorter : IComparer<KeyValuePair<Type, int>>
{
public int Compare(KeyValuePair<Type, int> x, KeyValuePair<Type, int> y)
{
var aCount = x.Value;
var bCount = y.Value;
var v = -aCount.CompareTo(bCount);
return v != 0 ? v : string.CompareOrdinal(x.Key.FullName, y.Key.FullName);
}
}
private class CountsSorter : IComparer<KeyValuePair<Type, int[]>>
{
public int Compare(KeyValuePair<Type, int[]> x, KeyValuePair<Type, int[]> y)
{
var aCount = 0;
foreach (var val in x.Value)
{
aCount += val;
}
var bCount = 0;
foreach (var val in y.Value)
{
bCount += val;
}
var v = -aCount.CompareTo(bCount);
return v != 0 ? v : string.CompareOrdinal(x.Key.FullName, y.Key.FullName);
}
}
}
}

View file

@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
namespace Server.Diagnostics;
public class GumpProfile : BaseProfile
{
private static readonly Dictionary<Type, GumpProfile> _profiles = new();
public GumpProfile(Type type) : base(type.FullName)
{
}
public static IEnumerable<GumpProfile> Profiles => _profiles.Values;
public static GumpProfile Acquire(Type type)
{
if (!Core.Profiling)
{
return null;
}
if (!_profiles.TryGetValue(type, out var prof))
{
_profiles.Add(type, prof = new GumpProfile(type));
}
return prof;
}
}

View file

@ -13,7 +13,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * * along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/ *************************************************************************/
using Server.Diagnostics;
using Server.Engines.Virtues; using Server.Engines.Virtues;
using Server.Exceptions; using Server.Exceptions;
using Server.Mobiles; using Server.Mobiles;
@ -157,10 +156,6 @@ public static partial class GumpSystem
Remove(state, baseGump); Remove(state, baseGump);
var prof = GumpProfile.Acquire(baseGump.GetType());
prof?.Start();
var relayInfo = new RelayInfo( var relayInfo = new RelayInfo(
buttonId, buttonId,
switches, switches,
@ -169,8 +164,6 @@ public static partial class GumpSystem
textBlock textBlock
); );
baseGump.OnResponse(state, relayInfo); baseGump.OnResponse(state, relayInfo);
prof?.Finish();
} }
if (typeId == 461) if (typeId == 461)

View file

@ -14,7 +14,6 @@
*************************************************************************/ *************************************************************************/
using System.Buffers; using System.Buffers;
using Server.Diagnostics;
using Server.Targeting; using Server.Targeting;
namespace Server.Network; namespace Server.Network;
@ -52,95 +51,83 @@ public static class IncomingTargetingPackets
return; return;
} }
var prof = TargetProfile.Acquire(t.GetType()); if (x == -1 && y == -1 && !serial.IsValid)
prof?.Start();
try
{ {
if (x == -1 && y == -1 && !serial.IsValid) // User pressed escape
{ t.Cancel(from, TargetCancelType.Canceled);
// User pressed escape }
t.Cancel(from, TargetCancelType.Canceled); else if (t.TargetID == targetID)
} {
else if (t.TargetID == targetID) object toTarget;
{
object toTarget;
if (type == 1) if (type == 1)
{
if (graphic == 0)
{ {
if (graphic == 0) toTarget = new LandTarget(new Point3D(x, y, z), from.Map);
}
else
{
var map = from.Map;
if (map == null || map == Map.Internal)
{ {
toTarget = new LandTarget(new Point3D(x, y, z), from.Map); t.Cancel(from, TargetCancelType.Canceled);
return;
} }
else else
{ {
var map = from.Map; if (state.HighSeas)
{
var id = TileData.ItemTable[graphic & TileData.MaxItemValue];
if (id.Surface)
{
z -= id.Height;
}
}
if (map == null || map == Map.Internal) int hue = 0;
var valid = false;
var eable = t.DisallowMultis
? map.Tiles.GetStaticTiles(x, y)
: map.Tiles.GetStaticAndMultiTiles(x, y);
foreach (var tile in eable)
{
if (tile.Z == z && tile.ID == graphic)
{
valid = true;
hue = tile.Hue;
break;
}
}
if (!valid)
{ {
t.Cancel(from, TargetCancelType.Canceled); t.Cancel(from, TargetCancelType.Canceled);
return; return;
} }
else
{
if (state.HighSeas)
{
var id = TileData.ItemTable[graphic & TileData.MaxItemValue];
if (id.Surface)
{
z -= id.Height;
}
}
int hue = 0; toTarget = new StaticTarget(new Point3D(x, y, z), graphic, hue);
var valid = false;
var eable = t.DisallowMultis
? map.Tiles.GetStaticTiles(x, y)
: map.Tiles.GetStaticAndMultiTiles(x, y);
foreach (var tile in eable)
{
if (tile.Z == z && tile.ID == graphic)
{
valid = true;
hue = tile.Hue;
break;
}
}
if (!valid)
{
t.Cancel(from, TargetCancelType.Canceled);
return;
}
else
{
toTarget = new StaticTarget(new Point3D(x, y, z), graphic, hue);
}
}
} }
} }
else if (serial.IsMobile)
{
toTarget = World.FindMobile(serial);
}
else if (serial.IsItem)
{
toTarget = World.FindItem(serial);
}
else
{
t.Cancel(from, TargetCancelType.Canceled);
return;
}
t.Invoke(from, toTarget);
} }
} else if (serial.IsMobile)
finally {
{ toTarget = World.FindMobile(serial);
prof?.Finish(); }
else if (serial.IsItem)
{
toTarget = World.FindItem(serial);
}
else
{
t.Cancel(from, TargetCancelType.Canceled);
return;
}
t.Invoke(from, toTarget);
} }
} }
} }