Read a small path through the game before choosing a hook. Start with a named behavior, find the method that performs it, then find callers that establish inputs and lifetime. A type browser or decompiler navigates the local reference assemblies. ILSpy provides type, method, property, and metadata navigation. Its desktop UI runs on Windows, Linux, and macOS, and its command-line tool also supports those systems. Keep decompiled output private and write original mod code.
Open RainWorld_Data/Managed/Assembly-CSharp.dll and BepInEx/plugins/HOOKS-Assembly-CSharp.dll from the same game installation used for the build. On a Linux or macOS development machine, those files can come from an owned Windows installation or the selected Proton or Wine environment. The decompiler’s operating-system support does not show that the Windows game or a plugin runs on that host.
The baseline for the symbols in this article is Rain World v1.11.8, Steam build 22785462. Runtime reference records the inspected assembly set. A method name can persist across builds while its parameters or surrounding logic change, so confirm the installed version before using a tutorial written for another release.
Begin with a question
Phrase the task as an observable action: “what updates a player’s input”, “where is this room object added”, or “which code selects this menu page”. Search type names, field names, and distinctive strings from that action. A type called Player gives a broad starting point. A field such as physicalObjects or a concrete method such as Room.AddObject gives a smaller one.
Write down the full owner and signature, including overloads. global::Player.Update(System.Boolean) is more precise than Update. The installed generated hook assembly exposes On.Player.Update for that method on build 22785462. The On event gives the delegate shape that a normal hook must match. Hook lifecycle explains how that delegate reaches the original method.
Read callers before adding behavior
Open the target method and ask:
- Who constructs or owns
self? - What state has the caller prepared before this method begins?
- Which fields can change during this call or after a room transition?
- What calls the method again, and how often?
For example, global::Room.Update() calls updateables during room processing, while global::Player.Update(System.Boolean) is a player update hook point. Those symbols identify frequency and lifetime questions. Check the actual field or property at its use.
Search references to the target method, then trace upward until the caller explains the condition you care about. Follow only the paths relevant to the feature. A page transition may require ProcessManager, but it rarely requires reading creature AI. Keep a short note containing the owner, signature, relevant fields, callers, and the build. That note is more useful than a copy of a decompiled body.
Choose a narrow hook point
Use a method whose inputs and outcome match the change. An interaction after player movement may belong after orig in On.Player.Update. Creating a room object belongs near the documented global::Room.AddObject(UpdatableAndDeletable) boundary, with room and object validity checked at the use site. Object interaction shows a bounded example using these symbols.
If the On surface has no suitable method, inspect whether an IL hook is justified. IL hooks covers its extra version and compatibility work. Do not select an IL edit only because the method body is easy to find.
Record what source reading does not prove
Local inspection established that global::ProcessManager.Update(System.Single) calls global::ModManager.WrapModInitHooks() during the relevant initialization transition in the recorded build. It does not show a plugin’s own code has loaded or that an intended hook has run. The same boundary applies to all source reading: it establishes implementation evidence for this assembly set, not a runtime test.
After choosing a hook point, compile against the local references, log one setup marker, and test one action. Use Reading error logs for loader or runtime failures and Testing a mod to expand the check without changing every variable at once.
Sources
- Local inspection for baseline
22785462:global::Player.Update(System.Boolean), generatedOn.Player.Update,global::Room.Update(),global::Room.AddObject(UpdatableAndDeletable),global::ProcessManager.Update(System.Single), andglobal::ModManager.WrapModInitHooks() - ILSpy assembly browser and decompiler