Preamble: this should not be attempted with online games or games which have an anti cheat in place, it could result in permanent bans.
The Cheat Engine section in the #tutorials channel in the Speedrun Tool Development Discord server has a few tutorials and guides. It is recommended to also do the Cheat Engine tutorial when the software is first launched. The Cheat Engine wiki and forums also have more resources.
Cheat Engine is usually used to find health or money addresses to edit their values. Those addresses will be the information your auto splitter will depend on for everything it does.
Should the goal be to split upon level changes, searching for an index (often int
(4 Bytes)) or a name (string
) can yield good results. For load removal, an address that has a specific value only when the game is loading is the most useful (e.g. 1 when loading, 0 otherwise (this would be a bool
, but shows up as a Byte in Cheat Engine)).
The values to look for will differ between games, the process to search will always be more or less the same. If none of your attempts result in success, some out-of-the-box techniques may need to be tried; other people's auto splitters may be able to lend inspiration.
Some important notes:
-
for most games, it is sufficient to find an address to a desired value (money, health, etc.) and perform a pointer scan on it
-
when the value's found address is in a green color, a pointer scan does not need to be done (this is called a static address)
-
for Unity games, some special techniques can be applied:
- decompile the game's
Assembly-CSharp.dll
file with one of these programs - for IL2CPP games, dump information as best as possible with one of these programs
- Cpp2IL (open
Assembly-CSharp.dll
with one of the above mentioned programs after) - IL2CppInspector
- IL2CppAssemblyUnhollower
- Cpp2IL (open
- when exploring the code, interesting classes are usually called something along the lines of
GameManager
,PlayerController
,GlobalData
,LevelScript
- especially good are classes which have a
static
"instance" field of themselves or inherit from aSingleton<T>
[1]
- especially good are classes which have a
- use Cheat Engine's mono dissect feature to explore classes, fields, offsets, and addresses
- IMPORTANT NOTE: mono has a very specific way for building pointers manually, such that blind pointer scans need not be used!
- all Unity games make use of a
SceneManager
, which stores the current scene, its index and all scenes which are meant to be loaded (making splitting and load removal incredibly easy)
- decompile the game's
-
for Unreal Engine games, some special techniques can be applied:
- some games' classes and fields can be retrieved along with their names and offsets
- most games' pointers start in the game's
GWorld
, a static pointer to the current game world, orGEngine
, a static pointer to the game engine - techniques to retrieve the mentioned objects vary throughout engine versions
- there is no free software to help with this!
- IMPORTANT NOTE: when the mentioned objects can be successfully retrieved, blind pointer scans need not be used!
-
for GameMaker Studio games, some special techniques can be applied:
- GMS games have a static address to an
int
(4 Bytes) value storing the currentroom_id
, making a lot of things very easy - all other variables in every GMS game are of type
double
, evenbool
s
- GMS games have a static address to an
For further questions, please join the Discord and post any and all questions in #auto-splitters!
Once all addresses and/or pointers have been found, a script can be created.
Resources:
Important notes from the documentation:
- How to run and test a script with LiveSplit
- To see errors and debug output (
print
s) for a script, use one of the following programs - A Cheat Engine address or pointer is declared as follows in ASL's
state {}
block
- ASL uses native C# 6 in every block except
state {}
; if something is unclear or other assistance is required, the Microsoft Docs can go a long way
public GameManager
{
public static GameManager _instance;
}
public GameManager : Singleton<GameManager> { }