fixed stuff up
This commit is contained in:
parent
18d8721dd5
commit
e5ebe05571
4 changed files with 69 additions and 63 deletions
|
|
@ -1,4 +1,6 @@
|
|||
using System.Text;
|
||||
using Roboto.GameOffsets.Components;
|
||||
using Roboto.GameOffsets.Natives;
|
||||
using Serilog;
|
||||
|
||||
namespace Roboto.Memory;
|
||||
|
|
@ -146,19 +148,21 @@ public sealed class ComponentReader
|
|||
if (high == 0 || high >= 0x7FFF) continue;
|
||||
if ((compPtr & 0x3) != 0) continue;
|
||||
|
||||
var hpTotal = mem.Read<int>(compPtr + offsets.LifeHealthOffset + offsets.VitalTotalOffset);
|
||||
var life = mem.Read<Life>(compPtr);
|
||||
|
||||
var hpTotal = life.Health.Total;
|
||||
if (hpTotal < 20 || hpTotal > 200000) continue;
|
||||
|
||||
var hpCurrent = mem.Read<int>(compPtr + offsets.LifeHealthOffset + offsets.VitalCurrentOffset);
|
||||
var hpCurrent = life.Health.Current;
|
||||
if (hpCurrent < 0 || hpCurrent > hpTotal + 1000) continue;
|
||||
|
||||
var manaTotal = mem.Read<int>(compPtr + offsets.LifeManaOffset + offsets.VitalTotalOffset);
|
||||
var manaTotal = life.Mana.Total;
|
||||
if (manaTotal < 0 || manaTotal > 200000) continue;
|
||||
|
||||
var manaCurrent = mem.Read<int>(compPtr + offsets.LifeManaOffset + offsets.VitalCurrentOffset);
|
||||
var manaCurrent = life.Mana.Current;
|
||||
if (manaCurrent < 0 || manaCurrent > manaTotal + 1000) continue;
|
||||
|
||||
var esTotal = mem.Read<int>(compPtr + offsets.LifeEsOffset + offsets.VitalTotalOffset);
|
||||
var esTotal = life.EnergyShield.Total;
|
||||
if (manaTotal == 0 && esTotal == 0) continue;
|
||||
|
||||
_cachedLifeIndex = i;
|
||||
|
|
@ -174,15 +178,14 @@ public sealed class ComponentReader
|
|||
/// </summary>
|
||||
public bool TryReadVitals(GameStateSnapshot snap, nint lifeComp)
|
||||
{
|
||||
var mem = _ctx.Memory;
|
||||
var offsets = _ctx.Offsets;
|
||||
var life = _ctx.Memory.Read<Life>(lifeComp);
|
||||
|
||||
var hp = mem.Read<int>(lifeComp + offsets.LifeHealthOffset + offsets.VitalCurrentOffset);
|
||||
var hpMax = mem.Read<int>(lifeComp + offsets.LifeHealthOffset + offsets.VitalTotalOffset);
|
||||
var mana = mem.Read<int>(lifeComp + offsets.LifeManaOffset + offsets.VitalCurrentOffset);
|
||||
var manaMax = mem.Read<int>(lifeComp + offsets.LifeManaOffset + offsets.VitalTotalOffset);
|
||||
var es = mem.Read<int>(lifeComp + offsets.LifeEsOffset + offsets.VitalCurrentOffset);
|
||||
var esMax = mem.Read<int>(lifeComp + offsets.LifeEsOffset + offsets.VitalTotalOffset);
|
||||
var hp = life.Health.Current;
|
||||
var hpMax = life.Health.Total;
|
||||
var mana = life.Mana.Current;
|
||||
var manaMax = life.Mana.Total;
|
||||
var es = life.EnergyShield.Current;
|
||||
var esMax = life.EnergyShield.Total;
|
||||
|
||||
if (hpMax <= 0 || hpMax > 200000 || hp < 0 || hp > hpMax + 1000) return false;
|
||||
if (manaMax < 0 || manaMax > 200000 || mana < 0) return false;
|
||||
|
|
@ -256,22 +259,17 @@ public sealed class ComponentReader
|
|||
/// </summary>
|
||||
public bool TryReadPosition(GameStateSnapshot snap, nint renderComp)
|
||||
{
|
||||
var mem = _ctx.Memory;
|
||||
var offsets = _ctx.Offsets;
|
||||
var pos = _ctx.Memory.Read<StdTuple3D<float>>(renderComp + _ctx.Offsets.PositionXOffset);
|
||||
|
||||
var x = mem.Read<float>(renderComp + offsets.PositionXOffset);
|
||||
var y = mem.Read<float>(renderComp + offsets.PositionYOffset);
|
||||
var z = mem.Read<float>(renderComp + offsets.PositionZOffset);
|
||||
|
||||
if (float.IsNaN(x) || float.IsNaN(y) || float.IsNaN(z)) return false;
|
||||
if (float.IsInfinity(x) || float.IsInfinity(y) || float.IsInfinity(z)) return false;
|
||||
if (x < 50 || x > 50000 || y < 50 || y > 50000) return false;
|
||||
if (MathF.Abs(z) > 5000) return false;
|
||||
if (float.IsNaN(pos.X) || float.IsNaN(pos.Y) || float.IsNaN(pos.Z)) return false;
|
||||
if (float.IsInfinity(pos.X) || float.IsInfinity(pos.Y) || float.IsInfinity(pos.Z)) return false;
|
||||
if (pos.X < 50 || pos.X > 50000 || pos.Y < 50 || pos.Y > 50000) return false;
|
||||
if (MathF.Abs(pos.Z) > 5000) return false;
|
||||
|
||||
snap.HasPosition = true;
|
||||
snap.PlayerX = x;
|
||||
snap.PlayerY = y;
|
||||
snap.PlayerZ = z;
|
||||
snap.PlayerX = pos.X;
|
||||
snap.PlayerY = pos.Y;
|
||||
snap.PlayerZ = pos.Z;
|
||||
CachedRenderComponentAddr = renderComp;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -281,12 +279,10 @@ public sealed class ComponentReader
|
|||
/// </summary>
|
||||
public bool TryReadPositionRaw(nint comp, out float x, out float y, out float z)
|
||||
{
|
||||
var mem = _ctx.Memory;
|
||||
var offsets = _ctx.Offsets;
|
||||
|
||||
x = mem.Read<float>(comp + offsets.PositionXOffset);
|
||||
y = mem.Read<float>(comp + offsets.PositionYOffset);
|
||||
z = mem.Read<float>(comp + offsets.PositionZOffset);
|
||||
var pos = _ctx.Memory.Read<StdTuple3D<float>>(comp + _ctx.Offsets.PositionXOffset);
|
||||
x = pos.X;
|
||||
y = pos.Y;
|
||||
z = pos.Z;
|
||||
|
||||
if (float.IsNaN(x) || float.IsNaN(y) || float.IsNaN(z)) return false;
|
||||
if (float.IsInfinity(x) || float.IsInfinity(y) || float.IsInfinity(z)) return false;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using Roboto.GameOffsets.Components;
|
||||
using Roboto.GameOffsets.Entities;
|
||||
using Serilog;
|
||||
|
||||
namespace Roboto.Memory;
|
||||
|
|
@ -36,15 +38,15 @@ public sealed class EntityReader
|
|||
var hasComponentLookup = offsets.ComponentLookupEntrySize > 0;
|
||||
var dirty = false;
|
||||
|
||||
WalkTreeInOrder(sentinel, root, maxNodes, node =>
|
||||
WalkTreeInOrder(sentinel, root, maxNodes, (_, treeNode) =>
|
||||
{
|
||||
var entityPtr = mem.ReadPointer(node + offsets.EntityNodeValueOffset);
|
||||
var entityPtr = treeNode.Data.EntityPtr;
|
||||
if (entityPtr == 0) return;
|
||||
|
||||
var high = (ulong)entityPtr >> 32;
|
||||
if (high == 0 || high >= 0x7FFF || (entityPtr & 0x3) != 0) return;
|
||||
|
||||
var entityId = mem.Read<uint>(entityPtr + offsets.EntityIdOffset);
|
||||
var entityId = treeNode.Data.Key.EntityId;
|
||||
var path = TryReadEntityPath(entityPtr);
|
||||
|
||||
var entity = new Entity(entityPtr, entityId, path);
|
||||
|
|
@ -84,13 +86,13 @@ public sealed class EntityReader
|
|||
var lifeComp = mem.ReadPointer(compFirst + lifeIdx * 8);
|
||||
if (lifeComp != 0)
|
||||
{
|
||||
var hp = mem.Read<int>(lifeComp + offsets.LifeHealthOffset + offsets.VitalCurrentOffset);
|
||||
var hpMax = mem.Read<int>(lifeComp + offsets.LifeHealthOffset + offsets.VitalTotalOffset);
|
||||
if (hpMax > 0 && hpMax < 200000 && hp >= 0 && hp <= hpMax + 1000)
|
||||
var life = mem.Read<Life>(lifeComp);
|
||||
if (life.Health.Total > 0 && life.Health.Total < 200000 &&
|
||||
life.Health.Current >= 0 && life.Health.Current <= life.Health.Total + 1000)
|
||||
{
|
||||
entity.HasVitals = true;
|
||||
entity.LifeCurrent = hp;
|
||||
entity.LifeTotal = hpMax;
|
||||
entity.LifeCurrent = life.Health.Current;
|
||||
entity.LifeTotal = life.Health.Total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -109,14 +111,24 @@ public sealed class EntityReader
|
|||
|
||||
/// <summary>
|
||||
/// Iterative in-order traversal of an MSVC std::map red-black tree.
|
||||
/// Backward-compatible overload — delegates to the struct-based version.
|
||||
/// </summary>
|
||||
public void WalkTreeInOrder(nint sentinel, nint root, int maxNodes, Action<nint> visitor)
|
||||
{
|
||||
WalkTreeInOrder(sentinel, root, maxNodes, (addr, _) => visitor(addr));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterative in-order traversal using Read<EntityTreeNode> — 1 kernel call per node
|
||||
/// instead of separate Left/Right reads. The visitor receives both the node address
|
||||
/// and the parsed struct data.
|
||||
/// </summary>
|
||||
public void WalkTreeInOrder(nint sentinel, nint root, int maxNodes, Action<nint, EntityTreeNode> visitor)
|
||||
{
|
||||
if (root == 0 || root == sentinel) return;
|
||||
|
||||
var offsets = _ctx.Offsets;
|
||||
var mem = _ctx.Memory;
|
||||
var stack = new Stack<nint>();
|
||||
var stack = new Stack<(nint Addr, EntityTreeNode Node)>();
|
||||
var current = root;
|
||||
var count = 0;
|
||||
var visited = new HashSet<nint> { sentinel };
|
||||
|
|
@ -130,18 +142,19 @@ public sealed class EntityReader
|
|||
current = sentinel;
|
||||
break;
|
||||
}
|
||||
stack.Push(current);
|
||||
current = mem.ReadPointer(current + offsets.EntityNodeLeftOffset);
|
||||
var node = mem.Read<EntityTreeNode>(current);
|
||||
stack.Push((current, node));
|
||||
current = node.Left;
|
||||
}
|
||||
|
||||
if (stack.Count == 0) break;
|
||||
|
||||
current = stack.Pop();
|
||||
visitor(current);
|
||||
var (nodeAddr, treeNode) = stack.Pop();
|
||||
visitor(nodeAddr, treeNode);
|
||||
count++;
|
||||
if (count >= maxNodes) break;
|
||||
|
||||
current = mem.ReadPointer(current + offsets.EntityNodeRightOffset);
|
||||
current = treeNode.Right;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -258,15 +258,8 @@ public class GameMemoryReader : IDisposable
|
|||
// Cache the resolved address for fast per-frame reads
|
||||
_cachedCameraMatrixAddr = matrixAddr;
|
||||
|
||||
// Read 64-byte Matrix4x4 as 16 floats
|
||||
var bytes = mem.ReadBytes(matrixAddr, 64);
|
||||
if (bytes is null || bytes.Length < 64) return;
|
||||
|
||||
var m = new Matrix4x4(
|
||||
BitConverter.ToSingle(bytes, 0), BitConverter.ToSingle(bytes, 4), BitConverter.ToSingle(bytes, 8), BitConverter.ToSingle(bytes, 12),
|
||||
BitConverter.ToSingle(bytes, 16), BitConverter.ToSingle(bytes, 20), BitConverter.ToSingle(bytes, 24), BitConverter.ToSingle(bytes, 28),
|
||||
BitConverter.ToSingle(bytes, 32), BitConverter.ToSingle(bytes, 36), BitConverter.ToSingle(bytes, 40), BitConverter.ToSingle(bytes, 44),
|
||||
BitConverter.ToSingle(bytes, 48), BitConverter.ToSingle(bytes, 52), BitConverter.ToSingle(bytes, 56), BitConverter.ToSingle(bytes, 60));
|
||||
// Read 64-byte Matrix4x4 as a single struct (System.Numerics.Matrix4x4 is already unmanaged/sequential)
|
||||
var m = mem.Read<Matrix4x4>(matrixAddr);
|
||||
|
||||
// Quick sanity check
|
||||
if (float.IsNaN(m.M11) || float.IsInfinity(m.M11)) return;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using Roboto.GameOffsets.States;
|
||||
using Serilog;
|
||||
using Terrain = Roboto.GameOffsets.States.Terrain;
|
||||
|
||||
namespace Roboto.Memory;
|
||||
|
||||
|
|
@ -64,9 +66,12 @@ public sealed class TerrainReader
|
|||
}
|
||||
|
||||
// Inline mode: TerrainStruct is inline at AreaInstance + TerrainListOffset
|
||||
// Single Read<Terrain> (0x1B0 = 432 bytes) replaces 5 individual reads
|
||||
var terrainBase = areaInstance + offsets.TerrainListOffset;
|
||||
var cols = (int)mem.Read<long>(terrainBase + offsets.TerrainDimensionsOffset);
|
||||
var rows = (int)mem.Read<long>(terrainBase + offsets.TerrainDimensionsOffset + 8);
|
||||
var t = mem.Read<Terrain>(terrainBase);
|
||||
|
||||
var cols = (int)t.Dimensions.X;
|
||||
var rows = (int)t.Dimensions.Y;
|
||||
|
||||
if (cols <= 0 || cols >= 1000 || rows <= 0 || rows >= 1000)
|
||||
return;
|
||||
|
|
@ -99,10 +104,9 @@ public sealed class TerrainReader
|
|||
return;
|
||||
}
|
||||
|
||||
// Read GridWalkableData StdVector (begin/end/cap pointers)
|
||||
var gridVecOffset = offsets.TerrainWalkableGridOffset;
|
||||
var gridBegin = mem.ReadPointer(terrainBase + gridVecOffset);
|
||||
var gridEnd = mem.ReadPointer(terrainBase + gridVecOffset + 8);
|
||||
// Grid vector pointers already available from the Terrain struct read
|
||||
var gridBegin = t.WalkableGrid.First;
|
||||
var gridEnd = t.WalkableGrid.Last;
|
||||
if (gridBegin == 0 || gridEnd <= gridBegin)
|
||||
return;
|
||||
|
||||
|
|
@ -110,7 +114,7 @@ public sealed class TerrainReader
|
|||
if (gridDataSize <= 0 || gridDataSize > 16 * 1024 * 1024)
|
||||
return;
|
||||
|
||||
var bytesPerRow = mem.Read<int>(terrainBase + offsets.TerrainBytesPerRowOffset);
|
||||
var bytesPerRow = t.BytesPerRow;
|
||||
if (bytesPerRow <= 0 || bytesPerRow > 0x10000)
|
||||
return;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue