Fix MC-128953
This commit is contained in:
parent
774084be35
commit
32bac210ff
4 changed files with 104 additions and 45 deletions
|
@ -1,5 +1,6 @@
|
|||
package org.dimdev.vanillafix;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.*;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.crash.CrashReport;
|
||||
|
@ -9,6 +10,8 @@ import net.minecraftforge.fml.relauncher.SideOnly;
|
|||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dimdev.ddutils.HasteUpload;
|
||||
import org.dimdev.vanillafix.mixins.client.IFixedMinecraft;
|
||||
import org.dimdev.vanillafix.mixins.client.MixinMinecraft;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
|
@ -39,6 +42,7 @@ public class GuiCrashScreen extends GuiScreen {
|
|||
try {
|
||||
if (button.id == 0) {
|
||||
mc.displayGuiScreen(new GuiMainMenu());
|
||||
((IFixedMinecraft) mc).clearCurrentReport();
|
||||
} else if (button.id == 1) {
|
||||
if (hasteLink == null) {
|
||||
hasteLink = HasteUpload.uploadToHaste(HASTE_BASE_URL, "txt", report.getCompleteReport());
|
||||
|
|
|
@ -9,7 +9,6 @@ import net.minecraft.network.play.INetHandlerPlayServer;
|
|||
import net.minecraft.network.play.client.CPacketConfirmTeleport;
|
||||
import net.minecraft.network.play.client.CPacketPlayer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
|
@ -117,9 +116,7 @@ public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer
|
|||
// Instead, we will send another packet both here and in processConfirmTeleport if the position the client
|
||||
// was sent is no longer good (exceeds tolerance):
|
||||
|
||||
LOGGER.info("Player sent move packets before confirming");
|
||||
if (targetPos.squareDistanceTo(player.posX, player.posY, player.posZ) > 1.0D) {
|
||||
LOGGER.info("Distance became too big: " + targetPos + " vs. " + new Vec3d(player.posX, player.posY, player.posY));
|
||||
setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch);
|
||||
}
|
||||
|
||||
|
@ -199,7 +196,6 @@ public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer
|
|||
// Entity.move already made sure that the player didn't cheat, and reverting the move would be wrong because
|
||||
// the prevX/Y/Z is no longer good in the new dimension. This fixes MC-123364.
|
||||
if (player.isInvulnerableDimensionChange()) {
|
||||
LOGGER.info("Doing teleport");
|
||||
// A better name for invulnerableDimensionChange would be "lastBlockCollisionCausedPlayerMove". See
|
||||
// https://github.com/ModCoderPack/MCPBot-Issues/issues/624. This happens when the move caused a
|
||||
// collision that teleported the player elsewhere.
|
||||
|
@ -283,11 +279,9 @@ public abstract class MixinNetHandlerPlayServer implements INetHandlerPlayServer
|
|||
|
||||
if (packet.getTeleportId() == teleportId) {
|
||||
if (targetPos.squareDistanceTo(player.posX, player.posY, player.posZ) > 1.0D) {
|
||||
LOGGER.info("Position accepted no longer good");
|
||||
// The client accepted the position change, but it's too late, something moved the player. Sync it again.
|
||||
setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch);
|
||||
} else {
|
||||
LOGGER.info("Teleport accepted");
|
||||
targetPos = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package org.dimdev.vanillafix.mixins.client;
|
||||
|
||||
public interface IFixedMinecraft {
|
||||
public void clearCurrentReport();
|
||||
}
|
|
@ -3,6 +3,8 @@ package org.dimdev.vanillafix.mixins.client;
|
|||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiMemoryErrorScreen;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.init.Bootstrap;
|
||||
import net.minecraft.profiler.ISnooperInfo;
|
||||
|
@ -14,10 +16,7 @@ import net.minecraftforge.fml.relauncher.SideOnly;
|
|||
import org.apache.logging.log4j.Logger;
|
||||
import org.dimdev.vanillafix.GuiCrashScreen;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
|
@ -28,18 +27,18 @@ import java.util.Date;
|
|||
@SuppressWarnings({"unused", "NonConstantFieldWithUpperCaseName", "RedundantThrows"}) // Shadow
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Mixin(Minecraft.class)
|
||||
public abstract class MixinMinecraft implements IThreadListener, ISnooperInfo {
|
||||
@Implements(@Interface(iface = IFixedMinecraft.class, prefix = "minecraft$"))
|
||||
public abstract class MixinMinecraft implements IThreadListener, ISnooperInfo, IFixedMinecraft {
|
||||
|
||||
@Shadow volatile boolean running;
|
||||
@Shadow private boolean hasCrashed;
|
||||
@Shadow private CrashReport crashReporter;
|
||||
@Shadow private long debugCrashKeyPressTime;
|
||||
|
||||
@Shadow private void init() throws LWJGLException, IOException {}
|
||||
|
||||
@Shadow private void runGameLoop() throws IOException {}
|
||||
|
||||
@Shadow public void freeMemory() {}
|
||||
|
||||
@Shadow public void displayGuiScreen(@Nullable GuiScreen guiScreenIn) {}
|
||||
|
||||
@Shadow public CrashReport addGraphicsAndWorldToCrashReport(CrashReport theCrash) { return null; }
|
||||
|
@ -50,6 +49,20 @@ public abstract class MixinMinecraft implements IThreadListener, ISnooperInfo {
|
|||
|
||||
@Shadow public void displayCrashReport(CrashReport crashReportIn) {}
|
||||
|
||||
@Shadow public abstract void loadWorld(@Nullable WorldClient worldClientIn);
|
||||
|
||||
@Shadow public WorldClient world;
|
||||
|
||||
@Shadow public static byte[] memoryReserve;
|
||||
|
||||
@Shadow public RenderGlobal renderGlobal;
|
||||
|
||||
private CrashReport currentReport = null;
|
||||
|
||||
/**
|
||||
* Allows the player to choose to return to the title screen after a crash, or get
|
||||
* a pasteable link to the crash report on paste.dimdev.org.
|
||||
*/
|
||||
@SuppressWarnings("CallToSystemGC")
|
||||
@Overwrite
|
||||
public void run() {
|
||||
|
@ -58,9 +71,9 @@ public abstract class MixinMinecraft implements IThreadListener, ISnooperInfo {
|
|||
try {
|
||||
init();
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Initializing game");
|
||||
crashreport.makeCategory("Initialization");
|
||||
displayCrashReport(addGraphicsAndWorldToCrashReport(crashreport)); // TODO: GUI for this too
|
||||
CrashReport report = CrashReport.makeCrashReport(throwable, "Initializing game");
|
||||
report.makeCategory("Initialization");
|
||||
displayCrashReport(addGraphicsAndWorldToCrashReport(report)); // TODO: GUI for this too
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -71,8 +84,8 @@ public abstract class MixinMinecraft implements IThreadListener, ISnooperInfo {
|
|||
runGameLoop();
|
||||
} catch (OutOfMemoryError e) {
|
||||
freeMemory();
|
||||
// TODO: Use displayCrashScreen?
|
||||
displayGuiScreen(new GuiMemoryErrorScreen());
|
||||
System.gc();
|
||||
} catch (ReportedException e) {
|
||||
addGraphicsAndWorldToCrashReport(e.getCrashReport());
|
||||
freeMemory();
|
||||
|
@ -95,35 +108,78 @@ public abstract class MixinMinecraft implements IThreadListener, ISnooperInfo {
|
|||
}
|
||||
|
||||
public void displayCrashScreen(CrashReport report) {
|
||||
try {
|
||||
File crashReportsDir = new File(Minecraft.getMinecraft().mcDataDir, "crash-reports");
|
||||
File crashReportSaveFile = new File(crashReportsDir, "crash-" + new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss").format(new Date()) + "-client.txt");
|
||||
|
||||
// Print the report in bootstrap
|
||||
Bootstrap.printToSYSOUT(report.getCompleteReport());
|
||||
|
||||
// Save the report and print file in bootstrap
|
||||
File reportFile = null;
|
||||
if (report.getFile() != null) {
|
||||
reportFile = report.getFile();
|
||||
} else if (report.saveToFile(crashReportSaveFile)) {
|
||||
reportFile = crashReportSaveFile;
|
||||
}
|
||||
|
||||
if (reportFile != null) {
|
||||
Bootstrap.printToSYSOUT("Recoverable game crash! Crash report saved to: " + reportFile);
|
||||
} else {
|
||||
Bootstrap.printToSYSOUT("Recoverable game crash! Crash report could not be saved.");
|
||||
}
|
||||
|
||||
// Display the crash screen
|
||||
displayGuiScreen(new GuiCrashScreen(reportFile, report));
|
||||
|
||||
// Keep running
|
||||
hasCrashed = false;
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error("The crash screen threw an error, reverting to default crash report", e);
|
||||
if (currentReport != null) {
|
||||
// There was already a crash being reported, the crash screen might have
|
||||
// crashed. Report it normally instead.
|
||||
LOGGER.error("An uncaught exception occured while displaying the crash screen, making normal report instead", report.getCrashCause());
|
||||
displayCrashReport(report);
|
||||
return;
|
||||
}
|
||||
|
||||
currentReport = report;
|
||||
|
||||
File crashReportsDir = new File(Minecraft.getMinecraft().mcDataDir, "crash-reports");
|
||||
File crashReportSaveFile = new File(crashReportsDir, "crash-" + new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss").format(new Date()) + "-client.txt");
|
||||
|
||||
// Print the report in bootstrap
|
||||
Bootstrap.printToSYSOUT(report.getCompleteReport());
|
||||
|
||||
// Save the report and print file in bootstrap
|
||||
File reportFile = null;
|
||||
if (report.getFile() != null) {
|
||||
reportFile = report.getFile();
|
||||
} else if (report.saveToFile(crashReportSaveFile)) {
|
||||
reportFile = crashReportSaveFile;
|
||||
}
|
||||
|
||||
if (reportFile != null) {
|
||||
Bootstrap.printToSYSOUT("Recoverable game crash! Crash report saved to: " + reportFile);
|
||||
} else {
|
||||
Bootstrap.printToSYSOUT("Recoverable game crash! Crash report could not be saved.");
|
||||
}
|
||||
|
||||
// Reset hasCrashed and debugCrashKeyPressTime
|
||||
hasCrashed = false;
|
||||
debugCrashKeyPressTime = -1;
|
||||
|
||||
// Display the crash screen
|
||||
displayGuiScreen(new GuiCrashScreen(reportFile, report));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the current world and free memory, using a memory reserve
|
||||
* to make sure that an OutOfMemory doesn't happen while doing this.
|
||||
* <p>
|
||||
* Bugs Fixed:
|
||||
* - https://bugs.mojang.com/browse/MC-128953
|
||||
* - Memory reserve not recreated after out-of memory
|
||||
*/
|
||||
@SuppressWarnings("CallToSystemGC")
|
||||
@Overwrite
|
||||
public void freeMemory() {
|
||||
try {
|
||||
memoryReserve = new byte[0];
|
||||
renderGlobal.deleteAllDisplayLists();
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
try {
|
||||
System.gc();
|
||||
// Fix: Close the connection to avoid receiving packets from old server
|
||||
// when playing in another world (MC-128953)
|
||||
if (world != null) {
|
||||
world.sendQuittingDisconnectingPacket();
|
||||
}
|
||||
loadWorld(null);
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
System.gc();
|
||||
|
||||
// Fix: Re-create memory reserve so that future crashes work well too
|
||||
memoryReserve = new byte[10485760];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCurrentReport() {
|
||||
currentReport = null;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue