Skip to content

Commit 16acf1c

Browse files
committed
feat: enhance diagnostics and storage/tab design handling with instant application
1 parent 9af8229 commit 16acf1c

5 files changed

Lines changed: 205 additions & 116 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,10 @@
1616
- Full diagnostics report logged on every startup (modpack info, config pack, settings, runtime, system)
1717
- Crash reports now include a **PackCore Diagnostics** section with the same data as the startup log
1818
- New `/packcore diagnose` command — shows a compact report in chat with a **click-to-copy** button for easy sharing when reporting issues
19-
- New `/packcore crashtest` command — triggers a test crash to verify the crash report enrichment is working
19+
- New `/packcore crashtest` command — triggers a test crash to verify the crash report enrichment is working
20+
21+
### Storage Design
22+
- Replaced the world-join command approach for toggling the Firmament storage overlay with direct reflection — the setting is now applied instantly without requiring a world join
23+
24+
### Tab Design
25+
- SkyHanni compact tab list toggle now applied instantly via reflection instead of a delayed chat command on world join

src/main/java/com/github/kd_gaming1/packcore/gui/wizard/page/ConfirmApplyPage.java

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ private enum RowStatus { SUCCESS, ERROR }
8787
private final List<SummaryRowComponent> muiRows = new ArrayList<>();
8888

8989
private CustomButtonWidget applyButton;
90-
private static String globalErrorMessage;
90+
private String globalErrorMessage;
9191
private Runnable onApplySucceeded;
9292
private boolean applyCompleted;
93-
private static String resourcePackWarningMessage;
93+
private String resourcePackWarningMessage;
9494

