add content pack API

This commit is contained in:
Jesse Plamondon-Willard 2018-12-13 01:16:38 -05:00
parent cd277e915f
commit e447ce225f
No known key found for this signature in database
GPG Key ID: 7D7C8097B62033CE
9 changed files with 136 additions and 68 deletions

View File

@ -5,6 +5,9 @@
* Fixed invalid NPC data propagated when a mod changes NPC dispositions.
* Fixed `Display.RenderedWorld` event broken in SMAPI 2.9.1.
* For modders:
* Added dedicated content pack API.
## 2.9.1
* For players:
* Fixed crash in SMAPI 2.9 when constructing certain buildings.

View File

@ -6,7 +6,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class CommandHelper : BaseHelper, ICommandHelper
{
/*********
** Accessors
** Properties
*********/
/// <summary>The mod using this instance.</summary>
private readonly IModMetadata Mod;

View File

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.IO;
using StardewModdingAPI.Toolkit.Serialisation.Models;
namespace StardewModdingAPI.Framework.ModHelpers
{
/// <summary>Provides an API for managing content packs.</summary>
internal class ContentPackHelper : BaseHelper, IContentPackHelper
{
/*********
** Properties
*********/
/// <summary>The content packs loaded for this mod.</summary>
private readonly Lazy<IContentPack[]> ContentPacks;
/// <summary>Create a temporary content pack.</summary>
private readonly Func<string, IManifest, IContentPack> CreateContentPack;
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="modID">The unique ID of the relevant mod.</param>
/// <param name="contentPacks">The content packs loaded for this mod.</param>
/// <param name="createContentPack">Create a temporary content pack.</param>
public ContentPackHelper(string modID, Lazy<IContentPack[]> contentPacks, Func<string, IManifest, IContentPack> createContentPack)
: base(modID)
{
this.ContentPacks = contentPacks;
this.CreateContentPack = createContentPack;
}
/// <summary>Get all content packs loaded for this mod.</summary>
public IEnumerable<IContentPack> GetOwned()
{
return this.ContentPacks.Value;
}
/// <summary>Create a temporary content pack to read files from a directory. This will generate fake manifest data; any <c>manifest.json</c> in the directory will be ignored. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary>
/// <param name="directoryPath">The absolute directory path containing the content pack files.</param>
public IContentPack CreateFake(string directoryPath)
{
string id = Guid.NewGuid().ToString("N");
return this.CreateFake(directoryPath, id, id, id, id, new SemanticVersion(1, 0, 0));
}
/// <summary>Create a temporary content pack to read files from a directory. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary>
/// <param name="directoryPath">The absolute directory path containing the content pack files.</param>
/// <param name="id">The content pack's unique ID.</param>
/// <param name="name">The content pack name.</param>
/// <param name="description">The content pack description.</param>
/// <param name="author">The content pack author's name.</param>
/// <param name="version">The content pack version.</param>
public IContentPack CreateFake(string directoryPath, string id, string name, string description, string author, ISemanticVersion version)
{
// validate
if (string.IsNullOrWhiteSpace(directoryPath))
throw new ArgumentNullException(nameof(directoryPath));
if (string.IsNullOrWhiteSpace(id))
throw new ArgumentNullException(nameof(id));
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentNullException(nameof(name));
if (!Directory.Exists(directoryPath))
throw new ArgumentException($"Can't create content pack for directory path '{directoryPath}' because no such directory exists.");
// create manifest
IManifest manifest = new Manifest(
uniqueID: id,
name: name,
author: author,
description: description,
version: version,
contentPackFor: this.ModID
);
// create content pack
return this.CreateContentPack(directoryPath, manifest);
}
}
}

View File

@ -15,12 +15,6 @@ namespace StardewModdingAPI.Framework.ModHelpers
/*********
** Properties
*********/
/// <summary>The content packs loaded for this mod.</summary>
private readonly Lazy<IContentPack[]> ContentPacks;
/// <summary>Create a transitional content pack.</summary>
private readonly Func<string, IManifest, IContentPack> CreateContentPack;
#if !SMAPI_3_0_STRICT
/// <summary>Manages deprecation warnings.</summary>
private readonly DeprecationManager DeprecationManager;
@ -44,6 +38,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <summary>An API for loading content assets.</summary>
public IContentHelper Content { get; }
/// <summary>An API for managing content packs.</summary>
public IContentPackHelper ContentPacks { get; }
/// <summary>An API for reading and writing persistent mod data.</summary>
public IDataHelper Data { get; }
@ -76,18 +73,17 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="inputState">Manages the game's input state.</param>
/// <param name="events">Manages access to events raised by SMAPI.</param>
/// <param name="contentHelper">An API for loading content assets.</param>
/// <param name="contentPackHelper">An API for managing content packs.</param>
/// <param name="commandHelper">An API for managing console commands.</param>
/// <param name="dataHelper">An API for reading and writing persistent mod data.</param>
/// <param name="modRegistry">an API for fetching metadata about loaded mods.</param>
/// <param name="reflectionHelper">An API for accessing private game code.</param>
/// <param name="multiplayer">Provides multiplayer utilities.</param>
/// <param name="translationHelper">An API for reading translations stored in the mod's <c>i18n</c> folder.</param>
/// <param name="contentPacks">The content packs loaded for this mod.</param>
/// <param name="createContentPack">Create a transitional content pack.</param>
/// <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, SInputState inputState, IModEvents events, IContentHelper contentHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper, Func<IContentPack[]> contentPacks, Func<string, IManifest, IContentPack> createContentPack, DeprecationManager deprecationManager)
public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, SInputState inputState, IModEvents events, IContentHelper contentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper, DeprecationManager deprecationManager)
: base(modID)
{
// validate directory
@ -99,6 +95,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
// initialise
this.DirectoryPath = modDirectory;
this.Content = contentHelper ?? throw new ArgumentNullException(nameof(contentHelper));
this.ContentPacks = contentPackHelper ?? throw new ArgumentNullException(nameof(contentPackHelper));
this.Data = dataHelper ?? throw new ArgumentNullException(nameof(dataHelper));
this.Input = new InputHelper(modID, inputState);
this.ModRegistry = modRegistry ?? throw new ArgumentNullException(nameof(modRegistry));
@ -106,8 +103,6 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.Reflection = reflectionHelper ?? throw new ArgumentNullException(nameof(reflectionHelper));
this.Multiplayer = multiplayer ?? throw new ArgumentNullException(nameof(multiplayer));
this.Translation = translationHelper ?? throw new ArgumentNullException(nameof(translationHelper));
this.ContentPacks = new Lazy<IContentPack[]>(contentPacks);
this.CreateContentPack = createContentPack;
this.Events = events;
#if !SMAPI_3_0_STRICT
this.JsonHelper = jsonHelper ?? throw new ArgumentNullException(nameof(jsonHelper));
@ -171,39 +166,6 @@ namespace StardewModdingAPI.Framework.ModHelpers
/****
** Content packs
****/
/// <summary>Create a temporary content pack to read files from a directory. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary>
/// <param name="directoryPath">The absolute directory path containing the content pack files.</param>
/// <param name="id">The content pack's unique ID.</param>
/// <param name="name">The content pack name.</param>
/// <param name="description">The content pack description.</param>
/// <param name="author">The content pack author's name.</param>
/// <param name="version">The content pack version.</param>
public IContentPack CreateTemporaryContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version)
{
// validate
if (string.IsNullOrWhiteSpace(directoryPath))
throw new ArgumentNullException(nameof(directoryPath));
if (string.IsNullOrWhiteSpace(id))
throw new ArgumentNullException(nameof(id));
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentNullException(nameof(name));
if (!Directory.Exists(directoryPath))
throw new ArgumentException($"Can't create content pack for directory path '{directoryPath}' because no such directory exists.");
// create manifest
IManifest manifest = new Manifest(
uniqueID: id,
name: name,
author: author,
description: description,
version: version,
contentPackFor: this.ModID
);
// create content pack
return this.CreateContentPack(directoryPath, manifest);
}
#if !SMAPI_3_0_STRICT
/// <summary>Manually create a transitional content pack to support pre-SMAPI content packs. This provides a way to access legacy content packs using the SMAPI content pack APIs, but the content pack will not be visible in the log or validated by SMAPI.</summary>
/// <param name="directoryPath">The absolute directory path containing the content pack files.</param>
@ -212,19 +174,20 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="description">The content pack description.</param>
/// <param name="author">The content pack author's name.</param>
/// <param name="version">The content pack version.</param>
[Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.CreateTemporaryContentPack) + " instead")]
[Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.CreateFake) + " instead")]
public IContentPack CreateTransitionalContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version)
{
this.DeprecationManager.Warn($"{nameof(IModHelper)}.{nameof(IModHelper.CreateTransitionalContentPack)}", "2.5", DeprecationLevel.Notice);
return this.CreateTemporaryContentPack(directoryPath, id, name, description, author, version);
return this.ContentPacks.CreateFake(directoryPath, id, name, description, author, version);
}
#endif
/// <summary>Get all content packs loaded for this mod.</summary>
[Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.GetOwned) + " instead")]
public IEnumerable<IContentPack> GetContentPacks()
{
return this.ContentPacks.Value;
return this.ContentPacks.GetOwned();
}
#endif
/****
** Disposal

View File

@ -1014,20 +1014,21 @@ namespace StardewModdingAPI.Framework
IModEvents events = new ModEvents(mod, this.EventManager);
ICommandHelper commandHelper = new CommandHelper(mod, this.GameInstance.CommandManager);
IContentHelper contentHelper = new ContentHelper(contentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, monitor);
IContentPackHelper contentPackHelper = new ContentPackHelper(manifest.UniqueID, new Lazy<IContentPack[]>(GetContentPacks), CreateFakeContentPack);
IDataHelper dataHelper = new DataHelper(manifest.UniqueID, mod.DirectoryPath, jsonHelper);
IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, mod.DisplayName, this.Reflection, this.DeprecationManager);
IModRegistry modRegistryHelper = new ModRegistryHelper(manifest.UniqueID, this.ModRegistry, proxyFactory, monitor);
IMultiplayerHelper multiplayerHelper = new MultiplayerHelper(manifest.UniqueID, this.GameInstance.Multiplayer);
ITranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, manifest.Name, contentCore.GetLocale(), contentCore.Language);
IContentPack CreateTransitionalContentPack(string packDirPath, IManifest packManifest)
IContentPack CreateFakeContentPack(string packDirPath, IManifest packManifest)
{
IMonitor packMonitor = this.GetSecondaryMonitor(packManifest.Name);
IContentHelper packContentHelper = new ContentHelper(contentCore, packDirPath, packManifest.UniqueID, packManifest.Name, packMonitor);
return new ContentPack(packDirPath, packManifest, packContentHelper, this.Toolkit.JsonHelper);
}
modHelper = new ModHelper(manifest.UniqueID, mod.DirectoryPath, this.Toolkit.JsonHelper, this.GameInstance.Input, events, contentHelper, commandHelper, dataHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper, GetContentPacks, CreateTransitionalContentPack, this.DeprecationManager);
modHelper = new ModHelper(manifest.UniqueID, mod.DirectoryPath, this.Toolkit.JsonHelper, this.GameInstance.Input, events, contentHelper, contentPackHelper, commandHelper, dataHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper, this.DeprecationManager);
}
// init mod

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace StardewModdingAPI
{

View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace StardewModdingAPI
{
/// <summary>Provides an API for managing content packs.</summary>
public interface IContentPackHelper : IModLinked
{
/*********
** Public methods
*********/
/// <summary>Get all content packs loaded for this mod.</summary>
IEnumerable<IContentPack> GetOwned();
/// <summary>Create a temporary content pack to read files from a directory. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary>
/// <param name="directoryPath">The absolute directory path containing the content pack files.</param>
IContentPack CreateFake(string directoryPath);
/// <summary>Create a temporary content pack to read files from a directory. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary>
/// <param name="directoryPath">The absolute directory path containing the content pack files.</param>
/// <param name="id">The content pack's unique ID.</param>
/// <param name="name">The content pack name.</param>
/// <param name="description">The content pack description.</param>
/// <param name="author">The content pack author's name.</param>
/// <param name="version">The content pack version.</param>
IContentPack CreateFake(string directoryPath, string id, string name, string description, string author, ISemanticVersion version);
}
}

