Repository: MinecraftForge/MinecraftForge (language: java)

Issue:
[1.20.4] Forge crashes loading the singleplayer world list screen with no mods installed
<!-- Thank you for reporting! -->

<!-- For support with crashes and issues, please create a post on the forum instead: -->
<!-- https://www.minecraftforge.net/forum/forum/18-support-bug-reports/ -->
Re: forum request - Forums have been down for over a week, I assume this is an outdated message?

**Minecraft Version:** 1.20.4

**Forge Version:** 49.0.30

**Logs:** https://gist.github.com/me4502/347a8bed6d2e0cc3dd09ee35d2c29128

**Steps to Reproduce:**
1. Open vanilla 1.20.4, and create a world
2. Install Forge
3. Open to main menu
4. Press singleplayer
5. Game crashes loading the world menu

**Description of issue:** All builds of Forge 1.20.4 crash for me when loading the world screen, with no mods installed. This doesn't occur until a world is added, and everything works fine in vanilla. Loading the world isn't the issue, but loading the actual world selection screen. This occurs with no mods installed. Same issue does not occur on 1.20.3.



Discussion / hints:
It looks like some sort of issue with the experimental settings warning screen. Thanks for the bug report!
I am unable to reproduce this issue. Following exact steps. Loaded vanilla, Created new world. Loaded Forge, Opened single player screen. Opened world. Everything worked fine.

Also the forums are not down. https://forums.minecraftforge.net/ We did install a new spam blocker so if you're using a VPN or have known bad actors using your IP then it may block you from posting.

If you have a world save that can reliably reproduce this issue then submit it here and I can take a look.
Also the full debug.log will be more useful then a crash report so post that.

I can fix it by having the code change to ` return this.settings != null && com.mojang.serialization.Lifecycle.experimental().equals(this.settings.getLifecycle());` but i'd prefer to have a reproduction case first.
I think this issue can be better reproduced by enabling a datapack (i.e. `update_1_21`) that has experimental lifecycle features in it. I'm back home, so I'll test this quickly.
Still no dice. I've tested both with the original test instructions and by doing the whole thing in Forge itself, both worlds created using the `update_1_21` datapack with no issues. If you have a better test case, please send it when you can.
I've never enabled an experimental features datapack so it shouldn't require one to reproduce. I'll zip and send the world I created when I'm at my computer.

Re: the forums, I'm behind a very over-provisioned CGNAT (~1000 people) so I quite often hit issues due to rate limits of popular services or IP bans from bad actors

Fix patch:
diff --git a/patches/minecraft/net/minecraft/world/level/storage/LevelSummary.java.patch b/patches/minecraft/net/minecraft/world/level/storage/LevelSummary.java.patch
index 88bd8e5d9e9..bb7d684f32a 100644
--- a/patches/minecraft/net/minecraft/world/level/storage/LevelSummary.java.patch
+++ b/patches/minecraft/net/minecraft/world/level/storage/LevelSummary.java.patch
@@ -1,13 +1,24 @@
 --- a/net/minecraft/world/level/storage/LevelSummary.java
 +++ b/net/minecraft/world/level/storage/LevelSummary.java
-@@ -277,6 +_,10 @@
-         }
-     }
+@@ -14,7 +_,7 @@
+ import net.minecraft.world.level.LevelSettings;
+ import org.apache.commons.lang3.StringUtils;
  
-+    public boolean isLifecycleExperimental() {
-+        return this.settings.getLifecycle().equals(com.mojang.serialization.Lifecycle.experimental());
+-public class LevelSummary implements Comparable<LevelSummary> {
++public class LevelSummary implements Comparable<LevelSummary>, net.minecraftforge.common.extensions.IForgeLevelSummary {
+     public static final Component PLAY_WORLD = Component.translatable("selectWorld.select");
+     private final LevelSettings settings;
+     private final LevelVersion levelVersion;
+@@ -275,6 +_,12 @@
+         public boolean canRecreate() {
+             return false;
+         }
 +    }
 +
++    // TODO Forge: Remove in 1.22. It is kept here for binary compatibility, but already exists in IForgeLevelSummary
++    @Deprecated(forRemoval = true, since = "1.21.4")
++    public boolean isLifecycleExperimental() {
++        return net.minecraftforge.common.extensions.IForgeLevelSummary.super.isLifecycleExperimental();
+     }
+ 
      public static class SymlinkLevelSummary extends LevelSummary {
-         private static final Component MORE_INFO_BUTTON = Component.translatable("symlink_warning.more_info");
-         private static final Component INFO = Component.translatable("symlink_warning.title").withColor(-65536);
diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeLevelSummary.java b/src/main/java/net/minecraftforge/common/extensions/IForgeLevelSummary.java
new file mode 100644
index 00000000000..c17a38cc053
--- /dev/null
+++ b/src/main/java/net/minecraftforge/common/extensions/IForgeLevelSummary.java
@@ -0,0 +1,22 @@
+package net.minecraftforge.common.extensions;
+
+import net.minecraft.world.level.storage.LevelSummary;
+
+public interface IForgeLevelSummary {
+    private LevelSummary self() {
+        return (LevelSummary) this;
+    }
+
+    /**
+     * Checks if the Forge lifecycle of this level is experimental. This is used to render the experimental warning
+     * tooltip on the level select screen.
+     *
+     * @return {@code true} if the level is experimental
+     */
+    default boolean isLifecycleExperimental() {
+        // NOTE: Because CorruptedLevelSummary and SymlinkLevelSummary can have null settings, we need to check it
+        var settings = this.self().getSettings();
+
+        return settings != null && settings.getLifecycle().equals(com.mojang.serialization.Lifecycle.experimental());
+    }
+}


Test patch: