SMAPI - Stardew Modding API

SMAPI - Stardew Modding API

971k Downloads

SMAPI generates broken saves with bundle translations; when using mod translations

MysticTempest opened this issue · 11 comments

commented

Describe the bug
Per the title, SMAPI is once again generating saves with bundle translations; when using modded translations.
And, it causes issues when reading the golden scrolls.

Discovered in this discussion: https://forums.stardewvalley.net/threads/translating-bundles-file.9708/
Dnksh, is creating a Polish translation mod that overwrites the Portuguese files via Content Patcher.
And, anytime you create a new save in the modded language; is where it appends the translations to the <bundleData> section of the savefile.

The Polish translation mod, log, saves; and some code snippet examples are in the linked thread above.

To Reproduce
Exact steps which reproduce the bug.

  1. Have SMAPI & Content Patcher.
  2. Install Dnksh's Polish translation mod: https://forums.stardewvalley.net/threads/translating-bundles-file.9708/post-57837
  3. Load SDV, and switch to the "Polski" language.
  4. Create a new save.
  5. You should be able to open up the save, & see the translations that were added to the <bundleData> section of the save.

Log file
Dnksh's log:
https://smapi.io/log/00f24201ebf34b02a698847772b349e0

commented

Another option might be to intercept assets one step lower as part of the content API redesign (#766), so mods could intercept Data/Bundles.pt-BR or Data/Bundles. That would be a major departure from the current design, but it would also align better with the way new modders intuitively expect it to work.

commented

tl;dr: I'm tentatively targeting SMAPI 3.14.0 for a fix. See below for the details and why I'm not doing it in 3.12.9 or 3.13.0.

Problem

That's a fundamental issue with how the content pipeline works.

The game expects to be able to do this:

// read translated bundles from Data/Bundles.pt-BR.xnb
contentManager.Load<>("Data/Bundles")

// read default bundles from Data/Bundles.xnb
contentManager.LoadBase<>("Data/Bundles")

The way that normally works is like this:

                           localized                      XNA
                            content                     content
game                        manager                     manager
----                       ---------                    -------
  |                           |                            |
  | Load("Data/Bundles")      |                            |
  |-------------------------> | Load("Data/Bundles.pt-BR") |
  |                           |--------------------------> | read Data/Bundles.pt-BR.xnb
  |                           |                            |----------------------------,
  |                           |                            |<---------------------------'
  |                           |                            |  cache Data/Bundles.pt-BR
  |                           |<---------------------------|
  |<--------------------------|                         OK
  |                        OK 


  | LoadBase("Data/Bundles")
  | ------------------------->| Load("Data/Bundles")
  |                           |--------------------------->| read Data/Bundles.xnb
  |                           |                            |----------------------,
  |                           |                            |<---------------------'
  |                           |                            | cache Data/Bundles
  |                           |<---------------------------|
  |<--------------------------|                          OK
  |                         OK

That works because in the second flow, Data/Bundles wasn't cached yet (the first flow cached Data/Bundles.pt-BR instead).

That assumption breaks when a SMAPI mod (like Content Patcher) provided the asset in the first flow. In that case there's no distinction between Data/Bundles.pt-BR.xnb and Data/Bundles.xnb, because it never got that far; the game requested Data/Bundles and received the version provided by the mod:

                             SMAPI                      
                            content                     Content
game                        manager                     Patcher
----                       ---------                    -------
  |                           |                            |
  | Load("Data/Bundles")      |                            |
  |-------------------------> | Load("Data/Bundles")       |
  |                           |--------------------------> | read assets/some-file.json
  |                           |                            |---------------------------,
  |                           |                            |<--------------------------'
  |                           |<---------------------------|
  |<--------------------------| cache Data/Bundles
  |                        OK 


  | LoadBase("Data/Bundles")
  | ------------------------->| Load("Data/Bundles")
  |                           |---------------------,
  |                           |<--------------------'
  |                           | already cached
  |<--------------------------|
  |                         OK

So when the game tries to load Data/Bundles without a language suffix, it's already cached with the (potentially translated) data provided by the mod.

Solution

The solution is for SMAPI to always cache assets based on the locale they were requested for in other languages. So if the game requests Load("Portraits/Abigail"), it'll be cached internally as Portraits/Abigail.pt-BR; if it then requests LoadBase("Portraits/Abigail"), it'll load a fresh copy and cache it as Portraits/Abigail.

Timeline

I'm tentatively targeting SMAPI 3.14.0 for this change, the first major update after the one for Stardew Valley 1.5.5.

The content pipeline has a lot of moving parts that depend on this particular logic, so there's a solid chance of regressions and side-effects. With Stardew Valley 1.5.5 expected within weeks, it would be difficult to untangle whether a particular issue was caused by the game changes, the SMAPI changes for them, the mod changes for both of those, or the content pipeline change.

commented

Thanks for the thorough explanation! That helps, a lot.

So, it looks like as a temporary workaround for this case. We can at least use the Content Patcher token; "HasFlag": "canReadJunimoText", to delay loading the bundle translations until after the save is created.
Bypassing the caching bug you referred to, and letting us generate a working save.

commented

The game sets the unlocalized bundles at two points: when creating the save and when loading it. So you'd need a condition that only becomes true after the save is loaded (maybe something like "Time |contains=0600": false?). We'd have to test whether that works and whether the bundle translations are shown though.

commented

Not sure if this is related, but there is a crash happening in our game (steam beta version) we started a few days back very similar to what is described here:

https://smapi.io/log/b88d9ef420f8454b806870779c168238

This happens, if any of the players (in the two-player game) tries to access a community center vault for the first time after Lewis opened the entrance.

Originally the game had its language set to German and the mods as listed in the log above enabled.
Changing the language to English, verifying the files and/or disabling the mods did not prevent the game from crashing.

Hope it helps!

commented

The game sets the unlocalized bundles at two points: when creating the save and when loading it. So you'd need a condition that only becomes true after the save is loaded (maybe something like "Time |contains=0600": false?). We'd have to test whether that works and whether the bundle translations are shown though.

I tried the Time token. It generated a working save, but the translations weren't properly applied.

It looks like the "canReadJunimoText" flag works the best to generate a working save & apply the translations. Though, it does take a manual reload of the save after the user has acquired it; to fully work.

But, still a good workaround for now.


Not sure if this is related, but there is a crash happening in our game (steam beta version) we started a few days back very similar to what is described here:

https://smapi.io/log/b88d9ef420f8454b806870779c168238

This happens, if any of the players (in the two-player game) tries to access a community center vault for the first time after Lewis opened the entrance.

Originally the game had its language set to German and the mods as listed in the log above enabled. Changing the language to English, verifying the files and/or disabling the mods did not prevent the game from crashing.

Hope it helps!

Yea, seems to be the same core bug; with the same error. You're playing in German, and a mod also intercepted the Bundle data.

18:05:58 | TRACE | SMAPI | TehCore edited Data/Bundles.
commented

The content API rewrite needed to fix this is in progress, with the first changes coming in SMAPI 3.14.0.

commented

The new content API is feature-complete in the upcoming SMAPI 3.14.0. Once that's released, the upcoming Content Patcher 2.0.0 will migrate to the new content API, and then only a small change will be needed in affected content packs to fix this.

commented

This is now fixed in the upcoming Stardew Valley 1.6 too, which changes how bundle data is managed so the issue no longer happens.

commented

@TheWeakCheer If you haven't completed any bundles yet, you can fix the save like this:

  1. Load the affected save.
  2. Run this command in the SMAPI console window:
    regenerate_bundles confirm
  3. Open a bundle menu in the community center to make sure it's fixed.
commented

Sorry, same problem here (start sliding after clicking the gold scroll) in game( version 1.5.6) smapi's cmd line keeps repeating this log as follows BundlesBugs.txt