diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java index 10fc3567..966f4f02 100644 --- a/src/main/java/carpet/CarpetSettings.java +++ b/src/main/java/carpet/CarpetSettings.java @@ -961,6 +961,12 @@ public class CarpetSettings ) public static String updateSuppressionBlock = "false"; + @Rule( + desc = "Fixes update suppression causing server crashes.", + category = {BUGFIX} + ) + public static boolean updateSuppressionCrashFix = false; + public static int getInteger(String s) { try { return Integer.parseInt(s); diff --git a/src/main/java/carpet/helpers/ThrowableSuppression.java b/src/main/java/carpet/helpers/ThrowableSuppression.java new file mode 100644 index 00000000..76ae4e14 --- /dev/null +++ b/src/main/java/carpet/helpers/ThrowableSuppression.java @@ -0,0 +1,12 @@ +package carpet.helpers; + +import net.minecraft.util.math.BlockPos; + +public class ThrowableSuppression extends RuntimeException{ + + public final BlockPos pos; + public ThrowableSuppression(String message, BlockPos pos) { + super(message); + this.pos = pos; + } +} \ No newline at end of file diff --git a/src/main/java/carpet/logging/LoggerRegistry.java b/src/main/java/carpet/logging/LoggerRegistry.java index be181fea..677f2553 100644 --- a/src/main/java/carpet/logging/LoggerRegistry.java +++ b/src/main/java/carpet/logging/LoggerRegistry.java @@ -28,6 +28,7 @@ public class LoggerRegistry public static boolean __packets; public static boolean __pathfinding; public static boolean __explosions; + public static boolean __updateSuppressedCrashes; public static void initLoggers() { @@ -47,6 +48,7 @@ public class LoggerRegistry registerLogger("counter",HUDLogger.stardardHUDLogger("counter","white", Arrays.stream(DyeColor.values()).map(Object::toString).toArray(String[]::new))); registerLogger("mobcaps", HUDLogger.stardardHUDLogger("mobcaps", "dynamic",new String[]{"dynamic", "overworld", "nether","end"})); registerLogger("explosions", HUDLogger.stardardLogger("explosions", "brief",new String[]{"brief", "full"})); + registerLogger("updateSuppressedCrashes", Logger.stardardLogger("updateSuppressedCrashes", null,null)); } @@ -172,7 +174,4 @@ public class LoggerRegistry log.onPlayerDisconnect(player); } } - - - } diff --git a/src/main/java/carpet/mixins/MinecraftServer_updateSuppressionCrashFixMixin.java b/src/main/java/carpet/mixins/MinecraftServer_updateSuppressionCrashFixMixin.java new file mode 100644 index 00000000..67d42b8e --- /dev/null +++ b/src/main/java/carpet/mixins/MinecraftServer_updateSuppressionCrashFixMixin.java @@ -0,0 +1,57 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.helpers.ThrowableSuppression; +import carpet.logging.LoggerRegistry; +import carpet.utils.Messenger; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.BaseText; +import net.minecraft.util.crash.CrashException; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.function.BooleanSupplier; + +@Mixin(MinecraftServer.class) +public class MinecraftServer_updateSuppressionCrashFixMixin { + + @Redirect( + method = "tickWorlds", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/world/ServerWorld;tick(Ljava/util/function/BooleanSupplier;)V" + ), + require = 0 + ) + private void fixUpdateSuppressionCrashTick(ServerWorld serverWorld, BooleanSupplier shouldKeepTicking){ + if (!CarpetSettings.updateSuppressionCrashFix) { + serverWorld.tick(shouldKeepTicking); + return; + } + try { + serverWorld.tick(shouldKeepTicking); + } catch (CrashException e) { + if (!(e.getCause() instanceof ThrowableSuppression throwableSuppression)) throw e; + logUpdateSuppression(throwableSuppression.pos); + } catch (ThrowableSuppression e) { + logUpdateSuppression(e.pos); + } + } + + + private void logUpdateSuppression(BlockPos pos) { + if(LoggerRegistry.__updateSuppressedCrashes) { + LoggerRegistry.getLogger("updateSuppressedCrashes").log(() -> { + return new BaseText[]{Messenger.c( + "w Server crash prevented in: ", + "m world tick ", + "w - at: ", + "g [ " + pos.toShortString() + " ]" + )}; + }); + } + } +} diff --git a/src/main/java/carpet/mixins/ServerPlayerEntity_updateSuppressionCrashFixMixin.java b/src/main/java/carpet/mixins/ServerPlayerEntity_updateSuppressionCrashFixMixin.java new file mode 100644 index 00000000..b1c3c009 --- /dev/null +++ b/src/main/java/carpet/mixins/ServerPlayerEntity_updateSuppressionCrashFixMixin.java @@ -0,0 +1,55 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.helpers.ThrowableSuppression; +import carpet.logging.LoggerRegistry; +import carpet.utils.Messenger; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.BaseText; +import net.minecraft.util.crash.CrashException; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ServerPlayerEntity.class) +public class ServerPlayerEntity_updateSuppressionCrashFixMixin { + + @Redirect( + method = "playerTick()V", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/entity/player/PlayerEntity;tick()V" + ), + require = 0 + ) + private void fixUpdateSuppressionCrashPlayerTick(PlayerEntity playerEntity){ + if (!CarpetSettings.updateSuppressionCrashFix) { + playerEntity.tick(); + return; + } + try { + playerEntity.tick(); + } catch (CrashException e) { + if (!(e.getCause() instanceof ThrowableSuppression throwableSuppression)) throw e; + logUpdateSuppressionPlayer(throwableSuppression.pos); + } catch (ThrowableSuppression e) { + logUpdateSuppressionPlayer(e.pos); + } + } + + + private void logUpdateSuppressionPlayer(BlockPos pos) { + if(LoggerRegistry.__updateSuppressedCrashes) { + LoggerRegistry.getLogger("updateSuppressedCrashes").log(() -> { + return new BaseText[]{Messenger.c( + "w Server crash prevented in: ", + "m player tick ", + "w - at: ", + "g [ " + pos.toShortString() + " ]" + )}; + }); + } + } +} diff --git a/src/main/java/carpet/mixins/World_updateSuppressionCrashFixMixin.java b/src/main/java/carpet/mixins/World_updateSuppressionCrashFixMixin.java new file mode 100644 index 00000000..300b8c90 --- /dev/null +++ b/src/main/java/carpet/mixins/World_updateSuppressionCrashFixMixin.java @@ -0,0 +1,32 @@ +package carpet.mixins; + +import carpet.CarpetSettings; +import carpet.helpers.ThrowableSuppression; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(World.class) +public class World_updateSuppressionCrashFixMixin { + + @Inject( + method = "updateNeighbor", + at = @At( + value="INVOKE", + target = "Lnet/minecraft/util/crash/CrashReport;create(Ljava/lang/Throwable;Ljava/lang/String;)Lnet/minecraft/util/crash/CrashReport;" + ), + locals = LocalCapture.CAPTURE_FAILHARD, + require = 0 + ) + public void checkUpdateSuppression(BlockPos sourcePos, Block sourceBlock, BlockPos neighborPos, CallbackInfo ci, BlockState state,Throwable throwable){ + if(CarpetSettings.updateSuppressionCrashFix && (throwable instanceof ThrowableSuppression || throwable instanceof StackOverflowError)){ + throw new ThrowableSuppression("Update suppression",neighborPos); + } + } +} diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index a0839af2..23a5e35f 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -220,7 +220,11 @@ "LivingEntity_creativeFlyMixin", "VeryBiasedToBottomHeightProvider_cleanLogsMixin", - "TrapezoidHeightProvider_cleanLogsMixin" + "TrapezoidHeightProvider_cleanLogsMixin", + + "World_updateSuppressionCrashFixMixin", + "MinecraftServer_updateSuppressionCrashFixMixin", + "ServerPlayerEntity_updateSuppressionCrashFixMixin" ], "client": [ "RenderTickCounter_tickSpeedMixin",