Skip to content

Commit ae1e104

Browse files
committed
Added a basic recovery mode.
1 parent 577feed commit ae1e104

10 files changed

Lines changed: 185 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Added a basic recovery mode that should prevent some scenarios where a world becomes inaccessible due to instantly crashing upon load.

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ org.gradle.warning.mode = all
88
org.gradle.configuration-cache = false
99
##########################################################################
1010
# Mod Properties
11-
mod_version = 5.3.3
11+
mod_version = 5.4.0
1212
mod_group = turniplabs
1313
mod_name = halplibe
1414
##########################################################################

src/main/java/turniplabs/halplibe/HalpLibe.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import turniplabs.halplibe.util.TomlConfigHandler;
1818
import turniplabs.halplibe.util.toml.Toml;
1919

20+
import java.io.File;
21+
import java.io.IOException;
2022
import java.util.HashMap;
2123

2224
public class HalpLibe implements ModInitializer, PreLaunchEntrypoint {
@@ -26,8 +28,39 @@ public class HalpLibe implements ModInitializer, PreLaunchEntrypoint {
2628
public static final boolean isClient = FabricLoader.getInstance().getEnvironmentType().equals(EnvType.CLIENT);
2729
public static final TomlConfigHandler CONFIG;
2830
static {
29-
Toml toml = new Toml();
30-
CONFIG = new TomlConfigHandler(MOD_ID, toml);
31+
Toml defaultConfig = new Toml("Halplibe configuration file.");
32+
defaultConfig.addEntry("recoveryMode",false);
33+
CONFIG = new TomlConfigHandler(MOD_ID, new Toml("Halplibe configuration file."), false);
34+
File configFile = CONFIG.getConfigFile();
35+
36+
boolean changed = false;
37+
if (CONFIG.getConfigFile().exists()) {
38+
CONFIG.loadConfig();
39+
Toml rawConfig = CONFIG.getRawParsed();
40+
CONFIG.setDefaults(rawConfig);
41+
42+
if(!rawConfig.contains("recoveryMode")) {
43+
rawConfig.addEntry("recoveryMode",false);
44+
changed = true;
45+
}
46+
47+
if(changed){
48+
CONFIG.setDefaults(rawConfig);
49+
CONFIG.writeConfig();
50+
CONFIG.loadConfig();
51+
}
52+
} else {
53+
CONFIG.setDefaults(defaultConfig);
54+
try {
55+
configFile.getParentFile().mkdirs();
56+
configFile.createNewFile();
57+
CONFIG.writeConfig();
58+
CONFIG.loadConfig();
59+
} catch (IOException e) {
60+
throw new RuntimeException("Failed to generate config!", e);
61+
}
62+
63+
}
3164
}
3265

3366
@SuppressWarnings("unused")

src/main/java/turniplabs/halplibe/mixin/MinecraftMixin.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,20 @@
22

33
import net.fabricmc.loader.api.FabricLoader;
44
import net.minecraft.client.Minecraft;
5+
import net.minecraft.client.gui.Screen;
6+
import net.minecraft.client.gui.popup.PopupBuilder;
7+
import net.minecraft.client.gui.popup.PopupComponent;
8+
import net.minecraft.client.gui.popup.PopupScreen;
59
import net.minecraft.core.enums.EnumOS;
10+
import net.minecraft.core.lang.I18n;
11+
import net.minecraft.core.net.command.TextFormatting;
12+
import org.jetbrains.annotations.Nullable;
613
import org.spongepowered.asm.mixin.Mixin;
14+
import org.spongepowered.asm.mixin.Shadow;
715
import org.spongepowered.asm.mixin.injection.At;
816
import org.spongepowered.asm.mixin.injection.Inject;
917
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
18+
import turniplabs.halplibe.HalpLibe;
1019
import turniplabs.halplibe.helper.network.NetworkHandler;
1120
import turniplabs.halplibe.mixin.accessors.ItemsAccessor;
1221
import turniplabs.halplibe.util.*;
@@ -18,6 +27,13 @@
1827

1928
public abstract class MinecraftMixin {
2029

30+
@Shadow
31+
@Nullable
32+
public Screen currentScreen;
33+
34+
@Shadow
35+
public abstract void displayScreen(Screen screen);
36+
2137
@Inject(method = "startGame", at = @At(value = "INVOKE",target = "Lnet/minecraft/core/data/DataLoader;loadRecipesFromFile(Ljava/lang/String;)V", ordinal = 3, shift = At.Shift.AFTER))
2238
public void recipeEntrypoint(CallbackInfo ci){
2339
FabricLoader.getInstance().getEntrypoints("recipesReady", RecipeEntrypoint.class).forEach(RecipeEntrypoint::initNamespaces);
@@ -35,6 +51,19 @@ public void afterGameStartEntrypoint(CallbackInfo ci){
3551
NetworkHandler.internalNetworkHandlerSetup();
3652
FabricLoader.getInstance().getEntrypoints("afterGameStart", GameStartEntrypoint.class).forEach(GameStartEntrypoint::afterGameStart);
3753
FabricLoader.getInstance().getEntrypoints("afterClientStart", ClientStartEntrypoint.class).forEach(ClientStartEntrypoint::afterClientStart);
54+
if(HalpLibe.CONFIG.getBoolean("recoveryMode")) {
55+
PopupScreen popup = new PopupBuilder(this.currentScreen, 246)
56+
.withLabelLiteral(I18n.getInstance().translateKey("halplibe.recoveryMode"))
57+
.withMessageBox("message",128,
58+
I18n.getInstance().translateKey("halplibe.recoveryMode.text")+"\n\n"
59+
+ I18n.getInstance().translateKey("halplibe.recoveryMode.text2")+"\n"
60+
+ I18n.getInstance().translateKey("halplibe.recoveryMode.action1")+"\n"
61+
+ I18n.getInstance().translateKey("halplibe.recoveryMode.action2")+"\n\n"
62+
+ I18n.getInstance().translateKey("halplibe.recoveryMode.text3"),44)
63+
.closeOnClickOut(0)
64+
.build();
65+
displayScreen(popup);
66+
}
3867
}
3968

4069
@Inject(method = "startGame", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/block/Blocks;init()V", shift = At.Shift.AFTER))

src/main/java/turniplabs/halplibe/mixin/MinecraftServerMixin.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
import net.fabricmc.loader.api.FabricLoader;
44
import net.minecraft.core.Global;
5+
import net.minecraft.core.lang.I18n;
56
import net.minecraft.server.MinecraftServer;
67
import org.spongepowered.asm.mixin.Mixin;
78
import org.spongepowered.asm.mixin.Shadow;
89
import org.spongepowered.asm.mixin.injection.At;
910
import org.spongepowered.asm.mixin.injection.Inject;
1011
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
12+
import turniplabs.halplibe.HalpLibe;
1113
import turniplabs.halplibe.helper.network.NetworkHandler;
1214
import turniplabs.halplibe.mixin.accessors.ItemsAccessor;
1315
import turniplabs.halplibe.util.BlockInitEntrypoint;
@@ -50,6 +52,14 @@ public void afterItemInitEntrypoint(CallbackInfoReturnable<Boolean> cir) {
5052
@Inject(method = "startServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/achievement/stat/StatList;init()V"))
5153
public void initStats(CallbackInfoReturnable<Boolean> cir) {
5254
ItemsAccessor.invokeInitStats();
55+
56+
//before game start is too early and after game start is too late so thats why this is here
57+
if(HalpLibe.CONFIG.getBoolean("recoveryMode")) {
58+
HalpLibe.LOGGER.warn(I18n.getInstance().translateKey("halplibe.recoveryMode"));
59+
HalpLibe.LOGGER.warn(I18n.getInstance().translateKey("halplibe.recoveryMode.text"));
60+
HalpLibe.LOGGER.warn(I18n.getInstance().translateKey("halplibe.recoveryMode.text2"));
61+
HalpLibe.LOGGER.warn(I18n.getInstance().translateKey("halplibe.recoveryMode.action1"));
62+
}
5363
}
5464

5565
/*
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package turniplabs.halplibe.mixin.recovery;
2+
3+
import net.minecraft.client.gui.hud.HudIngame;
4+
import net.minecraft.core.lang.I18n;
5+
import org.spongepowered.asm.mixin.Final;
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.Shadow;
8+
import org.spongepowered.asm.mixin.injection.At;
9+
import org.spongepowered.asm.mixin.injection.Inject;
10+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
11+
import turniplabs.halplibe.HalpLibe;
12+
13+
@Mixin(value = HudIngame.class,remap = false)
14+
public abstract class HudIngameMixin {
15+
16+
@Shadow
17+
protected abstract void drawDebugScreenLine(String string);
18+
19+
@Shadow
20+
protected abstract void drawDebugScreenLineRight(String string);
21+
22+
@Shadow
23+
private int debugLine;
24+
25+
@Shadow
26+
private int debugLineRight;
27+
28+
@Shadow
29+
@Final
30+
private int debugLineHeight;
31+
32+
@Shadow
33+
private int debugOffset;
34+
35+
@Shadow
36+
private int debugOffsetRight;
37+
38+
@Inject(method = "renderGameOverlay", at = @At("TAIL"))
39+
public void renderGameOverlay(float partialTicks, boolean flag, int mouseX, int mouseY, CallbackInfo ci) {
40+
if(HalpLibe.CONFIG.getBoolean("recoveryMode")){
41+
drawDebugScreenLineRight(I18n.getInstance().translateKey("halplibe.recoveryMode"));
42+
drawDebugScreenLineRight(I18n.getInstance().translateKey("halplibe.recoveryMode.action1"));
43+
drawDebugScreenLineRight(I18n.getInstance().translateKey("halplibe.recoveryMode.action2"));
44+
this.debugLine = 0;
45+
this.debugLineRight = 0;
46+
this.debugOffset = 0;
47+
this.debugOffsetRight = 0;
48+
}
49+
}
50+
51+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package turniplabs.halplibe.mixin.recovery;
2+
3+
import net.minecraft.client.render.TileEntityRenderDispatcher;
4+
import net.minecraft.client.render.tileentity.TileEntityRenderer;
5+
import org.spongepowered.asm.mixin.Final;
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.Shadow;
8+
import org.spongepowered.asm.mixin.injection.At;
9+
import org.spongepowered.asm.mixin.injection.Inject;
10+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
11+
import turniplabs.halplibe.HalpLibe;
12+
13+
import java.util.Map;
14+
15+
@Mixin(value = TileEntityRenderDispatcher.class,remap = false)
16+
public class TileEntityRenderDispatcherMixin {
17+
18+
@Shadow
19+
@Final
20+
private Map<Class<?>, TileEntityRenderer<?>> renderers;
21+
22+
@Inject(method = "<init>()V", at = @At("TAIL"))
23+
private void addQueuedModels(CallbackInfo ci){
24+
if(HalpLibe.CONFIG.getBoolean("recoveryMode")){
25+
renderers.clear();
26+
}
27+
}
28+
29+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package turniplabs.halplibe.mixin.recovery;
2+
3+
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
4+
import net.minecraft.core.block.entity.TileEntity;
5+
import net.minecraft.core.world.World;
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.injection.At;
8+
import turniplabs.halplibe.HalpLibe;
9+
10+
@Mixin(value = World.class,remap = false)
11+
public class WorldMixin {
12+
13+
@WrapWithCondition(method = "updateEntities", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/block/entity/TileEntity;tick()V"))
14+
public boolean disableTickingTileEntities(TileEntity instance) {
15+
return !HalpLibe.CONFIG.getBoolean("recoveryMode");
16+
}
17+
18+
}

src/main/resources/halplibe.mixins.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"accessors.ItemsAccessor",
1515
"accessors.LanguageAccessor",
1616
"accessors.WeightedRandomBagAccessor",
17-
"accessors.WeightedRandomBagEntryAccessor"
17+
"accessors.WeightedRandomBagEntryAccessor",
18+
"recovery.WorldMixin"
1819
],
1920
"client": [
2021
"AtlasStitcherMixin",
@@ -27,7 +28,9 @@
2728
"models.BlockModelDispatcherMixin",
2829
"models.EntityRenderDispatcherMixin",
2930
"models.ItemModelDispatcherMixin",
30-
"models.TileEntityRendererDispatcherMixin"
31+
"models.TileEntityRendererDispatcherMixin",
32+
"recovery.HudIngameMixin",
33+
"recovery.TileEntityRenderDispatcherMixin"
3134
],
3235
"server": [
3336
"MinecraftServerMixin",
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
halplibe.recoveryMode=/!\\ Recovery Mode /!\\
2+
halplibe.recoveryMode.text=Recovery mode disables certain parts of the game to prevent crash loops.
3+
halplibe.recoveryMode.text2=The following actions have been taken:
4+
halplibe.recoveryMode.action1=- Tile entity ticking disabled.
5+
halplibe.recoveryMode.action2=- Tile entity rendering disabled.
6+
halplibe.recoveryMode.text3=Click anywhere outside this popup to close.

0 commit comments

Comments
 (0)