Ref Hacking

From Resonite Wiki
Revision as of 05:09, 23 May 2024 by AmasterAmaster (talk | contribs) (Added links.)

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.

Ref hacking is not supported by the developers of Resonite. Depending on how one utilizes ref hacking in a creation, said creation may break across sessions, game updates, or may perhaps be resilient enough for a long while. Nothing is guaranteed, and it is important that this is understood before potential issues arise. If you find yourself using ref hacking consistently for a specific purpose, consider searching for, upvoting, or creating if it doesn't exist, an issue on the Resonite issue tracker
Ref hacking, while alone is not against the User Guidelines, can be used to violate guidelines, like many other tools. Have common sense and question if you are ref hacking to bypass a lock to information that you shouldn't be accessing.

Basic Concepts

An Inspector with four components: PrimitiveMemberEditor, TextEditor, Text, and ReferenceField<IWorldElement>. For the PrimitiveMemberEditor, _target points to the Reference field on the ReferenceField, _textEditor to the TextEditor component, and _textDrive to the Content field on the Text component.
The four core components to ref hacking. The ID of the referenced element (in this case, a permission role name) is contained in the Content field of the Text component.

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.