-
Notifications
You must be signed in to change notification settings - Fork 427
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
Fix cannot load world after uninstalling dimension mod/datapack without breaking world presets. #2856
Fix cannot load world after uninstalling dimension mod/datapack without breaking world presets. #2856
Conversation
…od/datapack, by making deserialization fail-soft, instead of removing non-vanilla dimensions.
I tested
|
And what about dimension datapacks using vanilla chunk generators/biome sources that users remove and fully expected the dimension to be removed? Vanilla behavior is to continue to try and create the dimension despite the datapack not on. There won’t be any parsing error since it has no modded entries. Yet the vanilla behavior is a bug and unexpected and against user’s wishes. The nuking behavior at least let users know the dimension is gone when datapack is removed. I don’t see any way of properly handling dimensions in level.dat file. It’s a painful and waste of time when the real solution is to make bug reports to Mojang and push Mojang to advance their plans of overhauling how dimension data is stored Situation: If a user select a preset and tries to load the world afterwards, then that means the level.dat’s dimension data needs to be trusted and read in order to keep the preset alive. But if the user adds a dimension mod and then later removes it, the level.dat would blow up due to unknown chunk generator types etc. The user expectation is the dimension just gets removed. Not blow up. So the level.dat data can’t be trusted. And if the user adds a dimension datapack and later removes it, the game still can parse the dimension data and creates it and keeps it persisted against user wishes. No way to know if the dimension datapack is on or not because level.dat is parsed so early before the datapack’s dimensions are read and no way to know if the dimension is removed because it uses valid existing vanilla chunk generators etc. So level.dat data here cannot be trusted either Conclusion, to fit all use cases, the level.dat file needs to be simultaneously trusted and not trusted at the same time. A paradox with no solution. |
fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/FailSoftMapCodec.java
Outdated
Show resolved
Hide resolved
fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/FailSoftMapCodec.java
Outdated
Show resolved
Hide resolved
fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/FailSoftMapCodec.java
Outdated
Show resolved
Hide resolved
if (k.get().left().isPresent() && v.get().left().isPresent()) { | ||
builder.put(k.get().left().get(), v.get().left().get()); | ||
} else { | ||
// ignore failure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its a little unclear to me (with only basic knowlage of dfu) what failuire path is taken when a world is missing. May be a good idea to log out when this happens, or this is already handled by one of the other cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The failure path is normally that, when it tries to parse a generator, it cannot recognize the generator type, then the failure propagates to the whole dimension information and the whole deserialization fails. In old versions it will still load the world but have the non-overworld dimensions removed. In newer versions it will show a screen that it fails to load and the user can click "safe mode", althought clicking "safe mode" doesn't do anything.
With this PR, when the generator fails to deserialize, it will be ignored.
As @TelepathicGrunt said, with this PR, removing a dimension datapack that only uses vanilla things will not have that dimension removed. This is a rare case (most dimension datapacks use its own things). It's possible to fix both issues by adding extra data to tell that some dimensions comes from preset and should not be removed before reading |
I think that Minecraft's world preset design is not good. The world preset can add dimensions but there was already the functionality of adding dimensions. So there is a duplication of functionality. The world preset should only be a list of dimension ids and the datapack should add the relevant dimensions in the old way, then in |
* In this implementation, if one deserialization fails, it will log and ignore. | ||
*/ | ||
@Override | ||
public <T> DataResult<Map<K, V>> decode(final DynamicOps<T> ops, final MapLike<T> input) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How and why is this different from Mojang/DataFixerUpper#55?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't see that PR. Yes this PR probably does similar thing. (The relevant datafixerupper code contains a lot functional abstractions so I am not sure whether that PR works the same as this PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the difference. In that PR, when one deserialization fails, the result will be in "partial result". In this PR, the result will always be normal result.
The DataResult
can be in 3 states:
- successful with one result
- failed with one partial result
- failed with no partial result
I am not sure how vanilla handles the partial result (DFU is very convoluted). As I tested, always yielding successful result works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update: In Minecraft 1.19.3, it rejects partial result of dimension data. (LevelStorage.createLevelDataParser
uses getOrThrow
with allowPartial
argument false.) So that PR won't directly solve the issue without extra changes.
fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/RegistryCodecsMixin.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great now, good call on adding the instanceof check.
fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/FailSoftMapCodec.java
Outdated
Show resolved
Hide resolved
…ut breaking world presets. (#2856) * Fix the issue that cannot load world after uninstalling a dimension mod/datapack, by making deserialization fail-soft, instead of removing non-vanilla dimensions. * Fix style. * Fix license. * Small changes to FailSoftMapCodec. * Make FailSoftMapCodec final. * Revert "Make FailSoftMapCodec final." This reverts commit 0c0642a. * Use ModifyVariable and change comments. * Remove unnecessary `equals` (cherry picked from commit 00a2eb1)
In 1.18.x, I had two solutions:
The first solution was chosen because of simplicity.
In 1.19.3, as I tested, there is no need to manually remove the dangling references to avoid error in freezing process. So the second solution become simpler. This PR removes the first solution and uses the second solution.