Skip to content

MonoBehaviours

Rudolf Kolbe edited this page Jul 23, 2022 · 3 revisions

Background

The MonoBehaviour object type in Unity assets can be interpreted as a serialized instance of a script class, which can be pretty much everything, going from scene definitions to localisations or game settings.

By definition, "[the] MonoBehaviour class is the base class from which every Unity script derives, by default"¹. So nearly all game developer-made script classes are children of the MonoBehaviour class, which allows a straightforward serialization of all of them via the MonoBehaviour class.

The MonoBehaviour class serves as a springboard, as seen by its signature.

class MonoBehaviour(Behaviour):
    m_GameObject: PPtr[GameObject] = None
    m_Enabled: int = None
    m_Script: PPtr[MonoScript] = None
    m_Name: str = None

m_GameObject points to the GameObject that uses the script. m_Enabled tells the engine if the script is enabled/turned on by default. m_Name is the name of the instance. m_Script points to a MonoScript object.

What is this MonoScript?

class MonoScript(TextAsset):
    m_Name: str = None
    m_ClassName: str = None
    m_Namespace: str = None
    m_AssemblyName: str = None
    m_IsEditorScript: Optional[bool] = None
    m_ExecutionOrder: Optional[int] = None
    m_PropertiesHash: Optional[Union[int, Hash128]] = None

As can be guessed by just reading the property names of the MonoScript class, it simply points to a specific class within an Assembly and is; therefore, used to find the class used by a MonoBehaviour object.

m_AssemblyName is the name of the assembly, e.g., Assembly-CSharp.dll. m_Namespace is the name of the namespace the target class resides in. m_ClassName is the name of the target class.

And that's already everything about the MonoBehaviour object type.

You may now ask, but what about the derived classes I want to parse? Let's talk about that next.

Reading

As explained in Background, all MonoBehaviour objects refer to a MonoScript object, which in turn refers to a class within an assembly. This class is the class type of the object stored with the MonoBehaviour object.

TODO

As Python can't directly interact with C#/Mono libraries, it's necessary to get the type tree nodes in another way than done by Unity itself.

One way for this is using TypeTreeGenerator to dump all type tree nodes to a .json, which UnityPy can then use to get the nodes required for reading instance stored in a MonoBehaviour object.

# 0. load the .json holding the type tree nodes
ASSEMBLY = json.load(open("assembly-csharp.json", "rt", encoding="utf8"))

# 1. read the MonoBehaviour object
monobehaviour = obj.read()

# 2. get the nodes
nodes = None

# 2.1 check if they might be stored in the asset
nodes = obj.serialized_type.nodes

# 2.2 if they aren't stored, fetch them from the json
if not nodes:
    # 2.2.1 get the script
    script = monobehaviour.m_Script
    if not script:
        raise Exception("Couldn't find script")
    script = script.read()

    # 2.2.2 get the type tree nodes via the script
    key = f"{script.m_Namespace}.{script.m_ClassName}" if script.m_Namespace else script.m_ClassName
    nodes = ASSEMBLY.get(key, None)
    if not nodes:
        raise Exception(f"Couldn't find {key}")

# 3. read instance via found nodes
data: dict = obj.read_typetree(nodes)

Editing

TODO

Clone this wiki locally