Reference Hacking, commonly shortened to Ref Hacking, is a term to describe any method that dynamically utilizes Reference IDs to read from and write to world elements.
Common uses for ref hacking include accessing elements that exist outside of Root, accessing elements without knowing their reference ID beforehand, and finding what components exist under a slot.
Basic Concepts
Before jumping into ref hacking, it's important to internalize a few concepts on how reference IDs are allocated when ref hacking on existing elements:
- Reference IDs for slots are allocated in a depth-first search manner and in the order as they appear in the inspector.
- Reference IDs for components of a slot reside between the slot the component is under and the next slot, and are allocated in the same order as they appear in the inspector (do note, however, that the order of components themselves is *not* guaranteed, just that the two follow the same order).
- Reference IDs for component fields always reside between two components, but are not guaranteed to be allocated in the same order as they appear in the inspector.
- The "reference ID offset" between a world element and a particular field in the element changes across sessions, but is consistent within any given session. E.g. the ID offset between any given Text component and its
Content
field is the same for all Text components in the session.
All points but the fourth do not apply for newly created elements, such as new slots on an object, new components on a slot, or new fields of a component (i.e. elements in a list). In this case, simply the next available reference ID is used for the new element, and it is not guaranteed to follow the first three rules above.
With that out of the way, the core of ref hacking involves a PrimitiveMemberEditor, ReferenceField<IWorldElement>, Text component, and TextEditor. The Reference
field of the ReferenceField is of type SyncRef, and the SyncRef type contains a "hidden" member, id
, which is the reference ID of the referenced element. This ID is then accessed via the PrimitiveMemberEditor, which implicitly converts the ID to a string to drive the Content
field of the Text component. From there, one can cast the string to a ulong to perform math on it.
Taking It Further
There are several ways to build upon the basic concept outlined above, and nearly all of them rely on two ways to work with reference IDs: offsets and iteration.
Offsets
Offsets are the easier and more performant way to work with reference IDs, but can only be used effectively with enough information and are less flexible. As such, offsets are usually used for dynamically accessing component fields.
One might naïvely find the offset between a known element and the field one wants to access, then hard-code the values into code to use later. However, recall point 4 from above: this can and will break across different sessions. To combat this, one can make an offset calculator. Essentially, by creating a static, known clone of the setup you are searching for (i.e. using the same component) and calculating the offset between the parent element and the intended element, then using the same offset for dynamic instances, this will work across different sessions, since the offset between a particular parent element and a field of the element is consistent in any given session.
Iteration
Iteration is the more expensive, yet more flexible way to work with reference IDs. The basic routine involves finding a "base" ID to start at, then repeatedly adding 256
to the current reference ID until a certain condition is met.