[Ready for Testing] Folder-Syncing
This commit is contained in:
parent
9cd0ef1f04
commit
0b9d6093a0
11 changed files with 1034 additions and 608 deletions
|
@ -98,22 +98,22 @@ public class DataExchangeAPI extends DataExchange {
|
||||||
* @param fileName The name of the File
|
* @param fileName The name of the File
|
||||||
*/
|
*/
|
||||||
public static void addAutoSyncFile(String modID, File fileName) {
|
public static void addAutoSyncFile(String modID, File fileName) {
|
||||||
getInstance().addAutoSyncFileData(modID, fileName, false, FileHash.NEED_TRANSFER);
|
getInstance().addAutoSyncFileData(modID, fileName, false, SyncFileHash.NEED_TRANSFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a File for automatic client syncing.
|
* Registers a File for automatic client syncing.
|
||||||
* <p>
|
* <p>
|
||||||
* The file is synced of the {@link FileHash} on client and server are not equal. This method will not copy the
|
* The file is synced of the {@link SyncFileHash} on client and server are not equal. This method will not copy the
|
||||||
* configs content from the client to the server.
|
* configs content from the client to the server.
|
||||||
*
|
*
|
||||||
* @param modID The ID of the calling Mod
|
* @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
|
* @param uniqueID A unique Identifier for the File. (see {@link SyncFileHash#uniqueID} for
|
||||||
* Details
|
* Details
|
||||||
* @param fileName The name of the File
|
* @param fileName The name of the File
|
||||||
*/
|
*/
|
||||||
public static void addAutoSyncFile(String modID, String uniqueID, File fileName) {
|
public static void addAutoSyncFile(String modID, String uniqueID, File fileName) {
|
||||||
getInstance().addAutoSyncFileData(modID, uniqueID, fileName, false, FileHash.NEED_TRANSFER);
|
getInstance().addAutoSyncFileData(modID, uniqueID, fileName, false, SyncFileHash.NEED_TRANSFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,7 +123,7 @@ public class DataExchangeAPI extends DataExchange {
|
||||||
* entire file from the client to the server.
|
* entire file from the client to the server.
|
||||||
* <p>
|
* <p>
|
||||||
* You should only use this option, if you need to compare parts of the file in order to decide
|
* 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}
|
* if the File needs to be copied. Normally using the {@link SyncFileHash}
|
||||||
* for comparison is sufficient.
|
* for comparison is sufficient.
|
||||||
*
|
*
|
||||||
* @param modID The ID of the calling Mod
|
* @param modID The ID of the calling Mod
|
||||||
|
@ -141,11 +141,11 @@ public class DataExchangeAPI extends DataExchange {
|
||||||
* entire file from the client to the server.
|
* entire file from the client to the server.
|
||||||
* <p>
|
* <p>
|
||||||
* You should only use this option, if you need to compare parts of the file in order to decide
|
* 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}
|
* if the File needs to be copied. Normally using the {@link SyncFileHash}
|
||||||
* for comparison is sufficient.
|
* for comparison is sufficient.
|
||||||
*
|
*
|
||||||
* @param modID The ID of the calling Mod
|
* @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
|
* @param uniqueID A unique Identifier for the File. (see {@link SyncFileHash#uniqueID} for
|
||||||
* Details
|
* Details
|
||||||
* @param fileName The name of the File
|
* @param fileName The name of the File
|
||||||
* @param needTransfer If the predicate returns true, the file needs to get copied to the server.
|
* @param needTransfer If the predicate returns true, the file needs to get copied to the server.
|
||||||
|
@ -174,14 +174,14 @@ public class DataExchangeAPI extends DataExchange {
|
||||||
* @param modID ID of the Mod
|
* @param modID ID of the Mod
|
||||||
* @return The path to the sync-folder
|
* @return The path to the sync-folder
|
||||||
*/
|
*/
|
||||||
public static File getSyncFolder(String modID) {
|
public static File getModSyncFolder(String modID) {
|
||||||
File fl = SYNC_FOLDER.resolve(modID.replace(".", "-")
|
File fl = SYNC_FOLDER.localFolder.resolve(modID.replace(".", "-")
|
||||||
.replace(":", "-")
|
.replace(":", "-")
|
||||||
.replace("\\", "-")
|
.replace("\\", "-")
|
||||||
.replace("/", "-"))
|
.replace("/", "-"))
|
||||||
.toFile();
|
.toFile();
|
||||||
|
|
||||||
if (!fl.exists()){
|
if (!fl.exists()) {
|
||||||
fl.mkdirs();
|
fl.mkdirs();
|
||||||
}
|
}
|
||||||
return fl;
|
return fl;
|
||||||
|
|
|
@ -3,8 +3,6 @@ package ru.bclib.api.dataexchange;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import ru.bclib.BCLib;
|
import ru.bclib.BCLib;
|
||||||
import ru.bclib.api.dataexchange.handler.AutoSyncID;
|
|
||||||
import ru.bclib.api.dataexchange.handler.DataExchange;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -15,182 +13,149 @@ import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
public class FileHash {
|
||||||
* Calculates a hash based on the contents of a File.
|
private static int ERR_DOES_NOT_EXIST = -10;
|
||||||
* <p>
|
private static int ERR_IO_ERROR = -20;
|
||||||
* A File-Hash contains the md5-sum of the File, as well as its size and byte-values from defined positions
|
|
||||||
* <p>
|
/**
|
||||||
* You can compare instances using {@link #equals(Object)} to determine if two files are
|
* The md5-hash of the file
|
||||||
* identical.
|
*/
|
||||||
*/
|
@NotNull
|
||||||
public class FileHash extends AutoSyncID {
|
public final byte[] md5;
|
||||||
/**
|
|
||||||
* The md5-hash of the file
|
/**
|
||||||
*/
|
* The size (in bytes) of the input.
|
||||||
@NotNull
|
*/
|
||||||
public final byte[] md5;
|
public final int size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The size (in bytes) of the input.
|
* a value that is directly calculated from defined byte positions.
|
||||||
*/
|
*/
|
||||||
public final int size;
|
public final int value;
|
||||||
|
|
||||||
/**
|
FileHash(byte[] md5, int size, int value) {
|
||||||
* a value that is directly calculated from defined byte positions.
|
Objects.nonNull(md5);
|
||||||
*/
|
|
||||||
public final int value;
|
this.md5 = md5;
|
||||||
|
this.size = size;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
FileHash(String modID, File file, byte[] md5, int size, int value) {
|
|
||||||
this(modID, file.getName(), md5, size, value);
|
static FileHash createForEmpty(int errCode) {
|
||||||
}
|
return new FileHash(new byte[0], 0, errCode);
|
||||||
|
}
|
||||||
FileHash(String modID, String uniqueID, byte[] md5, int size, int value) {
|
|
||||||
super(modID, uniqueID);
|
public boolean noFile() {
|
||||||
Objects.nonNull(md5);
|
return md5.length == 0;
|
||||||
|
}
|
||||||
this.md5 = md5;
|
|
||||||
this.size = size;
|
/**
|
||||||
this.value = value;
|
* Serializes the Object to a buffer
|
||||||
}
|
*
|
||||||
|
* @param buf The buffer to write to
|
||||||
private static int ERR_DOES_NOT_EXIST = -10;
|
*/
|
||||||
private static int ERR_IO_ERROR = -20;
|
public void serialize(FriendlyByteBuf buf) {
|
||||||
static FileHash createForEmpty(String modID, String uniqueID, int errCode){
|
buf.writeInt(size);
|
||||||
return new FileHash(modID, uniqueID, new byte[0], 0, errCode);
|
buf.writeInt(value);
|
||||||
}
|
buf.writeByteArray(md5);
|
||||||
|
}
|
||||||
final static DataExchange.NeedTransferPredicate NEED_TRANSFER = (clientHash, serverHash, content)-> !clientHash.equals(serverHash);
|
|
||||||
|
/**
|
||||||
public boolean noFile() {
|
* Deserialize a Buffer to a new {@link SyncFileHash}-Object
|
||||||
return md5.length == 0;
|
*
|
||||||
}
|
* @param buf Thea buffer to read from
|
||||||
|
* @return The received String
|
||||||
@Override
|
*/
|
||||||
public String toString() {
|
public static FileHash deserialize(FriendlyByteBuf buf) {
|
||||||
return super.toString()+": "+String.format("%08x", size)
|
final int size = buf.readInt();
|
||||||
+ "-"
|
final int value = buf.readInt();
|
||||||
+ String.format("%08x", value)
|
final byte[] md5 = buf.readByteArray();
|
||||||
+ "-"
|
|
||||||
+ getMd5String();
|
return new FileHash(md5, size, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean equals(Object o) {
|
* Convert the md5-hash to a human readable string
|
||||||
if (this == o) return true;
|
*
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
* @return The converted String
|
||||||
FileHash fileHash = (FileHash) o;
|
*/
|
||||||
return size == fileHash.size && value == fileHash.value && Arrays.equals(md5, fileHash.md5) && uniqueID.equals(fileHash.uniqueID) && modID.equals(fileHash.modID);
|
public String getMd5String() {
|
||||||
}
|
return toHexString(md5);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
/**
|
||||||
int result = Objects.hash(size, value, uniqueID, modID);
|
* Converts a byte-array to a hex-string representation
|
||||||
result = 31 * result + Arrays.hashCode(md5);
|
*
|
||||||
return result;
|
* @param bytes The source array
|
||||||
}
|
* @return The resulting string, or an empty String if the input was {@code null}
|
||||||
|
*/
|
||||||
/**
|
public static String toHexString(byte[] bytes) {
|
||||||
* Convert the md5-hash to a human readable string
|
if (bytes == null) return "";
|
||||||
* @return The converted String
|
|
||||||
*/
|
StringBuilder sb = new StringBuilder();
|
||||||
public String getMd5String(){
|
for (byte b : bytes) {
|
||||||
return toHexString(md5);
|
sb.append(String.format("%02x", b));
|
||||||
}
|
}
|
||||||
|
return sb.toString();
|
||||||
/**
|
}
|
||||||
* Serializes the Object to a buffer
|
|
||||||
* @param buf The buffer to write to
|
/**
|
||||||
*/
|
* Create a new {@link FileHash}.
|
||||||
public void serialize(FriendlyByteBuf buf) {
|
*
|
||||||
buf.writeInt(size);
|
* @param file The input file
|
||||||
buf.writeInt(value);
|
* @return A new Instance. You can compare instances using {@link #equals(Object)} to determine if two files are
|
||||||
buf.writeByteArray(md5);
|
* identical. Will return {@code null} when an error occurs or the File does not exist
|
||||||
DataHandler.writeString(buf, modID);
|
*/
|
||||||
DataHandler.writeString(buf, uniqueID);
|
public static FileHash create(File file) {
|
||||||
}
|
if (!file.exists()) return createForEmpty(ERR_DOES_NOT_EXIST);
|
||||||
|
final Path path = file.toPath();
|
||||||
/**
|
|
||||||
*Deserialize a Buffer to a new {@link FileHash}-Object
|
int size = 0;
|
||||||
* @param buf Thea buffer to read from
|
byte[] md5 = new byte[0];
|
||||||
* @return The received String
|
int value = 0;
|
||||||
*/
|
|
||||||
public static FileHash deserialize(FriendlyByteBuf buf){
|
try {
|
||||||
final int size = buf.readInt();
|
byte[] data = Files.readAllBytes(path);
|
||||||
final int value = buf.readInt();
|
|
||||||
final byte[] md5 = buf.readByteArray();
|
size = data.length;
|
||||||
final String modID = DataHandler.readString(buf);
|
|
||||||
final String uniqueID = DataHandler.readString(buf);
|
value = size > 0 ? (data[size / 3] | (data[size / 2] << 8) | (data[size / 5] << 16)) : -1;
|
||||||
|
if (size > 20) value |= data[20] << 24;
|
||||||
return new FileHash(modID, uniqueID, md5, size, value);
|
|
||||||
}
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
md.update(data);
|
||||||
/**
|
md5 = md.digest();
|
||||||
* Converts a byte-array to a hex-string representation
|
|
||||||
* @param bytes The source array
|
return new FileHash(md5, size, value);
|
||||||
* @return The resulting string, or an empty String if the input was {@code null}
|
}
|
||||||
*/
|
catch (IOException e) {
|
||||||
public static String toHexString(byte[] bytes) {
|
BCLib.LOGGER.error("Failed to read file: " + file);
|
||||||
if (bytes==null) return "";
|
return null;
|
||||||
|
}
|
||||||
StringBuilder sb = new StringBuilder();
|
catch (NoSuchAlgorithmException e) {
|
||||||
for (byte b : bytes) {
|
BCLib.LOGGER.error("Unable to build hash for file: " + file);
|
||||||
sb.append(String.format("%02x", b));
|
}
|
||||||
}
|
|
||||||
return sb.toString();
|
return createForEmpty(ERR_IO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Create a new {@link FileHash}.
|
public boolean equals(Object o) {
|
||||||
* <p>
|
if (this == o) return true;
|
||||||
* Will call {@link #create(String, File, String)} using the name of the File as {@code uniqueID}.
|
if (!(o instanceof FileHash)) return false;
|
||||||
* @param modID ID of the calling Mod
|
FileHash fileHash = (FileHash) o;
|
||||||
* @param file The input file
|
return size == fileHash.size && value == fileHash.value && Arrays.equals(md5, fileHash.md5);
|
||||||
*
|
}
|
||||||
* @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
|
@Override
|
||||||
*/
|
public int hashCode() {
|
||||||
public static FileHash create(String modID, File file){
|
int result = Objects.hash(size, value);
|
||||||
return create(modID, file, file.getName());
|
result = 31 * result + Arrays.hashCode(md5);
|
||||||
}
|
return result;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Create a new {@link FileHash}.
|
@Override
|
||||||
* @param modID ID of the calling Mod
|
public String toString() {
|
||||||
* @param file The input file
|
return String.format("%08x", size) + "-" + String.format("%08x", value) + "-" + getMd5String();
|
||||||
* @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;
|
|
||||||
byte[] md5 = new byte[0];
|
|
||||||
int value = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] data = Files.readAllBytes(path);
|
|
||||||
|
|
||||||
size = data.length;
|
|
||||||
|
|
||||||
value = size>0 ? (data[size/3] | (data [size/2]<<8) | (data [size/5]<<16)) : -1;
|
|
||||||
if (size>20) value |= data[20]<<24;
|
|
||||||
|
|
||||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
|
||||||
md.update(data);
|
|
||||||
md5 = md.digest();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
108
src/main/java/ru/bclib/api/dataexchange/SyncFileHash.java
Normal file
108
src/main/java/ru/bclib/api/dataexchange/SyncFileHash.java
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package ru.bclib.api.dataexchange;
|
||||||
|
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import ru.bclib.api.dataexchange.handler.AutoSyncID;
|
||||||
|
import ru.bclib.api.dataexchange.handler.DataExchange;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates a hash based on the contents of a File.
|
||||||
|
* <p>
|
||||||
|
* A File-Hash contains the md5-sum of the File, as well as its size and byte-values from defined positions
|
||||||
|
* <p>
|
||||||
|
* You can compare instances using {@link #equals(Object)} to determine if two files are
|
||||||
|
* identical.
|
||||||
|
*/
|
||||||
|
public class SyncFileHash extends AutoSyncID {
|
||||||
|
public final FileHash hash;
|
||||||
|
|
||||||
|
SyncFileHash(String modID, File file, byte[] md5, int size, int value) {
|
||||||
|
this(modID, file.getName(), md5, size, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncFileHash(String modID, String uniqueID, byte[] md5, int size, int value) {
|
||||||
|
this(modID, uniqueID, new FileHash(md5, size, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncFileHash(String modID, File file, FileHash hash) {
|
||||||
|
this(modID, file.getName(), hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncFileHash(String modID, String uniqueID, FileHash hash) {
|
||||||
|
super(modID, uniqueID);
|
||||||
|
this.hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final static DataExchange.NeedTransferPredicate NEED_TRANSFER = (clientHash, serverHash, content)-> !clientHash.equals(serverHash);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString()+": "+hash.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof SyncFileHash)) return false;
|
||||||
|
if (!super.equals(o)) return false;
|
||||||
|
SyncFileHash that = (SyncFileHash) o;
|
||||||
|
return hash.equals(that.hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(super.hashCode(), hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the Object to a buffer
|
||||||
|
* @param buf The buffer to write to
|
||||||
|
*/
|
||||||
|
public void serialize(FriendlyByteBuf buf) {
|
||||||
|
hash.serialize(buf);
|
||||||
|
DataHandler.writeString(buf, modID);
|
||||||
|
DataHandler.writeString(buf, uniqueID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*Deserialize a Buffer to a new {@link SyncFileHash}-Object
|
||||||
|
* @param buf Thea buffer to read from
|
||||||
|
* @return The received String
|
||||||
|
*/
|
||||||
|
public static SyncFileHash deserialize(FriendlyByteBuf buf){
|
||||||
|
final FileHash hash = FileHash.deserialize(buf);
|
||||||
|
final String modID = DataHandler.readString(buf);
|
||||||
|
final String uniqueID = DataHandler.readString(buf);
|
||||||
|
|
||||||
|
return new SyncFileHash(modID, uniqueID, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link SyncFileHash}.
|
||||||
|
* <p>
|
||||||
|
* 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. Will return {@code null} when an error occurs or the File does not exist
|
||||||
|
*/
|
||||||
|
public static SyncFileHash create(String modID, File file){
|
||||||
|
return create(modID, file, file.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link SyncFileHash}.
|
||||||
|
* @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 SyncFileHash#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 SyncFileHash create(String modID, File file, String uniqueID){
|
||||||
|
return new SyncFileHash(modID, uniqueID, FileHash.create(file));
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,9 @@ package ru.bclib.api.dataexchange.handler;
|
||||||
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import ru.bclib.api.dataexchange.DataHandler;
|
import ru.bclib.api.dataexchange.DataHandler;
|
||||||
import ru.bclib.api.dataexchange.FileHash;
|
import ru.bclib.api.dataexchange.SyncFileHash;
|
||||||
|
import ru.bclib.api.dataexchange.handler.DataExchange.SyncFolderDescriptor;
|
||||||
|
import ru.bclib.api.dataexchange.handler.DataExchange.SyncFolderDescriptor.SubFile;
|
||||||
import ru.bclib.util.Pair;
|
import ru.bclib.util.Pair;
|
||||||
import ru.bclib.util.Triple;
|
import ru.bclib.util.Triple;
|
||||||
|
|
||||||
|
@ -12,114 +14,157 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
class AutoFileSyncEntry extends AutoSyncID {
|
class AutoFileSyncEntry extends AutoSyncID {
|
||||||
public final DataExchange.NeedTransferPredicate needTransfer;
|
static class ForDirectFileRequest extends AutoFileSyncEntry {
|
||||||
public final File fileName;
|
final File relFile;
|
||||||
public final boolean requestContent;
|
|
||||||
private FileHash hash;
|
ForDirectFileRequest(String syncID, File relFile, File absFile) {
|
||||||
|
super(AutoSyncID.ForDirectFileRequest.MOD_ID, syncID, absFile, false, (a, b, c) -> false);
|
||||||
AutoFileSyncEntry(String modID, File fileName, boolean requestContent, DataExchange.NeedTransferPredicate needTransfer) {
|
this.relFile = relFile;
|
||||||
this(modID, fileName.getName(), fileName, requestContent, needTransfer);
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
AutoFileSyncEntry(String modID, String uniqueID, File fileName, boolean requestContent, DataExchange.NeedTransferPredicate needTransfer) {
|
public int serializeContent(FriendlyByteBuf buf) {
|
||||||
super(modID, uniqueID);
|
int res = super.serializeContent(buf);
|
||||||
this.needTransfer = needTransfer;
|
DataHandler.writeString(buf, relFile.toString());
|
||||||
this.fileName = fileName;
|
|
||||||
this.requestContent = requestContent;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileHash getFileHash() {
|
static AutoFileSyncEntry.ForDirectFileRequest finishDeserializeContent(String syncID, FriendlyByteBuf buf){
|
||||||
if (hash == null)
|
final String relFile = DataHandler.readString(buf);
|
||||||
{
|
SyncFolderDescriptor desc = DataExchange.getSyncFolderDescriptor(syncID);
|
||||||
hash = FileHash.create(modID, fileName, uniqueID);
|
if (desc!=null) {
|
||||||
}
|
return new AutoFileSyncEntry.ForDirectFileRequest(syncID, new File(relFile), desc.localFolder.resolve(relFile)
|
||||||
return hash;
|
.toFile());
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
public byte[] getContent() {
|
}
|
||||||
if (!fileName.exists()) return new byte[0];
|
}
|
||||||
final Path path = fileName.toPath();
|
public final DataExchange.NeedTransferPredicate needTransfer;
|
||||||
|
public final File fileName;
|
||||||
try {
|
public final boolean requestContent;
|
||||||
return Files.readAllBytes(path);
|
private SyncFileHash hash;
|
||||||
} catch (IOException e) {
|
|
||||||
|
AutoFileSyncEntry(String modID, File fileName, boolean requestContent, DataExchange.NeedTransferPredicate needTransfer) {
|
||||||
}
|
this(modID, fileName.getName(), fileName, requestContent, needTransfer);
|
||||||
return new byte[0];
|
}
|
||||||
}
|
|
||||||
|
AutoFileSyncEntry(String modID, String uniqueID, File fileName, boolean requestContent, DataExchange.NeedTransferPredicate needTransfer) {
|
||||||
public int serializeContent(FriendlyByteBuf buf) {
|
super(modID, uniqueID);
|
||||||
DataHandler.writeString(buf, modID);
|
this.needTransfer = needTransfer;
|
||||||
DataHandler.writeString(buf, uniqueID);
|
this.fileName = fileName;
|
||||||
return serializeFileContent(buf);
|
this.requestContent = requestContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Triple<AutoFileSyncEntry, byte[], AutoSyncID> deserializeContent(FriendlyByteBuf buf) {
|
public SyncFileHash getFileHash() {
|
||||||
final String modID = DataHandler.readString(buf);
|
if (hash == null) {
|
||||||
final String uniqueID = DataHandler.readString(buf);
|
hash = SyncFileHash.create(modID, fileName, uniqueID);
|
||||||
byte[] data = deserializeFileContent(buf);
|
}
|
||||||
|
return hash;
|
||||||
AutoFileSyncEntry entry = AutoFileSyncEntry.findMatching(modID, uniqueID);
|
}
|
||||||
return new Triple<>(entry, data, new AutoSyncID(modID, uniqueID));
|
|
||||||
}
|
public byte[] getContent() {
|
||||||
|
if (!fileName.exists()) return new byte[0];
|
||||||
|
final Path path = fileName.toPath();
|
||||||
public void serialize(FriendlyByteBuf buf) {
|
|
||||||
getFileHash().serialize(buf);
|
try {
|
||||||
buf.writeBoolean(requestContent);
|
return Files.readAllBytes(path);
|
||||||
|
}
|
||||||
if (requestContent) {
|
catch (IOException e) {
|
||||||
serializeFileContent(buf);
|
|
||||||
}
|
}
|
||||||
}
|
return new byte[0];
|
||||||
|
}
|
||||||
public static DataExchange.AutoSyncTriple deserializeAndMatch(FriendlyByteBuf buf) {
|
|
||||||
Pair<FileHash, byte[]> e = deserialize(buf);
|
public int serializeContent(FriendlyByteBuf buf) {
|
||||||
AutoFileSyncEntry match = findMatching(e.first);
|
DataHandler.writeString(buf, modID);
|
||||||
return new DataExchange.AutoSyncTriple(e.first, e.second, match);
|
DataHandler.writeString(buf, uniqueID);
|
||||||
}
|
return serializeFileContent(buf);
|
||||||
|
}
|
||||||
public static Pair<FileHash, byte[]> deserialize(FriendlyByteBuf buf) {
|
|
||||||
FileHash hash = FileHash.deserialize(buf);
|
public static Triple<AutoFileSyncEntry, byte[], AutoSyncID> deserializeContent(FriendlyByteBuf buf) {
|
||||||
boolean withContent = buf.readBoolean();
|
final String modID = DataHandler.readString(buf);
|
||||||
byte[] data = null;
|
final String uniqueID = DataHandler.readString(buf);
|
||||||
if (withContent) {
|
byte[] data = deserializeFileContent(buf);
|
||||||
data = deserializeFileContent(buf);
|
|
||||||
}
|
AutoFileSyncEntry entry;
|
||||||
|
if (AutoSyncID.ForDirectFileRequest.MOD_ID.equals(modID)){
|
||||||
return new Pair(hash, data);
|
entry = AutoFileSyncEntry.ForDirectFileRequest.finishDeserializeContent(uniqueID, buf);
|
||||||
}
|
} else {
|
||||||
|
entry = AutoFileSyncEntry.findMatching(modID, uniqueID);
|
||||||
private int serializeFileContent(FriendlyByteBuf buf) {
|
}
|
||||||
byte[] content = getContent();
|
return new Triple<>(entry, data, new AutoSyncID(modID, uniqueID));
|
||||||
buf.writeInt(content.length);
|
}
|
||||||
buf.writeByteArray(content);
|
|
||||||
return content.length;
|
|
||||||
}
|
public void serialize(FriendlyByteBuf buf) {
|
||||||
|
getFileHash().serialize(buf);
|
||||||
private static byte[] deserializeFileContent(FriendlyByteBuf buf) {
|
buf.writeBoolean(requestContent);
|
||||||
byte[] data;
|
|
||||||
int size = buf.readInt();
|
if (requestContent) {
|
||||||
data = buf.readByteArray(size);
|
serializeFileContent(buf);
|
||||||
return data;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DataExchange.AutoSyncTriple deserializeAndMatch(FriendlyByteBuf buf) {
|
||||||
public static AutoFileSyncEntry findMatching(FileHash hash) {
|
Pair<SyncFileHash, byte[]> e = deserialize(buf);
|
||||||
return findMatching(hash.modID, hash.uniqueID);
|
AutoFileSyncEntry match = findMatching(e.first);
|
||||||
}
|
return new DataExchange.AutoSyncTriple(e.first, e.second, match);
|
||||||
|
}
|
||||||
public static AutoFileSyncEntry findMatching(AutoSyncID aid) {
|
|
||||||
return findMatching(aid.modID, aid.uniqueID);
|
public static Pair<SyncFileHash, byte[]> deserialize(FriendlyByteBuf buf) {
|
||||||
}
|
SyncFileHash hash = SyncFileHash.deserialize(buf);
|
||||||
|
boolean withContent = buf.readBoolean();
|
||||||
public static AutoFileSyncEntry findMatching(String modID, String uniqueID) {
|
byte[] data = null;
|
||||||
return DataExchange
|
if (withContent) {
|
||||||
.getInstance()
|
data = deserializeFileContent(buf);
|
||||||
.getAutoSyncFiles()
|
}
|
||||||
.stream()
|
|
||||||
.filter(asf -> asf.modID.equals(modID) && asf.uniqueID.equals(uniqueID))
|
return new Pair(hash, data);
|
||||||
.findFirst()
|
}
|
||||||
.orElse(null);
|
|
||||||
}
|
private int serializeFileContent(FriendlyByteBuf buf) {
|
||||||
|
byte[] content = getContent();
|
||||||
|
buf.writeInt(content.length);
|
||||||
|
buf.writeByteArray(content);
|
||||||
|
return content.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] deserializeFileContent(FriendlyByteBuf buf) {
|
||||||
|
byte[] data;
|
||||||
|
int size = buf.readInt();
|
||||||
|
data = buf.readByteArray(size);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static AutoFileSyncEntry findMatching(SyncFileHash hash) {
|
||||||
|
return findMatching(hash.modID, hash.uniqueID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AutoFileSyncEntry findMatching(AutoSyncID aid) {
|
||||||
|
if (aid instanceof AutoSyncID.ForDirectFileRequest) {
|
||||||
|
AutoSyncID.ForDirectFileRequest freq = (AutoSyncID.ForDirectFileRequest) aid;
|
||||||
|
SyncFolderDescriptor desc = DataExchange.getSyncFolderDescriptor(freq.uniqueID);
|
||||||
|
if (desc != null) {
|
||||||
|
SubFile subFile = desc.getLocalSubFile(freq.relFile.toString());
|
||||||
|
if (subFile != null) {
|
||||||
|
final File absPath = desc.localFolder.resolve(subFile.relPath)
|
||||||
|
.toFile();
|
||||||
|
return new AutoFileSyncEntry.ForDirectFileRequest(freq.uniqueID, new File(subFile.relPath), absPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return findMatching(aid.modID, aid.uniqueID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AutoFileSyncEntry findMatching(String modID, String uniqueID) {
|
||||||
|
return DataExchange.getInstance()
|
||||||
|
.getAutoSyncFiles()
|
||||||
|
.stream()
|
||||||
|
.filter(asf -> asf.modID.equals(modID) && asf.uniqueID.equals(uniqueID))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package ru.bclib.api.dataexchange.handler;
|
package ru.bclib.api.dataexchange.handler;
|
||||||
|
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import ru.bclib.api.dataexchange.DataHandler;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -16,6 +18,27 @@ public class AutoSyncID {
|
||||||
this.localFile = localFile;
|
this.localFile = localFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ForDirectFileRequest extends AutoSyncID {
|
||||||
|
public final static String MOD_ID = "bclib::FILE";
|
||||||
|
final File relFile;
|
||||||
|
|
||||||
|
ForDirectFileRequest(String syncID, File relFile) {
|
||||||
|
super(ForDirectFileRequest.MOD_ID, syncID);
|
||||||
|
this.relFile = relFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void serializeData(FriendlyByteBuf buf) {
|
||||||
|
super.serializeData(buf);
|
||||||
|
DataHandler.writeString(buf, relFile.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
static ForDirectFileRequest finishDeserialize(String modID, String uniqueID, FriendlyByteBuf buf){
|
||||||
|
final File fl = new File(DataHandler.readString(buf));
|
||||||
|
return new ForDirectFileRequest(uniqueID, fl);
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* A Unique ID for the referenced File.
|
* A Unique ID for the referenced File.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -56,4 +79,20 @@ public class AutoSyncID {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(uniqueID, modID);
|
return Objects.hash(uniqueID, modID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serializeData(FriendlyByteBuf buf) {
|
||||||
|
DataHandler.writeString(buf, modID);
|
||||||
|
DataHandler.writeString(buf, uniqueID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AutoSyncID deserializeData(FriendlyByteBuf buf){
|
||||||
|
String modID = DataHandler.readString(buf);
|
||||||
|
String uID = DataHandler.readString(buf);
|
||||||
|
|
||||||
|
if (ForDirectFileRequest.MOD_ID.equals(modID)){
|
||||||
|
return ForDirectFileRequest.finishDeserialize(modID, uID, buf);
|
||||||
|
} else {
|
||||||
|
return new AutoSyncID(modID, uID);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,230 +5,426 @@ import net.fabricmc.api.Environment;
|
||||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import ru.bclib.BCLib;
|
||||||
import ru.bclib.api.dataexchange.ConnectorClientside;
|
import ru.bclib.api.dataexchange.ConnectorClientside;
|
||||||
import ru.bclib.api.dataexchange.ConnectorServerside;
|
import ru.bclib.api.dataexchange.ConnectorServerside;
|
||||||
import ru.bclib.api.dataexchange.DataExchangeAPI;
|
import ru.bclib.api.dataexchange.DataExchangeAPI;
|
||||||
import ru.bclib.api.dataexchange.DataHandler;
|
import ru.bclib.api.dataexchange.DataHandler;
|
||||||
import ru.bclib.api.dataexchange.DataHandlerDescriptor;
|
import ru.bclib.api.dataexchange.DataHandlerDescriptor;
|
||||||
import ru.bclib.api.dataexchange.FileHash;
|
import ru.bclib.api.dataexchange.FileHash;
|
||||||
|
import ru.bclib.api.dataexchange.SyncFileHash;
|
||||||
|
import ru.bclib.api.dataexchange.handler.AutoSyncID.ForDirectFileRequest;
|
||||||
import ru.bclib.config.Configs;
|
import ru.bclib.config.Configs;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
abstract public class DataExchange {
|
abstract public class DataExchange {
|
||||||
public final static Path SYNC_FOLDER = FabricLoader.getInstance()
|
public final static SyncFolderDescriptor SYNC_FOLDER = new SyncFolderDescriptor("BCLIB-SYNC", FabricLoader.getInstance()
|
||||||
.getGameDir()
|
.getGameDir()
|
||||||
.resolve("bclib-sync")
|
.resolve("bclib-sync")
|
||||||
.toAbsolutePath();
|
.toAbsolutePath(), true);
|
||||||
public final static String SYNC_FOLDER_ID = "BCLIB-SYNC";
|
final List<SyncFolderDescriptor> syncFolderDescriptions = Arrays.asList(SYNC_FOLDER);
|
||||||
|
|
||||||
@FunctionalInterface
|
public static class SyncFolderDescriptor {
|
||||||
public interface NeedTransferPredicate {
|
static class SubFile {
|
||||||
public boolean test(FileHash clientHash, FileHash serverHash, FileContentWrapper content);
|
public final String relPath;
|
||||||
}
|
public final FileHash hash;
|
||||||
|
|
||||||
protected final static List<BiConsumer<AutoSyncID, File>> onWriteCallbacks = new ArrayList<>(2);
|
|
||||||
|
SubFile(String relPath, FileHash hash) {
|
||||||
final static class AutoSyncTriple {
|
this.relPath = relPath;
|
||||||
public final FileHash serverHash;
|
this.hash = hash;
|
||||||
public final byte[] serverContent;
|
}
|
||||||
public final AutoFileSyncEntry localMatch;
|
|
||||||
|
@Override
|
||||||
public AutoSyncTriple(FileHash serverHash, byte[] serverContent, AutoFileSyncEntry localMatch) {
|
public String toString() {
|
||||||
this.serverHash = serverHash;
|
return relPath;
|
||||||
this.serverContent = serverContent;
|
}
|
||||||
this.localMatch = localMatch;
|
|
||||||
}
|
public void serialize(FriendlyByteBuf buf) {
|
||||||
|
DataHandler.writeString(buf, relPath);
|
||||||
@Override
|
hash.serialize(buf);
|
||||||
public String toString() {
|
}
|
||||||
return serverHash.modID+"."+serverHash.uniqueID;
|
|
||||||
}
|
public static SubFile deserialize(FriendlyByteBuf buf) {
|
||||||
}
|
final String relPath = DataHandler.readString(buf);
|
||||||
|
FileHash hash = FileHash.deserialize(buf);
|
||||||
private static DataExchangeAPI instance;
|
return new SubFile(relPath, hash);
|
||||||
protected static DataExchangeAPI getInstance(){
|
}
|
||||||
if (instance==null){
|
|
||||||
instance = new DataExchangeAPI();
|
@Override
|
||||||
}
|
public boolean equals(Object o) {
|
||||||
return instance;
|
if (this == o) return true;
|
||||||
}
|
if (o instanceof String) return relPath.equals(o);
|
||||||
|
if (!(o instanceof SubFile)) return false;
|
||||||
protected ConnectorServerside server;
|
SubFile subFile = (SubFile) o;
|
||||||
protected ConnectorClientside client;
|
return relPath.equals(subFile.relPath);
|
||||||
protected final Set<DataHandlerDescriptor> descriptors;
|
}
|
||||||
private final List<AutoFileSyncEntry> autoSyncFiles = new ArrayList<>(4);
|
|
||||||
|
@Override
|
||||||
private boolean didLoadSyncFolder = false;
|
public int hashCode() {
|
||||||
|
return relPath.hashCode();
|
||||||
abstract protected ConnectorClientside clientSupplier(DataExchange api);
|
}
|
||||||
abstract protected ConnectorServerside serverSupplier(DataExchange api);
|
}
|
||||||
|
|
||||||
protected DataExchange(){
|
@NotNull
|
||||||
descriptors = new HashSet<>();
|
public final String folderID;
|
||||||
}
|
public final boolean removeAdditionalFiles;
|
||||||
|
@NotNull
|
||||||
public Set<DataHandlerDescriptor> getDescriptors() { return descriptors; }
|
public final Path localFolder;
|
||||||
|
|
||||||
public List<AutoFileSyncEntry> getAutoSyncFiles(){
|
private List<SubFile> fileCache;
|
||||||
return autoSyncFiles;
|
|
||||||
}
|
SyncFolderDescriptor(String folderID, Path localFolder, boolean removeAdditionalFiles) {
|
||||||
|
this.removeAdditionalFiles = removeAdditionalFiles;
|
||||||
@Environment(EnvType.CLIENT)
|
this.folderID = folderID;
|
||||||
protected void initClientside(){
|
this.localFolder = localFolder;
|
||||||
if (client!=null) return;
|
fileCache = null;
|
||||||
client = clientSupplier(this);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SyncFolderDescriptor{" + "folderID='" + folderID + '\'' + ", removeAdditionalFiles=" + removeAdditionalFiles + ", localFolder=" + localFolder + ", files=" + (fileCache == null ? "?" : fileCache.size()) + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o instanceof String) {
|
||||||
|
return folderID.equals(o);
|
||||||
|
}
|
||||||
|
if (o instanceof AutoSyncID.ForDirectFileRequest) {
|
||||||
|
return folderID.equals(((ForDirectFileRequest) o).uniqueID);
|
||||||
|
}
|
||||||
|
if (!(o instanceof SyncFolderDescriptor)) return false;
|
||||||
|
SyncFolderDescriptor that = (SyncFolderDescriptor) o;
|
||||||
|
return folderID.equals(that.folderID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return folderID.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int fileCount(){
|
||||||
|
return fileCache==null?0:fileCache.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidateCache() {
|
||||||
|
fileCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadCache() {
|
||||||
|
if (fileCache == null) {
|
||||||
|
fileCache = new ArrayList<>(8);
|
||||||
|
fileWalker(localFolder.toFile(), p -> fileCache.add(new SubFile(localFolder.relativize(p)
|
||||||
|
.toString(), FileHash.create(p.toFile()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serialize(FriendlyByteBuf buf) {
|
||||||
|
final boolean debugHashes = Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "debugHashes", false);
|
||||||
|
loadCache();
|
||||||
|
|
||||||
|
DataHandler.writeString(buf, folderID);
|
||||||
|
buf.writeBoolean(removeAdditionalFiles);
|
||||||
|
buf.writeInt(fileCache.size());
|
||||||
|
fileCache.forEach(fl -> {
|
||||||
|
BCLib.LOGGER.info(" - " + fl.relPath);
|
||||||
|
if (debugHashes){
|
||||||
|
BCLib.LOGGER.info(" " + fl.hash);
|
||||||
|
}
|
||||||
|
fl.serialize(buf);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SyncFolderDescriptor deserialize(FriendlyByteBuf buf) {
|
||||||
|
final String folderID = DataHandler.readString(buf);
|
||||||
|
final boolean remAddFiles = buf.readBoolean();
|
||||||
|
final int count = buf.readInt();
|
||||||
|
SyncFolderDescriptor localDescriptor = DataExchange.getInstance()
|
||||||
|
.getSyncFolderDescriptor(folderID);
|
||||||
|
|
||||||
|
final SyncFolderDescriptor desc;
|
||||||
|
if (localDescriptor != null) {
|
||||||
|
desc = new SyncFolderDescriptor(folderID, localDescriptor.localFolder, remAddFiles);
|
||||||
|
desc.fileCache = new ArrayList<>(count);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BCLib.LOGGER.warning(BCLib.isClient() ? "Client" : "Server" + " does not know Sync-Folder ID '" + folderID + "'");
|
||||||
|
desc = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
SubFile relPath = SubFile.deserialize(buf);
|
||||||
|
if (desc != null) desc.fileCache.add(relPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Note: make sure loadCache was called before using this
|
||||||
|
boolean hasRelativeFile(String relFile) {
|
||||||
|
return fileCache.stream().filter(sf -> sf.equals(relFile)).findFirst().isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Note: make sure loadCache was called before using this
|
||||||
|
boolean hasRelativeFile(SubFile subFile) {
|
||||||
|
return hasRelativeFile(subFile.relPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Note: make sure loadCache was called before using this
|
||||||
|
SubFile getLocalSubFile(String relPath){
|
||||||
|
return fileCache.stream().filter(sf -> sf.relPath.equals(relPath)).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<SubFile> relativeFilesStream() {
|
||||||
|
loadCache();
|
||||||
|
return fileCache.stream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface NeedTransferPredicate {
|
||||||
|
public boolean test(SyncFileHash clientHash, SyncFileHash serverHash, FileContentWrapper content);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final static List<BiConsumer<AutoSyncID, File>> onWriteCallbacks = new ArrayList<>(2);
|
||||||
|
|
||||||
|
final static class AutoSyncTriple {
|
||||||
|
public final SyncFileHash serverHash;
|
||||||
|
public final byte[] serverContent;
|
||||||
|
public final AutoFileSyncEntry localMatch;
|
||||||
|
|
||||||
|
public AutoSyncTriple(SyncFileHash serverHash, byte[] serverContent, AutoFileSyncEntry localMatch) {
|
||||||
|
this.serverHash = serverHash;
|
||||||
|
this.serverContent = serverContent;
|
||||||
|
this.localMatch = localMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return serverHash.modID + "." + serverHash.uniqueID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DataExchangeAPI instance;
|
||||||
|
|
||||||
|
protected static DataExchangeAPI getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new DataExchangeAPI();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConnectorServerside server;
|
||||||
|
protected ConnectorClientside client;
|
||||||
|
protected final Set<DataHandlerDescriptor> descriptors;
|
||||||
|
private final List<AutoFileSyncEntry> autoSyncFiles = new ArrayList<>(4);
|
||||||
|
|
||||||
|
private boolean didLoadSyncFolder = false;
|
||||||
|
|
||||||
|
abstract protected ConnectorClientside clientSupplier(DataExchange api);
|
||||||
|
|
||||||
|
abstract protected ConnectorServerside serverSupplier(DataExchange api);
|
||||||
|
|
||||||
|
protected DataExchange() {
|
||||||
|
descriptors = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<DataHandlerDescriptor> getDescriptors() { return descriptors; }
|
||||||
|
|
||||||
|
public List<AutoFileSyncEntry> getAutoSyncFiles() {
|
||||||
|
return autoSyncFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Environment(EnvType.CLIENT)
|
||||||
|
protected void initClientside() {
|
||||||
|
if (client != null) return;
|
||||||
|
client = clientSupplier(this);
|
||||||
/*ClientLoginConnectionEvents.INIT.register((a, b) ->{
|
/*ClientLoginConnectionEvents.INIT.register((a, b) ->{
|
||||||
System.out.println("INIT");
|
System.out.println("INIT");
|
||||||
});
|
});
|
||||||
ClientLoginConnectionEvents.QUERY_START.register((a, b) ->{
|
ClientLoginConnectionEvents.QUERY_START.register((a, b) ->{
|
||||||
System.out.println("INIT");
|
System.out.println("INIT");
|
||||||
});*/
|
});*/
|
||||||
ClientPlayConnectionEvents.INIT.register(client::onPlayInit);
|
ClientPlayConnectionEvents.INIT.register(client::onPlayInit);
|
||||||
ClientPlayConnectionEvents.JOIN.register(client::onPlayReady);
|
ClientPlayConnectionEvents.JOIN.register(client::onPlayReady);
|
||||||
ClientPlayConnectionEvents.DISCONNECT.register(client::onPlayDisconnect);
|
ClientPlayConnectionEvents.DISCONNECT.register(client::onPlayDisconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initServerSide(){
|
protected void initServerSide() {
|
||||||
if (server!=null) return;
|
if (server != null) return;
|
||||||
server = serverSupplier(this);
|
server = serverSupplier(this);
|
||||||
|
|
||||||
ServerPlayConnectionEvents.INIT.register(server::onPlayInit);
|
ServerPlayConnectionEvents.INIT.register(server::onPlayInit);
|
||||||
ServerPlayConnectionEvents.JOIN.register(server::onPlayReady);
|
ServerPlayConnectionEvents.JOIN.register(server::onPlayReady);
|
||||||
ServerPlayConnectionEvents.DISCONNECT.register(server::onPlayDisconnect);
|
ServerPlayConnectionEvents.DISCONNECT.register(server::onPlayDisconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes all datastructures that need to exist in the client component.
|
* Initializes all datastructures that need to exist in the client component.
|
||||||
* <p>
|
* <p>
|
||||||
* This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called
|
* This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called
|
||||||
*/
|
*/
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
public static void prepareClientside(){
|
public static void prepareClientside() {
|
||||||
DataExchange api = DataExchange.getInstance();
|
DataExchange api = DataExchange.getInstance();
|
||||||
api.initClientside();
|
api.initClientside();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes all datastructures that need to exist in the server component.
|
* Initializes all datastructures that need to exist in the server component.
|
||||||
* <p>
|
* <p>
|
||||||
* This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called
|
* This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called
|
||||||
*/
|
*/
|
||||||
public static void prepareServerside(){
|
public static void prepareServerside() {
|
||||||
DataExchange api = DataExchange.getInstance();
|
DataExchange api = DataExchange.getInstance();
|
||||||
api.initServerSide();
|
api.initServerSide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
/**
|
* Automatically called before the player enters the world.
|
||||||
* Automatically called before the player enters the world.
|
* <p>
|
||||||
* <p>
|
* This is automatically called by BCLib. It will send all {@link DataHandler}-Objects that have {@link DataHandlerDescriptor#sendBeforeEnter} set to*
|
||||||
* This is automatically called by BCLib. It will send all {@link DataHandler}-Objects that have {@link DataHandlerDescriptor#sendBeforeEnter} set to*
|
* {@code true},
|
||||||
* {@code true},
|
*/
|
||||||
*/
|
@Environment(EnvType.CLIENT)
|
||||||
@Environment(EnvType.CLIENT)
|
public static void sendOnEnter() {
|
||||||
public static void sendOnEnter(){
|
getInstance().descriptors.forEach((desc) -> {
|
||||||
getInstance().descriptors.forEach((desc)-> {
|
if (desc.sendBeforeEnter) {
|
||||||
if (desc.sendBeforeEnter){
|
DataHandler h = desc.JOIN_INSTANCE.get();
|
||||||
DataHandler h = desc.JOIN_INSTANCE.get();
|
if (!h.getOriginatesOnServer()) {
|
||||||
if (!h.getOriginatesOnServer()) {
|
getInstance().client.sendToServer(h);
|
||||||
getInstance().client.sendToServer(h);
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Registers a File for automatic client syncing.
|
||||||
* Registers a File for automatic client syncing.
|
*
|
||||||
*
|
* @param modID The ID of the calling Mod
|
||||||
* @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 needTransfer If the predicate returns true, the file needs to get copied to the server.
|
* @param fileName The name of the File
|
||||||
* @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
|
||||||
* @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.
|
||||||
* entire file from the client to the server.
|
* <p>
|
||||||
* <p>
|
* You should only use this option, if you need to compare parts of the file in order to decide
|
||||||
* 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 SyncFileHash}
|
||||||
* If the File needs to be copied. Normally using the {@link ru.bclib.api.dataexchange.FileHash}
|
* for comparison is sufficient.
|
||||||
* for comparison is sufficient.
|
*/
|
||||||
*/
|
protected void addAutoSyncFileData(String modID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) {
|
||||||
protected void addAutoSyncFileData(String modID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer){
|
autoSyncFiles.add(new AutoFileSyncEntry(modID, fileName, requestContent, needTransfer));
|
||||||
autoSyncFiles.add(new AutoFileSyncEntry(modID, fileName, requestContent, needTransfer));
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Registers a File for automatic client syncing.
|
||||||
* Registers a File for automatic client syncing.
|
*
|
||||||
*
|
* @param modID The ID of the calling Mod
|
||||||
* @param modID The ID of the calling Mod
|
* @param uniqueID A unique Identifier for the File. (see {@link SyncFileHash#uniqueID} for
|
||||||
* @param uniqueID A unique Identifier for the File. (see {@link ru.bclib.api.dataexchange.FileHash#uniqueID} for
|
* Details
|
||||||
* Details
|
* @param needTransfer If the predicate returns true, the file needs to get copied to the server.
|
||||||
* @param needTransfer If the predicate returns true, the file needs to get copied to the server.
|
* @param fileName The name of the File
|
||||||
* @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
|
||||||
* @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.
|
||||||
* entire file from the client to the server.
|
* <p>
|
||||||
* <p>
|
* You should only use this option, if you need to compare parts of the file in order to decide
|
||||||
* 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 SyncFileHash}
|
||||||
* If the File needs to be copied. Normally using the {@link ru.bclib.api.dataexchange.FileHash}
|
* for comparison is sufficient.
|
||||||
* for comparison is sufficient.
|
*/
|
||||||
*/
|
protected void addAutoSyncFileData(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) {
|
||||||
protected void addAutoSyncFileData(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer){
|
autoSyncFiles.add(new AutoFileSyncEntry(modID, uniqueID, fileName, requestContent, needTransfer));
|
||||||
autoSyncFiles.add(new AutoFileSyncEntry(modID, uniqueID, fileName, requestContent, needTransfer));
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Called when {@code SendFiles} received a File on the Client and wrote it to the FileSystem.
|
||||||
* Called when {@code SendFiles} received a File on the Client and wrote it to the FileSystem.
|
* <p>
|
||||||
* <p>
|
* This is the place where reload Code should go.
|
||||||
* This is the place where reload Code should go.
|
*
|
||||||
* @param aid The ID of the received File
|
* @param aid The ID of the received File
|
||||||
* @param file The location of the FIle on the client
|
* @param file The location of the FIle on the client
|
||||||
*/
|
*/
|
||||||
static void didReceiveFile(AutoSyncID aid, File file){
|
static void didReceiveFile(AutoSyncID aid, File file) {
|
||||||
onWriteCallbacks.forEach(fkt -> fkt.accept(aid, file));
|
onWriteCallbacks.forEach(fkt -> fkt.accept(aid, file));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> syncFolderContent;
|
private List<String> syncFolderContent;
|
||||||
protected List<String> getSyncFolderContent(){
|
|
||||||
if (syncFolderContent==null){
|
protected List<String> getSyncFolderContent() {
|
||||||
return new ArrayList<>(0);
|
if (syncFolderContent == null) {
|
||||||
}
|
return new ArrayList<>(0);
|
||||||
return syncFolderContent;
|
}
|
||||||
}
|
return syncFolderContent;
|
||||||
|
}
|
||||||
//we call this from HelloServer to prepare transfer
|
|
||||||
protected void loadSyncFolder() {
|
//we call this from HelloServer to prepare transfer
|
||||||
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offserSyncFolder", true))
|
protected void loadSyncFolder() {
|
||||||
{
|
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offersSyncFolders", true)) {
|
||||||
final File syncPath = SYNC_FOLDER.toFile();
|
syncFolderDescriptions.forEach(desc -> desc.loadCache());
|
||||||
if (!syncPath.exists()) {
|
}
|
||||||
syncPath.mkdirs();
|
}
|
||||||
}
|
|
||||||
|
protected static SyncFolderDescriptor getSyncFolderDescriptor(String folderID) {
|
||||||
if (syncFolderContent == null) {
|
return ((DataExchange) getInstance()).syncFolderDescriptions.stream()
|
||||||
syncFolderContent = new ArrayList<>(8);
|
.filter(d -> d.equals(folderID))
|
||||||
addFilesForSyncFolder(syncPath);
|
.findFirst()
|
||||||
}
|
.orElse(null);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
protected static Path localBasePathForFolderID(String folderID) {
|
||||||
private void addFilesForSyncFolder(File path){
|
final SyncFolderDescriptor desc = getSyncFolderDescriptor(folderID);
|
||||||
for (final File f : path.listFiles()) {
|
if (desc != null) {
|
||||||
if (f.isDirectory()) {
|
return desc.localFolder;
|
||||||
addFilesForSyncFolder(f);
|
}
|
||||||
} else if (f.isFile()) {
|
else {
|
||||||
if (!f.getName().startsWith(".")) {
|
BCLib.LOGGER.warning("Unknown Sync-Folder ID '" + folderID + "'");
|
||||||
Path p = f.toPath();
|
return null;
|
||||||
p = SYNC_FOLDER.relativize(p);
|
}
|
||||||
syncFolderContent.add(p.toString());
|
}
|
||||||
}
|
|
||||||
}
|
protected void registerSyncFolder(String folderID, Path localBaseFolder, boolean removeAdditionalFiles) {
|
||||||
|
final SyncFolderDescriptor desc = new SyncFolderDescriptor(folderID, localBaseFolder, removeAdditionalFiles);
|
||||||
}
|
if (this.syncFolderDescriptions.contains(desc)) {
|
||||||
}
|
BCLib.LOGGER.warning("Tried to override Folder Sync '" + folderID + "' again.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.syncFolderDescriptions.add(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple directory walker that ignores dot-files
|
||||||
|
*
|
||||||
|
* @param path The path where you want to start
|
||||||
|
* @param pathConsumer The consumer called for each valid file. The consumer will get an absolute {@link Path}-Object
|
||||||
|
* for each visited file
|
||||||
|
*/
|
||||||
|
public static void fileWalker(File path, Consumer<Path> pathConsumer) {
|
||||||
|
if (!path.exists()) return;
|
||||||
|
for (final File f : path.listFiles()) {
|
||||||
|
if (f.getName()
|
||||||
|
.startsWith(".")) continue;
|
||||||
|
if (f.isDirectory()) {
|
||||||
|
fileWalker(f, pathConsumer);
|
||||||
|
}
|
||||||
|
else if (f.isFile()) {
|
||||||
|
pathConsumer.accept(f.toPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,14 @@ import ru.bclib.api.dataexchange.DataExchangeAPI;
|
||||||
import ru.bclib.api.dataexchange.DataHandler;
|
import ru.bclib.api.dataexchange.DataHandler;
|
||||||
import ru.bclib.api.dataexchange.DataHandlerDescriptor;
|
import ru.bclib.api.dataexchange.DataHandlerDescriptor;
|
||||||
import ru.bclib.api.dataexchange.handler.AutoSyncID.WithContentOverride;
|
import ru.bclib.api.dataexchange.handler.AutoSyncID.WithContentOverride;
|
||||||
|
import ru.bclib.api.dataexchange.handler.DataExchange.SyncFolderDescriptor;
|
||||||
|
import ru.bclib.api.dataexchange.handler.DataExchange.SyncFolderDescriptor.SubFile;
|
||||||
import ru.bclib.api.datafixer.DataFixerAPI;
|
import ru.bclib.api.datafixer.DataFixerAPI;
|
||||||
import ru.bclib.config.Configs;
|
import ru.bclib.config.Configs;
|
||||||
import ru.bclib.gui.screens.SyncFilesScreen;
|
import ru.bclib.gui.screens.SyncFilesScreen;
|
||||||
import ru.bclib.gui.screens.WarnBCLibVersionMismatch;
|
import ru.bclib.gui.screens.WarnBCLibVersionMismatch;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -41,28 +44,31 @@ public class HelloClient extends DataHandler {
|
||||||
super(DESCRIPTOR.IDENTIFIER, true);
|
super(DESCRIPTOR.IDENTIFIER, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getModVersion(String modID){
|
public static String getModVersion(String modID) {
|
||||||
Optional<ModContainer> optional = FabricLoader.getInstance().getModContainer(modID);
|
Optional<ModContainer> optional = FabricLoader.getInstance()
|
||||||
|
.getModContainer(modID);
|
||||||
if (optional.isPresent()) {
|
if (optional.isPresent()) {
|
||||||
ModContainer modContainer = optional.get();
|
ModContainer modContainer = optional.get();
|
||||||
return modContainer.getMetadata().getVersion().toString();
|
return modContainer.getMetadata()
|
||||||
|
.getVersion()
|
||||||
|
.toString();
|
||||||
}
|
}
|
||||||
return "0.0.0";
|
return "0.0.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getBCLibVersion(){
|
static String getBCLibVersion() {
|
||||||
return getModVersion(BCLib.MOD_ID);
|
return getModVersion(BCLib.MOD_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void serializeData(FriendlyByteBuf buf) {
|
protected void serializeData(FriendlyByteBuf buf) {
|
||||||
final String vbclib = getBCLibVersion();
|
final String vbclib = getBCLibVersion();
|
||||||
BCLib.LOGGER.info("Sending Hello to Client. (server="+vbclib+")");
|
BCLib.LOGGER.info("Sending Hello to Client. (server=" + vbclib + ")");
|
||||||
final List<String> mods = DataExchangeAPI.registeredMods();
|
final List<String> mods = DataExchangeAPI.registeredMods();
|
||||||
|
|
||||||
//write BCLibVersion (=protocol version)
|
//write BCLibVersion (=protocol version)
|
||||||
buf.writeInt(DataFixerAPI.getModVersion(vbclib));
|
buf.writeInt(DataFixerAPI.getModVersion(vbclib));
|
||||||
|
|
||||||
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offerMods", true)) {
|
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offerMods", true)) {
|
||||||
//write Plugin Versions
|
//write Plugin Versions
|
||||||
buf.writeInt(mods.size());
|
buf.writeInt(mods.size());
|
||||||
|
@ -72,128 +78,162 @@ public class HelloClient extends DataHandler {
|
||||||
buf.writeInt(DataFixerAPI.getModVersion(ver));
|
buf.writeInt(DataFixerAPI.getModVersion(ver));
|
||||||
BCLib.LOGGER.info(" - Listing Mod " + modID + " v" + ver);
|
BCLib.LOGGER.info(" - Listing Mod " + modID + " v" + ver);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
BCLib.LOGGER.info("Server will not list Mods.");
|
BCLib.LOGGER.info("Server will not list Mods.");
|
||||||
buf.writeInt(0);
|
buf.writeInt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offerFiles", true)) {
|
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offerFiles", true)) {
|
||||||
//do only include files that exist on the server
|
//do only include files that exist on the server
|
||||||
final List<AutoFileSyncEntry> existingAutoSyncFiles = DataExchange
|
final List<AutoFileSyncEntry> existingAutoSyncFiles = DataExchange.getInstance()
|
||||||
.getInstance()
|
.getAutoSyncFiles()
|
||||||
.getAutoSyncFiles()
|
.stream()
|
||||||
.stream()
|
.filter(e -> e.fileName.exists())
|
||||||
.filter(e -> e.fileName.exists())
|
.collect(Collectors.toList());
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
//send config Data
|
//send config Data
|
||||||
buf.writeInt(existingAutoSyncFiles.size());
|
buf.writeInt(existingAutoSyncFiles.size());
|
||||||
for (AutoFileSyncEntry entry : existingAutoSyncFiles) {
|
for (AutoFileSyncEntry entry : existingAutoSyncFiles) {
|
||||||
entry.serialize(buf);
|
entry.serialize(buf);
|
||||||
BCLib.LOGGER.info(" - Offering File " + entry);
|
BCLib.LOGGER.info(" - Offering File " + entry);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
BCLib.LOGGER.info("Server will not offer Files.");
|
BCLib.LOGGER.info("Server will not offer Files.");
|
||||||
buf.writeInt(0);
|
buf.writeInt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//for the moment this is only hardcoded for the sync-folder offered by BCLIB, but it can be extended in future
|
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offersSyncFolders", true)) {
|
||||||
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offserSyncFolder", true)) {
|
buf.writeInt(((DataExchange) DataExchange.getInstance()).syncFolderDescriptions.size());
|
||||||
buf.writeInt(1); //currently we do only sync a single folder
|
((DataExchange) DataExchange.getInstance()).syncFolderDescriptions.forEach(desc -> {
|
||||||
writeString(buf, DataExchange.SYNC_FOLDER_ID); //the UID of the Folder
|
BCLib.LOGGER.info(" - Offering Folder " + desc.localFolder + " (allowDelete="+desc.removeAdditionalFiles+")");
|
||||||
final List<String> fileNames = DataExchange.getInstance().getSyncFolderContent();
|
desc.serialize(buf);
|
||||||
buf.writeInt(fileNames.size());
|
});
|
||||||
fileNames.forEach(fl -> writeString(buf, fl));
|
}
|
||||||
} else {
|
else {
|
||||||
BCLib.LOGGER.info("Server will not offer Sync Folders.");
|
BCLib.LOGGER.info("Server will not offer Sync Folders.");
|
||||||
buf.writeInt(0);
|
buf.writeInt(0);
|
||||||
}
|
}
|
||||||
Configs.MAIN_CONFIG.saveChanges();
|
Configs.MAIN_CONFIG.saveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
String bclibVersion ="0.0.0";
|
String bclibVersion = "0.0.0";
|
||||||
Map<String, String> modVersion = new HashMap<>();
|
Map<String, String> modVersion = new HashMap<>();
|
||||||
List<DataExchange.AutoSyncTriple> autoSyncedFiles = null;
|
List<DataExchange.AutoSyncTriple> autoSyncedFiles = null;
|
||||||
|
List<SyncFolderDescriptor> autoSynFolders = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) {
|
protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) {
|
||||||
//read BCLibVersion (=protocol version)
|
//read BCLibVersion (=protocol version)
|
||||||
bclibVersion = DataFixerAPI.getModVersion(buf.readInt());
|
bclibVersion = DataFixerAPI.getModVersion(buf.readInt());
|
||||||
|
|
||||||
//read Plugin Versions
|
//read Plugin Versions
|
||||||
modVersion = new HashMap<>();
|
modVersion = new HashMap<>();
|
||||||
int count = buf.readInt();
|
int count = buf.readInt();
|
||||||
for (int i=0; i< count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
String id = readString(buf);
|
String id = readString(buf);
|
||||||
String version = DataFixerAPI.getModVersion(buf.readInt());
|
String version = DataFixerAPI.getModVersion(buf.readInt());
|
||||||
modVersion.put(id, version);
|
modVersion.put(id, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
//read config Data
|
//read config Data
|
||||||
count = buf.readInt();
|
count = buf.readInt();
|
||||||
autoSyncedFiles = new ArrayList<>(count);
|
autoSyncedFiles = new ArrayList<>(count);
|
||||||
for (int i=0; i< count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
//System.out.println("Deserializing ");
|
//System.out.println("Deserializing ");
|
||||||
DataExchange.AutoSyncTriple t = AutoFileSyncEntry.deserializeAndMatch(buf);
|
DataExchange.AutoSyncTriple t = AutoFileSyncEntry.deserializeAndMatch(buf);
|
||||||
autoSyncedFiles.add(t);
|
autoSyncedFiles.add(t);
|
||||||
//System.out.println(t.first);
|
//System.out.println(t.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
//since this version we also send the sync folders
|
|
||||||
|
autoSynFolders = new ArrayList<>(1);
|
||||||
|
//since v0.4.1 we also send the sync folders
|
||||||
if (DataFixerAPI.isLargerOrEqualVersion(bclibVersion, "0.4.1")) {
|
if (DataFixerAPI.isLargerOrEqualVersion(bclibVersion, "0.4.1")) {
|
||||||
final int folderCount = buf.readInt();
|
final int folderCount = buf.readInt();
|
||||||
for (int i=0; i<folderCount; i++){
|
for (int i = 0; i < folderCount; i++) {
|
||||||
final String folderID = readString(buf);
|
SyncFolderDescriptor desc = SyncFolderDescriptor.deserialize(buf);
|
||||||
final int entries = buf.readInt();
|
autoSynFolders.add(desc);
|
||||||
List<String> files = new ArrayList<>(entries);
|
|
||||||
for (int j=0; j<entries; j++){
|
|
||||||
files.add(readString(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (folderID.equals(DataExchange.SYNC_FOLDER_ID)) {
|
|
||||||
//TODO: implement the syncing here
|
|
||||||
} else {
|
|
||||||
BCLib.LOGGER.warning("Unknown Sync-Folder '"+folderID+"'");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processAutoSyncFolder(final List<AutoSyncID> filesToRequest, final List<AutoSyncID.ForDirectFileRequest> filesToRemove) {
|
||||||
@Override
|
if (!Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "syncFolders", true)) {
|
||||||
protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) {
|
return;
|
||||||
final boolean debugHashes = Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "debugHashes", false);
|
|
||||||
final String localBclibVersion = getBCLibVersion();
|
|
||||||
BCLib.LOGGER.info("Received Hello from Server. (client="+localBclibVersion+", server="+bclibVersion+")");
|
|
||||||
|
|
||||||
// if (DataFixerAPI.getModVersion(localBclibVersion) != DataFixerAPI.getModVersion(bclibVersion)){
|
|
||||||
// showBCLibError(client);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
final List<AutoSyncID> filesToRequest = new ArrayList<>(2);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (Entry<String, String> e : modVersion.entrySet()){
|
|
||||||
String ver = getModVersion(e.getKey());
|
|
||||||
BCLib.LOGGER.info(" - " + e.getKey() + " (client="+ver+", server="+ver+")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoSyncedFiles.size()>0) {
|
if (autoSynFolders.size() > 0) {
|
||||||
|
BCLib.LOGGER.info("Folders offered by Server:");
|
||||||
|
}
|
||||||
|
|
||||||
|
autoSynFolders.forEach(desc -> {
|
||||||
|
//desc contains the fileCache sent from the server, load the local version to get hold of the actual file cache on the client
|
||||||
|
SyncFolderDescriptor localDescriptor = DataExchange.getSyncFolderDescriptor(desc.folderID);
|
||||||
|
if (localDescriptor != null) {
|
||||||
|
BCLib.LOGGER.info(" - " + desc.folderID + " (" + desc.localFolder + ", allowRemove=" + desc.removeAdditionalFiles + ")");
|
||||||
|
localDescriptor.invalidateCache();
|
||||||
|
|
||||||
|
if (desc.removeAdditionalFiles) {
|
||||||
|
List<AutoSyncID.ForDirectFileRequest> additionalFiles = localDescriptor.relativeFilesStream()
|
||||||
|
.filter(subFile -> !desc.hasRelativeFile(subFile))
|
||||||
|
.map(subFile -> new AutoSyncID.ForDirectFileRequest(desc.folderID, desc.localFolder.resolve(subFile.relPath).toFile()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
additionalFiles.forEach(aid -> BCLib.LOGGER.info(" * Removing " + aid.relFile));
|
||||||
|
filesToRemove.addAll(additionalFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
desc.relativeFilesStream().forEach(subFile -> {
|
||||||
|
SubFile localSubFile = localDescriptor.getLocalSubFile(subFile.relPath);
|
||||||
|
if (localSubFile != null){
|
||||||
|
//the file exists locally, check if the hashes match
|
||||||
|
if (!localSubFile.hash.equals(subFile.hash)){
|
||||||
|
BCLib.LOGGER.info(" * Changed " + subFile.relPath);
|
||||||
|
filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, new File(subFile.relPath)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//the file is missing locally
|
||||||
|
BCLib.LOGGER.info(" * Missing " + subFile.relPath);
|
||||||
|
filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, new File(subFile.relPath)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//free some memory
|
||||||
|
localDescriptor.invalidateCache();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BCLib.LOGGER.info(" - " + desc.folderID + " (Failed to find)");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processSingleFileSync(final List<AutoSyncID> filesToRequest) {
|
||||||
|
final boolean debugHashes = Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "debugHashes", false);
|
||||||
|
|
||||||
|
if (autoSyncedFiles.size() > 0) {
|
||||||
BCLib.LOGGER.info("Files offered by Server:");
|
BCLib.LOGGER.info("Files offered by Server:");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Handle single sync files
|
||||||
|
//Single files need to be registered for sync on both client and server
|
||||||
|
//There are no restrictions to the target folder, but the client decides the final
|
||||||
|
//location.
|
||||||
for (DataExchange.AutoSyncTriple e : autoSyncedFiles) {
|
for (DataExchange.AutoSyncTriple e : autoSyncedFiles) {
|
||||||
String actionString = "";
|
String actionString = "";
|
||||||
FileContentWrapper contentWrapper = new FileContentWrapper(e.serverContent);
|
FileContentWrapper contentWrapper = new FileContentWrapper(e.serverContent);
|
||||||
if (e.localMatch == null) {
|
if (e.localMatch == null) {
|
||||||
actionString = "(new, prepare update)";
|
actionString = "(new, prepare update)";
|
||||||
filesToRequest.add(new AutoSyncID(e.serverHash.modID, e.serverHash.uniqueID));
|
filesToRequest.add(new AutoSyncID(e.serverHash.modID, e.serverHash.uniqueID));
|
||||||
} else if (e.localMatch.needTransfer.test(e.localMatch.getFileHash(), e.serverHash, contentWrapper)) {
|
}
|
||||||
|
else if (e.localMatch.needTransfer.test(e.localMatch.getFileHash(), e.serverHash, contentWrapper)) {
|
||||||
actionString = "(prepare update)";
|
actionString = "(prepare update)";
|
||||||
//we did not yet receive the new content
|
//we did not yet receive the new content
|
||||||
if (contentWrapper.getRawContent() == null) {
|
if (contentWrapper.getRawContent() == null) {
|
||||||
filesToRequest.add(new AutoSyncID(e.serverHash.modID, e.serverHash.uniqueID));
|
filesToRequest.add(new AutoSyncID(e.serverHash.modID, e.serverHash.uniqueID));
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
filesToRequest.add(new AutoSyncID.WithContentOverride(e.serverHash.modID, e.serverHash.uniqueID, contentWrapper, e.localMatch.fileName));
|
filesToRequest.add(new AutoSyncID.WithContentOverride(e.serverHash.modID, e.serverHash.uniqueID, contentWrapper, e.localMatch.fileName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,20 +245,48 @@ public class HelloClient extends DataHandler {
|
||||||
BCLib.LOGGER.info(" * local Content " + (contentWrapper.getRawContent() == null));
|
BCLib.LOGGER.info(" * local Content " + (contentWrapper.getRawContent() == null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (filesToRequest.size()>0 && SendFiles.acceptFiles()) {
|
|
||||||
showDownloadConfigs(client, filesToRequest);
|
|
||||||
|
@Override
|
||||||
|
protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) {
|
||||||
|
final String localBclibVersion = getBCLibVersion();
|
||||||
|
BCLib.LOGGER.info("Received Hello from Server. (client=" + localBclibVersion + ", server=" + bclibVersion + ")");
|
||||||
|
|
||||||
|
// if (DataFixerAPI.getModVersion(localBclibVersion) != DataFixerAPI.getModVersion(bclibVersion)){
|
||||||
|
// showBCLibError(client);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
final List<AutoSyncID> filesToRequest = new ArrayList<>(2);
|
||||||
|
final List<AutoSyncID.ForDirectFileRequest> filesToRemove = new ArrayList<>(2);
|
||||||
|
|
||||||
|
for (Entry<String, String> e : modVersion.entrySet()) {
|
||||||
|
String ver = getModVersion(e.getKey());
|
||||||
|
BCLib.LOGGER.info(" - " + e.getKey() + " (client=" + ver + ", server=" + ver + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
processSingleFileSync(filesToRequest);
|
||||||
|
processAutoSyncFolder(filesToRequest, filesToRemove);
|
||||||
|
|
||||||
|
//Handle folder sync
|
||||||
|
//Both client and server need to know about the folder you want to sync
|
||||||
|
//Files can only get placed within that folder
|
||||||
|
|
||||||
|
if ((filesToRequest.size() > 0 || filesToRemove.size() > 0) && SendFiles.acceptFiles()) {
|
||||||
|
showDownloadConfigs(client, filesToRequest, filesToRemove);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
protected void showBCLibError(Minecraft client){
|
protected void showBCLibError(Minecraft client) {
|
||||||
BCLib.LOGGER.error("BCLib differs on client and server.");
|
BCLib.LOGGER.error("BCLib differs on client and server.");
|
||||||
client.setScreen(new WarnBCLibVersionMismatch((download) -> {
|
client.setScreen(new WarnBCLibVersionMismatch((download) -> {
|
||||||
Minecraft.getInstance().setScreen((Screen)null);
|
Minecraft.getInstance()
|
||||||
if (download){
|
.setScreen((Screen) null);
|
||||||
requestBCLibDownload((hadErrors)->{
|
if (download) {
|
||||||
|
requestBCLibDownload((hadErrors) -> {
|
||||||
client.stop();
|
client.stop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -226,38 +294,45 @@ public class HelloClient extends DataHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
protected void showDownloadConfigs(Minecraft client, List<AutoSyncID> files){
|
protected void showDownloadConfigs(Minecraft client, List<AutoSyncID> files, final List<AutoSyncID.ForDirectFileRequest> filesToRemove) {
|
||||||
client.setScreen(new SyncFilesScreen((download) -> {
|
client.setScreen(new SyncFilesScreen((download) -> {
|
||||||
Minecraft.getInstance().setScreen((Screen)null);
|
Minecraft.getInstance()
|
||||||
if (download){
|
.setScreen((Screen) null);
|
||||||
|
if (download) {
|
||||||
BCLib.LOGGER.info("Updating local Files:");
|
BCLib.LOGGER.info("Updating local Files:");
|
||||||
List<AutoSyncID.WithContentOverride> localChanges = new ArrayList<>(files.toArray().length);
|
List<AutoSyncID.WithContentOverride> localChanges = new ArrayList<>(files.toArray().length);
|
||||||
List<AutoSyncID> requestFiles = new ArrayList<>(files.toArray().length);
|
List<AutoSyncID> requestFiles = new ArrayList<>(files.toArray().length);
|
||||||
|
|
||||||
files.forEach(aid -> {
|
files.forEach(aid -> {
|
||||||
if (aid instanceof WithContentOverride) {
|
if (aid instanceof WithContentOverride) {
|
||||||
final WithContentOverride aidc = (WithContentOverride)aid;
|
final WithContentOverride aidc = (WithContentOverride) aid;
|
||||||
BCLib.LOGGER.info(" - " + aid + " (updating Content)");
|
BCLib.LOGGER.info(" - " + aid + " (updating Content)");
|
||||||
|
|
||||||
SendFiles.writeSyncedFile(aid, aidc.contentWrapper.getRawContent(), aidc.localFile);
|
SendFiles.writeSyncedFile(aid, aidc.contentWrapper.getRawContent(), aidc.localFile);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
requestFiles.add(aid);
|
requestFiles.add(aid);
|
||||||
BCLib.LOGGER.info(" - " + aid + " (requesting)");
|
BCLib.LOGGER.info(" - " + aid + " (requesting)");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
filesToRemove.forEach(aid -> {
|
||||||
|
BCLib.LOGGER.info(" - " + aid.relFile + " (removing)");
|
||||||
|
aid.relFile.delete();
|
||||||
|
});
|
||||||
|
|
||||||
requestFileDownloads(requestFiles);
|
requestFileDownloads(requestFiles);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestBCLibDownload(Consumer<Boolean> whenFinished){
|
private void requestBCLibDownload(Consumer<Boolean> whenFinished) {
|
||||||
BCLib.LOGGER.warning("Starting download of BCLib");
|
BCLib.LOGGER.warning("Starting download of BCLib");
|
||||||
whenFinished.accept(true);
|
whenFinished.accept(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestFileDownloads(List<AutoSyncID> files){
|
private void requestFileDownloads(List<AutoSyncID> files) {
|
||||||
BCLib.LOGGER.info("Starting download of Files:" + files.size());
|
BCLib.LOGGER.info("Starting download of Files:" + files.size());
|
||||||
DataExchangeAPI.send(new RequestFiles(files));
|
DataExchangeAPI.send(new RequestFiles(files));
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,7 @@ public class RequestFiles extends DataHandler {
|
||||||
buf.writeInt(files.size());
|
buf.writeInt(files.size());
|
||||||
|
|
||||||
for (AutoSyncID a : files){
|
for (AutoSyncID a : files){
|
||||||
writeString(buf, a.modID);
|
a.serializeData(buf);
|
||||||
writeString(buf, a.uniqueID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,9 +49,7 @@ public class RequestFiles extends DataHandler {
|
||||||
|
|
||||||
BCLib.LOGGER.info("Client requested " + size + " Files:");
|
BCLib.LOGGER.info("Client requested " + size + " Files:");
|
||||||
for (int i=0; i<size; i++){
|
for (int i=0; i<size; i++){
|
||||||
String modID = readString(buf);
|
AutoSyncID asid = AutoSyncID.deserializeData(buf);
|
||||||
String uID = readString(buf);
|
|
||||||
AutoSyncID asid = new AutoSyncID(modID, uID);
|
|
||||||
files.add(asid);
|
files.add(asid);
|
||||||
BCLib.LOGGER.info(" - " + asid);
|
BCLib.LOGGER.info(" - " + asid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,11 @@ public class SendFiles extends DataHandler {
|
||||||
Path path = fileName.toPath();
|
Path path = fileName.toPath();
|
||||||
BCLib.LOGGER.info(" - Writing " + path + " (" + data.length + " Bytes)");
|
BCLib.LOGGER.info(" - Writing " + path + " (" + data.length + " Bytes)");
|
||||||
try {
|
try {
|
||||||
|
final File parentFile = path.getParent()
|
||||||
|
.toFile();
|
||||||
|
if (!parentFile.exists()){
|
||||||
|
parentFile.mkdirs();
|
||||||
|
}
|
||||||
Files.write(path, data);
|
Files.write(path, data);
|
||||||
DataExchange.didReceiveFile(e, fileName);
|
DataExchange.didReceiveFile(e, fileName);
|
||||||
} catch (IOException ioException) {
|
} catch (IOException ioException) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package ru.bclib.config;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import ru.bclib.BCLib;
|
import ru.bclib.BCLib;
|
||||||
import ru.bclib.api.dataexchange.DataExchangeAPI;
|
import ru.bclib.api.dataexchange.DataExchangeAPI;
|
||||||
import ru.bclib.api.dataexchange.FileHash;
|
import ru.bclib.api.dataexchange.SyncFileHash;
|
||||||
import ru.bclib.api.dataexchange.handler.AutoSyncID;
|
import ru.bclib.api.dataexchange.handler.AutoSyncID;
|
||||||
import ru.bclib.api.dataexchange.handler.FileContentWrapper;
|
import ru.bclib.api.dataexchange.handler.FileContentWrapper;
|
||||||
import ru.bclib.config.ConfigKeeper.BooleanEntry;
|
import ru.bclib.config.ConfigKeeper.BooleanEntry;
|
||||||
|
@ -50,7 +50,7 @@ public abstract class Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean compareForSync(FileHash fileHash, FileHash fileHash1, FileContentWrapper content) {
|
private boolean compareForSync(SyncFileHash syncFileHash, SyncFileHash syncFileHash1, FileContentWrapper content) {
|
||||||
return keeper.compareAndUpdateForSync(content);
|
return keeper.compareAndUpdateForSync(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ public final class ConfigKeeper {
|
||||||
changed = true;
|
changed = true;
|
||||||
me.add(myKey.first + myKey.second, otherValue);
|
me.add(myKey.first + myKey.second, otherValue);
|
||||||
}
|
}
|
||||||
else if (otherValue.isJsonPrimitive()) {
|
else if (otherValue.isJsonPrimitive() || otherValue.isJsonArray() || otherValue.isJsonNull()) {
|
||||||
if (!otherValue.equals(myValue)) {
|
if (!otherValue.equals(myValue)) {
|
||||||
changed = true;
|
changed = true;
|
||||||
me.add(myKey.first + myKey.second, otherValue);
|
me.add(myKey.first + myKey.second, otherValue);
|
||||||
|
@ -93,17 +93,13 @@ public final class ConfigKeeper {
|
||||||
else if (otherValue.isJsonObject()) {
|
else if (otherValue.isJsonObject()) {
|
||||||
changed |= compareAndUpdateForSync(myValue.getAsJsonObject(), otherValue.getAsJsonObject());
|
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
|
else { //no entry, just copy the value from other
|
||||||
changed = true;
|
if (!otherValue.isJsonNull()) {
|
||||||
me.add(otherKey.first + otherKey.second, otherValue);
|
changed = true;
|
||||||
|
temp = find(me, otherKey);
|
||||||
|
me.add(otherKey.first + otherKey.second, otherValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue