summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/SGameRunner.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI/Framework/SGameRunner.cs')
-rw-r--r--src/SMAPI/Framework/SGameRunner.cs156
1 files changed, 156 insertions, 0 deletions
diff --git a/src/SMAPI/Framework/SGameRunner.cs b/src/SMAPI/Framework/SGameRunner.cs
new file mode 100644
index 00000000..ae06f513
--- /dev/null
+++ b/src/SMAPI/Framework/SGameRunner.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using StardewModdingAPI.Framework.Events;
+using StardewModdingAPI.Framework.Input;
+using StardewModdingAPI.Framework.Reflection;
+using StardewValley;
+
+namespace StardewModdingAPI.Framework
+{
+ /// <summary>SMAPI's extension of the game's core <see cref="GameRunner"/>, used to inject SMAPI components.</summary>
+ internal class SGameRunner : GameRunner
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>Encapsulates monitoring and logging for SMAPI.</summary>
+ private readonly Monitor Monitor;
+
+ /// <summary>Manages SMAPI events for mods.</summary>
+ private readonly EventManager Events;
+
+ /// <summary>Simplifies access to private game code.</summary>
+ private readonly Reflector Reflection;
+
+ /// <summary>Immediately exit the game without saving. This should only be invoked when an irrecoverable fatal error happens that risks save corruption or game-breaking bugs.</summary>
+ private readonly Action<string> ExitGameImmediately;
+
+ /// <summary>The core SMAPI mod hooks.</summary>
+ private readonly SModHooks ModHooks;
+
+ /// <summary>The core multiplayer logic.</summary>
+ private readonly SMultiplayer Multiplayer;
+
+ /// <summary>Raised after the game finishes loading its initial content.</summary>
+ private readonly Action OnGameContentLoaded;
+
+ /// <summary>Raised when XNA is updating (roughly 60 times per second).</summary>
+ private readonly Action<GameTime, Action> OnGameUpdating;
+
+ /// <summary>Raised when the game instance for a local split-screen player is updating (once per <see cref="OnGameUpdating"/> per player).</summary>
+ private readonly Action<SGame, GameTime, Action> OnPlayerInstanceUpdating;
+
+ /// <summary>Raised before the game exits.</summary>
+ private readonly Action OnGameExiting;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="monitor">Encapsulates monitoring and logging for SMAPI.</param>
+ /// <param name="reflection">Simplifies access to private game code.</param>
+ /// <param name="eventManager">Manages SMAPI events for mods.</param>
+ /// <param name="modHooks">Handles mod hooks provided by the game.</param>
+ /// <param name="multiplayer">The core multiplayer logic.</param>
+ /// <param name="exitGameImmediately">Immediately exit the game without saving. This should only be invoked when an irrecoverable fatal error happens that risks save corruption or game-breaking bugs.</param>
+ /// <param name="onGameContentLoaded">Raised after the game finishes loading its initial content.</param>
+ /// <param name="onGameUpdating">Raised when XNA is updating its state (roughly 60 times per second).</param>
+ /// <param name="onPlayerInstanceUpdating">Raised when the game instance for a local split-screen player is updating (once per <see cref="OnGameUpdating"/> per player).</param>
+ /// <param name="onGameExiting">Raised before the game exits.</param>
+ public SGameRunner(Monitor monitor, Reflector reflection, EventManager eventManager, SModHooks modHooks, SMultiplayer multiplayer, Action<string> exitGameImmediately, Action onGameContentLoaded, Action<GameTime, Action> onGameUpdating, Action<SGame, GameTime, Action> onPlayerInstanceUpdating, Action onGameExiting)
+ {
+ // init XNA
+ Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef;
+
+ // hook into game
+ this.ModHooks = modHooks;
+
+ // init SMAPI
+ this.Monitor = monitor;
+ this.Events = eventManager;
+ this.Reflection = reflection;
+ this.Multiplayer = multiplayer;
+ this.ExitGameImmediately = exitGameImmediately;
+ this.OnGameContentLoaded = onGameContentLoaded;
+ this.OnGameUpdating = onGameUpdating;
+ this.OnPlayerInstanceUpdating = onPlayerInstanceUpdating;
+ this.OnGameExiting = onGameExiting;
+ }
+
+ /// <summary>Create a game instance for a local player.</summary>
+ /// <param name="playerIndex">The player index.</param>
+ /// <param name="instanceIndex">The instance index.</param>
+ public override Game1 CreateGameInstance(PlayerIndex playerIndex = PlayerIndex.One, int instanceIndex = 0)
+ {
+ SInputState inputState = new SInputState();
+ return new SGame(playerIndex, instanceIndex, this.Monitor, this.Reflection, this.Events, inputState, this.ModHooks, this.Multiplayer, this.ExitGameImmediately, this.OnPlayerInstanceUpdating);
+ }
+
+ /// <inheritdoc />
+ public override void AddGameInstance(PlayerIndex playerIndex)
+ {
+ base.AddGameInstance(playerIndex);
+
+ EarlyConstants.LogScreenId = Context.ScreenId;
+ this.UpdateForSplitScreenChanges();
+ }
+
+ /// <inheritdoc />
+ public override void RemoveGameInstance(Game1 instance)
+ {
+ base.RemoveGameInstance(instance);
+
+ if (this.gameInstances.Count <= 1)
+ EarlyConstants.LogScreenId = null;
+ this.UpdateForSplitScreenChanges();
+ }
+
+
+ /*********
+ ** Protected methods
+ *********/
+ /// <summary>Load content when the game is launched.</summary>
+ protected override void LoadContent()
+ {
+ base.LoadContent();
+
+ this.OnGameContentLoaded();
+ }
+
+ /// <summary>Perform cleanup logic when the game exits.</summary>
+ /// <param name="sender">The event sender.</param>
+ /// <param name="args">The event args.</param>
+ /// <remarks>This overrides the logic in <see cref="Game1.exitEvent"/> to let SMAPI clean up before exit.</remarks>
+ protected override void OnExiting(object sender, EventArgs args)
+ {
+ this.OnGameExiting();
+ }
+
+ /// <summary>The method called when the game is updating its state (roughly 60 times per second).</summary>
+ /// <param name="gameTime">A snapshot of the game timing state.</param>
+ protected override void Update(GameTime gameTime)
+ {
+ this.OnGameUpdating(gameTime, () => base.Update(gameTime));
+ }
+
+ private void UpdateForSplitScreenChanges()
+ {
+ HashSet<int> oldScreenIds = new HashSet<int>(Context.ActiveScreenIds);
+
+ // track active screens
+ Context.ActiveScreenIds.Clear();
+ foreach (var screen in this.gameInstances)
+ Context.ActiveScreenIds.Add(screen.instanceId);
+
+ // remember last removed screen
+ foreach (int id in oldScreenIds)
+ {
+ if (!Context.ActiveScreenIds.Contains(id))
+ Context.LastRemovedScreenId = id;
+ }
+ }
+ }
+}