|
18 | 18 | package org.jackhuang.hmcl.setting; |
19 | 19 |
|
20 | 20 | import com.google.gson.JsonParseException; |
| 21 | +import com.google.gson.JsonArray; |
21 | 22 | import com.google.gson.JsonElement; |
22 | 23 | import com.google.gson.JsonObject; |
23 | 24 | import org.jackhuang.hmcl.Metadata; |
|
30 | 31 | import java.nio.file.Files; |
31 | 32 | import java.nio.file.Path; |
32 | 33 | import java.nio.file.Paths; |
33 | | -import java.util.HashMap; |
34 | 34 | import java.util.Map; |
35 | 35 | import java.util.UUID; |
36 | 36 |
|
37 | | -import static org.jackhuang.hmcl.util.Lang.tryCast; |
38 | 37 | import static org.jackhuang.hmcl.util.logging.Logger.LOG; |
39 | 38 |
|
40 | 39 | /// Migrates legacy per-workspace config files into the current settings.json file. |
@@ -68,22 +67,27 @@ private LegacyConfigMigrator() { |
68 | 67 | // a separate versioning scheme and must not depend on this numeric value. |
69 | 68 | int configVersion = getLegacyConfigVersion(jsonObject); |
70 | 69 |
|
| 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 | + |
71 | 84 | Config deserialized = Config.fromJson(jsonObject); |
72 | 85 | if (deserialized == null) { |
73 | 86 | return null; |
74 | 87 | } |
75 | 88 |
|
76 | 89 | 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); |
87 | 91 | } |
88 | 92 |
|
89 | 93 | /// 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) { |
164 | 168 | } |
165 | 169 | } |
166 | 170 |
|
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) { |
169 | 173 | LOG.info(String.format("Updating legacy configuration from %d to %d.", configVersion, LEGACY_CURRENT_CONFIG_VERSION)); |
170 | 174 | 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); |
193 | 176 |
|
194 | 177 | // 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()); |
197 | 183 | } |
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()); |
200 | 189 | } |
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", ""))); |
203 | 192 | } |
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", ""))); |
206 | 195 | } |
207 | 196 |
|
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 | + } |
218 | 208 | } |
219 | 209 | } |
220 | 210 | } |
221 | 211 |
|
| 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 | + |
222 | 255 | /// Migrates profile-global game settings from HMCL 3.14.1 and older config files. |
223 | 256 | private static void migrateLegacyPresetSettings(Config config, JsonObject object) { |
224 | 257 | if (!(object.get("configurations") instanceof JsonObject configurations)) |
@@ -256,6 +289,14 @@ private static void migrateLegacyPresetSettings(Config config, JsonObject object |
256 | 289 | } |
257 | 290 | } |
258 | 291 |
|
| 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 | + |
259 | 300 | /// Result of loading a legacy config file. |
260 | 301 | /// |
261 | 302 | /// @param config The parsed config object. |
|
0 commit comments