Repository navigation
GameCode
Most mods never need this page: the FH2 classes read the game for you. It's for when you go lower, with your own hooks and addresses.
In C++, a mod can wrap a risky read in __try and carry on if the memory isn't there. .NET can't: an access violation can't be caught, and the runtime ends the game. Game data you didn't get from the game just now may be gone: an area that streamed out, a car that left, the HUD after a teleport. Read anything like that with the safe reads below.
Data the game hands you in a hook (its arguments, the object it's working on) is alive while the hook runs: read that directly, it's faster.
New in 0.10.0. Each is a copy through the operating system, so a bad address gives you "not readable" instead of a crash.
| Member | What it does |
|---|---|
GameCode.TryRead(nint address, byte[] into) |
Copies the bytes; false (nothing read) if any of them isn't readable. A Span<byte> overload too. |
GameCode.TryRead<T>(nint address, out T value) |
A value of type T (C#). |
GameCode.ReadSafe<T>(nint address) |
The value, or 0 / null if it isn't readable (C#). |
GameCode.ReadPointerSafe(nint address, int offset = 0) |
A pointer, or 0. |
GameCode.ReadInt32Safe(nint address, int offset = 0) |
An Integer, or 0. |
GameCode.ReadSingleSafe(nint address, int offset = 0) |
A Single, or 0. |
GameCode.IsReadable(nint address, int size = 8) |
True if that much memory is there right now. |
GameCode.ReadStdString(nint address) |
The text of one of the game's std::strings, or "" if it isn't readable. |
nint car = ...; // remembered from an earlier frame: may be gone now
if (GameCode.TryRead<int>(car + 0x4D08, out var kind) && kind == 0)
Log("still the player's car");Dim car As IntPtr = ... ' remembered from an earlier frame: may be gone now
If GameCode.ReadInt32Safe(car, &H4D08) = 0 Then Log("still the player's car")These cost a system call each. Use them for pointers you kept, not for every read in a hot loop.
Before hooking or calling the game's code, check its first bytes, so that on another build of the game your mod switches itself off instead of crashing:
| Member | What it does |
|---|---|
GameCode.Matches(nint address, byte[] expected) |
The bytes in memory are expected. |
GameCode.MatchesBuild(nint address, byte[] expected) |
The same, but still true where another mod has hooked that code first: it then reads the game's exe on disk. New in 0.10.0. |
GameCode.SlotMatchesBuild(nint slot, nint function) |
A function pointer (a vtable entry) is function, or another mod has hooked it and the exe on disk has function there. New in 0.10.0.
|
GameCode.ShippedBytes(nint address, Span<byte> into) |
The bytes as the exe on disk has them. New in 0.10.0. |
GameCode.Hex("48 8B C4") |
Bytes from text, for the checks above. |
Use MatchesBuild for code other mods hook too: with Matches, your mod would refuse to start whenever another one was loaded first.
nint b = ModPlatform.WaitForCode();
if (!GameCode.MatchesBuild(b + 0x384C00, GameCode.Hex("48 8B C4 55 56 57 41 54 41 55")))
{
Log("not the game build this mod was made for; off");
return;
}Playing
Getting started
Writing mods
The FH2 classes
- Game
- Player
- Vehicle
- World
- Garage, CarCatalog, Profile
- UI and Hud
- Camera
- Audio
- ANNA
- Skills
- DebugOverlay
- Controller
- GameInput
- GameWindow
- Menus and UiElement
- SettingsTile
- ButtonIcons and Picture
- MapView, PhotoCamera, GarageCamera
More