SMAPI - Stardew Modding API

SMAPI - Stardew Modding API

971k Downloads

Centralise content loading to fix map tilesheet edge case

Pathoschild opened this issue · 1 comments

commented

Centralise content loading so the game can correctly handle map tilesheets in the mod folder when those tilesheets aren't loaded in advance.

Current design

The content logic is split into two main parts: the single lower-level SContentManager and its shims, and the higher-level ContentHelper instances. When a mod loads content through the ContentHelper, some extra logic is applied to support reading files from the mod folder. This doesn't happen when the game loads an asset directly through SContentManager.

Problem

That distinction causes problems with custom tilesheets if they're not loaded in advance. Consider this code:

// load texture
helper.Content.Load<Texture2D>("tilesheet.png");

// create tilesheet
string tilesheetPath = helper.Content.GetActualAssetKey("tilesheet.png");
TileSheet tilesheet = new TileSheet("custom-tilesheet", location.map, tilesheetPath, new Size(16, 32), Size(16, 16));

// add & load tilesheet
location.map.AddTileSheet(tilesheet);
location.map.LoadTileSheets(Game1.mapDisplayDevice);

That code works fine, because Load<Texture2D> adds ..\Mods\ModName\tilesheet.png to the cache so it's available when the game loads the tilesheet. However, removing the Load<Texture2D> line makes the code crash — since SContentManager doesn't recognise mod paths, it ends up looking for Mods\ModName\tilesheet.png.xnb in the game's content folder.

Sample error

ContentLoadException: Error loading "..\Mods\ModName\tilesheet.png". File not found. ---> System.IO.FileNotFoundException: Error loading "Mods\ModName\tilesheet.png.xnb". File not found.
   at Microsoft.Xna.Framework.TitleContainer.OpenStream(String name)
   at Microsoft.Xna.Framework.Content.ContentManager.OpenStream(String assetName)
   --- End of inner exception stack trace ---
   at Microsoft.Xna.Framework.Content.ContentManager.OpenStream(String assetName)
   at Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[T](String assetName, Action`1 recordDisposableObject)
   at Microsoft.Xna.Framework.Content.ContentManager.Load[T](String assetName)
   at StardewValley.LocalizedContentManager.Load[T](String assetName)
   at StardewModdingAPI.Framework.SContentManager.<>n__0[T](String assetName)
   at StardewModdingAPI.Framework.SContentManager.<>c__DisplayClass24_0`1.<LoadFor>b__0() in C:\source\_Stardew\SMAPI\src\SMAPI\Framework\SContentManager.cs:line 179
   at StardewModdingAPI.Framework.Utilities.ContextHash`1.Track[TResult](T key, Func`1 action) in C:\source\_Stardew\SMAPI\src\SMAPI\Framework\Utilities\ContextHash.cs:line 53
   at StardewModdingAPI.Framework.SContentManager.LoadFor[T](String assetName, ContentManager instance) in C:\source\_Stardew\SMAPI\src\SMAPI\Framework\SContentManager.cs:line 176
   at StardewModdingAPI.Framework.ContentManagerShim.Load[T](String assetName) in C:\source\_Stardew\SMAPI\src\SMAPI\Framework\ContentManagerShim.cs:line 40
   at xTile.Display.XnaDisplayDevice.LoadTileSheet(TileSheet tileSheet)
   at xTile.Map.LoadTileSheets(IDisplayDevice displayDevice)
   at StardewCrossing.ModEntry.SaveEvents_AfterLoad(Object sender, EventArgs e) in C:\Users\Jesse\Downloads\StardewCrossing\StardewCrossing\ModEntry.cs:line 35
   at StardewModdingAPI.Framework.InternalExtensions.SafelyRaisePlainEvent(IMonitor monitor, String name, IEnumerable`1 handlers, Object sender, EventArgs args) in C:\source\_Stardew\SMAPI\src\SMAPI\Framework\InternalExtensions.cs:line 40
commented

Fixed in develop for the upcoming SMAPI 2.1 release.