add SMAPI 3.0 compatibility strict mode (#606)

This commit is contained in:
Jesse Plamondon-Willard 2018-12-03 02:39:20 -05:00
parent a2a0469cd0
commit 3744e2f1e5
No known key found for this signature in database
GPG Key ID: 7D7C8097B62033CE
51 changed files with 229 additions and 37 deletions

View File

@ -105,8 +105,8 @@ SMAPI uses a small number of conditional compilation constants, which you can se
flag | purpose
---- | -------
`SMAPI_FOR_WINDOWS` | Indicates that SMAPI is being compiled on Windows for players on Windows. Set automatically in `crossplatform.targets`.
`SMAPI_FOR_WINDOWS` | Whether SMAPI is being compiled on Windows for players on Windows. Set automatically in `crossplatform.targets`.
`SMAPI_3_0_STRICT` | Whether to exclude all deprecated APIs from compilation. This is useful for testing mods for SMAPI 3.0 compatibility.
# SMAPI web services
## Overview

View File

@ -29,7 +29,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands
helper.ConsoleCommands.Add(command.Name, command.Description, (name, args) => this.HandleCommand(command, name, args));
// hook events
GameEvents.UpdateTick += this.GameEvents_UpdateTick;
helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked;
}
@ -39,7 +39,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands
/// <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 GameEvents_UpdateTick(object sender, EventArgs e)
private void OnUpdateTicked(object sender, EventArgs e)
{
if (!Context.IsWorldReady)
return;

View File

@ -47,7 +47,7 @@ namespace StardewModdingAPI.Tests.Utilities
Assert.AreEqual(major, version.MajorVersion, "The major version doesn't match the given value.");
Assert.AreEqual(minor, version.MinorVersion, "The minor version doesn't match the given value.");
Assert.AreEqual(patch, version.PatchVersion, "The patch version doesn't match the given value.");
Assert.AreEqual(string.IsNullOrWhiteSpace(tag) ? null : tag.Trim(), version.Build, "The tag doesn't match the given value.");
Assert.AreEqual(string.IsNullOrWhiteSpace(tag) ? null : tag.Trim(), version.PrereleaseTag, "The tag doesn't match the given value.");
return version.ToString();
}

View File

@ -22,7 +22,7 @@ namespace StardewModdingAPI
/// <summary>Whether <see cref="IsPlayerFree"/> is true and the player is free to move (e.g. not using a tool).</summary>
public static bool CanPlayerMove => Context.IsPlayerFree && Game1.player.CanMove;
/// <summary>Whether the game is currently running the draw loop. This isn't relevant to most mods, since you should use <see cref="GraphicsEvents.OnPostRenderEvent"/> to draw to the screen.</summary>
/// <summary>Whether the game is currently running the draw loop. This isn't relevant to most mods, since you should use <see cref="IDisplayEvents"/> events to draw to the screen.</summary>
public static bool IsInDrawLoop { get; internal set; }
/// <summary>Whether <see cref="IsWorldReady"/> and the player loaded the save in multiplayer mode (regardless of whether any other players are connected).</summary>

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -46,3 +47,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework.Input;
using StardewModdingAPI.Framework;
@ -124,3 +125,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewValley.Menus;
@ -29,3 +30,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewValley.Menus;
@ -24,3 +25,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@ -30,3 +31,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,4 +1,5 @@
using System;
#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@ -30,3 +31,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@ -35,3 +36,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,4 +1,5 @@
using System;
#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@ -35,3 +36,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using System.Collections.Generic;
@ -60,3 +61,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
namespace StardewModdingAPI.Events
@ -28,3 +29,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using System.Collections.Generic;
using System.Linq;
@ -39,3 +40,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework.Input;
@ -24,3 +25,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework.Input;
@ -29,3 +30,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Enums;
@ -51,3 +52,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using System.Collections.Generic;
using System.Linq;
@ -37,3 +38,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using System.Collections.Generic;
using System.Linq;
@ -38,3 +39,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using System.Collections.Generic;
using System.Linq;
@ -31,3 +32,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,4 +1,5 @@
using System;
#if !SMAPI_3_0_STRICT
using System;
namespace StardewModdingAPI.Events
{
@ -28,3 +29,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,4 +1,5 @@
using System;
#if !SMAPI_3_0_STRICT
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@ -40,3 +41,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewValley;
@ -30,3 +31,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,4 +1,5 @@
using System;
#if !SMAPI_3_0_STRICT
using System;
namespace StardewModdingAPI.Events
{
@ -28,4 +29,5 @@ namespace StardewModdingAPI.Events
this.NewValue = newValue;
}
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -123,3 +124,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -121,3 +122,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -57,3 +58,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -68,3 +69,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -57,3 +58,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -46,3 +47,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -79,3 +80,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -69,3 +70,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -101,3 +102,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -46,3 +47,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,3 +1,4 @@
#if !SMAPI_3_0_STRICT
using System;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Events;
@ -57,3 +58,4 @@ namespace StardewModdingAPI.Events
}
}
}
#endif

