optimizations
This commit is contained in:
parent
419e2eb4a4
commit
d124f2c288
44 changed files with 1663 additions and 639 deletions
|
|
@ -35,6 +35,16 @@ public sealed class MemoryPoller : IDisposable
|
|||
private int _coldHz;
|
||||
private long _coldTickNumber;
|
||||
|
||||
// Stats snapshot (updated once per second)
|
||||
private long _lastStatsMs;
|
||||
private volatile int _readsPerSec;
|
||||
private volatile int _kbPerSec;
|
||||
private volatile int _entityCount;
|
||||
|
||||
public int ReadsPerSec => _readsPerSec;
|
||||
public int KBPerSec => _kbPerSec;
|
||||
public int EntityCount => _entityCount;
|
||||
|
||||
public event Action? StateUpdated;
|
||||
|
||||
public MemoryPoller(GameMemoryReader reader, GameDataCache cache, BotConfig config)
|
||||
|
|
@ -94,6 +104,25 @@ public sealed class MemoryPoller : IDisposable
|
|||
}
|
||||
|
||||
hotTickCount++;
|
||||
|
||||
// Update stats once per second
|
||||
var nowMs = Environment.TickCount64;
|
||||
if (nowMs - _lastStatsMs >= 1000)
|
||||
{
|
||||
_lastStatsMs = nowMs;
|
||||
if (_mem is not null)
|
||||
{
|
||||
var (reads, bytes) = _mem.SnapshotAndResetCounters();
|
||||
_readsPerSec = (int)reads;
|
||||
_kbPerSec = (int)(bytes / 1024);
|
||||
}
|
||||
_entityCount = _cache.Entities.Count;
|
||||
|
||||
if (MemoryProfiler.IsEnabled)
|
||||
MemoryProfiler.LatestData = MemoryProfiler.SnapshotAndReset();
|
||||
else
|
||||
MemoryProfiler.LatestData = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -120,8 +149,11 @@ public sealed class MemoryPoller : IDisposable
|
|||
_mem = ctx.Memory;
|
||||
_offsets = ctx.Offsets;
|
||||
|
||||
// Slow data (quests, character name) every 10th cold tick (~1Hz)
|
||||
var isSlowTick = _coldTickNumber % 10 == 0;
|
||||
|
||||
// Full snapshot
|
||||
var snap = _reader.ReadSnapshot();
|
||||
var snap = _reader.ReadSnapshot(readSlowData: isSlowTick);
|
||||
if (!snap.Attached) return previous;
|
||||
|
||||
// Re-resolve hot addresses
|
||||
|
|
@ -136,19 +168,24 @@ public sealed class MemoryPoller : IDisposable
|
|||
var state = BuildGameState(snap, previous);
|
||||
_coldTickNumber++;
|
||||
|
||||
// Update cache — cold fields
|
||||
// Update cache — cold fields (every tick)
|
||||
_cache.Entities = state.Entities;
|
||||
_cache.HostileMonsters = state.HostileMonsters;
|
||||
_cache.NearbyLoot = state.NearbyLoot;
|
||||
_cache.Terrain = state.Terrain;
|
||||
_cache.AreaHash = state.AreaHash;
|
||||
_cache.AreaLevel = state.AreaLevel;
|
||||
_cache.CharacterName = state.Player.CharacterName;
|
||||
_cache.GameUiPtr = snap.GameUiPtr;
|
||||
_cache.UiQuestGroups = snap.UiQuestGroups;
|
||||
_cache.QuestLinkedList = snap.QuestLinkedList;
|
||||
_cache.QuestStates = snap.QuestStates;
|
||||
_cache.LatestState = state;
|
||||
|
||||
// Slow fields — only update when actually read (1Hz)
|
||||
if (isSlowTick)
|
||||
{
|
||||
_cache.CharacterName = state.Player.CharacterName;
|
||||
_cache.UiQuestGroups = snap.UiQuestGroups;
|
||||
_cache.QuestLinkedList = snap.QuestLinkedList;
|
||||
_cache.QuestStates = snap.QuestStates;
|
||||
}
|
||||
_cache.ColdTickTimestamp = Environment.TickCount64;
|
||||
|
||||
// Also update hot fields from the snapshot (so they're never stale)
|
||||
|
|
@ -173,6 +210,7 @@ public sealed class MemoryPoller : IDisposable
|
|||
private void DoHotTick()
|
||||
{
|
||||
if (_mem is null || _offsets is null) return;
|
||||
MemoryProfiler.BeginSection("Hot");
|
||||
|
||||
// 1. Camera matrix (64 bytes, 1 RPM)
|
||||
if (_cameraMatrixAddr != 0)
|
||||
|
|
@ -249,6 +287,7 @@ public sealed class MemoryPoller : IDisposable
|
|||
}
|
||||
|
||||
_cache.HotTickTimestamp = Environment.TickCount64;
|
||||
MemoryProfiler.EndSection();
|
||||
}
|
||||
|
||||
private GameState BuildGameState(GameStateSnapshot snap, GameState? previous)
|
||||
|
|
@ -271,7 +310,7 @@ public sealed class MemoryPoller : IDisposable
|
|||
|
||||
state.Player = new PlayerState
|
||||
{
|
||||
CharacterName = snap.CharacterName,
|
||||
CharacterName = snap.CharacterName ?? previous?.Player.CharacterName,
|
||||
HasPosition = snap.HasPosition,
|
||||
Position = snap.HasPosition ? new Vector2(snap.PlayerX, snap.PlayerY) : Vector2.Zero,
|
||||
Z = snap.PlayerZ,
|
||||
|
|
@ -333,31 +372,32 @@ public sealed class MemoryPoller : IDisposable
|
|||
state.NearbyLoot = loot;
|
||||
}
|
||||
|
||||
if (snap.QuestFlags is { Count: > 0 })
|
||||
if (snap.QuestLinkedList is { Count: > 0 })
|
||||
{
|
||||
// StateId: 1=available/in-progress, 2=completed, 3+=special
|
||||
// Filter to non-completed quests for ActiveQuests
|
||||
state.ActiveQuests = snap.QuestFlags
|
||||
.Where(q => q.StateId != 2) // exclude completed
|
||||
// StateId: 0=done, -1=locked, positive=in-progress
|
||||
state.ActiveQuests = snap.QuestLinkedList
|
||||
.Where(q => q.StateId > 0) // in-progress only
|
||||
.Select(q => new QuestProgress
|
||||
{
|
||||
QuestStateIndex = q.QuestStateIndex,
|
||||
QuestName = q.QuestName,
|
||||
QuestName = q.DisplayName,
|
||||
InternalId = q.InternalId,
|
||||
StateId = q.StateId,
|
||||
StateId = (byte)Math.Clamp(q.StateId, 0, 255),
|
||||
IsTracked = q.IsTracked,
|
||||
StateText = q.StateText,
|
||||
ProgressText = q.ProgressText,
|
||||
}).ToList();
|
||||
|
||||
var activeCount = state.ActiveQuests.Count;
|
||||
if (_lastQuestCount != activeCount)
|
||||
{
|
||||
Log.Debug("Active quests: {Active}/{Total} (filtered ES!=2)",
|
||||
activeCount, snap.QuestFlags.Count);
|
||||
Log.Debug("Active quests: {Active}/{Total}",
|
||||
activeCount, snap.QuestLinkedList.Count);
|
||||
_lastQuestCount = activeCount;
|
||||
}
|
||||
}
|
||||
else if (previous is not null)
|
||||
{
|
||||
state.ActiveQuests = previous.ActiveQuests;
|
||||
}
|
||||
|
||||
if (snap.UiQuestGroups is { Count: > 0 })
|
||||
{
|
||||
|
|
@ -370,6 +410,35 @@ public sealed class MemoryPoller : IDisposable
|
|||
.ToList(),
|
||||
}).ToList();
|
||||
}
|
||||
else if (previous is not null)
|
||||
{
|
||||
state.UiQuests = previous.UiQuests;
|
||||
}
|
||||
|
||||
if (snap.QuestLinkedList is { Count: > 0 })
|
||||
{
|
||||
state.Quests = snap.QuestLinkedList
|
||||
.Where(q => q.StateId > 0)
|
||||
.Select(q => new QuestInfo
|
||||
{
|
||||
InternalId = q.InternalId,
|
||||
DisplayName = q.DisplayName,
|
||||
Act = q.Act,
|
||||
StateId = q.StateId,
|
||||
StateText = q.StateText,
|
||||
IsTracked = q.IsTracked,
|
||||
MapPinsText = q.MapPinsText,
|
||||
TargetAreas = q.TargetAreas?.Select(a => new QuestTargetArea
|
||||
{
|
||||
Id = a.Id, Name = a.Name, Act = a.Act, IsTown = a.IsTown,
|
||||
}).ToList(),
|
||||
PathToTarget = q.PathToTarget,
|
||||
}).ToList();
|
||||
}
|
||||
else if (previous is not null)
|
||||
{
|
||||
state.Quests = previous.Quests;
|
||||
}
|
||||
|
||||
if (snap.Terrain is not null)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue