模组:製作指南/APIs/Events

出自Stardew Valley Wiki
跳至導覽 跳至搜尋

製作SMAPI模組 SMAPI mascot.png


模組:目錄

Robin building.png
“我這裏還有很多事情需要處理。”
— 羅賓

不完整的翻譯

本文或部分尚未完全翻譯成中文。 歡迎您通過編輯幫助其建設。
最後編輯Shirotsuki於2023-07-10 18:55:18.

SMAPI 提供了幾個 C#事件,這些事件使模組在某些事情發生時(例如,玩家放置一個對象時)做出響應,或者定期運行代碼(例如,每個更新周期一次)

常見問題

什麼是事件(events) ?

事件使你可以在發生某些事情時運行代碼。可以在引發「事件」(發生的情況)時添加任意數量的「事件處理程序」(調用方法)。 可以將事件和處理程序視為「when...then」語句:

存档已加载              <-- 事件
然后运行我的代码        <-- 事件处理程序

有關詳細信息,請參閱C# 編程指南中的事件中的「事件」。

如何使用它們?

通常將事件處理程序添加到 Entry 方法中,可以隨時添加和刪除它們。例如,在每天開始時打印一條消息。首先,從下面的列表中選擇適當的事件 (GameLoop.DayStarted), 然後添加一個事件處理程序,並在方法代碼中執行以下操作:

/// <summary>模组的主要入口点。</summary>
public class ModEntry : Mod
{
    /**********
    ** 公共方法
    *********/
    /// <summary>模组入口点,加载模组后自动调用</summary>
    /// <param name="helper">提供用于编写模组的简化API</param>
    public override void Entry(IModHelper helper)
    {
        // 事件 += 方法
        helper.Events.GameLoop.DayStarted += this.OnDayStarted;
    }
    
    /**********
    ** 私有方法
    *********/
    /// <summary>在新的一天开始后调用的方法</summary>
    /// <param name="sender">事件对象</param>
    /// <param name="e">事件参数</param>
    private void OnDayStarted(object sender, DayStartedEventArgs e)
    {
       this.Monitor.Log("新的一天到来了!");
    }
}

提示:不需要記住方法參數。在 Visual Studio 中,輸入 helper.Events.GameLoop.SaveLoaded += 然後按 TAB 來自動生成方法

事件如何呈現到遊戲中?

每次遊戲計時(遊戲更新其狀態並呈現到屏幕時)都會引發事件,每秒60次。 一個事件可能會引發多次(例如,如果玩家同時按下兩個鍵),但是大多數事件不會每秒引發60次(例如,玩家不太可能每秒按下60個按鈕)

事件處理程序是「同步」運行的:遊戲暫停時模組的代碼不會運行,因此沒有更改衝突的風險。由於代碼運行非常迅速,因此除非你的代碼異常緩慢,否則玩家不會注意到任何延遲。就是說,當使用諸如 UpdateTicked 或者 Rendered 應該緩存繁重的操作(例如加載資源),而不是在每個刻度中重複執行這些操作,以免影響性能。

如果模組更改了事件的發起?

事件根據遊戲狀態的快照引發,這通常是「但不一定」是當前遊戲狀態

例如,考慮這種情況:

  1. 菜單 GameMenu 打開了
  2. SMAPI 引發 MenuChanged 事件,並且模組 A 和 B 正在監聽
  3. 模組 A 接收了事件並關閉了菜單
  4. 模組 B 接收了事件

每個模組仍在處理菜單打開的 MenuChanged 事件,即使第一個模組已將菜單關閉。SMAPI 將在下一個刻度時為關閉的菜單引發一個新的 MenuChanged 事件

這很少會影響模組,但是如果你需要當前狀態,則需要牢記 (例如考慮用 Game1.activeClickableMenu 代替 e.NewMenu)

事件

可用的事件記錄在下面

顯示

this.Helper.Events.Display 具有連結到 UI 並繪製到屏幕的事件

事件 描述
#MenuChanged 在打開、關閉或替換遊戲菜單後引發

事件參數:

參數 類型 描述
e.NewMenu IClickableMenu 新的菜單實例 (如果沒有,則為 null )
e.OldMenu IClickableMenu 舊的菜單實例 (如果沒有,則為 null)
#Rendering 打開 sprite batch 後,在遊戲將任何內容繪製到屏幕上之前就引發。 The sprite batch may be closed and reopened multiple times after this event is called, but it's only raised once per draw tick. 此事件對於在屏幕上繪製沒有用, since the game will draw over it.

事件參數:

參數 類型 描述
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#Rendered Raised after the game draws to the sprite patch in a draw tick, just before the final sprite batch is rendered to the screen. Since the game may open/close the sprite batch multiple times in a draw tick, the sprite batch may not contain everything being drawn and some things may already be rendered to the screen. Content drawn to the sprite batch at this point will be drawn over all vanilla content (including menus, HUD, and cursor).

事件參數:

參數 類型 描述
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderingWorld Raised before the game world is drawn to the screen. This event isn't useful for drawing to the screen, since the game will draw over it.

事件參數:

參數 類型 描述
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderedWorld Raised after the game world is drawn to the sprite patch, before it's rendered to the screen. Content drawn to the sprite batch at this point will be drawn over the world, but under any active menu, HUD elements, or cursor.

事件參數:

參數 類型 描述
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderingActiveMenu 當一個菜單被打開時 (Game1.activeClickableMenu != null), 在將該菜單繪製到屏幕上之前觸發。這包括遊戲的內部菜單,例如進入遊戲時的主菜單。Content drawn to the sprite batch at this point will appear under the menu.

事件參數:

參數 類型 描述
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderedActiveMenu When a menu is open (Game1.activeClickableMenu != null), raised after that menu is drawn to the sprite batch but before it's rendered to the screen. Content drawn to the sprite batch at this point will appear over the menu and menu cursor.

事件參數:

參數 類型 描述
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderingHud Raised before drawing the HUD (item toolbar, clock, etc) to the screen. The vanilla HUD may be hidden at this point (e.g., because a menu is open). Content drawn to the sprite batch at this point will appear under the HUD.

事件參數:

參數 類型 描述
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#RenderedHud Raised after drawing the HUD (item toolbar, clock, etc) to the sprite batch, but before it's rendered to the screen. The vanilla HUD may be hidden at this point (e.g., because a menu is open). Content drawn to the sprite batch at this point will appear over the HUD.

事件參數:

參數 類型 描述
e.SpriteBatch SpriteBatch The sprite batch being drawn. Add anything you want to appear on-screen to this sprite batch.
#WindowResized Raised after the game window is resized.

事件參數:

參數 類型 描述
e.OldSize Point The previous window width (e.OldSize.X) and height (e.OldSize.Y).
e.NewSize Point The new window width (e.NewSize.X) and height (e.NewSize.Y).

遊戲循環

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
#GameLaunched Raised after the game is launched, right before the first update tick. This happens once per game session (unrelated to loading saves). All mods are loaded and initialised at this point, so this is a good time to set up mod integrations.
#UpdateTicking
UpdateTicked
Raised before/after the game state is updated (≈60 times per second).

事件參數:

參數 類型 描述
e.Ticks int The number of ticks elapsed since the game started, including the current tick.
e.IsOneSecond bool Whether e.TicksElapsed is a multiple of 60, which happens approximately once per second.
e.IsMultipleOf(int number) method returns bool Whether e.TicksElapsed is a multiple of the given number. This is mainly useful if you want to run logic intermittently (e.g., e.IsMultipleOf(30) for every half-second).
#OneSecondUpdateTicking
OneSecondUpdateTicked
Raised before/after the game state is updated, once per second.

事件參數:

參數 類型 描述
e.Ticks int The number of ticks elapsed since the game started, including the current tick.
e.IsMultipleOf(int number) method returns bool Whether e.TicksElapsed is a multiple of the given number. This is mainly useful if you want to run logic intermittently (e.g., e.IsMultipleOf(120) for every two seconds).
#SaveCreating
SaveCreated
Raised before/after the game creates the save file (after the new-game intro). The save won't be written until all mods have finished handling this event. This is a somewhat specialised event, since the world isn't fully initialised at this point; in most cases you should use DayStarted, Saving, Saved instead.
#Saving
Saved
Raised before/after the game writes data to save file (except the initial save creation). The save won't be written until all mods have finished handling this event. This is also raised for farmhands in multiplayer.
#SaveLoaded Raised after loading a save (including the first day after creating a new save), or connecting to a multiplayer world. This happens right before DayStarted; at this point the save file is read and Context.IsWorldReady is true.

This event isn't raised after saving; if you want to do something at the start of each day, see DayStarted instead.

#DayStarted Raised after a new in-game day starts, or after connecting to a multiplayer world. Everything has already been initialised at this point. (To run code before the game sets up the day, see DayEnding instead.)
#DayEnding Raised before the game ends the current day. This happens before it starts setting up the next day and before Saving.
#TimeChanged Raised after the in-game clock time changes, which happens in intervals of ten in-game minutes.

事件參數:

參數 類型 描述
e.OldTime int The previous time of day in 24-hour notation (like 1600 for 4pm). The clock time resets when the player sleeps, so 2am (before sleeping) is 2600.
e.NewTime int The current time of day in 24-hour notation (like 1600 for 4pm). The clock time resets when the player sleeps, so 2am (before sleeping) is 2600.
#ReturnedToTitle Raised after the game returns to the title screen.

輸入

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 API to access more info or suppress input.

event summary
#ButtonsChanged Raised after the player pressed/released any buttons on the keyboard, mouse, or controller. This includes mouse clicks. If the player pressed/released multiple keys at once, this is only raised once.

事件參數:

參數 類型 描述
e.Pressed SButton[] The buttons that were pressed since the previous tick.
e.Held SButton[] The buttons that were held since the previous tick.
e.Released SButton[] The buttons that were released since the previous tick.
e.Cursor ICursorPosition The cursor position and grab tile. Note: mods won't receive input sent to the chatbox.
#ButtonPressed
ButtonReleased
Raised after the player pressed/released a keyboard, mouse, or controller button. This includes mouse clicks. If the player pressed/released multiple keys at once, this is raised for each button pressed.

事件參數:

參數 類型 描述
e.Button SButton The button pressed or released.
e.Cursor ICursorPosition The cursor position and grab tile.
e.IsDown method returns bool Indicates whether a given button is currently pressed.
e.IsSuppressed method returns bool A method which indicates whether a given button was suppressed by a mod, so the game itself won't see it. Note: mods won't receive input sent to the chatbox.
#CursorMoved Raised after the player moves the in-game cursor.

事件參數:

參數 類型 描述
e.OldPosition ICursorPosition The previous cursor position and grab tile.
e.NewPosition ICursorPosition The current cursor position and grab tile.
#MouseWheelScrolled Raised after the player scrolls the mouse wheel.

事件參數:

參數 類型 描述
e.Position ICursorPosition The current cursor position and grab tile.
e.Delta int The amount by which the mouse wheel was scrolled since the last update.
e.OldValue
e.NewValue
int The previous and current scroll value, cumulative since the game started. Mods should generally use e.Delta instead.

多人

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

event summary
#PeerContextReceived Raised after the mod context for a player is received. The event is raised for any player (whether host or farmhand), including when the connecting player doesn't have SMAPI installed. This is the earliest point where messages can be sent to the player via SMAPI.

This happens immediately before the game approves the connection, so the player doesn't exist in the game yet. When connecting to the host, contextual fields like Game1.IsMasterGame or Context.IsMultiplayer may not be set yet; you can check e.Peer.IsHost to know whether the current player is a farmhand, since the host context will always be received first. Assuming another mod doesn't block the connection, the connection will be approved on the next tick.

事件參數:

