diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/FileContentWrapper.java b/src/main/java/ru/bclib/api/dataexchange/handler/FileContentWrapper.java index e07b8a7b..f95b83fd 100644 --- a/src/main/java/ru/bclib/api/dataexchange/handler/FileContentWrapper.java +++ b/src/main/java/ru/bclib/api/dataexchange/handler/FileContentWrapper.java @@ -10,7 +10,7 @@ public class FileContentWrapper { private byte[] rawContent; private ByteArrayOutputStream outputStream; - FileContentWrapper(byte[] content){ + FileContentWrapper(byte[] content) { this.rawContent = content; this.outputStream = null; } @@ -20,14 +20,14 @@ public class FileContentWrapper { } public byte[] getRawContent() { - if (outputStream!=null){ + if (outputStream != null) { return outputStream.toByteArray(); } return rawContent; } - private void invalidateOutputStream(){ - if (this.outputStream!=null){ + private void invalidateOutputStream() { + if (this.outputStream != null) { try { this.outputStream.close(); } @@ -43,25 +43,33 @@ public class FileContentWrapper { invalidateOutputStream(); } - public void syncWithOutputStream(){ - if (outputStream!=null){ + public void syncWithOutputStream() { + if (outputStream != null) { + try { + outputStream.flush(); + } + catch (IOException e) { + BCLib.LOGGER.error(e.getMessage()); + e.printStackTrace(); + } setRawContent(getRawContent()); + invalidateOutputStream(); } } - public ByteArrayInputStream getInputStream(){ - if (rawContent==null) return new ByteArrayInputStream(new byte[0]); + public ByteArrayInputStream getInputStream() { + if (rawContent == null) return new ByteArrayInputStream(new byte[0]); return new ByteArrayInputStream(rawContent); } - public ByteArrayOutputStream getOrCreateOutputStream(){ - if (this.outputStream == null){ + public ByteArrayOutputStream getOrCreateOutputStream() { + if (this.outputStream == null) { return this.getEmptyOutputStream(); } return this.outputStream; } - public ByteArrayOutputStream getEmptyOutputStream(){ + public ByteArrayOutputStream getEmptyOutputStream() { invalidateOutputStream(); this.outputStream = new ByteArrayOutputStream(this.rawContent.length); return this.outputStream; diff --git a/src/main/java/ru/bclib/config/Config.java b/src/main/java/ru/bclib/config/Config.java index 344e5463..4a5925c7 100644 --- a/src/main/java/ru/bclib/config/Config.java +++ b/src/main/java/ru/bclib/config/Config.java @@ -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 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; } - - + + } diff --git a/src/main/java/ru/bclib/config/ConfigKeeper.java b/src/main/java/ru/bclib/config/ConfigKeeper.java index 2417e65f..ed255b55 100644 --- a/src/main/java/ru/bclib/config/ConfigKeeper.java +++ b/src/main/java/ru/bclib/config/ConfigKeeper.java @@ -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> find(JsonObject json, Pair key) { + for (var entry : json.entrySet()) { + final Pair 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 otherKey = ConfigKey.realKey(otherEntry.getKey()); + final JsonElement otherValue = otherEntry.getValue(); + + Pair> temp = find(me, otherKey); + //we already have an entry + if (temp != null) { + final Pair 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; } diff --git a/src/main/java/ru/bclib/config/ConfigKey.java b/src/main/java/ru/bclib/config/ConfigKey.java index 0c770a3e..5a1663c8 100644 --- a/src/main/java/ru/bclib/config/ConfigKey.java +++ b/src/main/java/ru/bclib/config/ConfigKey.java @@ -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 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, ""); + } } diff --git a/src/main/java/ru/bclib/util/JsonFactory.java b/src/main/java/ru/bclib/util/JsonFactory.java index f0d13e44..185c0b24 100644 --- a/src/main/java/ru/bclib/util/JsonFactory.java +++ b/src/main/java/ru/bclib/util/JsonFactory.java @@ -24,7 +24,8 @@ import java.io.OutputStreamWriter; import java.io.Reader; public class JsonFactory { - public final static Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + public final static Gson GSON = new GsonBuilder().setPrettyPrinting() + .create(); public static JsonObject getJsonObject(InputStream stream) { try { @@ -61,7 +62,8 @@ public class JsonFactory { @Nullable @Environment(EnvType.CLIENT) public static JsonObject getJsonObject(ResourceLocation location) { - ResourceManager manager = Minecraft.getInstance().getResourceManager(); + ResourceManager manager = Minecraft.getInstance() + .getResourceManager(); JsonObject obj = null; try { Resource resource = manager.getResource(location); @@ -108,6 +110,13 @@ public class JsonFactory { public static void storeJson(OutputStream outStream, JsonElement jsonObject) { OutputStreamWriter writer = new OutputStreamWriter(outStream); GSON.toJson(jsonObject, writer); + try { + writer.flush(); + } + catch (IOException e) { + BCLib.LOGGER.error(e.getMessage()); + e.printStackTrace(); + } } public static int getInt(JsonObject object, String member, int def) { diff --git a/src/main/java/ru/bclib/util/Pair.java b/src/main/java/ru/bclib/util/Pair.java index 61ceb6eb..c542ac09 100644 --- a/src/main/java/ru/bclib/util/Pair.java +++ b/src/main/java/ru/bclib/util/Pair.java @@ -1,11 +1,31 @@ package ru.bclib.util; -public class Pair { - public final A first; - public final B second; +import java.util.Objects; - public Pair(A first, B second) { - this.first = first; - this.second = second; - } +public class Pair { + public final A first; + public final B second; + + public Pair(A first, B second) { + this.first = first; + this.second = second; + } + + @Override + public String toString() { + return "Pair{" + "first=" + first + ", second=" + second + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Pair)) return false; + Pair pair = (Pair) o; + return Objects.equals(first, pair.first) && Objects.equals(second, pair.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } } diff --git a/src/main/java/ru/bclib/util/Triple.java b/src/main/java/ru/bclib/util/Triple.java index df88cf75..a40f1a90 100644 --- a/src/main/java/ru/bclib/util/Triple.java +++ b/src/main/java/ru/bclib/util/Triple.java @@ -1,10 +1,31 @@ package ru.bclib.util; -public class Triple extends Pair{ - public final C third; +import java.util.Objects; - public Triple(A first, B second, C third) { - super(first, second); - this.third = third; - } +public class Triple extends Pair { + public final C third; + + public Triple(A first, B second, C third) { + super(first, second); + this.third = third; + } + + @Override + public String toString() { + return "Triple{" + "first=" + first + ", second=" + second + ", third=" + third + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Triple)) return false; + if (!super.equals(o)) return false; + Triple triple = (Triple) o; + return Objects.equals(third, triple.third); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), third); + } }