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:
parent
65bc264402
commit
2e6ddcd31a
14 changed files with 80 additions and 949 deletions
|
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue