Refactored AutoSync

This commit is contained in:
Frank 2021-08-17 11:57:40 +02:00
parent b398e47056
commit 123a5e2dc4
17 changed files with 610 additions and 518 deletions

View file

@ -7,10 +7,10 @@ import net.minecraft.resources.ResourceLocation;
import ru.bclib.api.TagAPI;
import ru.bclib.api.WorldDataAPI;
import ru.bclib.api.dataexchange.DataExchangeAPI;
import ru.bclib.api.dataexchange.handler.HelloClient;
import ru.bclib.api.dataexchange.handler.HelloServer;
import ru.bclib.api.dataexchange.handler.RequestFiles;
import ru.bclib.api.dataexchange.handler.SendFiles;
import ru.bclib.api.dataexchange.handler.autosync.HelloClient;
import ru.bclib.api.dataexchange.handler.autosync.HelloServer;
import ru.bclib.api.dataexchange.handler.autosync.RequestFiles;
import ru.bclib.api.dataexchange.handler.autosync.SendFiles;
import ru.bclib.api.datafixer.DataFixerAPI;
import ru.bclib.config.Configs;
import ru.bclib.recipes.CraftingRecipes;

View file

@ -4,7 +4,9 @@ import com.google.common.collect.Lists;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.network.FriendlyByteBuf;
import ru.bclib.api.dataexchange.handler.AutoSyncID;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.NeedTransferPredicate;
import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID;
import ru.bclib.api.dataexchange.handler.DataExchange;
import ru.bclib.config.Config;
@ -98,7 +100,7 @@ public class DataExchangeAPI extends DataExchange {
* @param fileName The name of the File
*/
public static void addAutoSyncFile(String modID, File fileName) {
getInstance().addAutoSyncFileData(modID, fileName, false, SyncFileHash.NEED_TRANSFER);
AutoSync.addAutoSyncFileData(modID, fileName, false, SyncFileHash.NEED_TRANSFER);
}
/**
@ -113,7 +115,7 @@ public class DataExchangeAPI extends DataExchange {
* @param fileName The name of the File
*/
public static void addAutoSyncFile(String modID, String uniqueID, File fileName) {
getInstance().addAutoSyncFileData(modID, uniqueID, fileName, false, SyncFileHash.NEED_TRANSFER);
AutoSync.addAutoSyncFileData(modID, uniqueID, fileName, false, SyncFileHash.NEED_TRANSFER);
}
/**
@ -131,7 +133,7 @@ public class DataExchangeAPI extends DataExchange {
* @param needTransfer If the predicate returns true, the file needs to get copied to the server.
*/
public static void addAutoSyncFile(String modID, File fileName, NeedTransferPredicate needTransfer) {
getInstance().addAutoSyncFileData(modID, fileName, true, needTransfer);
AutoSync.addAutoSyncFileData(modID, fileName, true, needTransfer);
}
/**
@ -151,7 +153,7 @@ public class DataExchangeAPI extends DataExchange {
* @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, NeedTransferPredicate needTransfer) {
getInstance().addAutoSyncFileData(modID, uniqueID, fileName, true, needTransfer);
AutoSync.addAutoSyncFileData(modID, uniqueID, fileName, true, needTransfer);
}
/**
@ -163,7 +165,7 @@ public class DataExchangeAPI extends DataExchange {
* @param callback A Function that receives the AutoSyncID as well as the Filename.
*/
public static void addOnWriteCallback(BiConsumer<AutoSyncID, File> callback) {
onWriteCallbacks.add(callback);
AutoSync.addOnWriteCallback(callback);
}
/**
@ -175,7 +177,7 @@ public class DataExchangeAPI extends DataExchange {
* @return The path to the sync-folder
*/
public static File getModSyncFolder(String modID) {
File fl = SYNC_FOLDER.localFolder.resolve(modID.replace(".", "-")
File fl = AutoSync.SYNC_FOLDER.localFolder.resolve(modID.replace(".", "-")
.replace(":", "-")
.replace("\\", "-")
.replace("/", "-"))

View file

@ -1,8 +1,8 @@
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 ru.bclib.api.dataexchange.handler.autosync.AutoSync.NeedTransferPredicate;
import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID;
import java.io.File;
import java.util.Objects;
@ -36,7 +36,7 @@ public class SyncFileHash extends AutoSyncID {
}
final static DataExchange.NeedTransferPredicate NEED_TRANSFER = (clientHash, serverHash, content)-> !clientHash.equals(serverHash);
final static NeedTransferPredicate NEED_TRANSFER = (clientHash, serverHash, content)-> !clientHash.equals(serverHash);
@Override
public String toString() {

View file

@ -4,255 +4,17 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
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.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.api.dataexchange.SyncFileHash;
import ru.bclib.api.dataexchange.handler.AutoSyncID.ForDirectFileRequest;
import ru.bclib.config.Configs;
import ru.bclib.util.PathUtil;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
abstract public class DataExchange {
public final static SyncFolderDescriptor SYNC_FOLDER = new SyncFolderDescriptor("BCLIB-SYNC", FabricLoader.getInstance()
.getGameDir()
.resolve("bclib-sync")
.normalize()
.toAbsolutePath(), true);
final List<SyncFolderDescriptor> syncFolderDescriptions = Arrays.asList(SYNC_FOLDER);
public static class SyncFolderDescriptor {
static class SubFile {
public final String relPath;
public final FileHash hash;
SubFile(String relPath, FileHash hash) {
this.relPath = relPath;
this.hash = hash;
}
@Override
public String toString() {
return relPath;
}
public void serialize(FriendlyByteBuf buf) {
DataHandler.writeString(buf, relPath);
hash.serialize(buf);
}
public static SubFile deserialize(FriendlyByteBuf buf) {
final String relPath = DataHandler.readString(buf);
FileHash hash = FileHash.deserialize(buf);
return new SubFile(relPath, hash);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof String) return relPath.equals(o);
if (!(o instanceof SubFile)) return false;
SubFile subFile = (SubFile) o;
return relPath.equals(subFile.relPath);
}
@Override
public int hashCode() {
return relPath.hashCode();
}
}
@NotNull
public final String folderID;
public final boolean removeAdditionalFiles;
@NotNull
public final Path localFolder;
private List<SubFile> fileCache;
SyncFolderDescriptor(String folderID, Path localFolder, boolean removeAdditionalFiles) {
this.removeAdditionalFiles = removeAdditionalFiles;
this.folderID = folderID;
this.localFolder = localFolder;
fileCache = null;
}
@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);
PathUtil.fileWalker(localFolder.toFile(), p -> fileCache.add(new SubFile(localFolder.relativize(p)
.toString(), FileHash.create(p.toFile()))));
/*//this tests if we can trick the system to load files that are not beneath the base-folder
if (!BCLib.isClient()) {
fileCache.add(new SubFile("../breakout.json", FileHash.create(mapAbsolute("../breakout.json").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();
}
public Path mapAbsolute(String relPath) {
return this.localFolder.resolve(relPath)
.normalize();
}
public Path mapAbsolute(SubFile subFile) {
return this.localFolder.resolve(subFile.relPath)
.normalize();
}
public boolean acceptChildElements(Path absPath) {
return PathUtil.isChildOf(this.localFolder, absPath);
}
public boolean acceptChildElements(SubFile subFile) {
return acceptChildElements(mapAbsolute(subFile));
}
public boolean discardChildElements(SubFile subFile) {
return !acceptChildElements(subFile);
}
}
@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;
@ -266,7 +28,7 @@ abstract public class DataExchange {
protected ConnectorServerside server;
protected ConnectorClientside client;
protected final Set<DataHandlerDescriptor> descriptors;
private final List<AutoFileSyncEntry> autoSyncFiles = new ArrayList<>(4);
private boolean didLoadSyncFolder = false;
@ -280,9 +42,6 @@ abstract public class DataExchange {
public Set<DataHandlerDescriptor> getDescriptors() { return descriptors; }
public List<AutoFileSyncEntry> getAutoSyncFiles() {
return autoSyncFiles;
}
@Environment(EnvType.CLIENT)
protected void initClientside() {
@ -344,101 +103,6 @@ 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.
* <p>
* 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}
* for comparison is sufficient.
*/
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 SyncFileHash#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.
* <p>
* 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}
* 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));
}
/**
* Called when {@code SendFiles} received a File on the Client and wrote it to the FileSystem.
* <p>
* This is the place where reload Code should go.
*
* @param aid The ID of the received File
* @param file The location of the FIle on the client
*/
static void didReceiveFile(AutoSyncID aid, File file) {
onWriteCallbacks.forEach(fkt -> fkt.accept(aid, file));
}
private List<String> syncFolderContent;
protected List<String> getSyncFolderContent() {
if (syncFolderContent == null) {
return new ArrayList<>(0);
}
return syncFolderContent;
}
//we call this from HelloServer to prepare transfer
protected void loadSyncFolder() {
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offersSyncFolders", true)) {
syncFolderDescriptions.forEach(desc -> desc.loadCache());
}
}
protected static SyncFolderDescriptor getSyncFolderDescriptor(String folderID) {
return ((DataExchange) getInstance()).syncFolderDescriptions.stream()
.filter(d -> d.equals(folderID))
.findFirst()
.orElse(null);
}
protected static Path localBasePathForFolderID(String folderID) {
final SyncFolderDescriptor desc = getSyncFolderDescriptor(folderID);
if (desc != null) {
return desc.localFolder;
}
else {
BCLib.LOGGER.warning("Unknown Sync-Folder ID '" + folderID + "'");
return null;
}
}
protected void registerSyncFolder(String folderID, Path localBaseFolder, boolean removeAdditionalFiles) {
localBaseFolder = localBaseFolder.normalize();
if (PathUtil.isChildOf(PathUtil.GAME_FOLDER, localBaseFolder)) {
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);
}
}
else {
BCLib.LOGGER.error(localBaseFolder + " (from " + folderID + ") is outside the game directory " + PathUtil.GAME_FOLDER + ". Sync is not allowed.");
}
}
}

View file

@ -1,10 +1,10 @@
package ru.bclib.api.dataexchange.handler;
package ru.bclib.api.dataexchange.handler.autosync;
import net.minecraft.network.FriendlyByteBuf;
import ru.bclib.api.dataexchange.DataHandler;
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.api.dataexchange.handler.autosync.AutoSync.NeedTransferPredicate;
import ru.bclib.api.dataexchange.handler.autosync.SyncFolderDescriptor.SubFile;
import ru.bclib.util.Pair;
import ru.bclib.util.Triple;
@ -14,7 +14,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
class AutoFileSyncEntry extends AutoSyncID {
static class ForDirectFileRequest extends AutoFileSyncEntry {
static class ForDirectFileRequest extends AutoFileSyncEntry {
final File relFile;
ForDirectFileRequest(String syncID, File relFile, File absFile) {
@ -30,10 +30,10 @@ class AutoFileSyncEntry extends AutoSyncID {
return res;
}
static AutoFileSyncEntry.ForDirectFileRequest finishDeserializeContent(String syncID, FriendlyByteBuf buf){
static AutoFileSyncEntry.ForDirectFileRequest finishDeserializeContent(String syncID, FriendlyByteBuf buf) {
final String relFile = DataHandler.readString(buf);
SyncFolderDescriptor desc = DataExchange.getSyncFolderDescriptor(syncID);
if (desc!=null) {
SyncFolderDescriptor desc = AutoSync.getSyncFolderDescriptor(syncID);
if (desc != null) {
//ensures that the file is not above the base-folder
if (desc.acceptChildElements(desc.mapAbsolute(relFile))) {
return new AutoFileSyncEntry.ForDirectFileRequest(syncID, new File(relFile), desc.localFolder.resolve(relFile)
@ -49,16 +49,17 @@ class AutoFileSyncEntry extends AutoSyncID {
return uniqueID + " - " + relFile;
}
}
public final DataExchange.NeedTransferPredicate needTransfer;
public final NeedTransferPredicate needTransfer;
public final File fileName;
public final boolean requestContent;
private SyncFileHash hash;
AutoFileSyncEntry(String modID, File fileName, boolean requestContent, DataExchange.NeedTransferPredicate needTransfer) {
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, DataExchange.NeedTransferPredicate needTransfer) {
AutoFileSyncEntry(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) {
super(modID, uniqueID);
this.needTransfer = needTransfer;
this.fileName = fileName;
@ -97,9 +98,10 @@ class AutoFileSyncEntry extends AutoSyncID {
byte[] data = deserializeFileContent(buf);
AutoFileSyncEntry entry;
if (AutoSyncID.ForDirectFileRequest.MOD_ID.equals(modID)){
if (AutoSyncID.ForDirectFileRequest.MOD_ID.equals(modID)) {
entry = AutoFileSyncEntry.ForDirectFileRequest.finishDeserializeContent(uniqueID, buf);
} else {
}
else {
entry = AutoFileSyncEntry.findMatching(modID, uniqueID);
}
return new Triple<>(entry, data, new AutoSyncID(modID, uniqueID));
@ -115,10 +117,10 @@ class AutoFileSyncEntry extends AutoSyncID {
}
}
public static DataExchange.AutoSyncTriple deserializeAndMatch(FriendlyByteBuf buf) {
public static AutoSync.AutoSyncTriple deserializeAndMatch(FriendlyByteBuf buf) {
Pair<SyncFileHash, byte[]> e = deserialize(buf);
AutoFileSyncEntry match = findMatching(e.first);
return new DataExchange.AutoSyncTriple(e.first, e.second, match);
return new AutoSync.AutoSyncTriple(e.first, e.second, match);
}
public static Pair<SyncFileHash, byte[]> deserialize(FriendlyByteBuf buf) {
@ -154,15 +156,13 @@ class AutoFileSyncEntry extends AutoSyncID {
public static AutoFileSyncEntry findMatching(AutoSyncID aid) {
if (aid instanceof AutoSyncID.ForDirectFileRequest) {
AutoSyncID.ForDirectFileRequest freq = (AutoSyncID.ForDirectFileRequest) aid;
SyncFolderDescriptor desc = DataExchange.getSyncFolderDescriptor(freq.uniqueID);
SyncFolderDescriptor desc = AutoSync.getSyncFolderDescriptor(freq.uniqueID);
if (desc != null) {
SubFile subFile = desc.getLocalSubFile(freq.relFile.toString());
if (subFile != null) {
final File absPath = desc
.localFolder
.resolve(subFile.relPath)
.normalize()
.toFile();
final File absPath = desc.localFolder.resolve(subFile.relPath)
.normalize()
.toFile();
return new AutoFileSyncEntry.ForDirectFileRequest(freq.uniqueID, new File(subFile.relPath), absPath);
}
}
@ -172,11 +172,10 @@ class AutoFileSyncEntry extends AutoSyncID {
}
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);
return AutoSync.getAutoSyncFiles()
.stream()
.filter(asf -> asf.modID.equals(modID) && asf.uniqueID.equals(uniqueID))
.findFirst()
.orElse(null);
}
}

View file

@ -0,0 +1,210 @@
package ru.bclib.api.dataexchange.handler.autosync;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.FabricLoader;
import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.SyncFileHash;
import ru.bclib.config.Configs;
import ru.bclib.util.PathUtil;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;
public class AutoSync {
public static final String MAIN_SYNC_CATEGORY = "client_sync";
public final static SyncFolderDescriptor SYNC_FOLDER = new SyncFolderDescriptor("BCLIB-SYNC", FabricLoader.getInstance()
.getGameDir()
.resolve("bclib-sync")
.normalize()
.toAbsolutePath(), true);
@FunctionalInterface
public interface NeedTransferPredicate {
public boolean test(SyncFileHash clientHash, SyncFileHash serverHash, FileContentWrapper content);
}
@Environment(EnvType.CLIENT)
public static class ClientConfig {
@Environment(EnvType.CLIENT)
static boolean shouldClientConfigPrintDebugHashes() {
return Configs.CLIENT_CONFIG.getBoolean(MAIN_SYNC_CATEGORY, "debugHashes", true);
}
@Environment(EnvType.CLIENT)
static boolean isClientConfigAllowingAutoSync() {
return Configs.CLIENT_CONFIG.getBoolean(MAIN_SYNC_CATEGORY, "enabled", true);
}
@Environment(EnvType.CLIENT)
static boolean isClientConfigAcceptingFiles() {
return Configs.CLIENT_CONFIG.getBoolean(MAIN_SYNC_CATEGORY, "acceptFiles", true) && isClientConfigAllowingAutoSync();
}
@Environment(EnvType.CLIENT)
static boolean isClientConfigAcceptingFolders() {
return Configs.CLIENT_CONFIG.getBoolean(MAIN_SYNC_CATEGORY, "acceptFolders", true) && isClientConfigAllowingAutoSync();
}
}
public static class Config {
static boolean isAllowingAutoSync() {
return Configs.MAIN_CONFIG.getBoolean(MAIN_SYNC_CATEGORY, "enabled", true);
}
static boolean isOfferingFiles() {
return Configs.MAIN_CONFIG.getBoolean(MAIN_SYNC_CATEGORY, "offerFiles", true) && ClientConfig.isClientConfigAllowingAutoSync();
}
static boolean isOfferingFolders() {
return Configs.MAIN_CONFIG.getBoolean(MAIN_SYNC_CATEGORY, "offerFolders", true) && ClientConfig.isClientConfigAllowingAutoSync();
}
static boolean isOfferingMods() {
return Configs.MAIN_CONFIG.getBoolean(MAIN_SYNC_CATEGORY, "offerMods", true) && ClientConfig.isClientConfigAllowingAutoSync();
}
}
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;
}
}
// ##### File Syncing
protected final static List<BiConsumer<AutoSyncID, File>> onWriteCallbacks = new ArrayList<>(2);
/**
* Register a function that is called whenever the client receives a file from the server and replaced toe local
* file with the new content.
* <p>
* This callback is usefull if you need to reload the new content before the game is quit.
*
* @param callback A Function that receives the AutoSyncID as well as the Filename.
*/
public static void addOnWriteCallback(BiConsumer<AutoSyncID, File> callback) {
onWriteCallbacks.add(callback);
}
private static final List<AutoFileSyncEntry> autoSyncFiles = new ArrayList<>(4);
public static List<AutoFileSyncEntry> getAutoSyncFiles() {
return autoSyncFiles;
}
/**
* 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.
* <p>
* 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}
* for comparison is sufficient.
*/
public static 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 SyncFileHash#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.
* <p>
* 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}
* for comparison is sufficient.
*/
public static void addAutoSyncFileData(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate 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.
* <p>
* This is the place where reload Code should go.
*
* @param aid The ID of the received File
* @param file The location of the FIle on the client
*/
static void didReceiveFile(AutoSyncID aid, File file) {
onWriteCallbacks.forEach(fkt -> fkt.accept(aid, file));
}
// ##### Folder Syncing
static final List<SyncFolderDescriptor> syncFolderDescriptions = Arrays.asList(SYNC_FOLDER);
private List<String> syncFolderContent;
protected List<String> getSyncFolderContent() {
if (syncFolderContent == null) {
return new ArrayList<>(0);
}
return syncFolderContent;
}
//we call this from HelloServer to prepare transfer
protected static void loadSyncFolder() {
if (Configs.MAIN_CONFIG.getBoolean(AutoSync.MAIN_SYNC_CATEGORY, "offersSyncFolders", true)) {
syncFolderDescriptions.forEach(desc -> desc.loadCache());
}
}
protected static SyncFolderDescriptor getSyncFolderDescriptor(String folderID) {
return syncFolderDescriptions.stream()
.filter(d -> d.equals(folderID))
.findFirst()
.orElse(null);
}
protected static Path localBasePathForFolderID(String folderID) {
final SyncFolderDescriptor desc = getSyncFolderDescriptor(folderID);
if (desc != null) {
return desc.localFolder;
}
else {
BCLib.LOGGER.warning("Unknown Sync-Folder ID '" + folderID + "'");
return null;
}
}
public static void registerSyncFolder(String folderID, Path localBaseFolder, boolean removeAdditionalFiles) {
localBaseFolder = localBaseFolder.normalize();
if (PathUtil.isChildOf(PathUtil.GAME_FOLDER, localBaseFolder)) {
final SyncFolderDescriptor desc = new SyncFolderDescriptor(folderID, localBaseFolder, removeAdditionalFiles);
if (syncFolderDescriptions.contains(desc)) {
BCLib.LOGGER.warning("Tried to override Folder Sync '" + folderID + "' again.");
}
else {
syncFolderDescriptions.add(desc);
}
}
else {
BCLib.LOGGER.error(localBaseFolder + " (from " + folderID + ") is outside the game directory " + PathUtil.GAME_FOLDER + ". Sync is not allowed.");
}
}
}

View file

@ -1,4 +1,4 @@
package ru.bclib.api.dataexchange.handler;
package ru.bclib.api.dataexchange.handler.autosync;
import net.minecraft.network.FriendlyByteBuf;
import org.jetbrains.annotations.NotNull;

View file

@ -1,4 +1,4 @@
package ru.bclib.api.dataexchange.handler;
package ru.bclib.api.dataexchange.handler.autosync;
import ru.bclib.BCLib;

View file

@ -1,10 +1,8 @@
package ru.bclib.api.dataexchange.handler;
package ru.bclib.api.dataexchange.handler.autosync;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
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.network.FriendlyByteBuf;
@ -14,13 +12,15 @@ 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.handler.AutoSyncID.WithContentOverride;
import ru.bclib.api.dataexchange.handler.DataExchange.SyncFolderDescriptor;
import ru.bclib.api.dataexchange.handler.DataExchange.SyncFolderDescriptor.SubFile;
import ru.bclib.api.dataexchange.handler.DataExchange;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.ClientConfig;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.Config;
import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID.WithContentOverride;
import ru.bclib.api.dataexchange.handler.autosync.SyncFolderDescriptor.SubFile;
import ru.bclib.api.datafixer.DataFixerAPI;
import ru.bclib.config.Configs;
import ru.bclib.gui.screens.SyncFilesScreen;
import ru.bclib.gui.screens.WarnBCLibVersionMismatch;
import ru.bclib.util.PathUtil;
import java.io.File;
import java.util.ArrayList;
@ -28,7 +28,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@ -44,36 +43,18 @@ public class HelloClient extends DataHandler {
super(DESCRIPTOR.IDENTIFIER, true);
}
public static ModContainer getModContainer(String modID) {
Optional<ModContainer> optional = FabricLoader.getInstance()
.getModContainer(modID);
return optional.orElse(null);
}
public static String getModVersion(String modID) {
Optional<ModContainer> optional = FabricLoader.getInstance()
.getModContainer(modID);
if (optional.isPresent()) {
ModContainer modContainer = optional.get();
return modContainer.getMetadata()
.getVersion()
.toString();
}
return "0.0.0";
}
static String getBCLibVersion() {
return getModVersion(BCLib.MOD_ID);
return PathUtil.getModVersion(BCLib.MOD_ID);
}
@Override
protected boolean prepareData(boolean isClient) {
if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "enabled", true)) {
if (!Config.isAllowingAutoSync()) {
BCLib.LOGGER.info("Auto-Sync was disabled on the server.");
return false;
}
DataExchange.getInstance().loadSyncFolder();
AutoSync.loadSyncFolder();
return true;
}
@ -86,12 +67,12 @@ public class HelloClient extends DataHandler {
//write BCLibVersion (=protocol version)
buf.writeInt(DataFixerAPI.getModVersion(vbclib));
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offerMods", true)) {
if (Config.isOfferingMods()) {
//write Plugin Versions
buf.writeInt(mods.size());
for (String modID : mods) {
writeString(buf, modID);
final String ver = getModVersion(modID);
final String ver = PathUtil.getModVersion(modID);
buf.writeInt(DataFixerAPI.getModVersion(ver));
BCLib.LOGGER.info(" - Listing Mod " + modID + " v" + ver);
}
@ -101,9 +82,9 @@ public class HelloClient extends DataHandler {
buf.writeInt(0);
}
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offerFiles", true)) {
if (Config.isOfferingFiles()) {
//do only include files that exist on the server
final List<AutoFileSyncEntry> existingAutoSyncFiles = DataExchange.getInstance()
final List<AutoFileSyncEntry> existingAutoSyncFiles = AutoSync
.getAutoSyncFiles()
.stream()
.filter(e -> e.fileName.exists())
@ -121,9 +102,9 @@ public class HelloClient extends DataHandler {
buf.writeInt(0);
}
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offersSyncFolders", true)) {
buf.writeInt(((DataExchange) DataExchange.getInstance()).syncFolderDescriptions.size());
((DataExchange) DataExchange.getInstance()).syncFolderDescriptions.forEach(desc -> {
if (Config.isOfferingFolders()) {
buf.writeInt(AutoSync.syncFolderDescriptions.size());
AutoSync.syncFolderDescriptions.forEach(desc -> {
BCLib.LOGGER.info(" - Offering Folder " + desc.localFolder + " (allowDelete=" + desc.removeAdditionalFiles + ")");
desc.serialize(buf);
});
@ -132,12 +113,11 @@ public class HelloClient extends DataHandler {
BCLib.LOGGER.info("Server will not offer Sync Folders.");
buf.writeInt(0);
}
Configs.MAIN_CONFIG.saveChanges();
}
String bclibVersion = "0.0.0";
Map<String, String> modVersion = new HashMap<>();
List<DataExchange.AutoSyncTriple> autoSyncedFiles = null;
List<AutoSync.AutoSyncTriple> autoSyncedFiles = null;
List<SyncFolderDescriptor> autoSynFolders = null;
@Override
@ -159,7 +139,7 @@ public class HelloClient extends DataHandler {
autoSyncedFiles = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
//System.out.println("Deserializing ");
DataExchange.AutoSyncTriple t = AutoFileSyncEntry.deserializeAndMatch(buf);
AutoSync.AutoSyncTriple t = AutoFileSyncEntry.deserializeAndMatch(buf);
autoSyncedFiles.add(t);
//System.out.println(t.first);
}
@ -177,7 +157,7 @@ public class HelloClient extends DataHandler {
}
private void processAutoSyncFolder(final List<AutoSyncID> filesToRequest, final List<AutoSyncID.ForDirectFileRequest> filesToRemove) {
if (!Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "syncFolders", true)) {
if (!ClientConfig.isClientConfigAcceptingFolders()) {
return;
}
@ -187,7 +167,7 @@ public class HelloClient extends DataHandler {
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);
SyncFolderDescriptor localDescriptor = AutoSync.getSyncFolderDescriptor(desc.folderID);
if (localDescriptor != null) {
BCLib.LOGGER.info(" - " + desc.folderID + " (" + desc.localFolder + ", allowRemove=" + desc.removeAdditionalFiles + ")");
localDescriptor.invalidateCache();
@ -220,7 +200,8 @@ public class HelloClient extends DataHandler {
if (!localSubFile.hash.equals(subFile.hash)) {
BCLib.LOGGER.info(" * " + subFile.relPath + " (changed)");
filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, new File(subFile.relPath)));
} else {
}
else {
BCLib.LOGGER.info(" * " + subFile.relPath);
}
}
@ -241,7 +222,7 @@ public class HelloClient extends DataHandler {
}
private void processSingleFileSync(final List<AutoSyncID> filesToRequest) {
final boolean debugHashes = Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "debugHashes", false);
final boolean debugHashes = ClientConfig.shouldClientConfigPrintDebugHashes();
if (autoSyncedFiles.size() > 0) {
BCLib.LOGGER.info("Files offered by Server:");
@ -251,7 +232,7 @@ public class HelloClient extends DataHandler {
//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 (AutoSync.AutoSyncTriple e : autoSyncedFiles) {
String actionString = "";
FileContentWrapper contentWrapper = new FileContentWrapper(e.serverContent);
if (e.localMatch == null) {
@ -281,7 +262,7 @@ public class HelloClient extends DataHandler {
@Override
protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) {
if (!Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "enabled", true)) {
if (!ClientConfig.isClientConfigAllowingAutoSync()) {
BCLib.LOGGER.info("Auto-Sync was disabled on the client.");
return;
}
@ -297,7 +278,7 @@ public class HelloClient extends DataHandler {
final List<AutoSyncID.ForDirectFileRequest> filesToRemove = new ArrayList<>(2);
for (Entry<String, String> e : modVersion.entrySet()) {
String ver = getModVersion(e.getKey());
String ver = PathUtil.getModVersion(e.getKey());
BCLib.LOGGER.info(" - " + e.getKey() + " (client=" + ver + ", server=" + ver + ")");
}
@ -308,7 +289,7 @@ public class HelloClient extends DataHandler {
//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()) {
if ((filesToRequest.size() > 0 || filesToRemove.size() > 0) && ClientConfig.isClientConfigAcceptingFiles()) {
showDownloadConfigs(client, filesToRequest, filesToRemove);
return;
}

View file

@ -1,4 +1,4 @@
package ru.bclib.api.dataexchange.handler;
package ru.bclib.api.dataexchange.handler.autosync;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.minecraft.client.Minecraft;
@ -9,56 +9,58 @@ 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.handler.autosync.AutoSync.ClientConfig;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.Config;
import ru.bclib.api.datafixer.DataFixerAPI;
import ru.bclib.config.Configs;
import java.io.File;
/**
* 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.
* <table>
* <caption>Description</caption>
* <tr>
* <th>Server</th>
* <th></th>
* <th>Client</th>
* <th></th>
* </tr>
* <tr>
* <td colspan="4">Player enters World</td>
* </tr>
* <tr>
* <td></td>
* <td>&lt;--</td>
* <td>{@link HelloServer}</td>
* <td>Sends the current BLib-Version installed on the Client</td>
* </tr>
* <tr>
* <td>{@link HelloClient}</td>
* <td>--&gt;</td>
* <td></td>
* <td>Sends the current BClIb-Version, the Version of all Plugins and data for all AutpoSync-Files
* ({@link DataExchangeAPI#addAutoSyncFile(String, File)} on the Server</td>
* </tr>
* <tr>
* <td></td>
* <td>&lt;--</td>
* <td>{@link RequestFiles}</td>
* <td>Request missing or out of sync Files from the Server</td>
* </tr>
* <tr>
* <td>{@link SendFiles}</td>
* <td>--&gt;</td>
* <td></td>
* <td>Send Files from the Server to the Client</td>
* </tr>
* </table>
* <table>
* <caption>Description</caption>
* <tr>
* <th>Server</th>
* <th></th>
* <th>Client</th>
* <th></th>
* </tr>
* <tr>
* <td colspan="4">Player enters World</td>
* </tr>
* <tr>
* <td></td>
* <td>&lt;--</td>
* <td>{@link HelloServer}</td>
* <td>Sends the current BLib-Version installed on the Client</td>
* </tr>
* <tr>
* <td>{@link HelloClient}</td>
* <td>--&gt;</td>
* <td></td>
* <td>Sends the current BClIb-Version, the Version of all Plugins and data for all AutpoSync-Files
* ({@link DataExchangeAPI#addAutoSyncFile(String, File)} on the Server</td>
* </tr>
* <tr>
* <td></td>
* <td>&lt;--</td>
* <td>{@link RequestFiles}</td>
* <td>Request missing or out of sync Files from the Server</td>
* </tr>
* <tr>
* <td>{@link SendFiles}</td>
* <td>--&gt;</td>
* <td></td>
* <td>Send Files from the Server to the Client</td>
* </tr>
* </table>
*/
public class HelloServer extends DataHandler {
public static DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "hello_server"), HelloServer::new, true, false);
protected String bclibVersion ="0.0.0";
protected String bclibVersion = "0.0.0";
public HelloServer() {
super(DESCRIPTOR.IDENTIFIER, false);
}
@ -66,7 +68,7 @@ public class HelloServer extends DataHandler {
@Override
protected boolean prepareData(boolean isClient) {
if (!Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "enabled", true)) {
if (!ClientConfig.isClientConfigAllowingAutoSync()) {
BCLib.LOGGER.info("Auto-Sync was disabled on the client.");
return false;
}
@ -86,15 +88,15 @@ public class HelloServer extends DataHandler {
@Override
protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) {
if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "enabled", true)) {
if (!Config.isAllowingAutoSync()) {
BCLib.LOGGER.info("Auto-Sync was disabled on the server.");
return;
}
String localBclibVersion = HelloClient.getBCLibVersion();
BCLib.LOGGER.info("Received Hello from Client. (server="+localBclibVersion+", client="+bclibVersion+")");
BCLib.LOGGER.info("Received Hello from Client. (server=" + localBclibVersion + ", client=" + bclibVersion + ")");
if (!server.isPublished()){
if (!server.isPublished()) {
BCLib.LOGGER.info("Auto-Sync is disabled for Singleplayer worlds.");
return;
}

View file

@ -1,4 +1,4 @@
package ru.bclib.api.dataexchange.handler;
package ru.bclib.api.dataexchange.handler.autosync;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.minecraft.client.Minecraft;
@ -8,7 +8,8 @@ import net.minecraft.server.MinecraftServer;
import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.DataHandler;
import ru.bclib.api.dataexchange.DataHandlerDescriptor;
import ru.bclib.config.Configs;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.ClientConfig;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.Config;
import java.util.ArrayList;
import java.util.List;
@ -20,7 +21,8 @@ public class RequestFiles extends DataHandler {
static String currentToken = "";
protected List<AutoSyncID> files;
private RequestFiles(){
private RequestFiles() {
this(null);
}
@ -31,7 +33,7 @@ public class RequestFiles extends DataHandler {
@Override
protected boolean prepareData(boolean isClient) {
if (!Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "enabled", true)) {
if (!ClientConfig.isClientConfigAllowingAutoSync()) {
BCLib.LOGGER.info("Auto-Sync was disabled on the client.");
return false;
}
@ -45,12 +47,13 @@ public class RequestFiles extends DataHandler {
buf.writeInt(files.size());
for (AutoSyncID a : files){
for (AutoSyncID a : files) {
a.serializeData(buf);
}
}
String receivedToken = "";
@Override
protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) {
receivedToken = readString(buf);
@ -58,7 +61,7 @@ public class RequestFiles extends DataHandler {
files = new ArrayList<>(size);
BCLib.LOGGER.info("Client requested " + size + " Files:");
for (int i=0; i<size; i++){
for (int i = 0; i < size; i++) {
AutoSyncID asid = AutoSyncID.deserializeData(buf);
files.add(asid);
BCLib.LOGGER.info(" - " + asid);
@ -69,21 +72,22 @@ public class RequestFiles extends DataHandler {
@Override
protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) {
if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "enabled", true)) {
if (!Config.isAllowingAutoSync()) {
BCLib.LOGGER.info("Auto-Sync was disabled on the server.");
return;
}
List<AutoFileSyncEntry> syncEntries = files
.stream().map(asid -> AutoFileSyncEntry.findMatching(asid))
.filter(e -> e!=null)
.collect(Collectors.toList());
List<AutoFileSyncEntry> syncEntries = files.stream()
.map(asid -> AutoFileSyncEntry.findMatching(asid))
.filter(e -> e != null)
.collect(Collectors.toList());
reply(new SendFiles(syncEntries, receivedToken), server);
}
public static void newToken(){
currentToken = UUID.randomUUID().toString();
public static void newToken() {
currentToken = UUID.randomUUID()
.toString();
}
static {

View file

@ -1,4 +1,4 @@
package ru.bclib.api.dataexchange.handler;
package ru.bclib.api.dataexchange.handler.autosync;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@ -11,7 +11,9 @@ import net.minecraft.server.MinecraftServer;
import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.DataHandler;
import ru.bclib.api.dataexchange.DataHandlerDescriptor;
import ru.bclib.config.Configs;
import ru.bclib.api.dataexchange.handler.DataExchange;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.ClientConfig;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.Config;
import ru.bclib.gui.screens.ConfirmRestartScreen;
import ru.bclib.util.Pair;
import ru.bclib.util.Triple;
@ -29,23 +31,20 @@ public class SendFiles extends DataHandler {
protected List<AutoFileSyncEntry> files;
private String token = "";
public SendFiles(){
public SendFiles() {
this(null, "");
}
public SendFiles(List<AutoFileSyncEntry> files, String token) {
super(DESCRIPTOR.IDENTIFIER, true);
this.files = files;
this.token = token;
}
public static boolean acceptFiles() {
return Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "acceptFiles", true)
&& Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "enabled", true);
}
@Override
protected boolean prepareData(boolean isClient) {
if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "enabled", true)) {
if (!Config.isAllowingAutoSync()) {
BCLib.LOGGER.info("Auto-Sync was disabled on the server.");
return false;
}
@ -55,7 +54,9 @@ public class SendFiles extends DataHandler {
@Override
protected void serializeData(FriendlyByteBuf buf, boolean isClient) {
List<AutoFileSyncEntry> existingFiles = files.stream().filter(e -> e.fileName.exists()).collect(Collectors.toList());
List<AutoFileSyncEntry> existingFiles = files.stream()
.filter(e -> e.fileName.exists())
.collect(Collectors.toList());
/*
//this will try to send a file that was not registered or requested by the client
existingFiles.add(new AutoFileSyncEntry("none", new File("D:\\MinecraftPlugins\\BetterNether\\run\\server.properties"),true,(a, b, content) -> {
@ -81,9 +82,10 @@ public class SendFiles extends DataHandler {
}
private List<Pair<AutoFileSyncEntry, byte[]>> receivedFiles;
@Override
protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) {
if (acceptFiles()) {
if (ClientConfig.isClientConfigAcceptingFiles()) {
token = readString(buf);
if (!token.equals(RequestFiles.currentToken)) {
RequestFiles.newToken();
@ -101,7 +103,8 @@ public class SendFiles extends DataHandler {
if (p.first != null) {
receivedFiles.add(p);
BCLib.LOGGER.info(" - " + p.first + " (" + p.second.length + " Bytes)");
} else {
}
else {
BCLib.LOGGER.error(" - Failed to receive File " + p.third + ", possibly sent from a Mod that is not installed on the client.");
}
}
@ -110,7 +113,7 @@ public class SendFiles extends DataHandler {
@Override
protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) {
if (acceptFiles()) {
if (ClientConfig.isClientConfigAcceptingFiles()) {
BCLib.LOGGER.info("Writing Files:");
//TODO: Reject files that were not in the last RequestFiles.
@ -130,21 +133,23 @@ public class SendFiles extends DataHandler {
BCLib.LOGGER.info(" - Writing " + path + " (" + data.length + " Bytes)");
try {
final File parentFile = path.getParent()
.toFile();
if (!parentFile.exists()){
.toFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
Files.write(path, data);
DataExchange.didReceiveFile(e, fileName);
} catch (IOException ioException) {
AutoSync.didReceiveFile(e, fileName);
}
catch (IOException ioException) {
BCLib.LOGGER.error(" --> Writing " + fileName + " failed: " + ioException);
}
}
@Environment(EnvType.CLIENT)
protected void showConfirmRestart(Minecraft client){
protected void showConfirmRestart(Minecraft client) {
client.setScreen(new ConfirmRestartScreen(() -> {
Minecraft.getInstance().setScreen((Screen)null);
Minecraft.getInstance()
.setScreen((Screen) null);
client.stop();
}));

View file

@ -0,0 +1,206 @@
package ru.bclib.api.dataexchange.handler.autosync;
import net.minecraft.network.FriendlyByteBuf;
import org.jetbrains.annotations.NotNull;
import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.DataHandler;
import ru.bclib.api.dataexchange.FileHash;
import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID.ForDirectFileRequest;
import ru.bclib.config.Configs;
import ru.bclib.util.PathUtil;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class SyncFolderDescriptor {
static class SubFile {
public final String relPath;
public final FileHash hash;
SubFile(String relPath, FileHash hash) {
this.relPath = relPath;
this.hash = hash;
}
@Override
public String toString() {
return relPath;
}
public void serialize(FriendlyByteBuf buf) {
DataHandler.writeString(buf, relPath);
hash.serialize(buf);
}
public static SubFile deserialize(FriendlyByteBuf buf) {
final String relPath = DataHandler.readString(buf);
FileHash hash = FileHash.deserialize(buf);
return new SubFile(relPath, hash);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof String) return relPath.equals(o);
if (!(o instanceof SubFile)) return false;
SubFile subFile = (SubFile) o;
return relPath.equals(subFile.relPath);
}
@Override
public int hashCode() {
return relPath.hashCode();
}
}
@NotNull
public final String folderID;
public final boolean removeAdditionalFiles;
@NotNull
public final Path localFolder;
private List<SubFile> fileCache;
public SyncFolderDescriptor(String folderID, Path localFolder, boolean removeAdditionalFiles) {
this.removeAdditionalFiles = removeAdditionalFiles;
this.folderID = folderID;
this.localFolder = localFolder;
fileCache = null;
}
@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 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);
PathUtil.fileWalker(localFolder.toFile(), p -> fileCache.add(new SubFile(localFolder.relativize(p)
.toString(), FileHash.create(p.toFile()))));
/*//this tests if we can trick the system to load files that are not beneath the base-folder
if (!BCLib.isClient()) {
fileCache.add(new SubFile("../breakout.json", FileHash.create(mapAbsolute("../breakout.json").toFile())));
}*/
}
}
public void serialize(FriendlyByteBuf buf) {
final boolean debugHashes = Configs.CLIENT_CONFIG.getBoolean(AutoSync.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 = AutoSync.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();
}
public Path mapAbsolute(String relPath) {
return this.localFolder.resolve(relPath)
.normalize();
}
public Path mapAbsolute(SubFile subFile) {
return this.localFolder.resolve(subFile.relPath)
.normalize();
}
public boolean acceptChildElements(Path absPath) {
return PathUtil.isChildOf(this.localFolder, absPath);
}
public boolean acceptChildElements(SubFile subFile) {
return acceptChildElements(mapAbsolute(subFile));
}
public boolean discardChildElements(SubFile subFile) {
return !acceptChildElements(subFile);
}
}

View file

@ -4,8 +4,8 @@ import org.jetbrains.annotations.Nullable;
import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.DataExchangeAPI;
import ru.bclib.api.dataexchange.SyncFileHash;
import ru.bclib.api.dataexchange.handler.AutoSyncID;
import ru.bclib.api.dataexchange.handler.FileContentWrapper;
import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID;
import ru.bclib.api.dataexchange.handler.autosync.FileContentWrapper;
import ru.bclib.config.ConfigKeeper.BooleanEntry;
import ru.bclib.config.ConfigKeeper.Entry;
import ru.bclib.config.ConfigKeeper.FloatEntry;

View file

@ -6,7 +6,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.minecraft.util.GsonHelper;
import org.jetbrains.annotations.Nullable;
import ru.bclib.api.dataexchange.handler.FileContentWrapper;
import ru.bclib.api.dataexchange.handler.autosync.FileContentWrapper;
import ru.bclib.util.JsonFactory;
import ru.bclib.util.Pair;

View file

@ -8,7 +8,6 @@ public class Configs {
public static final PathConfig GENERATOR_CONFIG = new PathConfig(BCLib.MOD_ID, "generator");
public static final PathConfig MAIN_CONFIG = new PathConfig(BCLib.MOD_ID, "main");
public static final String MAIN_PATCH_CATEGORY = "patches";
public static final String MAIN_SYNC_CATEGORY = "client_sync";
public static final PathConfig RECIPE_CONFIG = new PathConfig(BCLib.MOD_ID, "recipes");

View file

@ -1,6 +1,7 @@
package ru.bclib.util;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.fabricmc.loader.metadata.ModMetadataParser;
import net.fabricmc.loader.metadata.ParseMetadataException;
@ -15,6 +16,7 @@ import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.jar.JarFile;
@ -127,4 +129,22 @@ public class PathUtil {
return mods;
}
public static String getModVersion(String modID) {
Optional<ModContainer> optional = FabricLoader.getInstance()
.getModContainer(modID);
if (optional.isPresent()) {
ModContainer modContainer = optional.get();
return modContainer.getMetadata()
.getVersion()
.toString();
}
return "0.0.0";
}
public static ModContainer getModContainer(String modID) {
Optional<ModContainer> optional = FabricLoader.getInstance()
.getModContainer(modID);
return optional.orElse(null);
}
}