move location events into new event system (#310)

This commit is contained in:
Jesse Plamondon-Willard 2018-05-31 22:47:56 -04:00
parent e5f8b1419a
commit 558fb8a865
16 changed files with 297 additions and 16 deletions

View File

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Netcode;
using StardewValley;
using SObject = StardewValley.Object;

View File

@ -0,0 +1,9 @@
namespace StardewModdingAPI.Events
{
/// <summary>Manages access to events raised by SMAPI.</summary>
public interface IModEvents
{
/// <summary>Events raised when something changes in the world.</summary>
IWorldEvents World { get; }
}
}

View File

@ -0,0 +1,20 @@
using System;
namespace StardewModdingAPI.Events
{
/// <summary>Provides events raised when something changes in the world.</summary>
public interface IWorldEvents
{
/*********
** Events
*********/
/// <summary>Raised after a game location is added or removed.</summary>
event EventHandler<WorldLocationsChangedEventArgs> LocationsChanged;
/// <summary>Raised after buildings are added or removed in a location.</summary>
event EventHandler<WorldBuildingsChangedEventArgs> BuildingsChanged;
/// <summary>Raised after objects are added or removed in a location.</summary>
event EventHandler<WorldObjectsChangedEventArgs> ObjectsChanged;
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using StardewValley;
using StardewValley.Buildings;
namespace StardewModdingAPI.Events
{
/// <summary>Event arguments for a <see cref="IWorldEvents.BuildingsChanged"/> event.</summary>
public class WorldBuildingsChangedEventArgs : EventArgs
{
/*********
** Accessors
*********/
/// <summary>The location which changed.</summary>
public GameLocation Location { get; }
/// <summary>The buildings added to the location.</summary>
public IEnumerable<Building> Added { get; }
/// <summary>The buildings removed from the location.</summary>
public IEnumerable<Building> Removed { get; }
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="location">The location which changed.</param>
/// <param name="added">The buildings added to the location.</param>
/// <param name="removed">The buildings removed from the location.</param>
public WorldBuildingsChangedEventArgs(GameLocation location, IEnumerable<Building> added, IEnumerable<Building> removed)
{
this.Location = location;
this.Added = added.ToArray();
this.Removed = removed.ToArray();
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using StardewValley;
namespace StardewModdingAPI.Events
{
/// <summary>Event arguments for a <see cref="IWorldEvents.LocationsChanged"/> event.</summary>
public class WorldLocationsChangedEventArgs : EventArgs
{
/*********
** Accessors
*********/
/// <summary>The added locations.</summary>
public IEnumerable<GameLocation> Added { get; }
/// <summary>The removed locations.</summary>
public IEnumerable<GameLocation> Removed { get; }
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="added">The added locations.</param>
/// <param name="removed">The removed locations.</param>
public WorldLocationsChangedEventArgs(IEnumerable<GameLocation> added, IEnumerable<GameLocation> removed)
{
this.Added = added.ToArray();
this.Removed = removed.ToArray();
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using StardewValley;
using Object = StardewValley.Object;
namespace StardewModdingAPI.Events
{
/// <summary>Event arguments for a <see cref="IWorldEvents.ObjectsChanged"/> event.</summary>
public class WorldObjectsChangedEventArgs : EventArgs
{
/*********
** Accessors
*********/
/// <summary>The location which changed.</summary>
public GameLocation Location { get; }
/// <summary>The objects added to the location.</summary>
public IEnumerable<KeyValuePair<Vector2, Object>> Added { get; }
/// <summary>The objects removed from the location.</summary>
public IEnumerable<KeyValuePair<Vector2, Object>> Removed { get; }
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="location">The location which changed.</param>
/// <param name="added">The objects added to the location.</param>
/// <param name="removed">The objects removed from the location.</param>
public WorldObjectsChangedEventArgs(GameLocation location, IEnumerable<KeyValuePair<Vector2, Object>> added, IEnumerable<KeyValuePair<Vector2, Object>> removed)
{
this.Location = location;
this.Added = added.ToArray();
this.Removed = removed.ToArray();
}
}
}

View File

@ -1,4 +1,3 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Xna.Framework.Input;
using StardewModdingAPI.Events;
@ -10,7 +9,23 @@ namespace StardewModdingAPI.Framework.Events
internal class EventManager
{
/*********
** Properties
** Events (new)
*********/
/****
** World
****/
/// <summary>Raised after a game location is added or removed.</summary>
public readonly ManagedEvent<WorldLocationsChangedEventArgs> World_LocationsChanged;
/// <summary>Raised after buildings are added or removed in a location.</summary>
public readonly ManagedEvent<WorldBuildingsChangedEventArgs> World_BuildingsChanged;
/// <summary>Raised after objects are added or removed in a location.</summary>
public readonly ManagedEvent<WorldObjectsChangedEventArgs> World_ObjectsChanged;
/*********
** Events (old)
*********/
/****
** ContentEvents
@ -209,7 +224,12 @@ namespace StardewModdingAPI.Framework.Events
ManagedEvent<TEventArgs> ManageEventOf<TEventArgs>(string typeName, string eventName) => new ManagedEvent<TEventArgs>($"{typeName}.{eventName}", monitor, modRegistry);
ManagedEvent ManageEvent(string typeName, string eventName) => new ManagedEvent($"{typeName}.{eventName}", monitor, modRegistry);
// init events
// init events (new)
this.World_BuildingsChanged = ManageEventOf<WorldBuildingsChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.LocationsChanged));
this.World_LocationsChanged = ManageEventOf<WorldLocationsChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.BuildingsChanged));
this.World_ObjectsChanged = ManageEventOf<WorldObjectsChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.ObjectsChanged));
// init events (old)
this.Content_LocaleChanged = ManageEventOf<EventArgsValueChanged<string>>(nameof(ContentEvents), nameof(ContentEvents.AfterLocaleChanged));
this.Control_ControllerButtonPressed = ManageEventOf<EventArgsControllerButtonPressed>(nameof(ControlEvents), nameof(ControlEvents.ControllerButtonPressed));

View File

@ -27,9 +27,17 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>Add an event handler.</summary>
/// <param name="handler">The event handler.</param>
public void Add(EventHandler<TEventArgs> handler)
{
this.Add(handler, this.ModRegistry.GetFromStack());
}
/// <summary>Add an event handler.</summary>
/// <param name="handler">The event handler.</param>
/// <param name="mod">The mod which added the event handler.</param>
public void Add(EventHandler<TEventArgs> handler, IModMetadata mod)
{
this.Event += handler;
this.AddTracking(handler, this.Event?.GetInvocationList().Cast<EventHandler<TEventArgs>>());
this.AddTracking(mod, handler, this.Event?.GetInvocationList().Cast<EventHandler<TEventArgs>>());
}
/// <summary>Remove an event handler.</summary>
@ -84,9 +92,17 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>Add an event handler.</summary>
/// <param name="handler">The event handler.</param>
public void Add(EventHandler handler)
{
this.Add(handler, this.ModRegistry.GetFromStack());
}
/// <summary>Add an event handler.</summary>
/// <param name="handler">The event handler.</param>
/// <param name="mod">The mod which added the event handler.</param>
public void Add(EventHandler handler, IModMetadata mod)
{
this.Event += handler;
this.AddTracking(handler, this.Event?.GetInvocationList().Cast<EventHandler>());
this.AddTracking(mod, handler, this.Event?.GetInvocationList().Cast<EventHandler>());
}
/// <summary>Remove an event handler.</summary>

View File

@ -17,7 +17,7 @@ namespace StardewModdingAPI.Framework.Events
private readonly IMonitor Monitor;
/// <summary>The mod registry with which to identify mods.</summary>
private readonly ModRegistry ModRegistry;
protected readonly ModRegistry ModRegistry;
/// <summary>The display names for the mods which added each delegate.</summary>
private readonly IDictionary<TEventHandler, IModMetadata> SourceMods = new Dictionary<TEventHandler, IModMetadata>();
@ -50,11 +50,12 @@ namespace StardewModdingAPI.Framework.Events
}
/// <summary>Track an event handler.</summary>
/// <param name="mod">The mod which added the handler.</param>
/// <param name="handler">The event handler.</param>
/// <param name="invocationList">The updated event invocation list.</param>
protected void AddTracking(TEventHandler handler, IEnumerable<TEventHandler> invocationList)
protected void AddTracking(IModMetadata mod, TEventHandler handler, IEnumerable<TEventHandler> invocationList)
{
this.SourceMods[handler] = this.ModRegistry.GetFromStack();
this.SourceMods[handler] = mod;
this.CachedInvocationList = invocationList?.ToArray() ?? new TEventHandler[0];
}
@ -64,7 +65,7 @@ namespace StardewModdingAPI.Framework.Events
protected void RemoveTracking(TEventHandler handler, IEnumerable<TEventHandler> invocationList)
{
this.CachedInvocationList = invocationList?.ToArray() ?? new TEventHandler[0];
if(!this.CachedInvocationList.Contains(handler)) // don't remove if there's still a reference to the removed handler (e.g. it was added twice and removed once)
if (!this.CachedInvocationList.Contains(handler)) // don't remove if there's still a reference to the removed handler (e.g. it was added twice and removed once)
this.SourceMods.Remove(handler);
}

View File

@ -0,0 +1,26 @@
using StardewModdingAPI.Events;
namespace StardewModdingAPI.Framework.Events
{
/// <summary>Manages access to events raised by SMAPI.</summary>
internal class ModEvents : IModEvents
{
/*********
** Accessors
*********/
/// <summary>Events raised when something changes in the world.</summary>
public IWorldEvents World { get; }
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="mod">The mod which uses this instance.</param>
/// <param name="eventManager">The underlying event manager.</param>
public ModEvents(IModMetadata mod, EventManager eventManager)
{
this.World = new ModWorldEvents(mod, eventManager);
}
}
}

View File

@ -0,0 +1,56 @@
using System;
using StardewModdingAPI.Events;
namespace StardewModdingAPI.Framework.Events
{
/// <summary>Events raised when something changes in the world.</summary>
public class ModWorldEvents : IWorldEvents
{
/*********
** Properties
*********/
/// <summary>The underlying event manager.</summary>
private readonly EventManager EventManager;
/// <summary>The mod which uses this instance.</summary>
private readonly IModMetadata Mod;
/*********
** Accessors
*********/
/// <summary>Raised after a game location is added or removed.</summary>
public event EventHandler<WorldLocationsChangedEventArgs> LocationsChanged
{
add => this.EventManager.World_LocationsChanged.Add(value, this.Mod);
remove => this.EventManager.World_LocationsChanged.Remove(value);
}
/// <summary>Raised after buildings are added or removed in a location.</summary>
public event EventHandler<WorldBuildingsChangedEventArgs> BuildingsChanged
{
add => this.EventManager.World_BuildingsChanged.Add(value, this.Mod);
remove => this.EventManager.World_BuildingsChanged.Remove(value);
}
/// <summary>Raised after objects are added or removed in a location.</summary>
public event EventHandler<WorldObjectsChangedEventArgs> ObjectsChanged
{
add => this.EventManager.World_ObjectsChanged.Add(value);
remove => this.EventManager.World_ObjectsChanged.Remove(value);
}
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="mod">The mod which uses this instance.</param>
/// <param name="eventManager">The underlying event manager.</param>
internal ModWorldEvents(IModMetadata mod, EventManager eventManager)
{
this.Mod = mod;
this.EventManager = eventManager;
}
}
}

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using StardewModdingAPI.Events;
using StardewModdingAPI.Framework.Models;
using StardewModdingAPI.Framework.Serialisation;
using StardewModdingAPI.Framework.Utilities;
@ -33,6 +34,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <summary>The full path to the mod's folder.</summary>
public string DirectoryPath { get; }
/// <summary>Manages access to events raised by SMAPI, which let your mod react when something happens in the game.</summary>
public IModEvents Events { get; }
/// <summary>An API for loading content assets.</summary>
public IContentHelper Content { get; }
@ -59,6 +63,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="modID">The mod's unique ID.</param>
/// <param name="modDirectory">The full path to the mod's folder.</param>
/// <param name="jsonHelper">Encapsulate SMAPI's JSON parsing.</param>
/// <param name="events">Manages access to events raised by SMAPI.</param>
/// <param name="contentHelper">An API for loading content assets.</param>
/// <param name="commandHelper">An API for managing console commands.</param>
/// <param name="modRegistry">an API for fetching metadata about loaded mods.</param>
@ -70,7 +75,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="deprecationManager">Manages deprecation warnings.</param>
/// <exception cref="ArgumentNullException">An argument is null or empty.</exception>
/// <exception cref="InvalidOperationException">The <paramref name="modDirectory"/> path does not exist on disk.</exception>
public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, IContentHelper contentHelper, ICommandHelper commandHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper, IEnumerable<IContentPack> contentPacks, Func<string, IManifest, IContentPack> createContentPack, DeprecationManager deprecationManager)
public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, IModEvents events, IContentHelper contentHelper, ICommandHelper commandHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper, IEnumerable<IContentPack> contentPacks, Func<string, IManifest, IContentPack> createContentPack, DeprecationManager deprecationManager)
: base(modID)
{
// validate directory
@ -91,6 +96,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.ContentPacks = contentPacks.ToArray();
this.CreateContentPack = createContentPack;
this.DeprecationManager = deprecationManager;
this.Events = events;
}
/****
@ -157,13 +163,13 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.DeprecationManager.Warn($"{nameof(IModHelper)}.{nameof(IModHelper.CreateTransitionalContentPack)}", "2.5", DeprecationLevel.Notice);
// validate
if(string.IsNullOrWhiteSpace(directoryPath))
if (string.IsNullOrWhiteSpace(directoryPath))
throw new ArgumentNullException(nameof(directoryPath));
if(string.IsNullOrWhiteSpace(id))
if (string.IsNullOrWhiteSpace(id))
throw new ArgumentNullException(nameof(id));
if(string.IsNullOrWhiteSpace(name))
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentNullException(nameof(name));
if(!Directory.Exists(directoryPath))
if (!Directory.Exists(directoryPath))
throw new ArgumentException($"Can't create content pack for directory path '{directoryPath}' because no such directory exists.");
// create manifest

View File

@ -543,6 +543,7 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Context: location list changed (added {addedText}; removed {removedText}).", LogLevel.Trace);
}
this.Events.World_LocationsChanged.Raise(new WorldLocationsChangedEventArgs(added, removed));
this.Events.Location_LocationsChanged.Raise(new EventArgsLocationsChanged(added, removed));
}
@ -559,6 +560,7 @@ namespace StardewModdingAPI.Framework
var removed = watcher.ObjectsWatcher.Removed.ToArray();
watcher.ObjectsWatcher.Reset();
this.Events.World_ObjectsChanged.Raise(new WorldObjectsChangedEventArgs(location, added, removed));
this.Events.Location_ObjectsChanged.Raise(new EventArgsLocationObjectsChanged(location, added, removed));
}
@ -570,6 +572,7 @@ namespace StardewModdingAPI.Framework
var removed = watcher.BuildingsWatcher.Removed.ToArray();
watcher.BuildingsWatcher.Reset();
this.Events.World_BuildingsChanged.Raise(new WorldBuildingsChangedEventArgs(location, added, removed));
this.Events.Location_BuildingsChanged.Raise(new EventArgsLocationBuildingsChanged(location, added, removed));
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using StardewModdingAPI.Events;
namespace StardewModdingAPI
{
@ -12,6 +13,10 @@ namespace StardewModdingAPI
/// <summary>The full path to the mod's folder.</summary>
string DirectoryPath { get; }
/// <summary>Manages access to events raised by SMAPI, which let your mod react when something happens in the game.</summary>
[Obsolete("This is an experimental interface which may change at any time. Don't depend on this for released mods.")]
IModEvents Events { get; }
/// <summary>An API for loading content assets.</summary>
IContentHelper Content { get; }

View File

@ -840,6 +840,7 @@ namespace StardewModdingAPI
IMonitor monitor = this.GetSecondaryMonitor(metadata.DisplayName);
IModHelper modHelper;
{
IModEvents events = new ModEvents(metadata, this.EventManager);
ICommandHelper commandHelper = new CommandHelper(manifest.UniqueID, metadata.DisplayName, this.CommandManager);
IContentHelper contentHelper = new ContentHelper(contentCore, metadata.DirectoryPath, manifest.UniqueID, metadata.DisplayName, monitor);
IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, metadata.DisplayName, this.Reflection, this.DeprecationManager);
@ -854,7 +855,7 @@ namespace StardewModdingAPI
return new ContentPack(packDirPath, packManifest, packContentHelper, this.JsonHelper);
}
modHelper = new ModHelper(manifest.UniqueID, metadata.DirectoryPath, jsonHelper, contentHelper, commandHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper, contentPacks, CreateTransitionalContentPack, this.DeprecationManager);
modHelper = new ModHelper(manifest.UniqueID, metadata.DirectoryPath, jsonHelper, events, contentHelper, commandHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper, contentPacks, CreateTransitionalContentPack, this.DeprecationManager);
}
// init mod

View File

@ -86,17 +86,23 @@
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Events\EventArgsLocationBuildingsChanged.cs" />
<Compile Include="Events\IWorldEvents.cs" />
<Compile Include="Events\MultiplayerEvents.cs" />
<Compile Include="Events\WorldBuildingsChangedEventArgs.cs" />
<Compile Include="Events\WorldLocationsChangedEventArgs.cs" />
<Compile Include="Events\WorldObjectsChangedEventArgs.cs" />
<Compile Include="Framework\ContentManagers\BaseContentManager.cs" />
<Compile Include="Framework\ContentManagers\GameContentManager.cs" />
<Compile Include="Framework\ContentManagers\IContentManager.cs" />
<Compile Include="Framework\ContentManagers\ModContentManager.cs" />
<Compile Include="Framework\Events\EventManager.cs" />
<Compile Include="Events\IModEvents.cs" />
<Compile Include="Framework\Events\ManagedEvent.cs" />
<Compile Include="Events\SpecialisedEvents.cs" />
<Compile Include="Framework\ContentPack.cs" />
<Compile Include="Framework\Content\ContentCache.cs" />
<Compile Include="Framework\Events\ManagedEventBase.cs" />
<Compile Include="Framework\Events\ModEvents.cs" />
<Compile Include="Framework\Input\GamePadStateBuilder.cs" />
<Compile Include="Framework\Input\SInputState.cs" />
<Compile Include="Framework\Input\InputStatus.cs" />
@ -129,6 +135,7 @@
<Compile Include="Framework\ModLoading\Rewriters\TypeReferenceRewriter.cs" />
<Compile Include="Framework\Exceptions\SAssemblyLoadFailedException.cs" />
<Compile Include="Framework\ModLoading\AssemblyLoadStatus.cs" />
<Compile Include="Framework\Events\ModWorldEvents.cs" />
<Compile Include="Framework\Reflection\InterfaceProxyBuilder.cs" />
<Compile Include="Framework\Reflection\InterfaceProxyFactory.cs" />
<Compile Include="Framework\RewriteFacades\SpriteBatchMethods.cs" />