Compare commits
2 Commits
develop
...
experiment
Author | SHA1 | Date |
---|---|---|
Jesse Plamondon-Willard | e16584527c | |
Jesse Plamondon-Willard | 90ca3d6ba1 |
|
@ -11,6 +11,7 @@
|
|||
* Added `IsLocalPlayer` to new player events.
|
||||
* Reloading a map asset will now update affected locations.
|
||||
* Reloading the `Data\NPCDispositions` asset will now update affected NPCs.
|
||||
* Improved how SMAPI events work at the end of day. Events are now raised correctly while end-of-day menus like the shipping menu are shown, and `BeforeSave` is now raised immediately before save instead of when the shipping menu is opened.
|
||||
* Fixed some map tilesheets not editable if not playing in English.
|
||||
* Fixed newlines in most manifest fields not being ignored.
|
||||
* Fixed `Display.RenderedWorld` event not invoked before overlays are rendered.
|
||||
|
|
|
@ -166,11 +166,6 @@ namespace StardewModdingAPI.Framework
|
|||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// apply game patches
|
||||
new GamePatcher(this.Monitor).Apply(
|
||||
new DialogueErrorPatch(this.MonitorForGame, this.Reflection)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Launch SMAPI.</summary>
|
||||
|
@ -284,6 +279,12 @@ namespace StardewModdingAPI.Framework
|
|||
File.Delete(Constants.FatalCrashMarker);
|
||||
}
|
||||
|
||||
// apply game patches
|
||||
new GamePatcher(this.Monitor).Apply(
|
||||
new DialogueErrorPatch(this.MonitorForGame, this.Reflection),
|
||||
new SaveGamePatch(onSaving: this.GameInstance.OnSaving)
|
||||
);
|
||||
|
||||
// start game
|
||||
this.Monitor.Log("Starting game...", LogLevel.Debug);
|
||||
try
|
||||
|
|
|
@ -199,11 +199,61 @@ namespace StardewModdingAPI.Framework
|
|||
}
|
||||
|
||||
/// <summary>A callback invoked before <see cref="Game1.newDayAfterFade"/> runs.</summary>
|
||||
protected void OnNewDayAfterFade()
|
||||
private void OnNewDayAfterFade()
|
||||
{
|
||||
this.Events.DayEnding.RaiseEmpty();
|
||||
}
|
||||
|
||||
/// <summary>A callback invoked before <see cref="SaveGame.Save"/> runs.</summary>
|
||||
internal void OnSaving()
|
||||
{
|
||||
// raise events
|
||||
if (!Context.IsWorldReady)
|
||||
{
|
||||
this.IsBetweenCreateEvents = true;
|
||||
this.Monitor.Log("Context: before save creation.", LogLevel.Trace);
|
||||
this.Events.SaveCreating.RaiseEmpty();
|
||||
#if !SMAPI_3_0_STRICT
|
||||
this.Events.Legacy_BeforeCreateSave.Raise();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
this.IsBetweenSaveEvents = true;
|
||||
this.Monitor.Log("Context: before save.", LogLevel.Trace);
|
||||
this.Events.Saving.RaiseEmpty();
|
||||
#if !SMAPI_3_0_STRICT
|
||||
this.Events.Legacy_BeforeSave.Raise();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>A callback invoked after <see cref="SaveGame.Save"/> runs.</summary>
|
||||
private void OnSaved()
|
||||
{
|
||||
// reset flags
|
||||
this.IsBetweenCreateEvents = false;
|
||||
this.IsBetweenSaveEvents = false;
|
||||
|
||||
// raise events
|
||||
if (!Context.IsWorldReady)
|
||||
{
|
||||
this.Monitor.Log($"Context: after save creation, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
|
||||
this.Events.SaveCreated.RaiseEmpty();
|
||||
#if !SMAPI_3_0_STRICT
|
||||
this.Events.Legacy_AfterCreateSave.Raise();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Monitor.Log($"Context: after save, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
|
||||
this.Events.Saved.RaiseEmpty();
|
||||
#if !SMAPI_3_0_STRICT
|
||||
this.Events.Legacy_AfterSave.Raise();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>A callback invoked when a mod message is received.</summary>
|
||||
/// <param name="message">The message to deliver to applicable mods.</param>
|
||||
private void OnModMessageReceived(ModMessageModel message)
|
||||
|
@ -362,67 +412,26 @@ namespace StardewModdingAPI.Framework
|
|||
inputState.TrueUpdate();
|
||||
|
||||
/*********
|
||||
** Save events + suppress events during save
|
||||
** Suppress events during save
|
||||
*********/
|
||||
// While the game is writing to the save file in the background, mods can unexpectedly
|
||||
// fail since they don't have exclusive access to resources (e.g. collection changed
|
||||
// during enumeration errors). To avoid problems, events are not invoked while a save
|
||||
// is in progress. It's safe to raise SaveEvents.BeforeSave as soon as the menu is
|
||||
// opened (since the save hasn't started yet), but all other events should be suppressed.
|
||||
if (Context.IsSaving)
|
||||
// is in progress.
|
||||
if (this.IsBetweenCreateEvents || this.IsBetweenSaveEvents)
|
||||
{
|
||||
// raise before-create
|
||||
if (!Context.IsWorldReady && !this.IsBetweenCreateEvents)
|
||||
if (!Context.IsSaving)
|
||||
this.OnSaved();
|
||||
else
|
||||
{
|
||||
this.IsBetweenCreateEvents = true;
|
||||
this.Monitor.Log("Context: before save creation.", LogLevel.Trace);
|
||||
this.Events.SaveCreating.RaiseEmpty();
|
||||
this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed));
|
||||
base.Update(gameTime);
|
||||
this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed));
|
||||
#if !SMAPI_3_0_STRICT
|
||||
this.Events.Legacy_BeforeCreateSave.Raise();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// raise before-save
|
||||
if (Context.IsWorldReady && !this.IsBetweenSaveEvents)
|
||||
{
|
||||
this.IsBetweenSaveEvents = true;
|
||||
this.Monitor.Log("Context: before save.", LogLevel.Trace);
|
||||
this.Events.Saving.RaiseEmpty();
|
||||
#if !SMAPI_3_0_STRICT
|
||||
this.Events.Legacy_BeforeSave.Raise();
|
||||
#endif
|
||||
}
|
||||
|
||||
// suppress non-save events
|
||||
this.Events.UnvalidatedUpdateTicking.Raise(new UnvalidatedUpdateTickingEventArgs(this.TicksElapsed));
|
||||
base.Update(gameTime);
|
||||
this.Events.UnvalidatedUpdateTicked.Raise(new UnvalidatedUpdateTickedEventArgs(this.TicksElapsed));
|
||||
#if !SMAPI_3_0_STRICT
|
||||
this.Events.Legacy_UnvalidatedUpdateTick.Raise();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (this.IsBetweenCreateEvents)
|
||||
{
|
||||
// raise after-create
|
||||
this.IsBetweenCreateEvents = false;
|
||||
this.Monitor.Log($"Context: after save creation, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
|
||||
this.Events.SaveCreated.RaiseEmpty();
|
||||
#if !SMAPI_3_0_STRICT
|
||||
this.Events.Legacy_AfterCreateSave.Raise();
|
||||
#endif
|
||||
}
|
||||
if (this.IsBetweenSaveEvents)
|
||||
{
|
||||
// raise after-save
|
||||
this.IsBetweenSaveEvents = false;
|
||||
this.Monitor.Log($"Context: after save, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
|
||||
this.Events.Saved.RaiseEmpty();
|
||||
this.Events.DayStarted.RaiseEmpty();
|
||||
#if !SMAPI_3_0_STRICT
|
||||
this.Events.Legacy_AfterSave.Raise();
|
||||
this.Events.Legacy_AfterDayStarted.Raise();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*********
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using Harmony;
|
||||
using StardewModdingAPI.Framework.Patching;
|
||||
using StardewValley;
|
||||
|
||||
namespace StardewModdingAPI.Patches
|
||||
{
|
||||
/// <summary>A Harmony patch for <see cref="SaveGame.Save"/> to detect when the game is saving.</summary>
|
||||
internal class SaveGamePatch : IHarmonyPatch
|
||||
{
|
||||
/*********
|
||||
** Private methods
|
||||
*********/
|
||||
/// <summary>A callback to invoke before <see cref="SaveGame.Save"/> runs.</summary>
|
||||
private static Action OnSaving;
|
||||
|
||||
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>A unique name for this patch.</summary>
|
||||
public string Name => $"{nameof(SaveGamePatch)}";
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="onSaving">A callback to invoke before <see cref="SaveGame.Save"/> runs.</param>
|
||||
public SaveGamePatch(Action onSaving)
|
||||
{
|
||||
SaveGamePatch.OnSaving = onSaving;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Apply the Harmony patch.</summary>
|
||||
/// <param name="harmony">The Harmony instance.</param>
|
||||
public void Apply(HarmonyInstance harmony)
|
||||
{
|
||||
MethodInfo method = AccessTools.Method(typeof(SaveGame), nameof(SaveGame.Save));
|
||||
MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(SaveGamePatch.Prefix));
|
||||
|
||||
harmony.Patch(method, prefix: new HarmonyMethod(prefix));
|
||||
}
|
||||
|
||||
|
||||
/*********
|
||||
** Private methods
|
||||
*********/
|
||||
/// <summary>The method to call before <see cref="SaveGame.Save"/>.</summary>
|
||||
/// <returns>Returns whether to execute the original method.</returns>
|
||||
/// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")]
|
||||
private static bool Prefix()
|
||||
{
|
||||
SaveGamePatch.OnSaving();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -323,6 +323,7 @@
|
|||
<Compile Include="Framework\Monitor.cs" />
|
||||
<Compile Include="Metadata\InstructionMetadata.cs" />
|
||||
<Compile Include="Mod.cs" />
|
||||
<Compile Include="Patches\SaveGamePatch.cs" />
|
||||
<Compile Include="Patches\DialogueErrorPatch.cs" />
|
||||
<Compile Include="PatchMode.cs" />
|
||||
<Compile Include="GamePlatform.cs" />
|
||||
|
|
Loading…
Reference in New Issue