View File

@ -22,6 +22,9 @@ namespace StardewModdingAPI
/// <summary>An API for loading content assets.</summary>
IContentHelper Content { get; }
/// <summary>An API for managing content packs.</summary>
IContentPackHelper ContentPacks { get; }
/// <summary>An API for reading and writing persistent mod data.</summary>
IDataHelper Data { get; }
@ -73,21 +76,7 @@ namespace StardewModdingAPI
/// <param name="model">The model to save.</param>
[Obsolete("Use " + nameof(IModHelper.Data) + "." + nameof(IDataHelper.WriteJsonFile) + " instead")]
void WriteJsonFile<TModel>(string path, TModel model) where TModel : class;
#endif
/****
** Content packs
****/
/// <summary>Create a temporary content pack to read files from a directory. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary>
/// <param name="directoryPath">The absolute directory path containing the content pack files.</param>
/// <param name="id">The content pack's unique ID.</param>
/// <param name="name">The content pack name.</param>
/// <param name="description">The content pack description.</param>
/// <param name="author">The content pack author's name.</param>
/// <param name="version">The content pack version.</param>
IContentPack CreateTemporaryContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version);
#if !SMAPI_3_0_STRICT
/// <summary>Manually create a transitional content pack to support pre-SMAPI content packs. This provides a way to access legacy content packs using the SMAPI content pack APIs, but the content pack will not be visible in the log or validated by SMAPI.</summary>
/// <param name="directoryPath">The absolute directory path containing the content pack files.</param>
/// <param name="id">The content pack's unique ID.</param>
@ -95,11 +84,12 @@ namespace StardewModdingAPI
/// <param name="description">The content pack description.</param>
/// <param name="author">The content pack author's name.</param>
/// <param name="version">The content pack version.</param>
[Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.CreateTemporaryContentPack) + " instead")]
[Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.CreateFake) + " instead")]
IContentPack CreateTransitionalContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version);
#endif
/// <summary>Get all content packs loaded for this mod.</summary>
[Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.GetOwned) + " instead")]
IEnumerable<IContentPack> GetContentPacks();
#endif
}
}

View File

@ -174,6 +174,7 @@
<Compile Include="Framework\Events\ModPlayerEvents.cs" />
<Compile Include="Framework\Events\ModSpecialisedEvents.cs" />
<Compile Include="Framework\Events\ModWorldEvents.cs" />
<Compile Include="Framework\ModHelpers\ContentPackHelper.cs" />
<Compile Include="Framework\ModHelpers\DataHelper.cs" />
<Compile Include="Framework\Networking\MessageType.cs" />
<Compile Include="Framework\Networking\ModMessageModel.cs" />
@ -207,6 +208,7 @@
<Compile Include="Framework\StateTracking\Comparers\GenericEqualsComparer.cs" />
<Compile Include="Framework\StateTracking\FieldWatchers\ComparableListWatcher.cs" />
<Compile Include="Framework\WatcherCore.cs" />
<Compile Include="IContentPackHelper.cs" />
<Compile Include="IDataHelper.cs" />
<Compile Include="IInputHelper.cs" />
<Compile Include="Framework\Input\SInputState.cs" />