Implemented content based syncing

This commit is contained in:
Frank 2021-08-15 13:55:31 +02:00
parent f80b55aa50
commit 5df6de1e3a
7 changed files with 194 additions and 49 deletions

View file

@ -1,6 +1,5 @@
package ru.bclib.config;
import com.google.gson.JsonObject;
import org.jetbrains.annotations.Nullable;
import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.DataExchangeAPI;
@ -13,9 +12,7 @@ import ru.bclib.config.ConfigKeeper.FloatEntry;
import ru.bclib.config.ConfigKeeper.IntegerEntry;
import ru.bclib.config.ConfigKeeper.RangeEntry;
import ru.bclib.config.ConfigKeeper.StringEntry;
import ru.bclib.util.JsonFactory;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
@ -24,46 +21,46 @@ public abstract class Config {
protected final static Map<AutoSyncID, Config> autoSyncConfigs = new HashMap<>();
protected final ConfigKeeper keeper;
protected final boolean autoSync;
protected abstract void registerEntries();
protected Config(String modID, String group) {
this(modID, group, true, false);
}
protected Config(String modID, String group, boolean autoSync){
protected Config(String modID, String group, boolean autoSync) {
this(modID, group, autoSync, false);
}
protected Config(String modID, String group, boolean autoSync, boolean diffContent) {
BCLib.LOGGER.info("Registered Config " + modID+"."+group+" ("+autoSync+")");
BCLib.LOGGER.info("Registered Config " + modID + "." + group + " (" + autoSync + ")");
this.keeper = new ConfigKeeper(modID, group);
this.registerEntries();
this.autoSync = autoSync;
if (autoSync) {
final String uid = "CONFIG_" + modID + "_" + group;
final AutoSyncID aid = new AutoSyncID(BCLib.MOD_ID, uid);
DataExchangeAPI.addAutoSyncFile(aid.modID, aid.uniqueID, keeper.getConfigFile(), this::compareForSync );
DataExchangeAPI.addAutoSyncFile(aid.modID, aid.uniqueID, keeper.getConfigFile(), this::compareForSync);
autoSyncConfigs.put(aid, this);
}
}
private boolean compareForSync(FileHash fileHash, FileHash fileHash1, FileContentWrapper content) {
ByteArrayInputStream inputStream = content.getInputStream();
final JsonObject other = JsonFactory.getJsonObject(inputStream);
return this.keeper.compareAndUpdateForSync(other);
return keeper.compareAndUpdateForSync(content);
}
public void saveChanges() {
this.keeper.save();
}
public static void reloadSyncedConfig(AutoSyncID aid, File file){
Config cfg = autoSyncConfigs.get(aid);
if (cfg!=null) {
public static void reloadSyncedConfig(AutoSyncID aid, File file) {
Config cfg = autoSyncConfigs.get(aid);
if (cfg != null) {
cfg.reload();
}
}
public void reload() {
this.keeper.reload();
BCLib.LOGGER.info("Did Reload " + keeper.getConfigFile());
@ -200,6 +197,6 @@ public abstract class Config {
}
return false;
}
}

View file

@ -6,9 +6,13 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.minecraft.util.GsonHelper;
import org.jetbrains.annotations.Nullable;
import ru.bclib.api.dataexchange.handler.FileContentWrapper;
import ru.bclib.util.JsonFactory;
import ru.bclib.util.Pair;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.function.Consumer;
@ -25,24 +29,97 @@ public final class ConfigKeeper {
this.writer = new ConfigWriter(modID, group);
this.configObject = writer.load();
}
File getConfigFile(){
File getConfigFile() {
return this.writer.getConfigFile();
}
boolean compareAndUpdateForSync(JsonObject other) {
final JsonObject me = this.configObject;
return true;
boolean compareAndUpdateForSync(FileContentWrapper content) {
ByteArrayInputStream inputStream = content.getInputStream();
final JsonObject other = JsonFactory.getJsonObject(inputStream);
boolean changed = this.compareAndUpdateForSync(other);
if (changed) {
OutputStream outStream = content.getEmptyOutputStream();
JsonFactory.storeJson(outStream, this.configObject);
content.syncWithOutputStream();
}
return changed;
}
boolean compareAndUpdateForSync(JsonObject other) {
return compareAndUpdateForSync(this.configObject, other);
}
private static Pair<JsonElement, Pair<String, String>> find(JsonObject json, Pair<String, String> key) {
for (var entry : json.entrySet()) {
final Pair<String, String> otherKey = ConfigKey.realKey(entry.getKey());
if (otherKey.first.equals(key.first)) return new Pair<>(entry.getValue(), otherKey);
}
return null;
}
/**
* Called for content based auto-sync.
*
* @param me - When called in AutoSync this represents the content of the client.
* @param other - When called in AutoSync, this represents the content of the server
* @return {@code true} if content was changed
*/
static boolean compareAndUpdateForSync(JsonObject me, JsonObject other) {
boolean changed = false;
for (var otherEntry : other.entrySet()) {
final Pair<String, String> otherKey = ConfigKey.realKey(otherEntry.getKey());
final JsonElement otherValue = otherEntry.getValue();
Pair<JsonElement, Pair<String, String>> temp = find(me, otherKey);
//we already have an entry
if (temp != null) {
final Pair<String, String> myKey = temp.second;
final JsonElement myValue = temp.first;
if ((otherValue.isJsonNull() && !myValue.isJsonNull()) || (otherValue.isJsonPrimitive() && !myValue.isJsonPrimitive()) || (otherValue.isJsonObject() && !myValue.isJsonObject()) || (otherValue.isJsonArray() && !myValue.isJsonArray())) {
//types are different => replace with "server"-version in other
changed = true;
me.add(myKey.first + myKey.second, otherValue);
}
else if (otherValue.isJsonPrimitive()) {
if (!otherValue.equals(myValue)) {
changed = true;
me.add(myKey.first + myKey.second, otherValue);
}
}
else if (otherValue.isJsonObject()) {
changed |= compareAndUpdateForSync(myValue.getAsJsonObject(), otherValue.getAsJsonObject());
}
else if (otherValue.isJsonArray()) {
if (!otherValue.equals(myValue)) {
changed = true;
me.add(myKey.first + myKey.second, otherValue);
}
}
}
else { //no entry, just copy the value from other
changed = true;
me.add(otherKey.first + otherKey.second, otherValue);
}
}
return changed;
}
public void save() {
if (!changed) return;
this.writer.save();
this.changed = false;
}
void reload() {
this.configObject = this.writer.reload();
this.configEntries.clear();
this.changed = false;
}

View file

@ -1,6 +1,8 @@
package ru.bclib.config;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
import ru.bclib.util.Pair;
import java.util.Arrays;
@ -84,4 +86,15 @@ public class ConfigKey {
throw new IndexOutOfBoundsException("Config key must be not empty!");
}
}
public static Pair<String, String> realKey(@NotNull String key) {
String[] parts = key.split("\\[default:", 2);
if (parts.length == 1) {
return new Pair(parts[0].trim(), "");
}
else if (parts.length == 2) {
return new Pair(parts[0].trim(), " " + ("[default:" + parts[1]).trim());
}
return new Pair(key, "");
}
}