33import net .minecraft .client .Minecraft ;
44import net .minecraft .client .gui .GuiMemoryErrorScreen ;
55import net .minecraft .client .gui .GuiScreen ;
6+ import net .minecraft .client .multiplayer .WorldClient ;
7+ import net .minecraft .client .renderer .RenderGlobal ;
68import net .minecraft .crash .CrashReport ;
79import net .minecraft .init .Bootstrap ;
810import net .minecraft .profiler .ISnooperInfo ;
1416import org .apache .logging .log4j .Logger ;
1517import org .dimdev .vanillafix .GuiCrashScreen ;
1618import org .lwjgl .LWJGLException ;
17- import org .spongepowered .asm .mixin .Final ;
18- import org .spongepowered .asm .mixin .Mixin ;
19- import org .spongepowered .asm .mixin .Overwrite ;
20- import org .spongepowered .asm .mixin .Shadow ;
19+ import org .spongepowered .asm .mixin .*;
2120
2221import javax .annotation .Nullable ;
2322import java .io .File ;
2827@ SuppressWarnings ({"unused" , "NonConstantFieldWithUpperCaseName" , "RedundantThrows" }) // Shadow
2928@ SideOnly (Side .CLIENT )
3029@ Mixin (Minecraft .class )
31- public abstract class MixinMinecraft implements IThreadListener , ISnooperInfo {
30+ @ Implements (@ Interface (iface = IFixedMinecraft .class , prefix = "minecraft$" ))
31+ public abstract class MixinMinecraft implements IThreadListener , ISnooperInfo , IFixedMinecraft {
3232
3333 @ Shadow volatile boolean running ;
3434 @ Shadow private boolean hasCrashed ;
3535 @ Shadow private CrashReport crashReporter ;
36+ @ Shadow private long debugCrashKeyPressTime ;
3637
3738 @ Shadow private void init () throws LWJGLException , IOException {}
3839
3940 @ Shadow private void runGameLoop () throws IOException {}
4041
41- @ Shadow public void freeMemory () {}
42-
4342 @ Shadow public void displayGuiScreen (@ Nullable GuiScreen guiScreenIn ) {}
4443
4544 @ Shadow public CrashReport addGraphicsAndWorldToCrashReport (CrashReport theCrash ) { return null ; }
@@ -50,6 +49,20 @@ public abstract class MixinMinecraft implements IThreadListener, ISnooperInfo {
5049
5150 @ Shadow public void displayCrashReport (CrashReport crashReportIn ) {}
5251
52+ @ Shadow public abstract void loadWorld (@ Nullable WorldClient worldClientIn );
53+
54+ @ Shadow public WorldClient world ;
55+
56+ @ Shadow public static byte [] memoryReserve ;
57+
58+ @ Shadow public RenderGlobal renderGlobal ;
59+
60+ private CrashReport currentReport = null ;
61+
62+ /**
63+ * Allows the player to choose to return to the title screen after a crash, or get
64+ * a pasteable link to the crash report on paste.dimdev.org.
65+ */
5366 @ SuppressWarnings ("CallToSystemGC" )
5467 @ Overwrite
5568 public void run () {
@@ -58,9 +71,9 @@ public void run() {
5871 try {
5972 init ();
6073 } catch (Throwable throwable ) {
61- CrashReport crashreport = CrashReport .makeCrashReport (throwable , "Initializing game" );
62- crashreport .makeCategory ("Initialization" );
63- displayCrashReport (addGraphicsAndWorldToCrashReport (crashreport )); // TODO: GUI for this too
74+ CrashReport report = CrashReport .makeCrashReport (throwable , "Initializing game" );
75+ report .makeCategory ("Initialization" );
76+ displayCrashReport (addGraphicsAndWorldToCrashReport (report )); // TODO: GUI for this too
6477 return ;
6578 }
6679
@@ -71,8 +84,8 @@ public void run() {
7184 runGameLoop ();
7285 } catch (OutOfMemoryError e ) {
7386 freeMemory ();
87+ // TODO: Use displayCrashScreen?
7488 displayGuiScreen (new GuiMemoryErrorScreen ());
75- System .gc ();
7689 } catch (ReportedException e ) {
7790 addGraphicsAndWorldToCrashReport (e .getCrashReport ());
7891 freeMemory ();
@@ -95,35 +108,78 @@ public void run() {
95108 }
96109
97110 public void displayCrashScreen (CrashReport report ) {
111+ if (currentReport != null ) {
112+ // There was already a crash being reported, the crash screen might have
113+ // crashed. Report it normally instead.
114+ LOGGER .error ("An uncaught exception occured while displaying the crash screen, making normal report instead" , report .getCrashCause ());
115+ displayCrashReport (report );
116+ return ;
117+ }
118+
119+ currentReport = report ;
120+
121+ File crashReportsDir = new File (Minecraft .getMinecraft ().mcDataDir , "crash-reports" );
122+ File crashReportSaveFile = new File (crashReportsDir , "crash-" + new SimpleDateFormat ("yyyy-MM-dd_HH.mm.ss" ).format (new Date ()) + "-client.txt" );
123+
124+ // Print the report in bootstrap
125+ Bootstrap .printToSYSOUT (report .getCompleteReport ());
126+
127+ // Save the report and print file in bootstrap
128+ File reportFile = null ;
129+ if (report .getFile () != null ) {
130+ reportFile = report .getFile ();
131+ } else if (report .saveToFile (crashReportSaveFile )) {
132+ reportFile = crashReportSaveFile ;
133+ }
134+
135+ if (reportFile != null ) {
136+ Bootstrap .printToSYSOUT ("Recoverable game crash! Crash report saved to: " + reportFile );
137+ } else {
138+ Bootstrap .printToSYSOUT ("Recoverable game crash! Crash report could not be saved." );
139+ }
140+
141+ // Reset hasCrashed and debugCrashKeyPressTime
142+ hasCrashed = false ;
143+ debugCrashKeyPressTime = -1 ;
144+
145+ // Display the crash screen
146+ displayGuiScreen (new GuiCrashScreen (reportFile , report ));
147+ }
148+
149+ /**
150+ * Disconnect from the current world and free memory, using a memory reserve
151+ * to make sure that an OutOfMemory doesn't happen while doing this.
152+ * <p>
153+ * Bugs Fixed:
154+ * - https://bugs.mojang.com/browse/MC-128953
155+ * - Memory reserve not recreated after out-of memory
156+ */
157+ @ SuppressWarnings ("CallToSystemGC" )
158+ @ Overwrite
159+ public void freeMemory () {
98160 try {
99- File crashReportsDir = new File (Minecraft .getMinecraft ().mcDataDir , "crash-reports" );
100- File crashReportSaveFile = new File (crashReportsDir , "crash-" + new SimpleDateFormat ("yyyy-MM-dd_HH.mm.ss" ).format (new Date ()) + "-client.txt" );
101-
102- // Print the report in bootstrap
103- Bootstrap .printToSYSOUT (report .getCompleteReport ());
104-
105- // Save the report and print file in bootstrap
106- File reportFile = null ;
107- if (report .getFile () != null ) {
108- reportFile = report .getFile ();
109- } else if (report .saveToFile (crashReportSaveFile )) {
110- reportFile = crashReportSaveFile ;
111- }
161+ memoryReserve = new byte [0 ];
162+ renderGlobal .deleteAllDisplayLists ();
163+ } catch (Throwable ignored ) {}
112164
113- if (reportFile != null ) {
114- Bootstrap .printToSYSOUT ("Recoverable game crash! Crash report saved to: " + reportFile );
115- } else {
116- Bootstrap .printToSYSOUT ("Recoverable game crash! Crash report could not be saved." );
165+ try {
166+ System .gc ();
167+ // Fix: Close the connection to avoid receiving packets from old server
168+ // when playing in another world (MC-128953)
169+ if (world != null ) {
170+ world .sendQuittingDisconnectingPacket ();
117171 }
172+ loadWorld (null );
173+ } catch (Throwable ignored ) {}
118174
119- // Display the crash screen
120- displayGuiScreen (new GuiCrashScreen (reportFile , report ));
175+ System .gc ();
121176
122- // Keep running
123- hasCrashed = false ;
124- } catch (Throwable e ) {
125- LOGGER .error ("The crash screen threw an error, reverting to default crash report" , e );
126- displayCrashReport (report );
127- }
177+ // Fix: Re-create memory reserve so that future crashes work well too
178+ memoryReserve = new byte [10485760 ];
179+ }
180+
181+ @ Override
182+ public void clearCurrentReport () {
183+ currentReport = null ;
128184 }
129185}
0 commit comments