Skip to content

Commit c00892c

Browse files
committed
Refactor legacy config migration logic and enhance JSON handling
1 parent 81b15f0 commit c00892c

1 file changed

Lines changed: 95 additions & 54 deletions

File tree

HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyConfigMigrator.java

Lines changed: 95 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.jackhuang.hmcl.setting;
1919

2020
import com.google.gson.JsonParseException;
21+
import com.google.gson.JsonArray;
2122
import com.google.gson.JsonElement;
2223
import com.google.gson.JsonObject;
2324
import org.jackhuang.hmcl.Metadata;
@@ -30,11 +31,9 @@
3031
import java.nio.file.Files;
3132
import java.nio.file.Path;
3233
import java.nio.file.Paths;
33-
import java.util.HashMap;
3434
import java.util.Map;
3535
import java.util.UUID;
3636

37-
import static org.jackhuang.hmcl.util.Lang.tryCast;
3837
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
3938

4039
/// Migrates legacy per-workspace config files into the current settings.json file.
@@ -68,22 +67,27 @@ private LegacyConfigMigrator() {
6867
// a separate versioning scheme and must not depend on this numeric value.
6968
int configVersion = getLegacyConfigVersion(jsonObject);
7069

70+
if (configVersion > LEGACY_CURRENT_CONFIG_VERSION) {
71+
LOG.warning(String.format("Current HMCL only support the legacy configuration version up to %d. However, the version now is %d.", LEGACY_CURRENT_CONFIG_VERSION, configVersion));
72+
Config deserialized = Config.fromJson(jsonObject);
73+
if (deserialized == null) {
74+
return null;
75+
}
76+
return new LoadedConfig(deserialized, Config.CONFIG_GSON.toJson(jsonObject), true);
77+
}
78+
79+
if (configVersion < LEGACY_CURRENT_CONFIG_VERSION) {
80+
upgradeConfig(jsonObject, configVersion);
81+
}
82+
migrateLegacyProfilePresetReferences(jsonObject);
83+
7184
Config deserialized = Config.fromJson(jsonObject);
7285
if (deserialized == null) {
7386
return null;
7487
}
7588

7689
migrateLegacyPresetSettings(deserialized, jsonObject);
77-
78-
if (configVersion < LEGACY_CURRENT_CONFIG_VERSION) {
79-
upgradeConfig(deserialized, jsonObject, configVersion);
80-
return new LoadedConfig(deserialized, deserialized.toJson(), false);
81-
} else if (configVersion > LEGACY_CURRENT_CONFIG_VERSION) {
82-
LOG.warning(String.format("Current HMCL only support the legacy configuration version up to %d. However, the version now is %d.", LEGACY_CURRENT_CONFIG_VERSION, configVersion));
83-
return new LoadedConfig(deserialized, Config.CONFIG_GSON.toJson(jsonObject), true);
84-
} else {
85-
return new LoadedConfig(deserialized, deserialized.toJson(), false);
86-
}
90+
return new LoadedConfig(deserialized, deserialized.toJson(), false);
8791
}
8892

8993
/// Looks for a legacy config file and prepares it for writing as the new config file.
@@ -164,61 +168,90 @@ private static int getLegacyConfigVersion(JsonObject jsonObject) {
164168
}
165169
}
166170