9595
public void setOnApplySucceeded(Runnable callback) { onApplySucceeded = callback; }
9696
public boolean isApplyCompleted() { return applyCompleted; }
@@ -138,29 +138,19 @@ public void onEnter() {
138138
// ── Warning area (restart + world-join, stacked if both needed) ──
139139
int buttonY = getHeight() - PADDING - BUTTON_HEIGHT;
140140
boolean showRestart = requiresRestart();
141-
boolean showWorldJoin = requiresWorldJoin();
142141
int warningLineHeight = font.lineHeight + WARNING_LINE_GAP;
143-
int warningCount = (showRestart ? 1 : 0) + (showWorldJoin ? 1 : 0);
144-
int warningBlockHeight = warningCount > 0 ? warningCount * warningLineHeight + BUTTON_GAP : 0;
145-
int firstWarningY = buttonY - warningBlockHeight;
142+
int warningBlockHeight = showRestart ? warningLineHeight + BUTTON_GAP : 0;
143+
int warningY = buttonY - warningBlockHeight;
146144

147-
int warningY = firstWarningY;
148145
if (showRestart) {
149146
addComponent(new MultiLineTextComponent(
150147
PADDING, warningY, getWidth() - PADDING * 2 - SCROLL_BAR_WIDTH,
151148
Component.translatable("gui.packcore.wizard.confirm.restart_required"),
152149
GuiColors.WARNING));
153-
warningY += warningLineHeight;
154-
}
155-
if (showWorldJoin) {
156-
addComponent(new MultiLineTextComponent(
157-
PADDING, warningY, getWidth() - PADDING * 2 - SCROLL_BAR_WIDTH,
158-
Component.translatable("gui.packcore.wizard.confirm.world_join_required"),
159-
GuiColors.WARNING));
160150
}
161151

162152
int scrollTop = PADDING + font.lineHeight + PADDING;
163-
int scrollHeight = (warningCount > 0 ? firstWarningY - BUTTON_GAP : buttonY - BUTTON_GAP) - scrollTop;
153+
int scrollHeight = (showRestart ? warningY - BUTTON_GAP : buttonY - BUTTON_GAP) - scrollTop;
164154

165155
// ── Summary rows ──
166156
EmptyComponent rowContainer = new EmptyComponent(0, 0, rowWidth, 0);
@@ -458,13 +448,6 @@ private boolean requiresRestart() {
458448
return wantsCustomFont != engineCurrentlyOn;
459449
}
460450

461-
private boolean requiresWorldJoin() {
462-
return (state.getSelection(TabDesignPage.STATE_KEY) != null
463-
&& FabricLoader.getInstance().isModLoaded("skyhanni"))
464-
|| (state.getSelection(StorageDesignPage.STATE_KEY) != null
465-
&& FabricLoader.getInstance().isModLoaded("firmament"));
466-
}
467-
468451
private boolean isHypixelPlusId(String packId) {
469452
return packId != null && packId.toLowerCase(Locale.ROOT).contains("hypixel");
470453
}
@@ -474,11 +457,22 @@ private boolean isHypixelPlusId(String packId) {
474457
@Override
475458
public void render(GuiGraphics graphics, int mouseX, int mouseY,
476459
float partialTick, int parentWidth, int parentHeight) {
477-
if (globalErrorMessage == null) return;
460+
if (globalErrorMessage == null && resourcePackWarningMessage == null) return;
461+
478462
var font = Minecraft.getInstance().font;
479-
int errorY = getTotalY() + getHeight() - PADDING - BUTTON_HEIGHT + BUTTON_HEIGHT + 4;
480-
graphics.drawCenteredString(font, globalErrorMessage,
481-
getTotalX() + getWidth() / 2, errorY, GuiColors.ERROR);
463+
int buttonY = getTotalY() + getHeight() - PADDING - BUTTON_HEIGHT;
464+
int messageY = buttonY - font.lineHeight - 4;
465+
466+
if (globalErrorMessage != null) {
467+
graphics.drawCenteredString(font, globalErrorMessage,
468+
getTotalX() + getWidth() / 2, messageY, GuiColors.ERROR);
469+
messageY -= font.lineHeight + 4;
470+
}
471+
472+
if (resourcePackWarningMessage != null) {
473+
graphics.drawCenteredString(font, resourcePackWarningMessage,
474+
getTotalX() + getWidth() / 2, messageY, GuiColors.WARNING);
475+
}
482476
}
483477

484478
// ── SummaryRowComponent ───────────────────────────────────────────────────
Lines changed: 77 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
package com.github.kd_gaming1.packcore.integration;
22

33
import com.github.kd_gaming1.packcore.PackCore;
4-
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
54
import net.fabricmc.loader.api.FabricLoader;
6-
import net.minecraft.client.Minecraft;
7-
import net.minecraft.client.player.LocalPlayer;
85

9-
import java.util.concurrent.atomic.AtomicReference;
6+
import java.lang.reflect.Field;
7+
import java.lang.reflect.Method;
8+
import java.util.Arrays;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.stream.Collectors;
1012

1113
public class StorageDesignManager {
1214

13-
private static final AtomicReference<Boolean> pendingOverlayState = new AtomicReference<>(null);
14-
15-
static {
16-
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
17-
Boolean state = pendingOverlayState.getAndSet(null);
18-
if (state != null) scheduleCommand(state);
19-
});
20-
}
15+
private static final String CONFIG_NAME = "storage-overlay";
16+
private static final String OPTION_NAME = "always-replace";
2117

2218
public enum StorageDesign {
2319
OVERLAY, VANILLA
@@ -28,37 +24,82 @@ public static boolean apply(StorageDesign design) {
2824
PackCore.LOGGER.warn("StorageDesign: Firmament not loaded, cannot apply");
2925
return false;
3026
}
27+
try {
28+
applyViaReflection(design == StorageDesign.OVERLAY);
29+
PackCore.LOGGER.info("StorageDesign: applied {} immediately", design);
30+
return true;
31+
} catch (Exception e) {
32+
PackCore.LOGGER.error("StorageDesign: failed to apply via reflection", e);
33+
return false;
34+
}
35+
}
36+
37+
@SuppressWarnings("unchecked")
38+
private static void applyViaReflection(boolean enable) throws Exception {
39+
Class<?> managedConfigClass = Class.forName("moe.nea.firmament.util.data.ManagedConfig");
40+
Field companionField = managedConfigClass.getDeclaredField("Companion");
41+
companionField.setAccessible(true);
42+
Object companion = companionField.get(null);
43+
44+
Method getAllConfigs = companion.getClass().getDeclaredMethod("getAllManagedConfigs");
45+
getAllConfigs.setAccessible(true);
46+
Object instanceList = getAllConfigs.invoke(companion);
47+
List<Object> configs =
48+
(List<Object>) instanceList.getClass().getMethod("getAll").invoke(instanceList);
3149

32-
boolean enableOverlay = design == StorageDesign.OVERLAY;
50+
for (Object config : configs) {
51+
String name = (String) config.getClass().getMethod("getName").invoke(config);
52+
if (!CONFIG_NAME.equals(name)) continue;
3353

34-
Minecraft client = Minecraft.getInstance();
35-
if (client.player != null) {
36-
scheduleCommand(enableOverlay);
37-
} else {
38-
pendingOverlayState.set(enableOverlay);
39-
PackCore.LOGGER.info("StorageDesign: queued command for next world join");
54+
Map<String, Object> options =
55+
(Map<String, Object>) config.getClass().getMethod("getAllOptions").invoke(config);
56+
57+
if (!options.containsKey(OPTION_NAME)) {
58+
PackCore.LOGGER.error(
59+
"StorageDesign: option '{}' not found. Available: {}", OPTION_NAME, options.keySet());
60+
throw new IllegalStateException(
61+
"Option '" + OPTION_NAME + "' not found in config '" + CONFIG_NAME + "'");
62+
}
63+
Object option = options.get(OPTION_NAME);
64+
65+
Method setter = findMethod(option.getClass(), "setValue", Object.class);
66+
setter.invoke(option, enable);
67+
68+
Method save = findMethod(config.getClass(), "markDirty", java.util.concurrent.CompletableFuture.class);
69+
save.invoke(config, (Object) null);
70+
return;
4071
}
4172

42-
return true;
73+
List<String> allNames = configs.stream()
74+
.map(c -> {
75+
try { return (String) c.getClass().getMethod("getName").invoke(c); }
76+
catch (Exception ex) { return "<error>"; }
77+
})
78+
.collect(Collectors.toList());
79+
PackCore.LOGGER.error(
80+
"StorageDesign: config '{}' not found. Known: {}", CONFIG_NAME, allNames);
81+
throw new IllegalStateException("Firmament config '" + CONFIG_NAME + "' not found");
4382
}
4483

45-
private static void scheduleCommand(boolean enable) {
46-
Minecraft client = Minecraft.getInstance();
47-
new Thread(() -> {
84+
private static Method findMethod(Class<?> start, String name, Class<?>... params)
85+
throws NoSuchMethodException {
86+
for (Class<?> c = start; c != null; c = c.getSuperclass()) {
4887
try {
49-
Thread.sleep(2000);
50-
client.execute(() -> {
51-
LocalPlayer player = client.player;
52-
if (player == null) return;
53-
// /firm config toggle storage-overlay always-replace
54-
// The toggle command flips the value, so we need to check the current state.
55-
String command = "firm config toggle storage-overlay always-replace";
56-
player.connection.sendCommand(command);
57-
PackCore.LOGGER.info("StorageDesign: executed /{}", command);
58-
});
59-
} catch (InterruptedException e) {
60-
Thread.currentThread().interrupt();
88+
Method m = c.getDeclaredMethod(name, params);
89+
m.setAccessible(true);
90+
return m;
91+
} catch (NoSuchMethodException ignored) {
92+
// keep walking
6193
}
62-
}, "packcore-firmament-config").start();
94+
}
95+
try {
96+
assert start != null;
97+
Method m = start.getMethod(name, params);
98+
m.setAccessible(true);
99+
return m;
100+
} catch (NoSuchMethodException ignored) {
101+
// fall through
102+
}
103+
throw new NoSuchMethodException("Cannot find " + name + " on " + start.getName());
63104
}
64105
}

0 commit comments

Comments
 (0)