View File

@ -1,5 +1,7 @@
using System.Diagnostics.CodeAnalysis;
#if !SMAPI_3_0_STRICT
using Microsoft.Xna.Framework.Input;
#endif
using StardewModdingAPI.Events;
namespace StardewModdingAPI.Framework.Events
@ -156,6 +158,7 @@ namespace StardewModdingAPI.Framework.Events
public readonly ManagedEvent<UnvalidatedUpdateTickedEventArgs> UnvalidatedUpdateTicked;
#if !SMAPI_3_0_STRICT
/*********
** Events (old)
*********/
@ -342,6 +345,7 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>Raised after the in-game clock changes.</summary>
public readonly ManagedEvent<EventArgsIntChanged> Legacy_TimeOfDayChanged;
#endif
/*********
@ -354,7 +358,9 @@ namespace StardewModdingAPI.Framework.Events
{
// create shortcut initialisers
ManagedEvent<TEventArgs> ManageEventOf<TEventArgs>(string typeName, string eventName) => new ManagedEvent<TEventArgs>($"{typeName}.{eventName}", monitor, modRegistry);
#if !SMAPI_3_0_STRICT
ManagedEvent ManageEvent(string typeName, string eventName) => new ManagedEvent($"{typeName}.{eventName}", monitor, modRegistry);
#endif
// init events (new)
this.MenuChanged = ManageEventOf<MenuChangedEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged));
@ -405,6 +411,7 @@ namespace StardewModdingAPI.Framework.Events
this.UnvalidatedUpdateTicking = ManageEventOf<UnvalidatedUpdateTickingEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicking));
this.UnvalidatedUpdateTicked = ManageEventOf<UnvalidatedUpdateTickedEventArgs>(nameof(IModEvents.Specialised), nameof(ISpecialisedEvents.UnvalidatedUpdateTicked));
#if !SMAPI_3_0_STRICT
// init events (old)
this.Legacy_LocaleChanged = ManageEventOf<EventArgsValueChanged<string>>(nameof(ContentEvents), nameof(ContentEvents.AfterLocaleChanged));
@ -466,6 +473,7 @@ namespace StardewModdingAPI.Framework.Events
this.Legacy_AfterDayStarted = ManageEvent(nameof(TimeEvents), nameof(TimeEvents.AfterDayStarted));
this.Legacy_TimeOfDayChanged = ManageEventOf<EventArgsIntChanged>(nameof(TimeEvents), nameof(TimeEvents.TimeOfDayChanged));
#endif
}
}
}

View File

@ -131,6 +131,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.Data.WriteJsonFile("config.json", config);
}
#if !SMAPI_3_0_STRICT
/****
** Generic JSON files
****/
@ -199,6 +200,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
// create content pack
return this.CreateContentPack(directoryPath, manifest);
}
#endif
/// <summary>Get all content packs loaded for this mod.</summary>
public IEnumerable<IContentPack> GetContentPacks()

View File

@ -23,7 +23,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>The instruction is compatible, but uses the <c>dynamic</c> keyword which won't work on Linux/Mac.</summary>
DetectedDynamic,
/// <summary>The instruction is compatible, but references <see cref="SpecialisedEvents.UnvalidatedUpdateTick"/> which may impact stability.</summary>
/// <summary>The instruction is compatible, but references <see cref="ISpecialisedEvents.UnvalidatedUpdateTicking"/> or <see cref="ISpecialisedEvents.UnvalidatedUpdateTicked"/> which may impact stability.</summary>
DetectedUnvalidatedUpdateTick,
/// <summary>The instruction accesses the filesystem directly.</summary>

View File

@ -22,7 +22,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>The mod uses the <c>dynamic</c> keyword which won't work on Linux/Mac.</summary>
UsesDynamic = 8,
/// <summary>The mod references <see cref="SpecialisedEvents.UnvalidatedUpdateTick"/> which may impact stability.</summary>
/// <summary>The mod references <see cref="ISpecialisedEvents.UnvalidatedUpdateTicking"/> or <see cref="ISpecialisedEvents.UnvalidatedUpdateTicked"/> which may impact stability.</summary>
UsesUnvalidatedUpdateTick = 16,
/// <summary>The mod has no update keys set.</summary>

View File

