Read-only mirror of the Stardew Valley Wiki, made while stardewvalleywiki.com is down. Text snapshot 14 September 2026; images not included. Please edit on the real wiki once it is back.

Modding:Modder Guide/APIs/Events

From Stardew Valley Wiki
Jump to navigation Jump to search

{{../../header}}

SMAPI provides several C# events which let your mod respond when something happens (like when the player places an object) or run code periodically (like once per update tick).

FAQs

What are events?

Events let you run code when something happens. You can add any number of event handlers (methods to call) when an event (what happened) is raised. You can think of events and handlers as a when..then statement:

WHEN the save is loaded,   <-- event
THEN run my code           <-- event handler

See Events in the C# programming guide for more info.

How do I use them?

Event handlers are usually added in your Entry method, though you can add and remove them anytime. For example, let's say you want to print a message when each day starts. First you would choose the appropriate event from the list below (GameLoop.DayStarted), then add an event handler, and do something in your method code:

/// <summary>The main entry point for the mod.</summary>
internal sealed class ModEntry : Mod
{
    /**********
    ** Public methods
    *********/
    /// <summary>The mod entry point, called after the mod is first loaded.</summary>
    /// <param name="helper">Provides simplified APIs for writing mods.</param>
    public override void Entry(IModHelper helper)
    {
        // event += method to call
        helper.Events.GameLoop.DayStarted += this.OnDayStarted;
    }

    /// <summary>The method called after a new day starts.</summary>
    /// <param name="sender">The event sender.</param>
    /// <param name="e">The event arguments.</param>
    private void OnDayStarted(object? sender, DayStartedEventArgs e)
    {
       this.Monitor.Log("A new day dawns!", LogLevel.Info);
    }
}

Tip: you don't need to memorise the method arguments. In Visual Studio, type helper.Events.GameLoop.SaveLoaded += and press TAB to auto-create a method. (There doesn't seem to be an equivalent feature in MonoDevelop.)

sender is part of the C# event pattern, and is unused by SMAPI.

How do events fit into the game?

Events are raised on each game tick (when the game updates its state and renders to the screen), which is 60 times per second. An event may be raised multiple times (e.g., if the player pressed two keys simultaneously), but most events won't be raised 60 times per second (e.g., players are unlikely to be pressing 60 buttons per second).

Your event handlers are run synchronously: the game is paused and no other mod's code will run simultaneously, so there's no risk of conflicting changes. Since code runs very quickly, players won't notice any delay unless your code is unusually slow. That said, when using frequent events like UpdateTicked or Rendered, you should cache expensive operations (like loading an asset) instead of repeating them in each tick to avoid impacting performance.

What if a mod changes what the event was raised for?

Events are raised based on a snapshot of the game state. That's usually but not necessarily the current game state.

For example, consider this case:

  1. The GameMenu opens.
  2. SMAPI raises the MenuChanged event, which mods A and B listen to.
  3. Mod A receives the event and closes the menu.
  4. Mod B receives the event.

Each mod is still handling the MenuChanged event for the opened menu, even though the first mod closed it. SMAPI will raise a new MenuChanged event for the closed menu on the next tick.

This rarely affects mods, but it's something to keep in mind if you need the current state (e.g., check Game1.activeClickableMenu instead of e.NewMenu).

Events

The available events are documented below.

Content

this.Helper.Events.Content has events related to assets loaded from the content pipeline.

event summary

Template:/event Template:/event Template:/event Template:/event

Display

this.Helper.Events.Display has events linked to UI and drawing to the screen.

event summary

Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event

Game loop

this.Helper.Events.GameLoop has events linked to the game's update loop. The update loop runs roughly ≈60 times/second to run game logic like state changes, action handling, etc. These are often useful, but you should consider semantic events like Input where applicable.

event summary

Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event

Input

this.Helper.Events.Input has events raised when the player uses a controller, keyboard, or mouse in some way. They can be used with the [[../Input|input API]] to access more info or suppress input.

event summary

Template:/event Template:/event Template:/event Template:/event

Multiplayer

this.Helper.Events.Multiplayer has events raised for multiplayer messages and connections.

event summary

Template:/event Template:/event Template:/event Template:/event

Player

this.Helper.Events.Player has events raised when the player data changes.

Currently these events are only raised for the current player. That will likely change in a future version, so make sure to check e.IsLocalPlayer if you only want to handle the current player.

event summary

Template:/event Template:/event Template:/event

World

this.Helper.Events.World has events raised when the in-game world changes in some way.

event summary

Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event Template:/event

Specialised

this.Helper.Events.Specialised has events for specialised edge cases. These shouldn't be used by most mods.

event summary

Template:/event Template:/event

Advanced

Change monitoring

You may want to handle a change that doesn't have its own event (e.g., an NPC's heart event ends, a letter is added to the mailbox, etc). You can usually do that by handling a general event like UpdateTicked, and detecting when the value(s) you're watching changed. For example, here's a complete mod which logs a message when an in-game event ends:

/// <summary>The main entry point for the mod.</summary>
internal sealed class ModEntry : Mod
{
    /*********
    ** Fields
    *********/
    /// <summary>The in-game event detected on the last update tick.</summary>
    private Event LastEvent;


    /*********
    ** Public methods
    *********/
    /// <summary>The mod entry point, called after the mod is first loaded.</summary>
    /// <param name="helper">Provides simplified APIs for writing mods.</param>
    public override void Entry(IModHelper helper)
    {
        helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked;
    }


    /*********
    ** Private methods
    *********/
    /// <summary>The method invoked when the game updates its state.</summary>
    /// <param name="sender">The event sender.</param>
    /// <param name="e">The event arguments.</param>
    private void OnUpdateTicked(object sender, EventArgs e)
    {
        if (this.LastEvent != null && Game1.CurrentEvent == null)
            this.Monitor.Log($"Event {this.LastEvent.id} just ended!");

        this.LastEvent = Game1.CurrentEvent;
    }
}

Custom priority

SMAPI calls event handlers in the same order they're registered by default, so the first event handler registered is the first to receive the event each time. This isn't always predictable, since it depends on mod load order and when each mod registers their handlers. This order is also an implementation detail, so it's not guaranteed.

If you need more control over the order, you can specify an event priority using the [EventPriority] attribute: Low (after most handlers), Default, High (before most handlers), or a custom value (e.g., High + 1 is higher priority than High). You should only do this if strictly needed; depending on event handler order between mods is fragile (e.g., the other mod might change its priority too).

/// <summary>The main entry point for the mod.</summary>
internal sealed class ModEntry : Mod
{
    /*********
    ** Public methods
    *********/
    /// <summary>The mod entry point, called after the mod is first loaded.</summary>
    /// <param name="helper">Provides simplified APIs for writing mods.</param>
    public override void Entry(IModHelper helper)
    {
        helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked;
    }


    /*********
    ** Private methods
    *********/
    /// <summary>The method invoked when the game updates its state.</summary>
    /// <param name="sender">The event sender.</param>
    /// <param name="e">The event arguments.</param>
    [EventPriority(EventPriority.High)]
    private void OnUpdateTicked(object sender, EventArgs e)
    {
        this.Monitor.Log("Update!");
    }
}