Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Investigate tilesheet corruption #424

Closed
Pathoschild opened this issue Jan 10, 2018 · 12 comments
Closed

Investigate tilesheet corruption #424

Pathoschild opened this issue Jan 10, 2018 · 12 comments
Assignees
Labels
bug This is an unintended error or behavior that can be addressed with specific development changes.
Milestone

Comments

@Pathoschild
Copy link
Owner

Some players report intermittent tilesheet corruption:

@Pathoschild Pathoschild added the bug This is an unintended error or behavior that can be addressed with specific development changes. label Jan 10, 2018
@Pathoschild Pathoschild self-assigned this Jan 10, 2018
@Pathoschild
Copy link
Owner Author

Pathoschild commented Jan 10, 2018

If you're affected, please reply with the following info. (Feel free to skip any questions you don't know.)

  • A screenshot of the issue (including the date/clock if possible).
  • Your latest SMAPI log after it happens.
  • Does it only happen on the farm?
  • If you restart the game and load the same save, does it still happen?
  • Do you use any XNB mods (not counting via XNB Loader)?
  • A zip of your save files and mods folder.

@InsaneJ
Copy link

InsaneJ commented Jan 13, 2018

I saw this happen on my farm yesterday the moment I got out of bed and went outside. It looked very much like the screenshot above. Unfortunately I don't have the log file for it. Reloading the safe game resolved the issue for me. I didn't venture outside of the farm so I don't know if this happens elsewhere as well.

This is the mod list I was using:

  • !EntoaroxFramework
  • Automate
  • ChestsAnywhere
  • ConsoleCommands
  • CraftingCounter
  • ExtendedMinecart
  • FarmExpansion
  • GateOpener
  • HorseWhistle
  • LevelExtender
  • LookupAnything
  • PurchasableRecipes
  • QuaintFarmBridge
  • QuestDelay
  • RecatchLegendaryFish
  • RentedTools
  • SaveBackup
  • SB_PotC
  • ScrollToBlank
  • ScytheHarvesting
  • ShroomSpotter
  • SkipIntro
  • SkullCavernElevator
  • StackSplitX
  • StardewValleyMP
  • TractorMod
  • TreeTransplant
  • UI Info Suite

@Pathoschild
Copy link
Owner Author

@InsaneJ Can you attach a zip of your save files and Mods folder?

@InsaneJ
Copy link

InsaneJ commented Jan 14, 2018

I've started a new game and noticed corruption in town:
stardew valley - tile corruption in town

Log up to the moment I walked into town: https://paste2.org/eGkODDKY
Save up to the day I walked into town: Bman_175631855.zip
Mods folder: Mods.zip

@InsaneJ
Copy link

InsaneJ commented Jan 14, 2018

Trying to figure out which mods trigger the tile corruption I've found that UI info suite and CarryChest both trigger the issue. Having either one of them in my mods folder results in the screenshot above.

@Pathoschild
Copy link
Owner Author

@InsaneJ Thanks! I can't reproduce the issue with your save and mods. Do you have any mods installed under Content?

@Pathoschild Pathoschild added this to the 2.4 milestone Jan 17, 2018
@Pathoschild
Copy link
Owner Author

@InsaneJ I can reproduce tilesheet corruption on the farm with your save (though it's intermittent), thanks! I'll look into this for SMAPI 2.4.

@Pathoschild
Copy link
Owner Author

Observations:

  • It happens intermittently, immediately when the save is loaded, persists until the game is closed (exiting to title has no effect), and affects all maps using the corrupted tilesheet.
  • Only reported outdoors.
  • The corruption is visually inconsistent (compare normal, case 1a, case 1b, case 1c, and half-corrupted animation).
  • When the outdoors tilesheet is corrupted, it consistently affects certain tile indexes (including 150, 151, 152, 153, 175, 179, 181, 200, 201, 202, 205, 207, 226, 227, and 231).
  • The map/tilesheet properties and tilesheet order are unchanged when the issue happens:
    No issue:
       Farm (id=Untitled, Layers=5, display size=(5120, 4160)):
          tilesheet #0: id=Paths, source=paths, sheet size=(4, 8), tile size=(16, 16),  margin=(0, 0), spacing=(0, 0), tile count=32
          tilesheet #1: id=untitled tile sheet, source=Maps\spring_outdoorsTileSheet, sheet size=(25, 79), tile size=(16, 16),  margin=(0, 0), spacing=(0, 0), tile count=1975
          tilesheet #2: id=zquaintwoodenbridge, source=..\Mods\QuaintFarmBridge\zpathquaintwoodenbridge_spring.png, sheet size=(2, 12), tile size=(16, 16),  margin=(0, 0), spacing=(0, 0), tile count=24
    
    Broken:
       Farm (id=Untitled, Layers=5, display size=(5120, 4160)):
          tilesheet #0: id=Paths, source=paths, sheet size=(4, 8), tile size=(16, 16),  margin=(0, 0), spacing=(0, 0), tile count=32
          tilesheet #1: id=untitled tile sheet, source=Maps\spring_outdoorsTileSheet, sheet size=(25, 79), tile size=(16, 16),  margin=(0, 0), spacing=(0, 0), tile count=1975
          tilesheet #2: id=zquaintwoodenbridge, source=..\Mods\QuaintFarmBridge\zpathquaintwoodenbridge_spring.png, sheet size=(2, 12), tile size=(16, 16),  margin=(0, 0), spacing=(0, 0), tile count=24
    

@Pathoschild
Copy link
Owner Author

Further testing:

  • The corruption almost always occurs in two consistent bands across the tilesheet:

  • In rare cases the corruption appears elsewhere (see case 3, case 4), but always in a full horizontal band.
  • This only occurs when the tilesheet is loaded by the game. I reloaded the tilesheet in-game a hundred times, no corruption.

@Pathoschild
Copy link
Owner Author

The issue most likely happens sometime during the async SaveGame.GetLoadEnumerator method. Here's the stack trace when the asset is first loaded by SMAPI (identical with and without corruption):

   at StardewModdingAPI.Framework.SContentManager.<>c__DisplayClass41_0`1.<LoadImpl>b__0()
   at StardewModdingAPI.Framework.SContentManager.WithWriteLock[T](Func`1 action)
   at StardewModdingAPI.Framework.SContentManager.LoadImpl[T](String assetName, ContentManager instance)
   at StardewModdingAPI.Framework.SContentManager.LoadFor[T](String assetName, ContentManager instance)
   at StardewModdingAPI.Framework.ContentManagerShim.Load[T](String assetName)
   at xTile.Display.XnaDisplayDevice.LoadTileSheet(TileSheet tileSheet)
   at xTile.Map.LoadTileSheets(IDisplayDevice displayDevice)
   at StardewValley.Game1.setGraphicsForSeason()
   at StardewValley.Game1.loadForNewGame(Boolean loadedGame)
   at StardewValley.SaveGame.<>c.<getLoadEnumerator>b__51_1()
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   at System.Threading.Tasks.Task.ExecutionContextCallback(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
   at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)
   at System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