@ -180,6 +180,7 @@ namespace StardewModdingAPI.Framework
// initialise SMAPI
try
{
#if !SMAPI_3_0_STRICT
// hook up events
ContentEvents.Init(this.EventManager, this.DeprecationManager);
ControlEvents.Init(this.EventManager, this.DeprecationManager);
@ -194,6 +195,7 @@ namespace StardewModdingAPI.Framework
SaveEvents.Init(this.EventManager, this.DeprecationManager);
SpecialisedEvents.Init(this.EventManager, this.DeprecationManager);
TimeEvents.Init(this.EventManager, this.DeprecationManager);
#endif
// init JSON parser
JsonConverter[] converters = {
@ -216,7 +218,7 @@ namespace StardewModdingAPI.Framework
// override game
SGame.ConstructorHack = new SGameConstructorHack(this.Monitor, this.Reflection, this.Toolkit.JsonHelper);
this.GameInstance = new SGame(this.Monitor, this.MonitorForGame, this.Reflection, this.EventManager, this.Toolkit.JsonHelper, this.ModRegistry, this.DeprecationManager, this.InitialiseAfterGameStart, this.Dispose);
this.GameInstance = new SGame(this.Monitor, this.MonitorForGame, this.Reflection, this.EventManager, this.Toolkit.JsonHelper, this.ModRegistry, this.DeprecationManager, this.OnLocaleChanged, this.InitialiseAfterGameStart, this.Dispose);
StardewValley.Program.gamePtr = this.GameInstance;
// add exit handler
@ -239,12 +241,13 @@ namespace StardewModdingAPI.Framework
}
}).Start();
// hook into game events
ContentEvents.AfterLocaleChanged += (sender, e) => this.OnLocaleChanged();
// set window titles
this.GameInstance.Window.Title = $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion}";
Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion}";
#if SMAPI_3_0_STRICT
this.GameInstance.Window.Title += " [SMAPI 3.0 strict mode]";
Console.Title += " [SMAPI 3.0 strict mode]";
#endif
}
catch (Exception ex)
{
@ -348,8 +351,11 @@ namespace StardewModdingAPI.Framework
private void InitialiseAfterGameStart()
{
// add headers
#if SMAPI_3_0_STRICT
this.Monitor.Log($"You're running SMAPI 3.0 strict mode, so most mods won't work correctly. If that wasn't intended, install the normal version of SMAPI from https://smapi.io instead.", LogLevel.Warn);
#endif
if (this.Settings.DeveloperMode)
this.Monitor.Log($"You configured SMAPI to run in developer mode. The console may be much more verbose. You can disable developer mode by installing the non-developer version of SMAPI, or by editing {Constants.ApiConfigPath}.", LogLevel.Info);
this.Monitor.Log($"You have SMAPI for developers, so the console will be much more verbose. You can disable developer mode by installing the non-developer version of SMAPI, or by editing {Constants.ApiConfigPath}.", LogLevel.Info);
if (!this.Settings.CheckForUpdates)
this.Monitor.Log($"You configured SMAPI to not check for updates. Running an old version of SMAPI is not recommended. You can enable update checks by reinstalling SMAPI or editing {Constants.ApiConfigPath}.", LogLevel.Warn);
if (!this.Monitor.WriteToConsole)
@ -409,6 +415,11 @@ namespace StardewModdingAPI.Framework
int modsLoaded = this.ModRegistry.GetAll().Count();
this.GameInstance.Window.Title = $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion} with {modsLoaded} mods";
Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion} with {modsLoaded} mods";
#if SMAPI_3_0_STRICT
this.GameInstance.Window.Title += " [SMAPI 3.0 strict mode]";
Console.Title += " [SMAPI 3.0 strict mode]";
#endif
// start SMAPI console
new Thread(this.RunConsoleLoop).Start();

View File

