Build configuration DFU system. (#2589)
* Build configuration DFU system. * Remove redundant object allocation. * Fix * Code cleanup * Remove unnecessary GSON usage. * Fix compiletime unchecked warning.
This commit is contained in:
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.setting;
|
package org.jackhuang.hmcl.setting;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import org.jackhuang.hmcl.Metadata;
|
import org.jackhuang.hmcl.Metadata;
|
||||||
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
||||||
@@ -28,7 +27,6 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||||
@@ -167,8 +165,7 @@ public final class ConfigHolder {
|
|||||||
if (deserialized == null) {
|
if (deserialized == null) {
|
||||||
LOG.info("Config is empty");
|
LOG.info("Config is empty");
|
||||||
} else {
|
} else {
|
||||||
Map<?, ?> raw = new Gson().fromJson(content, Map.class);
|
ConfigUpgrader.upgradeConfig(deserialized, content);
|
||||||
ConfigUpgrader.upgradeConfig(deserialized, raw);
|
|
||||||
return deserialized;
|
return deserialized;
|
||||||
}
|
}
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
|
|||||||
@@ -17,96 +17,118 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.setting;
|
package org.jackhuang.hmcl.setting;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
|
import org.jackhuang.hmcl.util.Pair;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||||
|
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||||
|
|
||||||
final class ConfigUpgrader {
|
final class ConfigUpgrader {
|
||||||
private static final int VERSION = 0;
|
|
||||||
|
|
||||||
private ConfigUpgrader() {
|
private ConfigUpgrader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int CURRENT_VERSION = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is for the compatibility with old HMCL 3.x as well as HMCL 2.x.
|
* This method is for the compatibility with old HMCL versions.
|
||||||
*
|
*
|
||||||
* @param deserialized deserialized config settings
|
* @param deserialized deserialized config settings
|
||||||
* @param rawJson raw json structure of the config settings without modification
|
* @param rawContent raw json content of the config settings without modification
|
||||||
* @return true if config version is upgraded
|
|
||||||
*/
|
*/
|
||||||
static boolean upgradeConfig(Config deserialized, Map<?, ?> rawJson) {
|
static void upgradeConfig(Config deserialized, String rawContent) {
|
||||||
boolean upgraded;
|
if (deserialized.getConfigVersion() == CURRENT_VERSION) {
|
||||||
if (deserialized.getConfigVersion() < VERSION) {
|
return;
|
||||||
deserialized.setConfigVersion(VERSION);
|
|
||||||
// TODO: Add upgrade code here.
|
|
||||||
upgraded = true;
|
|
||||||
} else {
|
|
||||||
upgraded = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
upgradeV2(deserialized, rawJson);
|
int configVersion = deserialized.getConfigVersion();
|
||||||
upgradeV3(deserialized, rawJson);
|
if (configVersion > CURRENT_VERSION) {
|
||||||
|
LOG.log(Level.WARNING, String.format("Current HMCL only support the configuration version up to %d. However, the version now is %d.", CURRENT_VERSION, configVersion));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return upgraded;
|
LOG.log(Level.INFO, String.format("Updating configuration from %d to %d.", configVersion, CURRENT_VERSION));
|
||||||
|
Map<?, ?> unmodifiableRawJson = Collections.unmodifiableMap(new Gson().<Map<?, ?>>fromJson(rawContent, Map.class));
|
||||||
|
for (Map.Entry<Integer, BiConsumer<Config, Map<?, ?>>> dfu : collectDFU()) {
|
||||||
|
if (configVersion < dfu.getKey()) {
|
||||||
|
dfu.getValue().accept(deserialized, unmodifiableRawJson);
|
||||||
|
configVersion = dfu.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialized.setConfigVersion(CURRENT_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upgrade configuration of HMCL 2.x
|
* <p>Initialize the dfu of HMCL. Feel free to use lambda as all the lambda here would not be initialized unless HMCL needs to update the configuration.
|
||||||
|
* For each item in this list, it should be a Map.Entry.</p>
|
||||||
*
|
*
|
||||||
* @param deserialized deserialized config settings
|
* <p>The key should be a version number. All the configuration with a version number which is less than the specific one will be applied to this upgrader.</p>
|
||||||
* @param rawJson raw json structure of the config settings without modification
|
* <p>The value should be the upgrader. The configuration which is waited to being processed, and the raw unmodifiable value of the json from the configuration file.</p>
|
||||||
*/
|
* <p>The return value should a target version number of this item.</p>
|
||||||
private static void upgradeV2(Config deserialized, Map<?, ?> rawJson) {
|
|
||||||
// Convert OfflineAccounts whose stored uuid is important.
|
|
||||||
tryCast(rawJson.get("auth"), Map.class).ifPresent(auth -> {
|
|
||||||
tryCast(auth.get("offline"), Map.class).ifPresent(offline -> {
|
|
||||||
String selected = rawJson.containsKey("selectedAccount") ? null
|
|
||||||
: tryCast(offline.get("IAuthenticator_UserName"), String.class).orElse(null);
|
|
||||||
|
|
||||||
tryCast(offline.get("uuidMap"), Map.class).ifPresent(uuidMap -> {
|
|
||||||
((Map<?, ?>) uuidMap).forEach((key, value) -> {
|
|
||||||
Map<Object, Object> storage = new HashMap<>();
|
|
||||||
storage.put("type", "offline");
|
|
||||||
storage.put("username", key);
|
|
||||||
storage.put("uuid", value);
|
|
||||||
if (key.equals(selected)) {
|
|
||||||
storage.put("selected", true);
|
|
||||||
}
|
|
||||||
deserialized.getAccountStorages().add(storage);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upgrade configuration of HMCL earlier than 3.1.70
|
|
||||||
*
|
*
|
||||||
* @param deserialized deserialized config settings
|
* <p>The last item must return CURRENT_VERSION, as the config file should always being updated to the latest version.</p>
|
||||||
* @param rawJson raw json structure of the config settings without modification
|
|
||||||
*/
|
*/
|
||||||
private static void upgradeV3(Config deserialized, Map<?, ?> rawJson) {
|
private static List<Map.Entry<Integer, BiConsumer<Config, Map<?, ?>>>> collectDFU() {
|
||||||
if (!rawJson.containsKey("commonDirType"))
|
List<Map.Entry<Integer, BiConsumer<Config, Map<?, ?>>>> dfu = Lang.immutableListOf(
|
||||||
deserialized.setCommonDirType(deserialized.getCommonDirectory().equals(Settings.getDefaultCommonDirectory()) ? EnumCommonDirectory.DEFAULT : EnumCommonDirectory.CUSTOM);
|
Pair.pair(1, (deserialized, rawJson) -> {
|
||||||
if (!rawJson.containsKey("backgroundType"))
|
// Upgrade configuration of HMCL 2.x: Convert OfflineAccounts whose stored uuid is important.
|
||||||
deserialized.setBackgroundImageType(StringUtils.isNotBlank(deserialized.getBackgroundImage()) ? EnumBackgroundImage.CUSTOM : EnumBackgroundImage.DEFAULT);
|
tryCast(rawJson.get("auth"), Map.class).ifPresent(auth -> {
|
||||||
if (!rawJson.containsKey("hasProxy"))
|
tryCast(auth.get("offline"), Map.class).ifPresent(offline -> {
|
||||||
deserialized.setHasProxy(StringUtils.isNotBlank(deserialized.getProxyHost()));
|
String selected = rawJson.containsKey("selectedAccount") ? null
|
||||||
if (!rawJson.containsKey("hasProxyAuth"))
|
: tryCast(offline.get("IAuthenticator_UserName"), String.class).orElse(null);
|
||||||
deserialized.setHasProxyAuth(StringUtils.isNotBlank(deserialized.getProxyUser()));
|
|
||||||
|
|
||||||
if (!rawJson.containsKey("downloadType")) {
|
tryCast(offline.get("uuidMap"), Map.class).ifPresent(uuidMap -> {
|
||||||
tryCast(rawJson.get("downloadtype"), Number.class)
|
((Map<?, ?>) uuidMap).forEach((key, value) -> {
|
||||||
.map(Number::intValue)
|
Map<Object, Object> storage = new HashMap<>();
|
||||||
.ifPresent(id -> {
|
storage.put("type", "offline");
|
||||||
if (id == 0) {
|
storage.put("username", key);
|
||||||
deserialized.setDownloadType("mojang");
|
storage.put("uuid", value);
|
||||||
} else if (id == 1) {
|
if (key.equals(selected)) {
|
||||||
deserialized.setDownloadType("bmclapi");
|
storage.put("selected", true);
|
||||||
}
|
}
|
||||||
|
deserialized.getAccountStorages().add(storage);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Upgrade configuration of HMCL earlier than 3.1.70
|
||||||
|
if (!rawJson.containsKey("commonDirType"))
|
||||||
|
deserialized.setCommonDirType(deserialized.getCommonDirectory().equals(Settings.getDefaultCommonDirectory()) ? EnumCommonDirectory.DEFAULT : EnumCommonDirectory.CUSTOM);
|
||||||
|
if (!rawJson.containsKey("backgroundType"))
|
||||||
|
deserialized.setBackgroundImageType(StringUtils.isNotBlank(deserialized.getBackgroundImage()) ? EnumBackgroundImage.CUSTOM : EnumBackgroundImage.DEFAULT);
|
||||||
|
if (!rawJson.containsKey("hasProxy"))
|
||||||
|
deserialized.setHasProxy(StringUtils.isNotBlank(deserialized.getProxyHost()));
|
||||||
|
if (!rawJson.containsKey("hasProxyAuth"))
|
||||||
|
deserialized.setHasProxyAuth(StringUtils.isNotBlank(deserialized.getProxyUser()));
|
||||||
|
|
||||||
|
if (!rawJson.containsKey("downloadType")) {
|
||||||
|
tryCast(rawJson.get("downloadtype"), Number.class)
|
||||||
|
.map(Number::intValue)
|
||||||
|
.ifPresent(id -> {
|
||||||
|
if (id == 0) {
|
||||||
|
deserialized.setDownloadType("mojang");
|
||||||
|
} else if (id == 1) {
|
||||||
|
deserialized.setDownloadType("bmclapi");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (dfu.get(dfu.size() - 1).getKey() != CURRENT_VERSION) {
|
||||||
|
throw new IllegalStateException("The last dfu must adapt all the config version below CURRENT_VERSION");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return dfu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user