Skip to content

Commit 665bd41

Browse files
committed
feat: enhance backup functionality with GUI scale support and exclude large directories
1 parent 02c4e45 commit 665bd41

11 files changed

Lines changed: 255 additions & 102 deletions

File tree

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
- fix: bug that disabled fancy tab and hud in Skyblocker when selecting Skyhanni compact tab in welcome wizard

gradle.properties

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ org.gradle.parallel=true
44
org.gradle.configuration-cache=false
55

66
# Mod properties
7-
mod.version=4.0.0-alpha.1
7+
mod.version=4.0.0-alpha.3
88
mod.group=com.github.kd_gaming1
99
mod.id=packcore
1010
mod.name=Pack Core
@@ -15,7 +15,6 @@ deps.fabric_loader=0.18.4
1515
# Versioned dependencies
1616
deps.fabric_api=[VERSIONED]
1717
deps.uilib_version=[VERSIONED]
18-
deps.moulconfig_version=[VERSIONED]
1918
deps.midnightlib_version=[VERSIONED]
2019
deps.hm_api_version=[VERSIONED]
2120
deps.modmenu_version=[VERSIONED]

src/main/java/com/github/kd_gaming1/packcore/PackCore.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public void onInitializeClient() {
3535
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> PackCoreCommands.register(dispatcher));
3636

3737
ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
38-
if (screen.getClass() != TitleScreen.class) return;
38+
if (!(screen instanceof TitleScreen)) return;
3939

4040
RamWarningHelper.onMainMenu();
4141

src/main/java/com/github/kd_gaming1/packcore/PackCorePreLaunch.java

