Well, again, there's no "object manager" in Wildstar. There's a general Game Manager. It handles just about everything. (From Lua, to input, to actors, to networking) There just happens to be an Actor list in the game manager.
Either way, here's a copy/pasta of my older GameManager class (new one has stuff that I can't really show )
Code:
using System;using System.Collections.Generic;
using ApocDev.Common;
using ApocDev.Wildstar.Engine;
using ApocDev.Wildstar.Updates;
using GreyMagic;
using log4net;
namespace ApocDev.Wildstar.Game
{
public class GameManager
{
private static readonly ILog Log = LogManager.GetLogger(typeof(GameManager));
private static bool _initialized;
private static PerFrameCachedValue<List<Actor>> _cachedActors;
private static ExternalProcessMemory Memory { get { return GameEngine.AttachedProcess.Memory; } }
internal static IntPtr GameMgrPtr { get { return Memory.Read<IntPtr>(Patterns.GameManager); } }
/// <summary>
/// A list of all available actors. This list is updated once per frame with new objects. Do not cache actor objects for more than 1 frame of execution.
/// They are not guaranteed to be valid.
/// </summary>
public static List<Actor> Actors { get { return _cachedActors; } }
/// <summary>
/// The current local player actor. (Your actual player in-game)
/// </summary>
public static Player LocalPlayer { get; private set; }
/// <summary>
/// Returns an instance of <see cref="GameLua"/> for Lua snippets in-game. Also provides an Events API for you to also hook in-game Lua events from C#.
/// </summary>
public static GameLua Lua { get; private set; }
/// <summary>
/// Used from GameLua to grab the LuaState ptr. Since these patterns are specific to GameManager (as they're only valid when in-game)
/// GameLua has patterns for the secondary Lua states for when out of game.
/// </summary>
internal static IntPtr LuaState
{
get
{
// Doing this because if we're not in game
if (GameMgrPtr == IntPtr.Zero)
{
return IntPtr.Zero;
}
var first = Memory.Read<IntPtr>(GameMgrPtr + (int) Patterns.LuaMgr);
if (first == IntPtr.Zero)
{
return IntPtr.Zero;
}
return Memory.Read<IntPtr>(first + (int) Patterns.LuaMgrLuaState);
}
}
internal static GameManagerPatterns Patterns { get; set; }
/// <summary>
/// Initializes the GameManager. Subsequent calls will do nothing.
/// </summary>
public static void Init()
{
if (_initialized)
{
return;
}
// Register our hotkeys.
// Their input window isn't just MainWindowHandle, so we'll just quickly find the Wildstar <version> window that they create
// And hook our hotkey stuff up to that.
Hotkeys.Initialize(WindowInterop.GetWindowHandleByClassName(Memory.Process, "WildStar " + GameEngineConfig.GameProcessVersion.Revision));
Patterns = new GameManagerPatterns(Memory);
_cachedActors = new PerFrameCachedValue<List<Actor>>(Update);
Lua = new GameLua();
_initialized = true;
}
/// <summary>
/// Only called from the per-frame-cache. No need to make this public!
/// </summary>
/// <returns></returns>
private static List<Actor> Update()
{
// This is the only time we'll be null.
// We want to force exceptions if the person is trying to access actors when not in game.
if (GameMgrPtr == IntPtr.Zero)
return null;
// g_GameMgr->UnitsList->FirstUnit = Actor*
// ActorList itself is one of their "search table" containers. The first unit is also stored at the end if the struct (0x14)
var unitsPtr = Memory.Read<IntPtr>(GameMgrPtr + (int) Patterns.ActorList + 0x14);
List<Actor> ret = new List<Actor>();
if (unitsPtr == IntPtr.Zero)
{
// No actors. Return an empty list.
// We're going with a never-null approach here, to avoid some shenanigans.
return ret;
}
// Read in the linked list of actors
Actor cur = new Actor(unitsPtr);
while (cur != null)
{
// Re-dress the unit as a player.
// TODO: Probably just read the actor type before creating the object.
// But, we're being a bit lazy at the moment.
// ActorType is at like actor+0x48 or something. I forget exactly. Simple enough to find.
if (cur.ActorType == ActorType.Player)
{
cur = new Player(cur.Address);
}
ret.Add(cur);
// actor+0x4C = Actor*
cur = cur.GetNextActor();
}
var lpAddr = Memory.Read<IntPtr>(GameMgrPtr + (int) Patterns.LocalPlayer);
//Log.DebugOnly("Local Player PTR: " + lpAddr.ToString("X8"));
// Update the local player if needed.
if (LocalPlayer == null)
{
LocalPlayer = new Player(lpAddr);
}
else if (LocalPlayer.Address != lpAddr)
{
// TODO: INativeObject.UpdateAddress(lpAddr) to auto-flush all frame caches
LocalPlayer = new Player(lpAddr);
}
return ret;
}
}
}
And some patterns
Code:
[Pattern("8B 0D ? ? ? ? 8B 04 B8 Add 2 Read32 DontRebase", Raw = true)]
[Pattern64("48 89 1D ? ? ? ? 48 89 40 10 48 8B 83 ? ? ? ? Add 3 Read32 DontRebase", Raw=true)]
public IntPtr GameManager; // 140A83920 20 39 A8 40 01
[Pattern("8B 81 ? ? ? ? 85 C0 74 06 8B 80 ? ? ? ? 39 47 04 Add 2 Read32")]
// Weird optimization in the compiler that causes it to use a single byte for the local player offset here.
// Whatever, still works.
[Pattern64("48 8B 50 ? 48 85 D2 75 10 48 8B 41 10 89 50 08 48 83 41 ? ? 8D 42 01 C3 E9 ? ? ? ? Add 3 Read8")]
public long LocalPlayer;
[Pattern("8D 89 ? ? ? ? E8 ? ? ? ? 85 C0 74 21 8B 10 Add 2 Read32")]
[Pattern64("FF 96 ? ? ? ? 48 8B 9E ? ? ? ? 33 D2 48 8B F8 48 F7 B6 ? ? ? ? 48 8B 1C D3 48 85 DB 74 24 0F 1F 00 48 3B 3B 75 13 48 8D 53 10 48 8D 4C 24 ? FF 96 ? ? ? ? 85 C0 75 1B 48 8B 5B 08 Add 2 Read32")]
public long ActorList;