參數 類型 描述
e.Peer IMultiplayerPeer The peer whose context was received (see Multiplayer#Get connected player info).
#PeerConnected Raised after a connection from another player is approved by the game. The event is raised for any player (whether host or farmhand), including when the connecting player doesn't have SMAPI installed. This happens after PeerContextReceived.

The player is connected to the game at this point, so methods like Game1.server.kick will work.

事件參數:

參數 類型 描述
e.Peer IMultiplayerPeer The peer who connected (see Multiplayer#Get connected player info).
#ModMessageReceived Raised after a mod message is received over the network.

事件參數:

參數 類型 描述
e.FromPlayerID long The unique ID of the player from whose computer the message was sent.
e.FromModID string The unique ID of the mod which sent the message.
e.Type string A message type which you can use to decide whether it's the one you want to handle. This isn't necessarily globally unique, so you should also check the FromModID field.
e.ReadAs<TModel>() method returns TModel Read the underlying message data into the given model type (like e.ReadAs<MyMessageClass>() or e.ReadAs<string>()). This will return a new instance each time.
#PeerDisconnected Raised after the connection to a player is severed.

事件參數:

參數 類型 描述
e.Peer IMultiplayerPeer The peer whose connection was severed (see Multiplayer#Get connected player info).

玩家

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
#InventoryChanged Raised after items are added or removed from the player inventory.

事件參數:

參數 類型 描述
e.Player Farmer The player whose inventory changed.
e.Added IEnumerable<Item> The added item stacks.
e.Removed IEnumerable<Item> The removed item stacks.
e.QuantityChanged IEnumerable<ItemStackSizeChange> The item stacks whose quantity changed. Each ItemStackSizeChange instance includes Item (the affected item stack), OldSize (the previous stack size), and NewSize (the new stack size).
e.IsLocalPlayer bool Whether the affected player is the local one.
#LevelChanged Raised after a player's skill level changes. When the player levels up normally, this is raised immediately (not when the game notifies the player after they go to bed).

事件參數:

參數 類型 描述
e.Player Farmer The player whose skill level changed.
e.Skill SkillType The skill whose level changed. This is a constant like SkillType.Combat, which can be converted to the game's internal skill ID using (int)e.Skill.
e.OldLevel int The player's previous level for that skill.
e.NewLevel int The player's new level for that skill.
e.IsLocalPlayer bool Whether the affected player is the local one.
#Warped Raised after the current player moves to a new location.

事件參數:

參數 類型 描述
e.Player Farmer The player who warped to a new location.
e.OldLocation GameLocation The player's previous location.
e.NewLocation GameLocation The player's new location.
e.IsLocalPlayer bool Whether the affected player is the local one.

世界

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

event summary
#LocationListChanged Raised after a game location is added or removed (including building interiors).

事件參數:

參數 類型 描述
e.Added IEnumerable<GameLocation> A list of locations added since the last update tick.
e.Removed IEnumerable<GameLocation> A list of locations removed since the last update tick.
#BuildingListChanged Raised after buildings are added/removed in any location.

This event isn't raised for buildings already present when a location is added. If you need to handle those too, use LocationListChanged and check e.Added.OfType<BuildableGameLocation>()buildings.

事件參數:

參數 類型 描述
e.Location GameLocation The location which changed.
e.Added IEnumerable<Building> A list of buildings added since the last update tick.
e.Removed IEnumerable<Building> A list of buildings removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#ChestInventoryChanged Raised after items are added or removed from a chest's inventory.

事件參數:

參數 類型 描述
e.Chest Chest The chest whose inventory changed.
e.Location Location The location containing the chest.
e.Added IEnumerable<Item> The added item stacks.
e.Removed IEnumerable<Item> The removed item stacks.
e.QuantityChanged IEnumerable<ItemStackSizeChange> The item stacks whose quantity changed. Each ItemStackSizeChange instance includes Item (the affected item stack), OldSize (the previous stack size), and NewSize (the new stack size).
#DebrisListChanged Raised after debris is added/removed in any location (including dropped or spawned floating items).

This event isn't raised for debris already present when a location is added. If you need to handle those too, use LocationListChanged and check e.Addeddebris.

事件參數:

參數 類型 描述
e.Location GameLocation The location which changed.
e.Added IEnumerable<Debris> A list of debris added since the last update tick.
e.Removed IEnumerable<Debris> A list of debris removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#LargeTerrainFeatureListChanged Raised after large terrain features (like bushes) are added/removed in any location.

This event isn't raised for large terrain features already present when a location is added. If you need to handle those too, use LocationListChanged and check e.AddedlargeTerrainFeatures.

事件參數:

參數 類型 描述
e.Location GameLocation The location which changed.
e.Added IEnumerable<LargeTerrainFeature> A list of large terrain features added since the last update tick.
e.Removed IEnumerable<LargeTerrainFeature> A list of large terrain features removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#NpcListChanged Raised after NPCs are added/removed in any location (including villagers, horses, Junimos, monsters, and pets).

This event isn't raised for characters already present when a location is added. If you need to handle those too, use LocationListChanged and check e.Addedcharacters.

事件參數:

參數 類型 描述
e.Location GameLocation The location which changed.
e.Added IEnumerable<NPC> A list of NPCs added since the last update tick.
e.Removed IEnumerable<NPC> A list of NPCs removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#ObjectListChanged Raised after objects are added/removed in any location (including machines, furniture, fences, etc). For floating items, see DebrisListChanged.

This event isn't raised for objects already present when a location is added. If you need to handle those too, use LocationListChanged and check e.Addedobjects.

事件參數:

參數 類型 描述
e.Location GameLocation The location which changed.
e.Added IEnumerable<KeyValuePair<Vector2, Object>> A list of tile coordinate + object pairs added since the last update tick.
e.Removed IEnumerable<KeyValuePair<Vector2, Object>> A list of tile coordinate + object pairs removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.
#TerrainFeatureListChanged Raised after terrain features are added/removed in any location (including trees, hoed dirt, and flooring). For bushes, see LargeTerrainFeatureListChanged.

This event isn't raised for terrain features already present when a location is added. If you need to handle those too, use LocationListChanged and check e.AddedterrainFeatures.

事件參數:

參數 類型 描述
e.Location GameLocation The location which changed.
e.Added IEnumerable<KeyValuePair<Vector2, TerrainFeature>> A list of tile coordinate + terrain feature pairs added since the last update tick.
e.Removed IEnumerable<KeyValuePair<Vector2, TerrainFeature>> A list of tile coordinate + terrain feature pairs removed since the last update tick.
e.IsCurrentLocation bool Whether e.Location is the location containing the local player.

特殊

this.Helper.Events.Specialised 針對特殊情況的事件. 大多數模組不應使用這些功能

event summary
#LoadStageChanged Raised when the low-level stage in the game's loading process has changed, for mods which need to run code at specific points in the loading process. The available stages or when they happen might change without warning in future versions (e.g., due to changes in the game's load process), so mods using this event are more likely to break or have bugs. Most mods should use the game loop events instead.

事件參數:

參數 類型 描述
e.NewStage LoadStage The new load stage. The possible values are...
  • None: a save is not loaded or loading. (For example, the player is on the title screen.)
  • When creating a new save slot:
    • CreatedBasicInfo: the game has initialised the basic save info.
    • CreatedInitialLocations: the game has added the location instances, but hasn't fully initialized them yet.
    • CreatedLocations: the game has initialised the in-game locations.
    • CreatedSaveFile: the game has created the physical save files.
  • When loading an existing save slot:
    • SaveParsed: the game has read the raw save data into StardewValley.SaveGame.loaded. Not applicable when connecting to a multiplayer host. This is equivalent to StardewValley.SaveGame.getLoadEnumerator value 20.
    • SaveLoadedBasicInfo: the game has applied the basic save info (including player data). Not applicable when connecting to a multiplayer host. Note that some basic info (like daily luck) is not initialised at this point. This is equivalent to StardewValley.SaveGame.getLoadEnumerator value 36.
    • SaveAddedLocations: the game has added the location instances to the game, but hasn't restored their save data yet.
    • SaveLoadedLocations: the game has restored the in-game location data. Not applicable when connecting to a multiplayer host. This is equivalent to StardewValley.SaveGame.getLoadEnumerator value 50.
  • Preloaded: the final metadata has been loaded from the save file. This happens before the game applies problem fixes, checks for achievements, starts music, etc. Not applicable when connecting to a multiplayer host.
  • Loaded: the save is fully loaded, but the world may not be fully initialised yet.
  • Ready: the save is fully loaded, the world has been initialised, and Context.IsWorldReady is now true.
  • ReturningToTitle: The game is exiting the loaded save and returning to the title screen. This happens before it returns to title; the stage after it returns to title is None.
e.OldStage LoadStage The previous load stage. See comments for e.NewStage.
#UnvalidatedUpdateTicking
UnvalidatedUpdateTicked
Raised before/after the game updates its state (≈60 times per second), regardless of normal SMAPI validation. This event is not thread-safe and may be invoked while game logic is running asynchronously. Changes to game state in this method may crash the game or corrupt an in-progress save. Do not use this event unless you're fully aware of the context in which your code will be run. Using this event will trigger a warning in the SMAPI console.

事件參數:

參數 類型 描述
e.Ticks int The number of ticks elapsed since the game started, including the current tick.
e.IsOneSecond bool Whether e.TicksElapsed is a multiple of 60, which happens approximately once per second.
e.IsMultipleOf(int number) method returns bool Whether e.TicksElapsed is a multiple of the given number. This is mainly useful if you want to run logic intermittently (e.g., e.IsMultipleOf(30) for every half-second).

進階

變化監控

You may want to handle a change that doesn't have its own event (e.g., an in-game 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>
public 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;
    }
}

自定義優先級

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>
public 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!");
    }
}