167-
/// Upgrades old config fields to the current schema.
168-
private static void upgradeConfig(Config deserialized, JsonObject jsonObject, int configVersion) {
171+
/// Upgrades old config fields in the raw JSON object to the current schema.
172+
private static void upgradeConfig(JsonObject jsonObject, int configVersion) {
169173
LOG.info(String.format("Updating legacy configuration from %d to %d.", configVersion, LEGACY_CURRENT_CONFIG_VERSION));
170174
if (configVersion < 1) {
171-
Map<?, ?> rawJson = Config.CONFIG_GSON.fromJson(jsonObject, Map.class);
172-
173-
// Upgrade configuration of HMCL 2.x: Convert OfflineAccounts whose stored uuid is important.
174-
tryCast(rawJson.get("auth"), Map.class).ifPresent(auth -> {
175-
tryCast(auth.get("offline"), Map.class).ifPresent(offline -> {
176-
String selected = rawJson.containsKey("selectedAccount") ? null
177-
: tryCast(offline.get("IAuthenticator_UserName"), String.class).orElse(null);
178-
179-
tryCast(offline.get("uuidMap"), Map.class).ifPresent(uuidMap -> {
180-
((Map<?, ?>) uuidMap).forEach((key, value) -> {
181-
Map<Object, Object> storage = new HashMap<>();
182-
storage.put("type", "offline");
183-
storage.put("username", key);
184-
storage.put("uuid", value);
185-
if (key.equals(selected)) {
186-
storage.put("selected", true);
187-
}
188-
deserialized.getAccountStorages().add(storage);
189-
});
190-
});
191-
});
192-
});
175+
migrateLegacyOfflineAccounts(jsonObject);
193176

194177
// Upgrade configuration of HMCL earlier than 3.1.70.
195-
if (!rawJson.containsKey("commonDirType")) {
196-
deserialized.setCommonDirType(deserialized.getCommonDirectory().equals(Settings.getDefaultCommonDirectory()) ? EnumCommonDirectory.DEFAULT : EnumCommonDirectory.CUSTOM);
178+
if (!jsonObject.has("commonDirType")) {
179+
String commonDirectory = readString(jsonObject, "commonpath", Settings.getDefaultCommonDirectory());
180+
jsonObject.addProperty("commonDirType", commonDirectory.equals(Settings.getDefaultCommonDirectory())
181+
? EnumCommonDirectory.DEFAULT.name()
182+
: EnumCommonDirectory.CUSTOM.name());
197183
}
198-
if (!rawJson.containsKey("backgroundType")) {
199-
deserialized.setBackgroundImageType(StringUtils.isNotBlank(deserialized.getBackgroundImage()) ? EnumBackgroundImage.CUSTOM : EnumBackgroundImage.DEFAULT);
184+
if (!jsonObject.has("backgroundType")) {
185+
String backgroundImage = readString(jsonObject, "bgpath", "");
186+
jsonObject.addProperty("backgroundType", StringUtils.isNotBlank(backgroundImage)
187+
? EnumBackgroundImage.CUSTOM.name()
188+
: EnumBackgroundImage.DEFAULT.name());
200189
}
201-
if (!rawJson.containsKey("hasProxy")) {
202-
deserialized.setHasProxy(StringUtils.isNotBlank(deserialized.getProxyHost()));
190+
if (!jsonObject.has("hasProxy")) {
191+
jsonObject.addProperty("hasProxy", StringUtils.isNotBlank(readString(jsonObject, "proxyHost", "")));
203192
}
204-
if (!rawJson.containsKey("hasProxyAuth")) {
205-
deserialized.setHasProxyAuth(StringUtils.isNotBlank(deserialized.getProxyUser()));
193+
if (!jsonObject.has("hasProxyAuth")) {
194+
jsonObject.addProperty("hasProxyAuth", StringUtils.isNotBlank(readString(jsonObject, "proxyUserName", "")));
206195
}
207196

208-
if (!rawJson.containsKey("downloadType")) {
209-
tryCast(rawJson.get("downloadtype"), Number.class)
210-
.map(Number::intValue)
211-
.ifPresent(id -> {
212-
if (id == 0) {
213-
deserialized.setDownloadType("mojang");
214-
} else if (id == 1) {
215-
deserialized.setDownloadType("bmclapi");
216-
}
217-
});
197+
if (!jsonObject.has("downloadType")) {
198+
JsonElement legacyDownloadType = jsonObject.get("downloadtype");
199+
if (legacyDownloadType != null && legacyDownloadType.isJsonPrimitive()
200+
&& legacyDownloadType.getAsJsonPrimitive().isNumber()) {
201+
int id = legacyDownloadType.getAsInt();
202+
if (id == 0) {
203+
jsonObject.addProperty("downloadType", "mojang");
204+
} else if (id == 1) {
205+
jsonObject.addProperty("downloadType", "bmclapi");
206+
}
207+
}
218208
}
219209
}
220210
}
221211

212+
/// Converts legacy offline account UUID entries into the current account storage array.
213+
private static void migrateLegacyOfflineAccounts(JsonObject jsonObject) {
214+
if (!(jsonObject.get("auth") instanceof JsonObject auth)
215+
|| !(auth.get("offline") instanceof JsonObject offline)
216+
|| !(offline.get("uuidMap") instanceof JsonObject uuidMap)) {
217+
return;
218+
}
219+
220+
String selected = jsonObject.has("selectedAccount")
221+
? null
222+
: readString(offline, "IAuthenticator_UserName", null);
223+
JsonArray accounts = jsonObject.get("accounts") instanceof JsonArray array ? array : new JsonArray();
224+
for (Map.Entry<String, JsonElement> entry : uuidMap.entrySet()) {
225+
JsonObject storage = new JsonObject();
226+
storage.addProperty("type", "offline");
227+
storage.addProperty("username", entry.getKey());
228+
storage.add("uuid", entry.getValue());
229+
if (entry.getKey().equals(selected)) {
230+
storage.addProperty("selected", true);
231+
}
232+
accounts.add(storage);
233+
}
234+
jsonObject.add("accounts", accounts);
235+
}
236+
237+
/// Writes legacy profile preset references into profile JSON before profile deserialization.
238+
private static void migrateLegacyProfilePresetReferences(JsonObject object) {
239+
if (!(object.get("configurations") instanceof JsonObject configurations)) {
240+
return;
241+
}
242+
243+
for (Map.Entry<String, JsonElement> entry : configurations.entrySet()) {
244+
if (!(entry.getValue() instanceof JsonObject profileObject)
245+
|| profileObject.has("legacyGameSettingsParent")
246+
|| !(profileObject.get("global") instanceof JsonObject)) {
247+
continue;
248+
}
249+
250+
profileObject.addProperty("legacyGameSettingsParent",
251+
LegacyGameSettingsMigrator.getLegacyPresetId(entry.getKey()).toString());
252+
}
253+
}
254+
222255
/// Migrates profile-global game settings from HMCL 3.14.1 and older config files.
223256
private static void migrateLegacyPresetSettings(Config config, JsonObject object) {
224257
if (!(object.get("configurations") instanceof JsonObject configurations))
@@ -256,6 +289,14 @@ private static void migrateLegacyPresetSettings(Config config, JsonObject object
256289
}
257290
}
258291

292+
/// Reads a string field from a JSON object.
293+
private static @Nullable String readString(JsonObject object, String key, @Nullable String defaultValue) {
294+
JsonElement element = object.get(key);
295+
return element != null && element.isJsonPrimitive() && element.getAsJsonPrimitive().isString()
296+
? element.getAsString()
297+
: defaultValue;
298+
}
299+
259300
/// Result of loading a legacy config file.
260301
///
261302
/// @param config The parsed config object.

0 commit comments

Comments
 (0)