Lines changed: 56 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.github.kd_gaming1.packcore.configpack.ConfigPackExtractor;
99
import com.google.gson.JsonObject;
1010
import eu.midnightdust.lib.config.MidnightConfig;
11+
import net.fabricmc.loader.api.FabricLoader;
1112
import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
1213
import org.slf4j.Logger;
1314
import org.slf4j.LoggerFactory;
@@ -25,24 +26,25 @@ public class PackCorePreLaunch implements PreLaunchEntrypoint {
2526

2627
@Override
2728
public void onPreLaunch() {
29+
Path gameDir = FabricLoader.getInstance().getGameDir();
2830
Path packcoreDir = PackCore.PACKCORE_DIR;
29-
Path configsDir = packcoreDir.resolve("configs");
31+
Path configsDir = packcoreDir.resolve("configs");
3032

3133
MidnightConfig.init("packcore", PackCoreConfig.class);
3234

3335
// Highest priority: restore a backup if one is pending
3436
if (!PackCoreConfig.pendingRestoreBackup.isBlank()) {
35-
applyPendingRestore(packcoreDir);
37+
applyPendingRestore(packcoreDir, gameDir);
3638
return;
3739
}
3840

3941
// Next priority: the user explicitly chose a config from the wizard GUI
4042
if (!PackCoreConfig.pendingConfigPack.isBlank()) {
41-
applyPendingConfig(packcoreDir, configsDir);
43+
applyPendingConfig(gameDir, configsDir);
4244
return;
4345
}
4446

45-
// ── Normal path: auto-detect the best resolution match ─────────────────
47+
// Normal path: auto-detect the best resolution + GUI scale match
4648
ScreenResolution.ScreenSize screen = ScreenResolution.detect();
4749

4850
ConfigPackScanner scanner = new ConfigPackScanner();
@@ -60,38 +62,32 @@ public void onPreLaunch() {
6062
return;
6163
}
6264

63-
ConfigPackEntry selectedPack = findBestResolutionMatch(scannedPacks, screen.width(), screen.height());
65+
ConfigPackEntry selectedPack = findBestMatch(scannedPacks, screen.width(), screen.height());
6466

6567
if (selectedPack == null) {
6668
LOGGER.warn("No packs had valid resolution fields, aborting.");
6769
return;
6870
}
6971

70-
extractIfNeeded(selectedPack, packcoreDir);
72+
extractIfNeeded(selectedPack, gameDir);
7173
}
7274

73-
// ── Pending (user-selected) config ────────────────────────────────────────
74-
7575
/**
7676
* Applies the pack whose filename is stored in {@link PackCoreConfig#pendingConfigPack}.
77-
* <p>
78-
* Always uses {@link ConfigPackExtractor.OverwriteMode#REPLACE_EXISTING} because
79-
* the user explicitly asked to switch, so we want a clean application of the new pack.
77+
* Always uses REPLACE_EXISTING because the user explicitly asked to switch.
8078
* The pending flag is cleared regardless of success or failure.
8179
*/
82-
private void applyPendingConfig(Path packcoreDir, Path configsDir) {
80+
private void applyPendingConfig(Path gameDir, Path configsDir) {
8381
String pendingFile = PackCoreConfig.pendingConfigPack;
8482
LOGGER.info("Pending config switch requested: {}", pendingFile);
8583

86-
// Locate the zip in the configs directory
8784
Path zipPath = configsDir.resolve(pendingFile);
8885
if (!Files.exists(zipPath)) {
8986
LOGGER.error("Pending config zip not found at: {}", zipPath);
9087
clearPending();
9188
return;
9289
}
9390

94-
// Re-scan so we get the ConfigPackEntry (with the parsed JsonObject)
9591
ConfigPackScanner scanner = new ConfigPackScanner();
9692
List<ConfigPackEntry> packs;
9793
try {
@@ -113,10 +109,9 @@ private void applyPendingConfig(Path packcoreDir, Path configsDir) {
113109
return;
114110
}
115111

116-
// Extract — fully replace
117112
try {
118113
ConfigPackExtractor.extractAll(
119-
entry.zipPath(), packcoreDir,
114+
entry.zipPath(), gameDir,
120115
ConfigPackExtractor.OverwriteMode.REPLACE_EXISTING
121116
);
122117
} catch (IOException e) {
@@ -125,18 +120,18 @@ private void applyPendingConfig(Path packcoreDir, Path configsDir) {
125120
return;
126121
}
127122

128-
JsonObject config = entry.config();
129-
String packVersion = config.has("version") ? config.get("version").getAsString() : "";
123+
JsonObject config = entry.config();
124+
String packVersion = config.has("version") ? config.get("version").getAsString() : "";
130125

131-
PackCoreConfig.lastAppliedVersion = packVersion;
126+
PackCoreConfig.lastAppliedVersion = packVersion;
132127
PackCoreConfig.lastAppliedPackFile = pendingFile;
133-
PackCoreConfig.pendingConfigPack = "";
128+
PackCoreConfig.pendingConfigPack = "";
134129
MidnightConfig.write(MOD_ID);
135130

136131
LOGGER.info("Successfully applied pending config: {} (version: {})", pendingFile, packVersion);
137132
}
138133

139-
private void applyPendingRestore(Path packcoreDir) {
134+
private void applyPendingRestore(Path packcoreDir, Path gameDir) {
140135
String backupFile = PackCoreConfig.pendingRestoreBackup;
141136
Path backupPath = packcoreDir.resolve("backups").resolve(backupFile);
142137

@@ -149,9 +144,10 @@ private void applyPendingRestore(Path packcoreDir) {
149144
}
150145

151146
try {
152-
// Backup files are relative to the game dir, so extract there
153-
ConfigPackExtractor.extractAll(backupPath, packcoreDir.getParent(),
154-
ConfigPackExtractor.OverwriteMode.REPLACE_EXISTING);
147+
ConfigPackExtractor.extractAll(
148+
backupPath, gameDir,
149+
ConfigPackExtractor.OverwriteMode.REPLACE_EXISTING
150+
);
155151
LOGGER.info("Successfully restored backup: {}", backupFile);
156152
} catch (IOException e) {
157153
LOGGER.error("Failed to restore backup '{}': {}", backupFile, e.getMessage());
@@ -160,44 +156,50 @@ private void applyPendingRestore(Path packcoreDir) {
160156
clearPendingRestore();
161157
}
162158

163-
/** Clears {@code applyPendingRestore} and persists the change. */
164159
private static void clearPendingRestore() {
165160
PackCoreConfig.pendingRestoreBackup = "";
166161
MidnightConfig.write(MOD_ID);
167162
}
168163

169-
/** Clears {@code pendingConfigPack} and persists the change. */
170164
private static void clearPending() {
171165
PackCoreConfig.pendingConfigPack = "";
172166
MidnightConfig.write(MOD_ID);
173167
}
174168

175-
// ── Auto-detect helpers ───────────────────────────────────────────────────
176-
177169
/**
178-
* Extracts {@code selectedPack} if it is newer than the installed version,
179-
* or if no version has been applied yet.
170+
* Extracts {@code selectedPack} into the game directory if:
171+
* <ul>
172+
* <li>No version has been applied yet, or</li>
173+
* <li>The selected pack's filename matches the last applied file AND its version is newer.</li>
174+
* </ul>
175+
* If the filenames differ, extraction is skipped to preserve what the user has installed.
180176
*/
181-
private void extractIfNeeded(ConfigPackEntry selectedPack, Path packcoreDir) {
182-
JsonObject config = selectedPack.config();
183-
String packVersion = config.has("version") ? config.get("version").getAsString() : "";
184-
String installedVersion = PackCoreConfig.lastAppliedVersion;
177+
private void extractIfNeeded(ConfigPackEntry selectedPack, Path gameDir) {
178+
JsonObject config = selectedPack.config();
179+
String packVersion = config.has("version") ? config.get("version").getAsString() : "";
180+
String installedVersion = PackCoreConfig.lastAppliedVersion;
181+
String installedPackFile = PackCoreConfig.lastAppliedPackFile;
182+
String selectedPackFile = selectedPack.zipPath().getFileName().toString();
185183

186-
LOGGER.info("Best resolution match: {} (version: {})",
187-
selectedPack.zipPath().getFileName(), packVersion);
184+
LOGGER.info("Best match: {} (version: {})", selectedPackFile, packVersion);
188185

189186
try {
190187
if (installedVersion.isEmpty()) {
191188
LOGGER.info("No config applied yet — performing full extraction.");
192189
ConfigPackExtractor.extractAll(
193-
selectedPack.zipPath(), packcoreDir,
190+
selectedPack.zipPath(), gameDir,
194191
ConfigPackExtractor.OverwriteMode.REPLACE_EXISTING
195192
);
193+
} else if (!installedPackFile.equals(selectedPackFile)) {
194+
// Selected pack differs from last applied — keep what's installed.
195+
LOGGER.info("Selected pack '{}' differs from last applied '{}', skipping.",
196+
selectedPackFile, installedPackFile);
197+
return;
196198
} else if (UpdateChecker.isNewerVersion(packVersion, installedVersion)) {
197199
LOGGER.info("Newer config available ({} → {}), applying with SKIP_EXISTING.",
198200
installedVersion, packVersion);
199201
ConfigPackExtractor.extractAll(
200-
selectedPack.zipPath(), packcoreDir,
202+
selectedPack.zipPath(), gameDir,
201203
ConfigPackExtractor.OverwriteMode.SKIP_EXISTING
202204
);
203205
} else {
@@ -209,21 +211,22 @@ private void extractIfNeeded(ConfigPackEntry selectedPack, Path packcoreDir) {
209211
return;
210212
}
211213

212-
PackCoreConfig.lastAppliedVersion = packVersion;
213-
PackCoreConfig.lastAppliedPackFile = selectedPack.zipPath().getFileName().toString();
214+
PackCoreConfig.lastAppliedVersion = packVersion;
215+
PackCoreConfig.lastAppliedPackFile = selectedPackFile;
214216
MidnightConfig.write(MOD_ID);
215217

216218
LOGGER.info("Successfully applied config version: {}", packVersion);
217219
}
218220

219221
/**
220222
* Returns the pack whose target resolution is closest to the screen resolution
221-
* using squared Euclidean distance.
223+
* using squared Euclidean distance. When two packs tie on distance, the one
224+
* with the higher guiScale wins.
222225
*/
223-
private ConfigPackEntry findBestResolutionMatch(List<ConfigPackEntry> packs,
224-
int screenWidth, int screenHeight) {
225-
ConfigPackEntry selectedPack = null;
226-
long bestDistanceSquared = Long.MAX_VALUE;
226+
private ConfigPackEntry findBestMatch(List<ConfigPackEntry> packs, int screenWidth, int screenHeight) {
227+
ConfigPackEntry best = null;
228+
long bestDistSq = Long.MAX_VALUE;
229+
int bestGuiScale = -1;
227230

228231
for (ConfigPackEntry pack : packs) {
229232
JsonObject config = pack.config();
@@ -233,16 +236,18 @@ private ConfigPackEntry findBestResolutionMatch(List<ConfigPackEntry> packs,
233236
continue;
234237
}
235238

236-
long widthDiff = config.get("targetWidth").getAsInt() - screenWidth;
239+
long widthDiff = config.get("targetWidth").getAsInt() - screenWidth;
237240
long heightDiff = config.get("targetHeight").getAsInt() - screenHeight;
238-
long distSq = widthDiff * widthDiff + heightDiff * heightDiff;
241+
long distSq = widthDiff * widthDiff + heightDiff * heightDiff;
242+
int guiScale = config.has("guiScale") ? config.get("guiScale").getAsInt() : 0;
239243

240-
if (distSq < bestDistanceSquared) {
241-
bestDistanceSquared = distSq;
242-
selectedPack = pack;
244+
if (distSq < bestDistSq || (distSq == bestDistSq && guiScale > bestGuiScale)) {
245+
bestDistSq = distSq;
246+
bestGuiScale = guiScale;
247+
best = pack;
243248
}
244249
}
245250

246-
return selectedPack;
251+
return best;
247252
}
248253
}

src/main/java/com/github/kd_gaming1/packcore/command/PackCoreCommands.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.github.kd_gaming1.packcore.command;
22

3+
import com.github.kd_gaming1.packcore.gui.screen.WelcomeWizardScreen;
4+
import com.github.kd_gaming1.packcore.gui.screen.config.ConfigScreen;
35
import com.github.kd_gaming1.packcore.integration.ItemBackgroundManager;
46
import com.github.kd_gaming1.packcore.integration.PerformanceProfileService;
57
import com.github.kd_gaming1.packcore.integration.StorageDesignManager;
@@ -85,6 +87,22 @@ public static void register(CommandDispatcher<FabricClientCommandSource> dispatc
8587
return 1;
8688
}))
8789
)
90+
.then(literal("wizard")
91+
.executes(ctx -> {
92+
Minecraft.getInstance().execute(() ->
93+
Minecraft.getInstance().setScreen(new WelcomeWizardScreen(Minecraft.getInstance().screen))
94+
);
95+
return 1;
96+
})
97+
)
98+
.then(literal("modpack_config")
99+
.executes(ctx -> {
100+
Minecraft.getInstance().execute(() ->
101+
Minecraft.getInstance().setScreen(new ConfigScreen())
102+
);
103+
return 1;
104+
})
105+
)
88106
);
89107
}
90108

0 commit comments

Comments
 (0)