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.TagAPI;
import ru.bclib.api.WorldDataAPI; import ru.bclib.api.WorldDataAPI;
import ru.bclib.api.dataexchange.DataExchangeAPI; import ru.bclib.api.dataexchange.DataExchangeAPI;
import ru.bclib.api.dataexchange.handler.HelloClient; import ru.bclib.api.dataexchange.handler.autosync.HelloClient;
import ru.bclib.api.dataexchange.handler.HelloServer; import ru.bclib.api.dataexchange.handler.autosync.HelloServer;
import ru.bclib.api.dataexchange.handler.RequestFiles; import ru.bclib.api.dataexchange.handler.autosync.RequestFiles;
import ru.bclib.api.dataexchange.handler.SendFiles; import ru.bclib.api.dataexchange.handler.autosync.SendFiles;
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.recipes.CraftingRecipes; 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.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
import net.minecraft.network.FriendlyByteBuf; 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.api.dataexchange.handler.DataExchange;
import ru.bclib.config.Config; import ru.bclib.config.Config;
@ -98,7 +100,7 @@ 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, 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 * @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, 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. * @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) { 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. * @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) { 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. * @param callback A Function that receives the AutoSyncID as well as the Filename.
*/ */
public static void addOnWriteCallback(BiConsumer<AutoSyncID, File> callback) { 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 * @return The path to the sync-folder
*/ */
public static File getModSyncFolder(String modID) { 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("\\", "-") .replace("\\", "-")
.replace("/", "-")) .replace("/", "-"))

View file

@ -1,8 +1,8 @@
package ru.bclib.api.dataexchange; package ru.bclib.api.dataexchange;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import ru.bclib.api.dataexchange.handler.AutoSyncID; import ru.bclib.api.dataexchange.handler.autosync.AutoSync.NeedTransferPredicate;
import ru.bclib.api.dataexchange.handler.DataExchange; import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID;
import java.io.File; import java.io.File;
import java.util.Objects; 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 @Override
public String toString() { public String toString() {

View file

@ -4,255 +4,17 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; 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.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.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.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
abstract public class DataExchange { 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; private static DataExchangeAPI instance;
@ -266,7 +28,7 @@ abstract public class DataExchange {
protected ConnectorServerside server; protected ConnectorServerside server;
protected ConnectorClientside client; protected ConnectorClientside client;
protected final Set<DataHandlerDescriptor> descriptors; protected final Set<DataHandlerDescriptor> descriptors;
private final List<AutoFileSyncEntry> autoSyncFiles = new ArrayList<>(4);
private boolean didLoadSyncFolder = false; private boolean didLoadSyncFolder = false;
@ -280,9 +42,6 @@ abstract public class DataExchange {
public Set<DataHandlerDescriptor> getDescriptors() { return descriptors; } public Set<DataHandlerDescriptor> getDescriptors() { return descriptors; }
public List<AutoFileSyncEntry> getAutoSyncFiles() {
return autoSyncFiles;
}
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
protected void initClientside() { 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 net.minecraft.network.FriendlyByteBuf;
import ru.bclib.api.dataexchange.DataHandler; import ru.bclib.api.dataexchange.DataHandler;
import ru.bclib.api.dataexchange.SyncFileHash; import ru.bclib.api.dataexchange.SyncFileHash;
import ru.bclib.api.dataexchange.handler.DataExchange.SyncFolderDescriptor; import ru.bclib.api.dataexchange.handler.autosync.AutoSync.NeedTransferPredicate;
import ru.bclib.api.dataexchange.handler.DataExchange.SyncFolderDescriptor.SubFile; import ru.bclib.api.dataexchange.handler.autosync.SyncFolderDescriptor.SubFile;
import ru.bclib.util.Pair; import ru.bclib.util.Pair;
import ru.bclib.util.Triple; import ru.bclib.util.Triple;
@ -14,7 +14,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
class AutoFileSyncEntry extends AutoSyncID { class AutoFileSyncEntry extends AutoSyncID {
static class ForDirectFileRequest extends AutoFileSyncEntry { static class ForDirectFileRequest extends AutoFileSyncEntry {
final File relFile; final File relFile;
ForDirectFileRequest(String syncID, File relFile, File absFile) { ForDirectFileRequest(String syncID, File relFile, File absFile) {
@ -30,10 +30,10 @@ class AutoFileSyncEntry extends AutoSyncID {
return res; return res;
} }
static AutoFileSyncEntry.ForDirectFileRequest finishDeserializeContent(String syncID, FriendlyByteBuf buf){ static AutoFileSyncEntry.ForDirectFileRequest finishDeserializeContent(String syncID, FriendlyByteBuf buf) {
final String relFile = DataHandler.readString(buf); final String relFile = DataHandler.readString(buf);
SyncFolderDescriptor desc = DataExchange.getSyncFolderDescriptor(syncID); SyncFolderDescriptor desc = AutoSync.getSyncFolderDescriptor(syncID);
if (desc!=null) { if (desc != null) {
//ensures that the file is not above the base-folder //ensures that the file is not above the base-folder
if (desc.acceptChildElements(desc.mapAbsolute(relFile))) { if (desc.acceptChildElements(desc.mapAbsolute(relFile))) {
return new AutoFileSyncEntry.ForDirectFileRequest(syncID, new File(relFile), desc.localFolder.resolve(relFile) return new AutoFileSyncEntry.ForDirectFileRequest(syncID, new File(relFile), desc.localFolder.resolve(relFile)
@ -49,16 +49,17 @@ class AutoFileSyncEntry extends AutoSyncID {
return uniqueID + " - " + relFile; return uniqueID + " - " + relFile;
} }
} }
public final DataExchange.NeedTransferPredicate needTransfer;
public final NeedTransferPredicate needTransfer;
public final File fileName; public final File fileName;
public final boolean requestContent; public final boolean requestContent;
private SyncFileHash hash; 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); 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); super(modID, uniqueID);
this.needTransfer = needTransfer; this.needTransfer = needTransfer;
this.fileName = fileName; this.fileName = fileName;
@ -97,9 +98,10 @@ class AutoFileSyncEntry extends AutoSyncID {
byte[] data = deserializeFileContent(buf); byte[] data = deserializeFileContent(buf);
AutoFileSyncEntry entry; AutoFileSyncEntry entry;
if (AutoSyncID.ForDirectFileRequest.MOD_ID.equals(modID)){ if (AutoSyncID.ForDirectFileRequest.MOD_ID.equals(modID)) {
entry = AutoFileSyncEntry.ForDirectFileRequest.finishDeserializeContent(uniqueID, buf); entry = AutoFileSyncEntry.ForDirectFileRequest.finishDeserializeContent(uniqueID, buf);
} else { }
else {
entry = AutoFileSyncEntry.findMatching(modID, uniqueID); entry = AutoFileSyncEntry.findMatching(modID, uniqueID);
} }
return new Triple<>(entry, data, new AutoSyncID(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); Pair<SyncFileHash, byte[]> e = deserialize(buf);
AutoFileSyncEntry match = findMatching(e.first); 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) { public static Pair<SyncFileHash, byte[]> deserialize(FriendlyByteBuf buf) {
@ -154,15 +156,13 @@ class AutoFileSyncEntry extends AutoSyncID {
public static AutoFileSyncEntry findMatching(AutoSyncID aid) { public static AutoFileSyncEntry findMatching(AutoSyncID aid) {
if (aid instanceof AutoSyncID.ForDirectFileRequest) { if (aid instanceof AutoSyncID.ForDirectFileRequest) {
AutoSyncID.ForDirectFileRequest freq = (AutoSyncID.ForDirectFileRequest) aid; AutoSyncID.ForDirectFileRequest freq = (AutoSyncID.ForDirectFileRequest) aid;
SyncFolderDescriptor desc = DataExchange.getSyncFolderDescriptor(freq.uniqueID); SyncFolderDescriptor desc = AutoSync.getSyncFolderDescriptor(freq.uniqueID);
if (desc != null) { if (desc != null) {
SubFile subFile = desc.getLocalSubFile(freq.relFile.toString()); SubFile subFile = desc.getLocalSubFile(freq.relFile.toString());
if (subFile != null) { if (subFile != null) {
final File absPath = desc final File absPath = desc.localFolder.resolve(subFile.relPath)
.localFolder .normalize()
.resolve(subFile.relPath) .toFile();
.normalize()
.toFile();
return new AutoFileSyncEntry.ForDirectFileRequest(freq.uniqueID, new File(subFile.relPath), absPath); 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) { public static AutoFileSyncEntry findMatching(String modID, String uniqueID) {
return DataExchange.getInstance() return AutoSync.getAutoSyncFiles()
.getAutoSyncFiles() .stream()
.stream() .filter(asf -> asf.modID.equals(modID) && asf.uniqueID.equals(uniqueID))
.filter(asf -> asf.modID.equals(modID) && asf.uniqueID.equals(uniqueID)) .findFirst()
.findFirst() .orElse(null);
.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 net.minecraft.network.FriendlyByteBuf;
import org.jetbrains.annotations.NotNull; 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; 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.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.networking.v1.PacketSender; 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.Minecraft;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
@ -14,13 +12,15 @@ import ru.bclib.BCLib;
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.handler.AutoSyncID.WithContentOverride; import ru.bclib.api.dataexchange.handler.DataExchange;
import ru.bclib.api.dataexchange.handler.DataExchange.SyncFolderDescriptor; import ru.bclib.api.dataexchange.handler.autosync.AutoSync.ClientConfig;
import ru.bclib.api.dataexchange.handler.DataExchange.SyncFolderDescriptor.SubFile; 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.api.datafixer.DataFixerAPI;
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 ru.bclib.util.PathUtil;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
@ -28,7 +28,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -44,36 +43,18 @@ public class HelloClient extends DataHandler {
super(DESCRIPTOR.IDENTIFIER, true); 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() { static String getBCLibVersion() {
return getModVersion(BCLib.MOD_ID); return PathUtil.getModVersion(BCLib.MOD_ID);
} }
@Override @Override
protected boolean prepareData(boolean isClient) { 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."); BCLib.LOGGER.info("Auto-Sync was disabled on the server.");
return false; return false;
} }
DataExchange.getInstance().loadSyncFolder(); AutoSync.loadSyncFolder();
return true; return true;
} }
@ -86,12 +67,12 @@ public class HelloClient extends DataHandler {
//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 (Config.isOfferingMods()) {
//write Plugin Versions //write Plugin Versions
buf.writeInt(mods.size()); buf.writeInt(mods.size());
for (String modID : mods) { for (String modID : mods) {
writeString(buf, modID); writeString(buf, modID);
final String ver = getModVersion(modID); final String ver = PathUtil.getModVersion(modID);
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);
} }
@ -101,9 +82,9 @@ public class HelloClient extends DataHandler {
buf.writeInt(0); 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 //do only include files that exist on the server
final List<AutoFileSyncEntry> existingAutoSyncFiles = DataExchange.getInstance() final List<AutoFileSyncEntry> existingAutoSyncFiles = AutoSync
.getAutoSyncFiles() .getAutoSyncFiles()
.stream() .stream()
.filter(e -> e.fileName.exists()) .filter(e -> e.fileName.exists())
@ -121,9 +102,9 @@ public class HelloClient extends DataHandler {
buf.writeInt(0); buf.writeInt(0);
} }
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offersSyncFolders", true)) { if (Config.isOfferingFolders()) {
buf.writeInt(((DataExchange) DataExchange.getInstance()).syncFolderDescriptions.size()); buf.writeInt(AutoSync.syncFolderDescriptions.size());
((DataExchange) DataExchange.getInstance()).syncFolderDescriptions.forEach(desc -> { AutoSync.syncFolderDescriptions.forEach(desc -> {
BCLib.LOGGER.info(" - Offering Folder " + desc.localFolder + " (allowDelete=" + desc.removeAdditionalFiles + ")"); BCLib.LOGGER.info(" - Offering Folder " + desc.localFolder + " (allowDelete=" + desc.removeAdditionalFiles + ")");
desc.serialize(buf); desc.serialize(buf);
}); });
@ -132,12 +113,11 @@ public class HelloClient extends DataHandler {
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();
} }
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<AutoSync.AutoSyncTriple> autoSyncedFiles = null;
List<SyncFolderDescriptor> autoSynFolders = null; List<SyncFolderDescriptor> autoSynFolders = null;
@Override @Override
@ -159,7 +139,7 @@ public class HelloClient extends DataHandler {
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); AutoSync.AutoSyncTriple t = AutoFileSyncEntry.deserializeAndMatch(buf);
autoSyncedFiles.add(t); autoSyncedFiles.add(t);
//System.out.println(t.first); //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) { 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; return;
} }
@ -187,7 +167,7 @@ public class HelloClient extends DataHandler {
autoSynFolders.forEach(desc -> { 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 //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) { if (localDescriptor != null) {
BCLib.LOGGER.info(" - " + desc.folderID + " (" + desc.localFolder + ", allowRemove=" + desc.removeAdditionalFiles + ")"); BCLib.LOGGER.info(" - " + desc.folderID + " (" + desc.localFolder + ", allowRemove=" + desc.removeAdditionalFiles + ")");
localDescriptor.invalidateCache(); localDescriptor.invalidateCache();
@ -220,7 +200,8 @@ public class HelloClient extends DataHandler {
if (!localSubFile.hash.equals(subFile.hash)) { if (!localSubFile.hash.equals(subFile.hash)) {
BCLib.LOGGER.info(" * " + subFile.relPath + " (changed)"); BCLib.LOGGER.info(" * " + subFile.relPath + " (changed)");
filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, new File(subFile.relPath))); filesToRequest.add(new AutoSyncID.ForDirectFileRequest(desc.folderID, new File(subFile.relPath)));
} else { }
else {
BCLib.LOGGER.info(" * " + subFile.relPath); BCLib.LOGGER.info(" * " + subFile.relPath);
} }
} }
@ -241,7 +222,7 @@ public class HelloClient extends DataHandler {
} }
private void processSingleFileSync(final List<AutoSyncID> filesToRequest) { 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) { if (autoSyncedFiles.size() > 0) {
BCLib.LOGGER.info("Files offered by Server:"); 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 //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 //There are no restrictions to the target folder, but the client decides the final
//location. //location.
for (DataExchange.AutoSyncTriple e : autoSyncedFiles) { for (AutoSync.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) {
@ -281,7 +262,7 @@ public class HelloClient extends DataHandler {
@Override @Override
protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) { 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."); BCLib.LOGGER.info("Auto-Sync was disabled on the client.");
return; return;
} }
@ -297,7 +278,7 @@ public class HelloClient extends DataHandler {
final List<AutoSyncID.ForDirectFileRequest> filesToRemove = new ArrayList<>(2); final List<AutoSyncID.ForDirectFileRequest> filesToRemove = new ArrayList<>(2);
for (Entry<String, String> e : modVersion.entrySet()) { 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 + ")"); 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 //Both client and server need to know about the folder you want to sync
//Files can only get placed within that folder //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); showDownloadConfigs(client, filesToRequest, filesToRemove);
return; 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.fabricmc.fabric.api.networking.v1.PacketSender;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -9,56 +9,58 @@ import ru.bclib.BCLib;
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.handler.autosync.AutoSync.ClientConfig;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.Config;
import ru.bclib.api.datafixer.DataFixerAPI; import ru.bclib.api.datafixer.DataFixerAPI;
import ru.bclib.config.Configs;
import java.io.File; 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 * 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. * client and server.
* <table> * <table>
* <caption>Description</caption> * <caption>Description</caption>
* <tr> * <tr>
* <th>Server</th> * <th>Server</th>
* <th></th> * <th></th>
* <th>Client</th> * <th>Client</th>
* <th></th> * <th></th>
* </tr> * </tr>
* <tr> * <tr>
* <td colspan="4">Player enters World</td> * <td colspan="4">Player enters World</td>
* </tr> * </tr>
* <tr> * <tr>
* <td></td> * <td></td>
* <td>&lt;--</td> * <td>&lt;--</td>
* <td>{@link HelloServer}</td> * <td>{@link HelloServer}</td>
* <td>Sends the current BLib-Version installed on the Client</td> * <td>Sends the current BLib-Version installed on the Client</td>
* </tr> * </tr>
* <tr> * <tr>
* <td>{@link HelloClient}</td> * <td>{@link HelloClient}</td>
* <td>--&gt;</td> * <td>--&gt;</td>
* <td></td> * <td></td>
* <td>Sends the current BClIb-Version, the Version of all Plugins and data for all AutpoSync-Files * <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> * ({@link DataExchangeAPI#addAutoSyncFile(String, File)} on the Server</td>
* </tr> * </tr>
* <tr> * <tr>
* <td></td> * <td></td>
* <td>&lt;--</td> * <td>&lt;--</td>
* <td>{@link RequestFiles}</td> * <td>{@link RequestFiles}</td>
* <td>Request missing or out of sync Files from the Server</td> * <td>Request missing or out of sync Files from the Server</td>
* </tr> * </tr>
* <tr> * <tr>
* <td>{@link SendFiles}</td> * <td>{@link SendFiles}</td>
* <td>--&gt;</td> * <td>--&gt;</td>
* <td></td> * <td></td>
* <td>Send Files from the Server to the Client</td> * <td>Send Files from the Server to the Client</td>
* </tr> * </tr>
* </table> * </table>
*/ */
public class HelloServer extends DataHandler { public class HelloServer extends DataHandler {
public static DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "hello_server"), HelloServer::new, true, false); 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() { public HelloServer() {
super(DESCRIPTOR.IDENTIFIER, false); super(DESCRIPTOR.IDENTIFIER, false);
} }
@ -66,7 +68,7 @@ public class HelloServer extends DataHandler {
@Override @Override
protected boolean prepareData(boolean isClient) { 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."); BCLib.LOGGER.info("Auto-Sync was disabled on the client.");
return false; return false;
} }
@ -86,15 +88,15 @@ public class HelloServer extends DataHandler {
@Override @Override
protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) { 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."); BCLib.LOGGER.info("Auto-Sync was disabled on the server.");
return; return;
} }
String localBclibVersion = HelloClient.getBCLibVersion(); 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."); BCLib.LOGGER.info("Auto-Sync is disabled for Singleplayer worlds.");
return; 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.fabricmc.fabric.api.networking.v1.PacketSender;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -8,7 +8,8 @@ import net.minecraft.server.MinecraftServer;
import ru.bclib.BCLib; import ru.bclib.BCLib;
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.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.ArrayList;
import java.util.List; import java.util.List;
@ -20,7 +21,8 @@ public class RequestFiles extends DataHandler {
static String currentToken = ""; static String currentToken = "";
protected List<AutoSyncID> files; protected List<AutoSyncID> files;
private RequestFiles(){
private RequestFiles() {
this(null); this(null);
} }
@ -31,7 +33,7 @@ public class RequestFiles extends DataHandler {
@Override @Override
protected boolean prepareData(boolean isClient) { 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."); BCLib.LOGGER.info("Auto-Sync was disabled on the client.");
return false; return false;
} }
@ -42,15 +44,16 @@ public class RequestFiles extends DataHandler {
protected void serializeData(FriendlyByteBuf buf, boolean isClient) { protected void serializeData(FriendlyByteBuf buf, boolean isClient) {
newToken(); newToken();
writeString(buf, currentToken); writeString(buf, currentToken);
buf.writeInt(files.size()); buf.writeInt(files.size());
for (AutoSyncID a : files){ for (AutoSyncID a : files) {
a.serializeData(buf); a.serializeData(buf);
} }
} }
String receivedToken = ""; String receivedToken = "";
@Override @Override
protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) { protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) {
receivedToken = readString(buf); receivedToken = readString(buf);
@ -58,7 +61,7 @@ public class RequestFiles extends DataHandler {
files = new ArrayList<>(size); files = new ArrayList<>(size);
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++) {
AutoSyncID asid = AutoSyncID.deserializeData(buf); AutoSyncID asid = AutoSyncID.deserializeData(buf);
files.add(asid); files.add(asid);
BCLib.LOGGER.info(" - " + asid); BCLib.LOGGER.info(" - " + asid);
@ -69,23 +72,24 @@ public class RequestFiles extends DataHandler {
@Override @Override
protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) { 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."); BCLib.LOGGER.info("Auto-Sync was disabled on the server.");
return; return;
} }
List<AutoFileSyncEntry> syncEntries = files List<AutoFileSyncEntry> syncEntries = files.stream()
.stream().map(asid -> AutoFileSyncEntry.findMatching(asid)) .map(asid -> AutoFileSyncEntry.findMatching(asid))
.filter(e -> e!=null) .filter(e -> e != null)
.collect(Collectors.toList()); .collect(Collectors.toList());
reply(new SendFiles(syncEntries, receivedToken), server); reply(new SendFiles(syncEntries, receivedToken), server);
} }
public static void newToken(){ public static void newToken() {
currentToken = UUID.randomUUID().toString(); currentToken = UUID.randomUUID()
.toString();
} }
static { static {
newToken(); newToken();
} }

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.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
@ -11,7 +11,9 @@ import net.minecraft.server.MinecraftServer;
import ru.bclib.BCLib; import ru.bclib.BCLib;
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.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.gui.screens.ConfirmRestartScreen;
import ru.bclib.util.Pair; import ru.bclib.util.Pair;
import ru.bclib.util.Triple; import ru.bclib.util.Triple;
@ -26,26 +28,23 @@ import java.util.stream.Collectors;
public class SendFiles extends DataHandler { public class SendFiles extends DataHandler {
public static DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "send_files"), SendFiles::new, false, false); public static DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "send_files"), SendFiles::new, false, false);
protected List<AutoFileSyncEntry> files; protected List<AutoFileSyncEntry> files;
private String token = ""; private String token = "";
public SendFiles(){
public SendFiles() {
this(null, ""); this(null, "");
} }
public SendFiles(List<AutoFileSyncEntry> files, String token) { public SendFiles(List<AutoFileSyncEntry> files, String token) {
super(DESCRIPTOR.IDENTIFIER, true); super(DESCRIPTOR.IDENTIFIER, true);
this.files = files; this.files = files;
this.token = token; 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 @Override
protected boolean prepareData(boolean isClient) { 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."); BCLib.LOGGER.info("Auto-Sync was disabled on the server.");
return false; return false;
} }
@ -55,7 +54,9 @@ public class SendFiles extends DataHandler {
@Override @Override
protected void serializeData(FriendlyByteBuf buf, boolean isClient) { 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 //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) -> { existingFiles.add(new AutoFileSyncEntry("none", new File("D:\\MinecraftPlugins\\BetterNether\\run\\server.properties"),true,(a, b, content) -> {
@ -72,18 +73,19 @@ public class SendFiles extends DataHandler {
writeString(buf, token); writeString(buf, token);
buf.writeInt(existingFiles.size()); buf.writeInt(existingFiles.size());
BCLib.LOGGER.info("Sending " + existingFiles.size() + " Files to Client:"); BCLib.LOGGER.info("Sending " + existingFiles.size() + " Files to Client:");
for (AutoFileSyncEntry entry : existingFiles) { for (AutoFileSyncEntry entry : existingFiles) {
int length = entry.serializeContent(buf); int length = entry.serializeContent(buf);
BCLib.LOGGER.info(" - " + entry + " (" + length + " Bytes)"); BCLib.LOGGER.info(" - " + entry + " (" + length + " Bytes)");
} }
} }
private List<Pair<AutoFileSyncEntry, byte[]>> receivedFiles; private List<Pair<AutoFileSyncEntry, byte[]>> receivedFiles;
@Override @Override
protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) { protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) {
if (acceptFiles()) { if (ClientConfig.isClientConfigAcceptingFiles()) {
token = readString(buf); token = readString(buf);
if (!token.equals(RequestFiles.currentToken)) { if (!token.equals(RequestFiles.currentToken)) {
RequestFiles.newToken(); RequestFiles.newToken();
@ -92,7 +94,7 @@ public class SendFiles extends DataHandler {
return; return;
} }
RequestFiles.newToken(); RequestFiles.newToken();
int size = buf.readInt(); int size = buf.readInt();
receivedFiles = new ArrayList<>(size); receivedFiles = new ArrayList<>(size);
BCLib.LOGGER.info("Server sent " + size + " Files:"); BCLib.LOGGER.info("Server sent " + size + " Files:");
@ -101,7 +103,8 @@ public class SendFiles extends DataHandler {
if (p.first != null) { if (p.first != null) {
receivedFiles.add(p); receivedFiles.add(p);
BCLib.LOGGER.info(" - " + p.first + " (" + p.second.length + " Bytes)"); 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."); 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 @Override
protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) { protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) {
if (acceptFiles()) { if (ClientConfig.isClientConfigAcceptingFiles()) {
BCLib.LOGGER.info("Writing Files:"); BCLib.LOGGER.info("Writing Files:");
//TODO: Reject files that were not in the last RequestFiles. //TODO: Reject files that were not in the last RequestFiles.
@ -120,7 +123,7 @@ public class SendFiles extends DataHandler {
writeSyncedFile(e, data, e.fileName); writeSyncedFile(e, data, e.fileName);
} }
showConfirmRestart(client); showConfirmRestart(client);
} }
} }
@ -130,23 +133,25 @@ public class SendFiles extends DataHandler {
BCLib.LOGGER.info(" - Writing " + path + " (" + data.length + " Bytes)"); BCLib.LOGGER.info(" - Writing " + path + " (" + data.length + " Bytes)");
try { try {
final File parentFile = path.getParent() final File parentFile = path.getParent()
.toFile(); .toFile();
if (!parentFile.exists()){ if (!parentFile.exists()) {
parentFile.mkdirs(); parentFile.mkdirs();
} }
Files.write(path, data); Files.write(path, data);
DataExchange.didReceiveFile(e, fileName); AutoSync.didReceiveFile(e, fileName);
} catch (IOException ioException) { }
catch (IOException ioException) {
BCLib.LOGGER.error(" --> Writing " + fileName + " failed: " + ioException); BCLib.LOGGER.error(" --> Writing " + fileName + " failed: " + ioException);
} }
} }
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
protected void showConfirmRestart(Minecraft client){ protected void showConfirmRestart(Minecraft client) {
client.setScreen(new ConfirmRestartScreen(() -> { client.setScreen(new ConfirmRestartScreen(() -> {
Minecraft.getInstance().setScreen((Screen)null); Minecraft.getInstance()
.setScreen((Screen) null);
client.stop(); 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.BCLib;
import ru.bclib.api.dataexchange.DataExchangeAPI; import ru.bclib.api.dataexchange.DataExchangeAPI;
import ru.bclib.api.dataexchange.SyncFileHash; import ru.bclib.api.dataexchange.SyncFileHash;
import ru.bclib.api.dataexchange.handler.AutoSyncID; import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID;
import ru.bclib.api.dataexchange.handler.FileContentWrapper; import ru.bclib.api.dataexchange.handler.autosync.FileContentWrapper;
import ru.bclib.config.ConfigKeeper.BooleanEntry; import ru.bclib.config.ConfigKeeper.BooleanEntry;
import ru.bclib.config.ConfigKeeper.Entry; import ru.bclib.config.ConfigKeeper.Entry;
import ru.bclib.config.ConfigKeeper.FloatEntry; import ru.bclib.config.ConfigKeeper.FloatEntry;

View file

@ -6,7 +6,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import net.minecraft.util.GsonHelper; import net.minecraft.util.GsonHelper;
import org.jetbrains.annotations.Nullable; 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.JsonFactory;
import ru.bclib.util.Pair; 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 GENERATOR_CONFIG = new PathConfig(BCLib.MOD_ID, "generator");
public static final PathConfig MAIN_CONFIG = new PathConfig(BCLib.MOD_ID, "main"); 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_PATCH_CATEGORY = "patches";
public static final String MAIN_SYNC_CATEGORY = "client_sync";
public static final PathConfig RECIPE_CONFIG = new PathConfig(BCLib.MOD_ID, "recipes"); public static final PathConfig RECIPE_CONFIG = new PathConfig(BCLib.MOD_ID, "recipes");

View file

@ -1,6 +1,7 @@
package ru.bclib.util; package ru.bclib.util;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.metadata.ModMetadata; import net.fabricmc.loader.api.metadata.ModMetadata;
import net.fabricmc.loader.metadata.ModMetadataParser; import net.fabricmc.loader.metadata.ModMetadataParser;
import net.fabricmc.loader.metadata.ParseMetadataException; import net.fabricmc.loader.metadata.ParseMetadataException;
@ -15,6 +16,7 @@ import java.nio.file.FileSystems;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.jar.JarFile; import java.util.jar.JarFile;
@ -127,4 +129,22 @@ public class PathUtil {
return mods; 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);
}
} }