diff --git a/src/main/java/ru/bclib/api/dataexchange/DataExchangeAPI.java b/src/main/java/ru/bclib/api/dataexchange/DataExchangeAPI.java index 83d8ca53..e74a6b92 100644 --- a/src/main/java/ru/bclib/api/dataexchange/DataExchangeAPI.java +++ b/src/main/java/ru/bclib/api/dataexchange/DataExchangeAPI.java @@ -7,9 +7,14 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.minecraft.network.FriendlyByteBuf; +import ru.bclib.BCLib; import ru.bclib.api.dataexchange.handler.DataExchange; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -20,7 +25,10 @@ import java.util.function.Predicate; public class DataExchangeAPI extends DataExchange { private final static List MODS = Lists.newArrayList(); - protected DataExchangeAPI() { + /** + * You should never need to create a custom instance of this Object. + */ + public DataExchangeAPI() { super((api) -> new ConnectorClientside(api), (api) -> new ConnectorServerside(api)); } @@ -47,8 +55,8 @@ public class DataExchangeAPI extends DataExchange { * @param desc The Descriptor you want to add. */ public static void registerDescriptor(DataHandlerDescriptor desc){ - DataExchangeAPI api = DataExchange.getInstance(); - api.descriptors.add(desc); + DataExchange api = DataExchange.getInstance(); + api.getDescriptors().add(desc); } /** @@ -72,10 +80,58 @@ public class DataExchangeAPI extends DataExchange { /** * Registers a File for automatic client syncing. * - * @param needTransfer If the predicate returns true, the file needs to get copied to the server. + * @param modID The ID of the calling Mod * @param fileName The name of the File */ - public static void addAutoSyncFile(Predicate needTransfer, File fileName){ - DataExchangeAPI.getInstance().addAutoSyncFileData(needTransfer, fileName); + public static void addAutoSyncFile(String modID, File fileName){ + getInstance().addAutoSyncFileData(modID, fileName, false, FileHash.NEED_TRANSFER); + } + + /** + * Registers a File for automatic client syncing. + * + * @param modID The ID of the calling Mod + * @param uniqueID A unique Identifier for the File. (see {@link ru.bclib.api.dataexchange.FileHash#uniqueID} for + * Details + * @param fileName The name of the File + */ + public static void addAutoSyncFile(String modID, String uniqueID, File fileName){ + getInstance().addAutoSyncFileData(modID, uniqueID, fileName, false, FileHash.NEED_TRANSFER); + } + + /** + * Registers a File for automatic client syncing. + * + * @param modID The ID of the calling Mod + * @param fileName The name of the File + * @param requestContent When {@code true} the content of the file is requested for comparison. This will copy the + * entire file from the client to the server. + *

+ * You should only use this option, if you need to compare parts of the file in order to decide + * If the File needs to be copied. Normally using the {@link ru.bclib.api.dataexchange.FileHash} + * for comparison is sufficient. + * @param needTransfer If the predicate returns true, the file needs to get copied to the server. + */ + public static void addAutoSyncFile(String modID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer){ + getInstance().addAutoSyncFileData(modID, fileName, requestContent, needTransfer); + } + + /** + * Registers a File for automatic client syncing. + * + * @param modID The ID of the calling Mod + * @param uniqueID A unique Identifier for the File. (see {@link ru.bclib.api.dataexchange.FileHash#uniqueID} for + * Details + * @param fileName The name of the File + * @param requestContent When {@code true} the content of the file is requested for comparison. This will copy the + * entire file from the client to the server. + *

+ * You should only use this option, if you need to compare parts of the file in order to decide + * If the File needs to be copied. Normally using the {@link ru.bclib.api.dataexchange.FileHash} + * for comparison is sufficient. + * @param needTransfer If the predicate returns true, the file needs to get copied to the server. + */ + public static void addAutoSyncFile(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer){ + getInstance().addAutoSyncFileData(modID, uniqueID, fileName, requestContent, needTransfer); } } diff --git a/src/main/java/ru/bclib/api/dataexchange/DataHandler.java b/src/main/java/ru/bclib/api/dataexchange/DataHandler.java index fb11bd25..c9912843 100644 --- a/src/main/java/ru/bclib/api/dataexchange/DataHandler.java +++ b/src/main/java/ru/bclib/api/dataexchange/DataHandler.java @@ -48,16 +48,16 @@ public abstract class DataHandler { deserializeFromIncomingData(buf, responseSender, false); server.execute(() -> runOnGameThread(null, server, false)); } - + + protected void serializeData(FriendlyByteBuf buf) { + } + protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean isClient){ } protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient){ } - - protected void serializeData(FriendlyByteBuf buf) { - } - + final protected boolean reply(DataHandler message, MinecraftServer server){ if (lastMessageSender==null) return false; message.sendToClient(server, lastMessageSender); diff --git a/src/main/java/ru/bclib/api/dataexchange/FileHash.java b/src/main/java/ru/bclib/api/dataexchange/FileHash.java index 867aff49..44296b85 100644 --- a/src/main/java/ru/bclib/api/dataexchange/FileHash.java +++ b/src/main/java/ru/bclib/api/dataexchange/FileHash.java @@ -3,6 +3,7 @@ package ru.bclib.api.dataexchange; import net.minecraft.network.FriendlyByteBuf; import org.jetbrains.annotations.NotNull; import ru.bclib.BCLib; +import ru.bclib.api.dataexchange.handler.DataExchange; import java.io.File; import java.io.IOException; @@ -12,6 +13,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Objects; +import java.util.function.Predicate; /** * Calculates a hash based on the contents of a File. @@ -38,11 +40,47 @@ public class FileHash { */ public final int value; - FileHash(byte[] md5, int size, int value) { + /** + * A Unique ID for the referenced File. + *

+ * Files with the same {@link #modID} need to have a unique IDs. Normally the filename from {@link #FileHash(String, File, byte[], int, int)} + * is used to generated that ID, but you can directly specify one using {@link #FileHash(String, String, byte[], int, int)}. + */ + @NotNull + public final String uniqueID; + + /** + * The ID of the Mod that is registering the File + */ + @NotNull + public final String modID; + + FileHash(String modID, File file, byte[] md5, int size, int value) { + this(modID, file.getName(), md5, size, value); + } + + FileHash(String modID, String uniqueID, byte[] md5, int size, int value) { + Objects.nonNull(modID); + Objects.nonNull(uniqueID); Objects.nonNull(md5); + this.md5 = md5; this.size = size; this.value = value; + this.modID = modID; + this.uniqueID = uniqueID; + } + + private static int ERR_DOES_NOT_EXIST = -10; + private static int ERR_IO_ERROR = -20; + static FileHash createForEmpty(String modID, String uniqueID, int errCode){ + return new FileHash(modID, uniqueID, new byte[0], 0, errCode); + } + + final static DataExchange.NeedTransferPredicate NEED_TRANSFER = (clientHash, serverHash, content)-> !clientHash.equals(serverHash); + + public boolean noFile() { + return md5.length == 0; } @Override @@ -58,13 +96,13 @@ public class FileHash { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - FileHash that = (FileHash) o; - return size == that.size && value == that.value && Arrays.equals(md5, that.md5); + FileHash fileHash = (FileHash) o; + return size == fileHash.size && value == fileHash.value && Arrays.equals(md5, fileHash.md5) && uniqueID.equals(fileHash.uniqueID) && modID.equals(fileHash.modID); } @Override public int hashCode() { - int result = Objects.hash(size, value); + int result = Objects.hash(size, value, uniqueID, modID); result = 31 * result + Arrays.hashCode(md5); return result; } @@ -76,14 +114,17 @@ public class FileHash { public String getMd5String(){ return toHexString(md5); } + /** * Serializes the Object to a buffer * @param buf The buffer to write to */ - public void writeString(FriendlyByteBuf buf) { + public void serialize(FriendlyByteBuf buf) { buf.writeInt(size); buf.writeInt(value); buf.writeByteArray(md5); + DataHandler.writeString(buf, modID); + DataHandler.writeString(buf, uniqueID); } /** @@ -91,12 +132,14 @@ public class FileHash { * @param buf Thea buffer to read from * @return The received String */ - public static FileHash readString(FriendlyByteBuf buf){ + public static FileHash deserialize(FriendlyByteBuf buf){ final int size = buf.readInt(); final int value = buf.readInt(); final byte[] md5 = buf.readByteArray(); + final String modID = DataHandler.readString(buf); + final String uniqueID = DataHandler.readString(buf); - return new FileHash(md5, size, value); + return new FileHash(modID, uniqueID, md5, size, value); } /** @@ -116,12 +159,28 @@ public class FileHash { /** * Create a new {@link FileHash}. + *

+ * Will call {@link #create(String, File, String)} using the name of the File as {@code uniqueID}. + * @param modID ID of the calling Mod * @param file The input file + * * @return A new Instance. You can compare instances using {@link #equals(Object)} to determine if two files are - * identical. + * identical. Will return {@code null} when an error occurs or the File does not exist */ - public static FileHash createFromBinary(File file){ - if (!file.exists()) return null; + public static FileHash create(String modID, File file){ + return create(modID, file, file.getName()); + } + + /** + * Create a new {@link FileHash}. + * @param modID ID of the calling Mod + * @param file The input file + * @param uniqueID The unique ID that is used for this File (see {@link FileHash#uniqueID} for Details. + * @return A new Instance. You can compare instances using {@link #equals(Object)} to determine if two files are + * identical. Will return {@code null} when an error occurs or the File does not exist + */ + public static FileHash create(String modID, File file, String uniqueID){ + if (!file.exists()) return createForEmpty(modID, uniqueID, ERR_DOES_NOT_EXIST); final Path path = file.toPath(); int size = 0; @@ -140,12 +199,14 @@ public class FileHash { md.update(data); md5 = md.digest(); - return new FileHash(md5, size, value); + return new FileHash(modID, uniqueID, md5, size, value); } catch (IOException e) { BCLib.LOGGER.error("Failed to read file: " + file); return null; } catch (NoSuchAlgorithmException e) { BCLib.LOGGER.error("Unable to build hash for file: " + file); } + + return createForEmpty(modID, uniqueID, ERR_IO_ERROR); } } diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/DataExchange.java b/src/main/java/ru/bclib/api/dataexchange/handler/DataExchange.java index 138aedc9..c0b87c11 100644 --- a/src/main/java/ru/bclib/api/dataexchange/handler/DataExchange.java +++ b/src/main/java/ru/bclib/api/dataexchange/handler/DataExchange.java @@ -5,29 +5,117 @@ import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; +import net.minecraft.network.FriendlyByteBuf; import ru.bclib.api.dataexchange.ConnectorClientside; import ru.bclib.api.dataexchange.ConnectorServerside; import ru.bclib.api.dataexchange.DataExchangeAPI; import ru.bclib.api.dataexchange.DataHandler; import ru.bclib.api.dataexchange.DataHandlerDescriptor; +import ru.bclib.api.dataexchange.FileHash; +import ru.bclib.util.Pair; +import ru.bclib.util.Triple; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.Supplier; abstract public class DataExchange { - private static class AutoFileSyncEntry { - public final Predicate needTransfer; - public final File fileName; + @FunctionalInterface + public interface NeedTransferPredicate { + public boolean test(FileHash clientHash, FileHash serverHash, byte[] content); + } - private AutoFileSyncEntry(Predicate needTransfer, File fileName) { + final static class AutoSyncTriple extends Triple{ + public AutoSyncTriple(FileHash first, byte[] second, AutoFileSyncEntry third) { + super(first, second, third); + } + } + static class AutoFileSyncEntry { + public final NeedTransferPredicate needTransfer; + public final File fileName; + public final String modID; + public final String uniqueID; + public final boolean requestContent; + private FileHash hash; + + AutoFileSyncEntry(String modID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) { + this(modID, fileName.getName(), fileName, requestContent, needTransfer); + } + + AutoFileSyncEntry(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) { this.needTransfer = needTransfer; this.fileName = fileName; + this.modID = modID; + this.uniqueID = uniqueID; + this.requestContent = requestContent; + } + + public FileHash getFileHash(){ + if (hash == null) { + hash = FileHash.create(modID, fileName, uniqueID); + } + return hash; + } + + public byte[] getContent(){ + if (!fileName.exists()) return new byte[0]; + final Path path = fileName.toPath(); + + try { + return Files.readAllBytes(path); + } catch (IOException e) { + + } + return new byte[0]; + } + + + public void serialize(FriendlyByteBuf buf){ + getFileHash().serialize(buf); + buf.writeBoolean(requestContent); + + if (requestContent) { + byte[] content = getContent(); + buf.writeInt(content.length); + buf.writeByteArray(content); + } + } + + public static AutoSyncTriple deserializeAndMatch(FriendlyByteBuf buf){ + Pair e = deserialize(buf); + AutoFileSyncEntry match = findMatching(e.first); + return new AutoSyncTriple(e.first, e.second, match); + } + + public static Pair deserialize(FriendlyByteBuf buf){ + FileHash hash = FileHash.deserialize(buf); + boolean withContent = buf.readBoolean(); + byte[] data = null; + if (withContent) { + int size = buf.readInt(); + data = buf.readByteArray(size); + } + + return new Pair(hash, data); + } + + public static AutoFileSyncEntry findMatching(FileHash hash){ + return DataExchange + .getInstance() + .autoSyncFiles + .stream() + .filter(asf -> asf.modID.equals(hash.modID) && asf.uniqueID.equals(hash.uniqueID)) + .findFirst() + .orElse(null); } } @@ -85,8 +173,8 @@ abstract public class DataExchange { * This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called */ @Environment(EnvType.CLIENT) - public void prepareClientside(){ - DataExchangeAPI api = DataExchangeAPI.getInstance(); + public static void prepareClientside(){ + DataExchange api = DataExchange.getInstance(); api.initClientside(); } @@ -97,7 +185,7 @@ abstract public class DataExchange { * This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called */ public static void prepareServerside(){ - DataExchange api = DataExchangeAPI.getInstance(); + DataExchange api = DataExchange.getInstance(); api.initServerSide(); } @@ -124,10 +212,36 @@ abstract public class DataExchange { /** * Registers a File for automatic client syncing. * + * @param modID The ID of the calling Mod * @param needTransfer If the predicate returns true, the file needs to get copied to the server. * @param fileName The name of the File + * @param requestContent When {@code true} the content of the file is requested for comparison. This will copy the + * entire file from the client to the server. + *

+ * You should only use this option, if you need to compare parts of the file in order to decide + * If the File needs to be copied. Normally using the {@link ru.bclib.api.dataexchange.FileHash} + * for comparison is sufficient. */ - protected void addAutoSyncFileData(Predicate needTransfer, File fileName){ - autoSyncFiles.add(new AutoFileSyncEntry(needTransfer, fileName)); + protected void addAutoSyncFileData(String modID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer){ + autoSyncFiles.add(new AutoFileSyncEntry(modID, fileName, requestContent, needTransfer)); + } + + /** + * Registers a File for automatic client syncing. + * + * @param modID The ID of the calling Mod + * @param uniqueID A unique Identifier for the File. (see {@link ru.bclib.api.dataexchange.FileHash#uniqueID} for + * Details + * @param needTransfer If the predicate returns true, the file needs to get copied to the server. + * @param fileName The name of the File + * @param requestContent When {@code true} the content of the file is requested for comparison. This will copy the + * entire file from the client to the server. + *

+ * You should only use this option, if you need to compare parts of the file in order to decide + * If the File needs to be copied. Normally using the {@link ru.bclib.api.dataexchange.FileHash} + * for comparison is sufficient. + */ + protected void addAutoSyncFileData(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer){ + autoSyncFiles.add(new AutoFileSyncEntry(modID, uniqueID, fileName, requestContent, needTransfer)); } } diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/HelloClient.java b/src/main/java/ru/bclib/api/dataexchange/handler/HelloClient.java index 6bdccd89..758ed4ae 100644 --- a/src/main/java/ru/bclib/api/dataexchange/handler/HelloClient.java +++ b/src/main/java/ru/bclib/api/dataexchange/handler/HelloClient.java @@ -7,7 +7,6 @@ import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.worldselection.EditWorldScreen; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; @@ -15,10 +14,12 @@ import ru.bclib.BCLib; import ru.bclib.api.dataexchange.DataExchangeAPI; import ru.bclib.api.dataexchange.DataHandler; import ru.bclib.api.dataexchange.DataHandlerDescriptor; +import ru.bclib.api.dataexchange.FileHash; import ru.bclib.api.datafixer.DataFixerAPI; -import ru.bclib.gui.screens.ConfirmFixScreen; import ru.bclib.gui.screens.WarnBCLibVersionMismatch; +import ru.bclib.util.Triple; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,6 +27,11 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.function.Consumer; +/** + * Sent from the Server to the Client. + *

+ * For Details refer to {@link HelloServer} + */ public class HelloClient extends DataHandler { public static DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "hello_client"), HelloClient::new, false, false); @@ -45,20 +51,56 @@ public class HelloClient extends DataHandler { static String getBCLibVersion(){ return getModVersion(BCLib.MOD_ID); } + + @Override + protected void serializeData(FriendlyByteBuf buf) { + final List mods = DataExchangeAPI.registeredMods(); + + //write BCLibVersion (=protocol version) + buf.writeInt(DataFixerAPI.getModVersion(getBCLibVersion())); + + //write Plugin Versions + buf.writeInt(mods.size()); + for (String modID : mods) { + writeString(buf, modID); + buf.writeInt(DataFixerAPI.getModVersion(getModVersion(modID))); + } + + //send config Data + final List autoSyncFiles = DataExchange.getInstance().autoSyncFiles; + buf.writeInt(autoSyncFiles.size()); + for (DataExchange.AutoFileSyncEntry entry : autoSyncFiles) { + System.out.println("Serializing " + entry.getFileHash()); + entry.serialize(buf); + } + } String bclibVersion ="0.0.0"; Map modVersion = new HashMap<>(); + List autoSyncedFiles = null; @Override protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) { + //read BCLibVersion (=protocol version) bclibVersion = DataFixerAPI.getModVersion(buf.readInt()); + + //read Plugin Versions modVersion = new HashMap<>(); - int count = buf.readInt(); - for (int i=0; i< count; i++){ + for (int i=0; i< count; i++) { String id = readString(buf); String version = DataFixerAPI.getModVersion(buf.readInt()); modVersion.put(id, version); } + + //read config Data + count = buf.readInt(); + autoSyncedFiles = new ArrayList<>(count); + for (int i=0; i< count; i++) { + System.out.println("Deserializing "); + DataExchange.AutoSyncTriple t = DataExchange.AutoFileSyncEntry.deserializeAndMatch(buf); + autoSyncedFiles.add(t); + System.out.println(t.first); + } } @Override @@ -75,17 +117,13 @@ public class HelloClient extends DataHandler { String ver = getModVersion(e.getKey()); BCLib.LOGGER.info(" - " + e.getKey() + " (client="+ver+", server="+ver+")"); } - } - - @Override - protected void serializeData(FriendlyByteBuf buf) { - final List mods = DataExchangeAPI.registeredMods(); - buf.writeInt(DataFixerAPI.getModVersion(getBCLibVersion())); - - buf.writeInt(mods.size()); - for (String modID : mods) { - writeString(buf, modID); - buf.writeInt(DataFixerAPI.getModVersion(getModVersion(modID))); + + for (DataExchange.AutoSyncTriple e : autoSyncedFiles) { + if (e.third == null) { + BCLib.LOGGER.info(" - File " + e.first.modID + "." + e.first.uniqueID + ": Does not exist on client."); + } else if (e.third.needTransfer.test(e.third.getFileHash(), e.first, e.second)) { + BCLib.LOGGER.info(" - File " + e.first.modID + "." + e.first.uniqueID + ": Needs Transfer"); + } } } diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/HelloServer.java b/src/main/java/ru/bclib/api/dataexchange/handler/HelloServer.java index 481139d5..411de2b1 100644 --- a/src/main/java/ru/bclib/api/dataexchange/handler/HelloServer.java +++ b/src/main/java/ru/bclib/api/dataexchange/handler/HelloServer.java @@ -10,6 +10,34 @@ import ru.bclib.api.dataexchange.DataHandler; import ru.bclib.api.dataexchange.DataHandlerDescriptor; import ru.bclib.api.datafixer.DataFixerAPI; +/** + * This message is sent once a player enters the world. It initiates a sequence of Messages that will sync files between both + * client and server. + *

+ * + * + * Server + * + * Client + * + * + * + * Player enters World + * + * + * + * <-- + * {@link HelloServer} + * Sends the current BLib-Version installed on the Client + * + * + * {@link HelloClient} + * --> + * + * Sends the current BClIb-Version and the Version of all Plugins on the Server + * + * + */ public class HelloServer extends DataHandler { public static DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "hello_server"), HelloServer::new, false, true); @@ -17,6 +45,11 @@ public class HelloServer extends DataHandler { public HelloServer() { super(DESCRIPTOR.IDENTIFIER, false); } + + @Override + protected void serializeData(FriendlyByteBuf buf) { + buf.writeInt(DataFixerAPI.getModVersion(HelloClient.getBCLibVersion())); + } @Override protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) { @@ -29,9 +62,4 @@ public class HelloServer extends DataHandler { BCLib.LOGGER.info("Received Hello from Client. (server="+localBclibVersion+", client="+bclibVersion+")"); reply(new HelloClient(), server); } - - @Override - protected void serializeData(FriendlyByteBuf buf) { - buf.writeInt(DataFixerAPI.getModVersion(HelloClient.getBCLibVersion())); - } } diff --git a/src/main/java/ru/bclib/config/Config.java b/src/main/java/ru/bclib/config/Config.java index d49d802f..d2280eb9 100644 --- a/src/main/java/ru/bclib/config/Config.java +++ b/src/main/java/ru/bclib/config/Config.java @@ -26,7 +26,7 @@ public abstract class Config { this.registerEntries(); this.autoSync = autoSync; - DataExchangeAPI.addAutoSyncFile((content)->{return false;}, keeper.getConfigFile()); + DataExchangeAPI.addAutoSyncFile(BCLib.MOD_ID, "CONFIG_"+modID+"_"+group, keeper.getConfigFile()); } public void saveChanges() { diff --git a/src/main/java/ru/bclib/util/Pair.java b/src/main/java/ru/bclib/util/Pair.java new file mode 100644 index 00000000..61ceb6eb --- /dev/null +++ b/src/main/java/ru/bclib/util/Pair.java @@ -0,0 +1,11 @@ +package ru.bclib.util; + +public class Pair { + public final A first; + public final B second; + + public Pair(A first, B second) { + this.first = first; + this.second = second; + } +} diff --git a/src/main/java/ru/bclib/util/Triple.java b/src/main/java/ru/bclib/util/Triple.java new file mode 100644 index 00000000..df88cf75 --- /dev/null +++ b/src/main/java/ru/bclib/util/Triple.java @@ -0,0 +1,10 @@ +package ru.bclib.util; + +public class Triple extends Pair{ + public final C third; + + public Triple(A first, B second, C third) { + super(first, second); + this.third = third; + } +}