@ -9,7 +9,6 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Netcode;
using StardewModdingAPI.Enums;
using StardewModdingAPI.Events;
@ -70,12 +69,15 @@ namespace StardewModdingAPI.Framework
/// <summary>Whether the after-load events were raised for this session.</summary>
private bool RaisedAfterLoadEvent;
/// <summary>Whether the game is saving and SMAPI has already raised <see cref="SaveEvents.BeforeSave"/>.</summary>
/// <summary>Whether the game is saving and SMAPI has already raised <see cref="IGameLoopEvents.Saving"/>.</summary>
private bool IsBetweenSaveEvents;
/// <summary>Whether the game is creating the save file and SMAPI has already raised <see cref="SaveEvents.BeforeCreate"/>.</summary>
/// <summary>Whether the game is creating the save file and SMAPI has already raised <see cref="IGameLoopEvents.SaveCreating"/>.</summary>
private bool IsBetweenCreateEvents;
/// <summary>A callback to invoke after the content language changes.</summary>
private readonly Action OnLocaleChanged;
/// <summary>A callback to invoke after the game finishes initialising.</summary>
private readonly Action OnGameInitialised;
@ -138,9 +140,10 @@ namespace StardewModdingAPI.Framework
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
/// <param name="modRegistry">Tracks the installed mods.</param>
/// <param name="deprecationManager">Manages deprecation warnings.</param>
/// <param name="onLocaleChanged">A callback to invoke after the content language changes.</param>
/// <param name="onGameInitialised">A callback to invoke after the game finishes initialising.</param>
/// <param name="onGameExiting">A callback to invoke when the game exits.</param>
internal SGame(IMonitor monitor, IMonitor monitorForGame, Reflector reflection, EventManager eventManager, JsonHelper jsonHelper, ModRegistry modRegistry, DeprecationManager deprecationManager, Action onGameInitialised, Action onGameExiting)
internal SGame(IMonitor monitor, IMonitor monitorForGame, Reflector reflection, EventManager eventManager, JsonHelper jsonHelper, ModRegistry modRegistry, DeprecationManager deprecationManager, Action onLocaleChanged, Action onGameInitialised, Action onGameExiting)
{
SGame.ConstructorHack = null;
@ -158,6 +161,7 @@ namespace StardewModdingAPI.Framework
this.ModRegistry = modRegistry;
this.Reflection = reflection;
this.DeprecationManager = deprecationManager;
this.OnLocaleChanged = onLocaleChanged;
this.OnGameInitialised = onGameInitialised;
this.OnGameExiting = onGameExiting;
Game1.input = new SInputState();
@ -212,7 +216,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log("Context: returned to title", LogLevel.Trace);
this.Multiplayer.Peers.Clear();
this.Events.ReturnedToTitle.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_AfterReturnToTitle.Raise();
#endif
}
/// <summary>Constructor a content manager to read XNB files.</summary>
@ -296,7 +302,9 @@ namespace StardewModdingAPI.Framework
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;
}
@ -343,7 +351,9 @@ namespace StardewModdingAPI.Framework
// This should *always* run, even when suppressing mod events, since the game uses
// this too. For example, doing this after mod event suppression would prevent the
// user from doing anything on the overnight shipping screen.
#if !SMAPI_3_0_STRICT
SInputState previousInputState = this.Input.Clone();
#endif
SInputState inputState = this.Input;
if (this.IsActive)
inputState.TrueUpdate();
@ -364,7 +374,9 @@ namespace StardewModdingAPI.Framework
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
}
// raise before-save
@ -373,14 +385,18 @@ namespace StardewModdingAPI.Framework
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)
@ -389,7 +405,9 @@ namespace StardewModdingAPI.Framework
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)
{
@ -398,9 +416,10 @@ namespace StardewModdingAPI.Framework
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
}
/*********
@ -430,7 +449,11 @@ namespace StardewModdingAPI.Framework
var now = this.Watchers.LocaleWatcher.CurrentValue;
this.Monitor.Log($"Context: locale set to {now}.", LogLevel.Trace);
this.OnLocaleChanged();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_LocaleChanged.Raise(new EventArgsValueChanged<string>(was.ToString(), now.ToString()));
#endif
this.Watchers.LocaleWatcher.Reset();
}
@ -457,9 +480,10 @@ namespace StardewModdingAPI.Framework
this.RaisedAfterLoadEvent = true;
this.Events.SaveLoaded.RaiseEmpty();
this.Events.DayStarted.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_AfterLoad.Raise();
this.Events.Legacy_AfterDayStarted.Raise();
#endif
}
/*********
@ -478,7 +502,9 @@ namespace StardewModdingAPI.Framework
Point newSize = this.Watchers.WindowSizeWatcher.CurrentValue;
this.Events.WindowResized.Raise(new WindowResizedEventArgs(oldSize, newSize));
#if !SMAPI_3_0_STRICT
this.Events.Legacy_Resize.Raise();
#endif
this.Watchers.WindowSizeWatcher.Reset();
}
@ -527,9 +553,10 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Events: button {button} pressed.", LogLevel.Trace);
this.Events.ButtonPressed.Raise(new ButtonPressedEventArgs(button, cursor, inputState));
this.Events.Legacy_ButtonPressed.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
#if !SMAPI_3_0_STRICT
// legacy events
this.Events.Legacy_ButtonPressed.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
if (button.TryGetKeyboard(out Keys key))
{
if (key != Keys.None)
@ -542,6 +569,7 @@ namespace StardewModdingAPI.Framework
else
this.Events.Legacy_ControllerButtonPressed.Raise(new EventArgsControllerButtonPressed(PlayerIndex.One, controllerButton));
}
#endif
}
else if (status == InputStatus.Released)
{
@ -549,9 +577,10 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Events: button {button} released.", LogLevel.Trace);
this.Events.ButtonReleased.Raise(new ButtonReleasedEventArgs(button, cursor, inputState));
this.Events.Legacy_ButtonReleased.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
#if !SMAPI_3_0_STRICT
// legacy events
this.Events.Legacy_ButtonReleased.Raise(new EventArgsInput(button, cursor, inputState.SuppressButtons));
if (button.TryGetKeyboard(out Keys key))
{
if (key != Keys.None)
@ -564,14 +593,17 @@ namespace StardewModdingAPI.Framework
else
this.Events.Legacy_ControllerButtonReleased.Raise(new EventArgsControllerButtonReleased(PlayerIndex.One, controllerButton));
}
#endif
}
}
#if !SMAPI_3_0_STRICT
// raise legacy state-changed events
if (inputState.RealKeyboard != previousInputState.RealKeyboard)
this.Events.Legacy_KeyboardChanged.Raise(new EventArgsKeyboardStateChanged(previousInputState.RealKeyboard, inputState.RealKeyboard));
if (inputState.RealMouse != previousInputState.RealMouse)
this.Events.Legacy_MouseChanged.Raise(new EventArgsMouseStateChanged(previousInputState.RealMouse, inputState.RealMouse, new Point((int)previousInputState.CursorPosition.ScreenPixels.X, (int)previousInputState.CursorPosition.ScreenPixels.Y), new Point((int)inputState.CursorPosition.ScreenPixels.X, (int)inputState.CursorPosition.ScreenPixels.Y)));
#endif
}
}
@ -589,10 +621,12 @@ namespace StardewModdingAPI.Framework
// raise menu events
this.Events.MenuChanged.Raise(new MenuChangedEventArgs(was, now));
#if !SMAPI_3_0_STRICT
if (now != null)
this.Events.Legacy_MenuChanged.Raise(new EventArgsClickableMenuChanged(was, now));
else
this.Events.Legacy_MenuClosed.Raise(new EventArgsClickableMenuClosed(was));
#endif
}
/*********
@ -620,7 +654,9 @@ namespace StardewModdingAPI.Framework
}
this.Events.LocationListChanged.Raise(new LocationListChangedEventArgs(added, removed));
#if !SMAPI_3_0_STRICT
this.Events.Legacy_LocationsChanged.Raise(new EventArgsLocationsChanged(added, removed));
#endif
}
// raise location contents changed
@ -637,7 +673,9 @@ namespace StardewModdingAPI.Framework
watcher.BuildingsWatcher.Reset();
this.Events.BuildingListChanged.Raise(new BuildingListChangedEventArgs(location, added, removed));
#if !SMAPI_3_0_STRICT
this.Events.Legacy_BuildingsChanged.Raise(new EventArgsLocationBuildingsChanged(location, added, removed));
#endif
}
// debris changed
@ -682,7 +720,9 @@ namespace StardewModdingAPI.Framework
watcher.ObjectsWatcher.Reset();
this.Events.ObjectListChanged.Raise(new ObjectListChangedEventArgs(location, added, removed));
#if !SMAPI_3_0_STRICT
this.Events.Legacy_ObjectsChanged.Raise(new EventArgsLocationObjectsChanged(location, added, removed));
#endif
}
// terrain features changed
@ -712,7 +752,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Events: time changed from {was} to {now}.", LogLevel.Trace);
this.Events.TimeChanged.Raise(new TimeChangedEventArgs(was, now));
#if !SMAPI_3_0_STRICT
this.Events.Legacy_TimeOfDayChanged.Raise(new EventArgsIntChanged(was, now));
#endif
}
else
this.Watchers.TimeWatcher.Reset();
@ -730,7 +772,9 @@ namespace StardewModdingAPI.Framework
GameLocation oldLocation = playerTracker.LocationWatcher.PreviousValue;
this.Events.Warped.Raise(new WarpedEventArgs(playerTracker.Player, oldLocation, newLocation));
#if !SMAPI_3_0_STRICT
this.Events.Legacy_PlayerWarped.Raise(new EventArgsPlayerWarped(oldLocation, newLocation));
#endif
}
// raise player leveled up a skill
@ -740,7 +784,9 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Events: player skill '{pair.Key}' changed from {pair.Value.PreviousValue} to {pair.Value.CurrentValue}.", LogLevel.Trace);
this.Events.LevelChanged.Raise(new LevelChangedEventArgs(playerTracker.Player, pair.Key, pair.Value.PreviousValue, pair.Value.CurrentValue));
#if !SMAPI_3_0_STRICT
this.Events.Legacy_LeveledUp.Raise(new EventArgsLevelUp((EventArgsLevelUp.LevelType)pair.Key, pair.Value.CurrentValue));
#endif
}
// raise player inventory changed
@ -750,7 +796,9 @@ namespace StardewModdingAPI.Framework
if (this.Monitor.IsVerbose)
this.Monitor.Log("Events: player inventory changed.", LogLevel.Trace);
this.Events.InventoryChanged.Raise(new InventoryChangedEventArgs(playerTracker.Player, changedItems));
#if !SMAPI_3_0_STRICT
this.Events.Legacy_InventoryChanged.Raise(new EventArgsInventoryChanged(Game1.player.Items, changedItems));
#endif
}
// raise mine level changed
@ -758,7 +806,9 @@ namespace StardewModdingAPI.Framework
{
if (this.Monitor.IsVerbose)
this.Monitor.Log($"Context: mine level changed to {mineLevel}.", LogLevel.Trace);
#if !SMAPI_3_0_STRICT
this.Events.Legacy_MineLevelChanged.Raise(new EventArgsMineLevelChanged(playerTracker.MineLevelWatcher.PreviousValue, mineLevel));
#endif
}
}
this.Watchers.CurrentPlayerTracker?.Reset();
@ -790,6 +840,7 @@ namespace StardewModdingAPI.Framework
/*********
** Update events
*********/
#if !SMAPI_3_0_STRICT
this.Events.Legacy_UnvalidatedUpdateTick.Raise();
if (this.TicksElapsed == 1)
this.Events.Legacy_FirstUpdateTick.Raise();
@ -806,6 +857,7 @@ namespace StardewModdingAPI.Framework
this.Events.Legacy_HalfSecondTick.Raise();
if (this.CurrentUpdateTick % 60 == 0)
this.Events.Legacy_OneSecondTick.Raise();
#endif
this.CurrentUpdateTick += 1;
if (this.CurrentUpdateTick >= 60)
this.CurrentUpdateTick = 0;
@ -895,10 +947,14 @@ namespace StardewModdingAPI.Framework
try
{
this.Events.RenderingActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderGuiEvent.Raise();
#endif
activeClickableMenu.draw(Game1.spriteBatch);
this.Events.RenderedActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderGuiEvent.Raise();
#endif
}
catch (Exception ex)
{
@ -906,7 +962,9 @@ namespace StardewModdingAPI.Framework
activeClickableMenu.exitThisMenu();
}
this.Events.Rendered.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderEvent.Raise();
#endif
Game1.spriteBatch.End();
}
@ -930,10 +988,14 @@ namespace StardewModdingAPI.Framework
{
Game1.activeClickableMenu.drawBackground(Game1.spriteBatch);
this.Events.RenderingActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderGuiEvent.Raise();
#endif
Game1.activeClickableMenu.draw(Game1.spriteBatch);
this.Events.RenderedActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderGuiEvent.Raise();
#endif
}
catch (Exception ex)
{
@ -941,7 +1003,9 @@ namespace StardewModdingAPI.Framework
Game1.activeClickableMenu.exitThisMenu();
}
this.Events.Rendered.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderEvent.Raise();
#endif
Game1.spriteBatch.End();
this.drawOverlays(Game1.spriteBatch);
if ((double)Game1.options.zoomLevel != 1.0)
@ -966,7 +1030,9 @@ namespace StardewModdingAPI.Framework
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString("Strings\\StringsFromCSFiles:Game1.cs.3686"), new Vector2(16f, 32f), new Color(0, (int)byte.MaxValue, 0));
Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.parseText(Game1.errorMessage, Game1.dialogueFont, Game1.graphics.GraphicsDevice.Viewport.Width), new Vector2(16f, 48f), Color.White);
this.Events.Rendered.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderEvent.Raise();
#endif
Game1.spriteBatch.End();
}
else if (Game1.currentMinigame != null)
@ -979,7 +1045,9 @@ namespace StardewModdingAPI.Framework
Game1.spriteBatch.End();
}
this.drawOverlays(Game1.spriteBatch);
#if !SMAPI_3_0_STRICT
this.RaisePostRender(needsNewBatch: true);
#endif
if ((double)Game1.options.zoomLevel == 1.0)
return;
this.GraphicsDevice.SetRenderTarget((RenderTarget2D)null);
@ -997,10 +1065,14 @@ namespace StardewModdingAPI.Framework
try
{
this.Events.RenderingActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderGuiEvent.Raise();
#endif
Game1.activeClickableMenu.draw(Game1.spriteBatch);
this.Events.RenderedActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderGuiEvent.Raise();
#endif
}
catch (Exception ex)
{
@ -1090,7 +1162,9 @@ namespace StardewModdingAPI.Framework
if (++batchOpens == 1)
this.Events.Rendering.RaiseEmpty();
this.Events.RenderingWorld.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderEvent.Raise();
#endif
if (Game1.background != null)
Game1.background.draw(Game1.spriteBatch);
Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch);
@ -1403,10 +1477,14 @@ namespace StardewModdingAPI.Framework
if ((Game1.displayHUD || Game1.eventUp) && (Game1.currentBillboard == 0 && Game1.gameMode == (byte)3) && (!Game1.freezeControls && !Game1.panMode && !Game1.HostPaused))
{
this.Events.RenderingHud.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderHudEvent.Raise();
#endif
this.drawHUD();
this.Events.RenderedHud.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderHudEvent.Raise();
#endif
}
else if (Game1.activeClickableMenu == null && Game1.farmEvent == null)
Game1.spriteBatch.Draw(Game1.mouseCursors, new Vector2((float)Game1.getOldMouseX(), (float)Game1.getOldMouseY()), new Microsoft.Xna.Framework.Rectangle?(Game1.getSourceRectForStandardTileSheet(Game1.mouseCursors, 0, 16, 16)), Color.White, 0.0f, Vector2.Zero, (float)(4.0 + (double)Game1.dialogueButtonScale / 150.0), SpriteEffects.None, 1f);
@ -1515,10 +1593,14 @@ namespace StardewModdingAPI.Framework
try
{
this.Events.RenderingActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPreRenderGuiEvent.Raise();
#endif
Game1.activeClickableMenu.draw(Game1.spriteBatch);
this.Events.RenderedActiveMenu.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderGuiEvent.Raise();
#endif
}
catch (Exception ex)
{
@ -1535,7 +1617,9 @@ namespace StardewModdingAPI.Framework
}
this.Events.Rendered.RaiseEmpty();
#if !SMAPI_3_0_STRICT
this.Events.Legacy_OnPostRenderEvent.Raise();
#endif
Game1.spriteBatch.End();
this.drawOverlays(Game1.spriteBatch);
this.renderScreenBuffer();
@ -1555,6 +1639,7 @@ namespace StardewModdingAPI.Framework
this.RaisedAfterLoadEvent = false;
}
#if !SMAPI_3_0_STRICT
/// <summary>Raise the <see cref="GraphicsEvents.OnPostRenderEvent"/> if there are any listeners.</summary>
/// <param name="needsNewBatch">Whether to create a new sprite batch.</param>
private void RaisePostRender(bool needsNewBatch = false)
@ -1568,5 +1653,6 @@ namespace StardewModdingAPI.Framework
Game1.spriteBatch.End();
}
}
#endif
}
}

