diff --git a/BlazorServer/Pages/GoalsComponent.razor b/BlazorServer/Pages/GoalsComponent.razor
index 50080be32..e07a6baa3 100644
--- a/BlazorServer/Pages/GoalsComponent.razor
+++ b/BlazorServer/Pages/GoalsComponent.razor
@@ -6,7 +6,10 @@
@if (ShowGoals)
{
diff --git a/Core/BotController.cs b/Core/BotController.cs
index 0f9b5de53..6916be5bc 100644
--- a/Core/BotController.cs
+++ b/Core/BotController.cs
@@ -15,6 +15,7 @@
using WinAPI;
using Microsoft.Extensions.Configuration;
using SharedLib.NpcFinder;
+using Cyotek.Collections.Generic;
namespace Core
{
@@ -34,7 +35,7 @@ public sealed class BotController : IBotController, IDisposable
public Thread? screenshotThread { get; set; }
- private const int screenshotTickMs = 150;
+ private const int screenshotTickMs = 200;
private DateTime lastScreenshot;
public Thread addonThread { get; set; }
@@ -74,6 +75,34 @@ public sealed class BotController : IBotController, IDisposable
private readonly AutoResetEvent addonAutoResetEvent = new(false);
private readonly AutoResetEvent npcNameFinderAutoResetEvent = new(false);
+ public double AvgScreenLatency
+ {
+ get
+ {
+ double avg = 0;
+ for (int i = 0; i < ScreenLatencys.Size; i++)
+ {
+ avg += ScreenLatencys.PeekAt(i);
+ }
+ return avg /= ScreenLatencys.Size;
+ }
+ }
+ private readonly CircularBuffer ScreenLatencys;
+
+ public double AvgNPCLatency
+ {
+ get
+ {
+ double avg = 0;
+ for (int i = 0; i < NPCLatencys.Size; i++)
+ {
+ avg += NPCLatencys.PeekAt(i);
+ }
+ return avg /= NPCLatencys.Size;
+ }
+ }
+ private readonly CircularBuffer NPCLatencys;
+
public BotController(ILogger logger, IPPather pather, DataConfig dataConfig, IConfiguration configuration)
{
this.logger = logger;
@@ -112,6 +141,9 @@ public BotController(ILogger logger, IPPather pather, DataConfig dataConfig, ICo
minimapNodeFinder = new MinimapNodeFinder(WowScreen, new PixelClassifier());
MinimapImageFinder = minimapNodeFinder as IImageProvider;
+ ScreenLatencys = new CircularBuffer(5);
+ NPCLatencys = new CircularBuffer(5);
+
addonThread = new Thread(AddonRefreshThread);
addonThread.Start();
@@ -157,14 +189,21 @@ public void AddonRefreshThread()
public void ScreenshotRefreshThread()
{
var nodeFound = false;
+ var stopWatch = new Stopwatch();
while (this.Enabled)
{
if ((DateTime.UtcNow - lastScreenshot).TotalMilliseconds > screenshotTickMs)
{
if (this.WowScreen.Enabled)
{
+ stopWatch.Restart();
this.WowScreen.UpdateScreenshot();
+ ScreenLatencys.Put(stopWatch.ElapsedMilliseconds);
+
+ stopWatch.Restart();
this.npcNameFinder.Update();
+ NPCLatencys.Put(stopWatch.ElapsedMilliseconds);
+
this.WowScreen.PostProcess();
}
else
@@ -189,11 +228,10 @@ public void ScreenshotRefreshThread()
MapId = this.AddonReader.UIMapId.Value,
Spot = this.AddonReader.PlayerReader.PlayerLocation
});
- updatePlayerPostion.Reset();
updatePlayerPostion.Restart();
}
- Thread.Sleep(10);
+ Thread.Sleep(5);
}
this.logger.LogInformation("Screenshot thread stoppped!");
}
diff --git a/Core/ConfigBotController.cs b/Core/ConfigBotController.cs
index 75585fc8a..38d128faf 100644
--- a/Core/ConfigBotController.cs
+++ b/Core/ConfigBotController.cs
@@ -37,6 +37,9 @@ public class ConfigBotController : IBotController
public event EventHandler? ProfileLoaded;
public event EventHandler? StatusChanged;
+ public double AvgScreenLatency { get => throw new NotImplementedException(); }
+ public double AvgNPCLatency { get => throw new NotImplementedException(); }
+
public void Shutdown()
{
diff --git a/Core/IBotController.cs b/Core/IBotController.cs
index f9aed4046..ca39429a5 100644
--- a/Core/IBotController.cs
+++ b/Core/IBotController.cs
@@ -33,6 +33,9 @@ public interface IBotController
event System.EventHandler? ProfileLoaded;
event System.EventHandler StatusChanged;
+ double AvgScreenLatency { get; }
+ double AvgNPCLatency { get; }
+
void ToggleBotStatus();
void StopBot();
diff --git a/CoreTests/NpcNameFinder/MockWoWProcess.cs b/CoreTests/NpcNameFinder/MockWoWProcess.cs
index 82ecc15b3..6dc9ba055 100644
--- a/CoreTests/NpcNameFinder/MockWoWProcess.cs
+++ b/CoreTests/NpcNameFinder/MockWoWProcess.cs
@@ -6,12 +6,12 @@ namespace CoreTests
{
public class MockWoWProcess : IMouseInput
{
- public ValueTask RightClickMouse(Point position)
+ public void RightClickMouse(Point position)
{
throw new System.NotImplementedException();
}
- public ValueTask LeftClickMouse(Point position)
+ public void LeftClickMouse(Point position)
{
throw new System.NotImplementedException();
}
diff --git a/CoreTests/NpcNameFinder/Test_NpcNameFinderTarget.cs b/CoreTests/NpcNameFinder/Test_NpcNameFinderTarget.cs
index d1d651520..822cab2c4 100644
--- a/CoreTests/NpcNameFinder/Test_NpcNameFinderTarget.cs
+++ b/CoreTests/NpcNameFinder/Test_NpcNameFinderTarget.cs
@@ -32,10 +32,13 @@ public void Execute()
{
npcNameFinder.ChangeNpcType(NpcNames.Enemy | NpcNames.Neutral);
- capturer.Capture();
-
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
+ capturer.Capture();
+ stopwatch.Stop();
+ logger.LogInformation($"Capture: {stopwatch.ElapsedMilliseconds}ms");
+
+ stopwatch.Restart();
this.npcNameFinder.Update();
stopwatch.Stop();
logger.LogInformation($"Update: {stopwatch.ElapsedMilliseconds}ms");
diff --git a/CoreTests/Program.cs b/CoreTests/Program.cs
index 4ba221cb9..aa3eda2c2 100644
--- a/CoreTests/Program.cs
+++ b/CoreTests/Program.cs
@@ -1,5 +1,6 @@
using Serilog;
using Serilog.Extensions.Logging;
+using System.Threading;
using System.Threading.Tasks;
namespace CoreTests
@@ -25,7 +26,14 @@ public static void Main()
//var test = new Test_NpcNameFinderTarget(logger);
var test = new Test_NpcNameFinderLoot(logger);
- test.Execute();
+ int count = 1;
+ int i = 0;
+ while (i < count)
+ {
+ test.Execute();
+ i++;
+ Thread.Sleep(150);
+ }
//MainAsync().GetAwaiter().GetResult();
}
diff --git a/SharedLib/NpcFinder/NpcNameFinder.cs b/SharedLib/NpcFinder/NpcNameFinder.cs
index d39c59c29..8dab6d7fc 100644
--- a/SharedLib/NpcFinder/NpcNameFinder.cs
+++ b/SharedLib/NpcFinder/NpcNameFinder.cs
@@ -6,6 +6,8 @@
using System.Linq;
using System.Drawing.Imaging;
using System.Threading;
+using System.Diagnostics;
+using System.Threading.Tasks;
namespace SharedLib.NpcFinder
{
@@ -19,8 +21,15 @@ public enum NpcNames
Corpse = 8
}
+ public enum SearchMode
+ {
+ Simple = 0,
+ Fuzzy = 1
+ }
+
public class NpcNameFinder
{
+ private readonly SearchMode searchMode = SearchMode.Simple;
private NpcNames nameType = NpcNames.Enemy | NpcNames.Neutral;
private readonly List npcNameLine = new List();
@@ -46,9 +55,13 @@ public class NpcNameFinder
public bool PotentialAddsExist { get; private set; }
public DateTime LastPotentialAddsSeen { get; private set; }
+ private Func colorMatcher;
+
#region variables
- public int topOffset { get; set; } = 30;
+ public float colorFuzziness { get; set; } = 15f;
+
+ public int topOffset { get; set; } = 110;
public int npcPosYOffset { get; set; } = 0;
public int npcPosYHeightMul { get; set; } = 10;
@@ -69,12 +82,26 @@ public class NpcNameFinder
#endregion
+ #region Colors
+
+ private readonly Color fEnemy = Color.FromArgb(0, 250, 5, 5);
+ private readonly Color fFriendly = Color.FromArgb(0, 5, 250, 5);
+ private readonly Color fNeutrual = Color.FromArgb(0, 250, 250, 5);
+ private readonly Color fCorpse = Color.FromArgb(0, 128, 128, 128);
+
+ private readonly Color sEnemy = Color.FromArgb(0, 240, 35, 35);
+ private readonly Color sFriendly = Color.FromArgb(0, 0, 250, 0);
+ private readonly Color sNeutrual = Color.FromArgb(0, 250, 250, 0);
+
+ #endregion
public NpcNameFinder(ILogger logger, IBitmapProvider bitmapProvider, AutoResetEvent autoResetEvent)
{
this.logger = logger;
this.bitmapProvider = bitmapProvider;
this.autoResetEvent = autoResetEvent;
+
+ UpdateSearchMode();
}
private float ScaleWidth(int value)
@@ -106,32 +133,121 @@ public void ChangeNpcType(NpcNames type)
npcPosYHeightMul = 10;
}
- logger.LogInformation($"{nameof(NpcNameFinder)}.{nameof(ChangeNpcType)} = {type}");
+ UpdateSearchMode();
+
+ logger.LogInformation($"{nameof(NpcNameFinder)}.{nameof(ChangeNpcType)} = {type} | searchMode = {searchMode}");
}
}
- private bool ColorMatch(Color p)
+ private void UpdateSearchMode()
{
- return nameType switch
+ switch (searchMode)
{
- NpcNames.Enemy | NpcNames.Neutral => (p.R > 240 && p.G <= 35 && p.B <= 35) || (p.R > 250 && p.G > 250 && p.B == 0),
- NpcNames.Friendly | NpcNames.Neutral => (p.R == 0 && p.G > 250 && p.B == 0) || (p.R > 250 && p.G > 250 && p.B == 0),
- NpcNames.Enemy => p.R > 240 && p.G <= 35 && p.B <= 35,
- NpcNames.Friendly => p.R == 0 && p.G > 250 && p.B == 0,
- NpcNames.Neutral => p.R > 250 && p.G > 250 && p.B == 0,
- NpcNames.Corpse => p.R == 128 && p.G == 128 && p.B == 128,
- _ => false,
- };
+ case SearchMode.Simple:
+ BakeSimpleColorMatcher();
+ break;
+ case SearchMode.Fuzzy:
+ BakeFuzzyColorMatcher();
+ break;
+ }
}
+ #region Simple Color matcher
+
+ private void BakeSimpleColorMatcher()
+ {
+ switch (nameType)
+ {
+ case NpcNames.Enemy | NpcNames.Neutral:
+ colorMatcher = (Color c) => SimpleColorEnemy(c) || SimpleColorNeutral(c);
+ return;
+ case NpcNames.Friendly | NpcNames.Neutral:
+ colorMatcher = (Color c) => SimpleColorFriendly(c) || SimpleColorNeutral(c);
+ return;
+ case NpcNames.Enemy:
+ colorMatcher = SimpleColorEnemy;
+ return;
+ case NpcNames.Friendly:
+ colorMatcher = SimpleColorFriendly;
+ return;
+ case NpcNames.Neutral:
+ colorMatcher = SimpleColorNeutral;
+ return;
+ case NpcNames.Corpse:
+ colorMatcher = SimpleColorCorpse;
+ return;
+ }
+ }
+
+ private bool SimpleColorEnemy(Color p)
+ {
+ return p.R > sEnemy.R && p.G <= sEnemy.G && p.B <= sEnemy.B;
+ }
+
+ private bool SimpleColorFriendly(Color p)
+ {
+ return p.R == sFriendly.R && p.G > sFriendly.G && p.B == sFriendly.B;
+ }
+
+ private bool SimpleColorNeutral(Color p)
+ {
+ return p.R > sNeutrual.R && p.G > sNeutrual.G && p.B == sNeutrual.B;
+ }
+
+ private bool SimpleColorCorpse(Color p)
+ {
+ return p.R == fCorpse.R && p.G == fCorpse.G && p.B == fCorpse.B;
+ }
+
+ #endregion
+
+
+ #region Color Fuzziness matcher
+
+ private void BakeFuzzyColorMatcher()
+ {
+ switch (nameType)
+ {
+ case NpcNames.Enemy | NpcNames.Neutral:
+ colorMatcher = (Color c) => FuzzyColor(fEnemy, c) || FuzzyColor(fNeutrual, c);
+ return;
+ case NpcNames.Friendly | NpcNames.Neutral:
+ colorMatcher = (Color c) => FuzzyColor(fFriendly, c) || FuzzyColor(fNeutrual, c);
+ return;
+ case NpcNames.Enemy:
+ colorMatcher = (Color c) => FuzzyColor(fEnemy, c);
+ return;
+ case NpcNames.Friendly:
+ colorMatcher = (Color c) => FuzzyColor(fFriendly, c);
+ return;
+ case NpcNames.Neutral:
+ colorMatcher = (Color c) => FuzzyColor(fNeutrual, c);
+ return;
+ case NpcNames.Corpse:
+ colorMatcher = (Color c) => FuzzyColor(fCorpse, c);
+ return;
+ }
+ }
+
+ private bool FuzzyColor(Color target, Color c)
+ {
+ return MathF.Sqrt(
+ ((target.R - c.R) * (target.R - c.R)) +
+ ((target.G - c.G) * (target.G - c.G)) +
+ ((target.B - c.B) * (target.B - c.B)))
+ <= colorFuzziness;
+ }
+
+ #endregion
+
public void Update()
{
scaleToRefWidth = ScaleWidth(1);
scaleToRefHeight = ScaleHeight(1);
Area = new Rectangle(new Point(0, (int)ScaleHeight(topOffset)),
- new Size(bitmapProvider.Bitmap.Width, (int)(bitmapProvider.Bitmap.Height * 0.6f)));
+ new Size((int)(bitmapProvider.Bitmap.Width * 0.87f), (int)(bitmapProvider.Bitmap.Height * 0.6f)));
PopulateLinesOfNpcNames();
@@ -222,7 +338,8 @@ private void PopulateLinesOfNpcNames()
BitmapData bitmapData = bitmapProvider.Bitmap.LockBits(new Rectangle(0, 0, bitmapProvider.Bitmap.Width, bitmapProvider.Bitmap.Height), ImageLockMode.ReadOnly, bitmapProvider.Bitmap.PixelFormat);
int bytesPerPixel = Bitmap.GetPixelFormatSize(bitmapProvider.Bitmap.PixelFormat) / 8;
- for (int y = Area.Top; y < Area.Height; y += incY)
+ //for (int y = Area.Top; y < Area.Height; y += incY)
+ Parallel.For(Area.Top, Area.Height, y =>
{
bool isEndOfSection;
var lengthStart = -1;
@@ -233,7 +350,7 @@ private void PopulateLinesOfNpcNames()
{
int xi = x * bytesPerPixel;
- if (ColorMatch(Color.FromArgb(255, currentLine[xi + 2], currentLine[xi + 1], currentLine[xi])))
+ if (colorMatcher(Color.FromArgb(255, currentLine[xi + 2], currentLine[xi + 1], currentLine[xi])))
{
var isSameSection = lengthStart > -1 && (x - lengthEnd) < minLength;
if (isSameSection)
@@ -259,7 +376,7 @@ private void PopulateLinesOfNpcNames()
{
npcNameLine.Add(new LineOfNpcName(lengthStart, lengthEnd, y));
}
- }
+ });
bitmapProvider.Bitmap.UnlockBits(bitmapData);
}