@Pathoschild
Copy link
Owner Author

Pathoschild commented Jan 19, 2018

I ran some tests to track corruption of Maps\spring_outdoorsTileSheet while progressively reducing SMAPI's asset interception:

test case corruption in test loads
normal 00001 00100 00000 00001 ✖
SContentManager.LoadImpl<T> ignores loaders 00000 00111 10011 10010 ✖
SContentManager.LoadImpl<T> ignores loaders + editors 11100 10010 10001 10000 ✖
SContentManager.LoadImpl<T> defers to base.Load for loading 01010 01000 00000 01000 ✖
SContentManager.LoadImpl<T> just calls base.Load 10000 10011 10000 00000 ✖
SContentManager.LoadFor<T> just calls base.Load 00000 10011 11101 01000 ✖
IContentHelper.Load<T> does nothing 00000 00000 10011 10000 ✖
IContentHelper does nothing 01001 00100 10110 00000 ✖
SContentManager does nothing (except passthrough Load) 01001 01100 10110 11110 ✖
content interception feature removed entirely 00000 00000 00000 00000 ✓

So the issue is most likely caused by the content interception or something interacting with it, but the issue occurs even if that content interception does nothing.

In very rare cases (roughly 3 of those 200 tests), I saw errors like this. These suggest a race condition due to parallel code; but if that's the cause, it's unclear why it doesn't happen when content interception is removed.

[23:29:37 TRACE Console.Out] getLoadEnumerator('Bman_175631855')
[23:29:39 ERROR Extended Minecart] Could not patch the Beach due to a unknown error
System.ArgumentException: An item with the same key has already been added.
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at Microsoft.Xna.Framework.Content.ContentManager.Load[T](String assetName)
   at StardewValley.LocalizedContentManager.Load[T](String assetName)
   at StardewModdingAPI.Framework.SContentManager.LoadFor[T](String assetName, ContentManager instance) in C:\source\_Stardew\SMAPI\src\SMAPI\Framework\SContentManager.cs:line 206
   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 Entoarox.ExtendedMinecart.ExtendedMinecartMod.GameEvents_UpdateTick(Object s, EventArgs e)
[23:29:39 TRACE Console.Out] gameMode was 'loadingMode (6)', set to 'selectGameScreen (9)'.
[23:29:39 ERROR Console.Out] System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at System.Collections.Generic.List`1.Enumerator.MoveNext()
   at xTile.Map.LoadTileSheets(IDisplayDevice displayDevice)
   at StardewValley.Game1.setGraphicsForSeason()
   at StardewValley.Game1.loadForNewGame(Boolean loadedGame)
   at StardewValley.SaveGame.<>c.<getLoadEnumerator>b__51_1()
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()

@Pathoschild
Copy link
Owner Author

Fixed in develop for the upcoming SMAPI 2.4.

The cause is pretty obvious in retrospect. SMAPI pauses mods when the game is running async code, but it doesn't pause mods while a save file is loading. Loading happens asynchronously, which means mods using update-tick events can run code while the save is loading on a separate thread. In rare cases, a mod will use the GPU just as a tilesheet file is being loaded, corrupting the tilesheet. The reason mods which use the content manager tend to trigger the corruption isn't because of the content manager directly, it's just because they're more likely to use the GPU while a tilesheet is being loaded.

As of SMAPI 2.4, SMAPI will no longer raise events while a load is in progress.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This is an unintended error or behavior that can be addressed with specific development changes.
Projects
None yet
Development

No branches or pull requests

2 participants