View File

@ -82,6 +82,7 @@ namespace StardewModdingAPI.Framework
this.OnModMessageReceived = onModMessageReceived;
}
#if !SMAPI_3_0_STRICT
/// <summary>Handle sync messages from other players and perform other initial sync logic.</summary>
public override void UpdateEarly()
{
@ -97,6 +98,7 @@ namespace StardewModdingAPI.Framework
base.UpdateLate(forceSync);
this.EventManager.Legacy_AfterMainBroadcast.Raise();
}
#endif
/// <summary>Initialise a client before the game connects to a remote server.</summary>
/// <param name="client">The client to initialise.</param>

View File

@ -56,6 +56,7 @@ namespace StardewModdingAPI
/// <param name="config">The config settings to save.</param>
void WriteConfig<TConfig>(TConfig config) where TConfig : class, new();
#if !SMAPI_3_0_STRICT
/****
** Generic JSON files
****/
@ -88,5 +89,6 @@ namespace StardewModdingAPI
/// <summary>Get all content packs loaded for this mod.</summary>
IEnumerable<IContentPack> GetContentPacks();
#endif
}
}

View File

@ -52,7 +52,10 @@ namespace StardewModdingAPI.Metadata
new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.serializer), InstructionHandleResult.DetectedSaveSerialiser),
new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.farmerSerializer), InstructionHandleResult.DetectedSaveSerialiser),
new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.locationSerializer), InstructionHandleResult.DetectedSaveSerialiser),
new TypeFinder(typeof(ISpecialisedEvents).FullName, InstructionHandleResult.DetectedUnvalidatedUpdateTick),
#if !SMAPI_3_0_STRICT
new EventFinder(typeof(SpecialisedEvents).FullName, nameof(SpecialisedEvents.UnvalidatedUpdateTick), InstructionHandleResult.DetectedUnvalidatedUpdateTick),
#endif
/****
** detect paranoid issues

View File

@ -29,6 +29,7 @@ namespace StardewModdingAPI
/// <summary>The patch version for backwards-compatible bug fixes.</summary>
public int PatchVersion => this.Version.PatchVersion;
#if !SMAPI_3_0_STRICT
/// <summary>An optional build tag.</summary>
[Obsolete("Use " + nameof(ISemanticVersion.PrereleaseTag) + " instead")]
public string Build
@ -39,6 +40,7 @@ namespace StardewModdingAPI
return this.Version.PrereleaseTag;
}
}
#endif
/// <summary>An optional prerelease tag.</summary>
public string PrereleaseTag => this.Version.PrereleaseTag;

View File

@ -32,7 +32,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>DEBUG;TRACE;SMAPI_3_0_STRICT</DefineConstants>
<UseVSHostingProcess>true</UseVSHostingProcess>
<Optimize>false</Optimize>
<OutputPath>$(SolutionDir)\..\bin\Debug\SMAPI</OutputPath>

View File

@ -17,9 +17,11 @@ namespace StardewModdingAPI
/// <summary>The patch version for backwards-compatible bug fixes.</summary>
int PatchVersion { get; }
#if !SMAPI_3_0_STRICT
/// <summary>An optional build tag.</summary>
[Obsolete("Use " + nameof(ISemanticVersion.PrereleaseTag) + " instead")]
string Build { get; }
#endif
/// <summary>An optional prerelease tag.</summary>
string PrereleaseTag { get; }

View File

@ -8,6 +8,10 @@
<DocumentationFile>..\..\bin\$(Configuration)\SMAPI.Toolkit.CoreInterfaces\$(TargetFramework)\StardewModdingAPI.Toolkit.CoreInterfaces.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);SMAPI_3_0_STRICT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\build\GlobalAssemblyInfo.cs" Link="Properties\GlobalAssemblyInfo.cs" />
</ItemGroup>

View File

@ -39,9 +39,11 @@ namespace StardewModdingAPI.Toolkit
/// <summary>The patch version for backwards-compatible bug fixes.</summary>
public int PatchVersion { get; }
#if !SMAPI_3_0_STRICT
/// <summary>An optional prerelease tag.</summary>
[Obsolete("Use " + nameof(ISemanticVersion.PrereleaseTag) + " instead")]
public string Build => this.PrereleaseTag;
#endif
/// <summary>An optional prerelease tag.</summary>
public string PrereleaseTag { get; }
@ -114,7 +116,7 @@ namespace StardewModdingAPI.Toolkit
{
if (other == null)
throw new ArgumentNullException(nameof(other));
return this.CompareTo(other.MajorVersion, other.MinorVersion, other.PatchVersion, other.Build);
return this.CompareTo(other.MajorVersion, other.MinorVersion, other.PatchVersion, other.PrereleaseTag);
}
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
@ -128,7 +130,7 @@ namespace StardewModdingAPI.Toolkit
/// <summary>Whether this is a pre-release version.</summary>
public bool IsPrerelease()
{
return !string.IsNullOrWhiteSpace(this.Build);
return !string.IsNullOrWhiteSpace(this.PrereleaseTag);
}
/// <summary>Get whether this version is older than the specified version.</summary>
@ -187,7 +189,7 @@ namespace StardewModdingAPI.Toolkit
: $"{this.MajorVersion}.{this.MinorVersion}";
// tag
string tag = this.Build;
string tag = this.PrereleaseTag;
if (tag != null)
result += $"-{tag}";
return result;
@ -241,11 +243,11 @@ namespace StardewModdingAPI.Toolkit
return this.MinorVersion.CompareTo(otherMinor);
if (this.PatchVersion != otherPatch)
return this.PatchVersion.CompareTo(otherPatch);
if (this.Build == otherTag)
if (this.PrereleaseTag == otherTag)
return same;
// stable supercedes pre-release
bool curIsStable = string.IsNullOrWhiteSpace(this.Build);
bool curIsStable = string.IsNullOrWhiteSpace(this.PrereleaseTag);
bool otherIsStable = string.IsNullOrWhiteSpace(otherTag);
if (curIsStable)
return curNewer;
@ -253,7 +255,7 @@ namespace StardewModdingAPI.Toolkit
return curOlder;
// compare two pre-release tag values
string[] curParts = this.Build.Split('.', '-');
string[] curParts = this.PrereleaseTag.Split('.', '-');
string[] otherParts = otherTag.Split('.', '-');
for (int i = 0; i < curParts.Length; i++)
{
@ -292,11 +294,11 @@ namespace StardewModdingAPI.Toolkit
throw new FormatException($"{this} isn't a valid semantic version. The major, minor, and patch numbers can't be negative.");
if (this.MajorVersion == 0 && this.MinorVersion == 0 && this.PatchVersion == 0)
throw new FormatException($"{this} isn't a valid semantic version. At least one of the major, minor, and patch numbers must be more than zero.");
if (this.Build != null)
if (this.PrereleaseTag != null)
{
if (this.Build.Trim() == "")
if (this.PrereleaseTag.Trim() == "")
throw new FormatException($"{this} isn't a valid semantic version. The tag cannot be a blank string (but may be omitted).");
if (!Regex.IsMatch(this.Build, $"^{SemanticVersion.TagPattern}$", RegexOptions.IgnoreCase))
if (!Regex.IsMatch(this.PrereleaseTag, $"^{SemanticVersion.TagPattern}$", RegexOptions.IgnoreCase))
throw new FormatException($"{this} isn't a valid semantic version. The tag is invalid.");
}
}

View File

@ -7,6 +7,10 @@
<DocumentationFile>..\..\bin\$(Configuration)\SMAPI.Toolkit\$(TargetFramework)\StardewModdingAPI.Toolkit.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);SMAPI_3_0_STRICT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\build\GlobalAssemblyInfo.cs" Link="Properties\GlobalAssemblyInfo.cs" />
</ItemGroup>