An On hook is a C# event that stores a delegate. A delegate is a value that can call a method with a specified parameter and return shape. The generated On.Player.Update event for Rain World build 22785462 accepts a handler shaped like On.Player.hook_Update. Its parameters include orig, Player self, and bool eu. Write a named method with that exact shape, then add and remove it from the event.

private void OnEnable()
{
    On.Player.Update += PlayerUpdate;
}
 
private void OnDisable()
{
    On.Player.Update -= PlayerUpdate;
}
 
private void PlayerUpdate(On.Player.orig_Update orig, Player self, bool eu)
{
    orig(self, eu);
}

orig is also a delegate. Calling it continues through the hook chain and eventually the original global::Player.Update(System.Boolean) method. The handler above changes nothing, but it establishes the subscription and argument-forwarding pattern. Hook lifecycle explains when code can go before or after orig.

Instance methods and static methods

An instance method belongs to one object. The OnEnable, OnDisable, and PlayerUpdate methods above belong to the plugin component that BepInEx created. That component can use its own fields, such as a bool that prevents duplicate registration.

A static field or method belongs to its type and has one shared copy for that type. It has no plugin instance or this. A static logger field can make one plugin’s logger available to helper classes after startup, but it must be assigned before a helper uses it. Prefer instance fields for state that belongs to the plugin and per-object state for state that belongs to a Player, Room, or other game object. Per-instance state gives patterns that avoid making a shared dictionary stand in for object ownership.

Do not unsubscribe with a newly written lambda. Two visually identical lambda expressions create different delegate values, so the second one does not identify the subscription made by the first. A named handler makes -= target the same method. C# events permit outside code to subscribe and unsubscribe, while the declaring type controls invocation.

Null is state, not a type name

null means a reference points to no object. Calling an instance member through that reference throws NullReferenceException. In Rain World, a hook can receive a valid Player while a related reference such as self.room is absent or changes as rooms load and unload. Check close to the operation that needs it.

private void PlayerUpdate(On.Player.orig_Update orig, Player self, bool eu)
{
    orig(self, eu);
 
    Room room = self.room;
    if (room == null)
        return;
 
    ObserveRoom(room);
}

This is an illustrative guard. It does not establish that ObserveRoom is safe in every player update. Validate the fields and collection indexes used by the actual feature. Abstract and realized objects covers why retained physical references need fresh checks.

Small rules that prevent large mistakes

Preserve the delegate’s parameters unless the target method documents a reason to change them. Call orig once in ordinary additive hooks. Keep expensive scans and logging out of per-frame handlers. Catch an exception only when the handler can add useful context or disable an isolated feature, then log the exception rather than only its message.

The local hook surface and global::Player.Update(System.Boolean) owner were inspected for baseline 22785462. The snippets have not been compiled or run as a complete plugin. Use Development workspace to build against local references and Reading error logs when the first run fails.

Sources