*WIP* Prepared Folder Syncing - Filelist exchange

This commit is contained in:
Frank 2021-08-15 15:06:36 +02:00
parent 5df6de1e3a
commit 1f239baeb9
9 changed files with 323 additions and 190 deletions

View file

@ -11,7 +11,7 @@ loader_version= 0.11.6
fabric_version = 0.36.1+1.17 fabric_version = 0.36.1+1.17
# Mod Properties # Mod Properties
mod_version = 0.4.0 mod_version = 0.4.1
maven_group = ru.bclib maven_group = ru.bclib
archives_base_name = bclib archives_base_name = bclib

View file

@ -39,6 +39,7 @@ public class BCLib implements ModInitializer {
TagAPI.init(); TagAPI.init();
CraftingRecipes.init(); CraftingRecipes.init();
WorldDataAPI.registerModCache(MOD_ID); WorldDataAPI.registerModCache(MOD_ID);
DataExchangeAPI.registerMod(MOD_ID);
DataFixerAPI.registerPatch(() -> new BCLibPatch()); DataFixerAPI.registerPatch(() -> new BCLibPatch());
DataExchangeAPI.registerDescriptors(List.of( DataExchangeAPI.registerDescriptors(List.of(
HelloClient.DESCRIPTOR, HelloClient.DESCRIPTOR,

View file

@ -8,7 +8,6 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess;
import ru.bclib.BCLib; import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.DataExchangeAPI;
import ru.bclib.api.datafixer.DataFixerAPI; import ru.bclib.api.datafixer.DataFixerAPI;
import java.io.File; import java.io.File;
@ -20,7 +19,7 @@ import java.util.function.Consumer;
/** /**
* Mod-specifix data-storage for a world. * Mod-specifix data-storage for a world.
* * <p>
* This class provides the ability for mod to store persistent data inside a world. The Storage for the world is * This class provides the ability for mod to store persistent data inside a world. The Storage for the world is
* currently initialized as part of the {@link DataFixerAPI} in {@link DataFixerAPI#fixData(LevelStorageAccess, boolean, Consumer)} * currently initialized as part of the {@link DataFixerAPI} in {@link DataFixerAPI#fixData(LevelStorageAccess, boolean, Consumer)}
* or {@link DataFixerAPI#initializeWorldData(File, boolean)} * or {@link DataFixerAPI#initializeWorldData(File, boolean)}
@ -32,7 +31,9 @@ public class WorldDataAPI {
public static void load(File dataDir) { public static void load(File dataDir) {
WorldDataAPI.dataDir = dataDir; WorldDataAPI.dataDir = dataDir;
MODS.stream().parallel().forEach(modID -> { MODS.stream()
.parallel()
.forEach(modID -> {
File file = new File(dataDir, modID + ".nbt"); File file = new File(dataDir, modID + ".nbt");
CompoundTag root = new CompoundTag(); CompoundTag root = new CompoundTag();
if (file.exists()) { if (file.exists()) {
@ -44,14 +45,17 @@ public class WorldDataAPI {
} }
} }
else { else {
Optional<ModContainer> optional = FabricLoader.getInstance().getModContainer(modID); Optional<ModContainer> optional = FabricLoader.getInstance()
.getModContainer(modID);
if (optional.isPresent()) { if (optional.isPresent()) {
ModContainer modContainer = optional.get(); ModContainer modContainer = optional.get();
if (BCLib.isDevEnvironment()) { if (BCLib.isDevEnvironment()) {
root.putString("version", "255.255.9999"); root.putString("version", "255.255.9999");
} }
else { else {
root.putString("version", modContainer.getMetadata().getVersion().toString()); root.putString("version", modContainer.getMetadata()
.getVersion()
.toString());
} }
saveFile(modID); saveFile(modID);
} }
@ -63,14 +67,11 @@ public class WorldDataAPI {
/** /**
* Register mod cache, world cache is located in world data folder. * Register mod cache, world cache is located in world data folder.
* <p>
* Will also register the Mod for the {@link DataExchangeAPI} using {@link DataExchangeAPI#registerMod(String)}
* *
* @param modID - {@link String} modID. * @param modID - {@link String} modID.
*/ */
public static void registerModCache(String modID) { public static void registerModCache(String modID) {
MODS.add(modID); MODS.add(modID);
DataExchangeAPI.registerMod(modID);
} }
/** /**

View file

@ -37,8 +37,7 @@ public class DataExchangeAPI extends DataExchange {
* @param modID - {@link String} modID. * @param modID - {@link String} modID.
*/ */
public static void registerMod(String modID) { public static void registerMod(String modID) {
if (!MODS.contains(modID)) if (!MODS.contains(modID)) MODS.add(modID);
MODS.add(modID);
} }
/** /**
@ -57,7 +56,8 @@ public class DataExchangeAPI extends DataExchange {
*/ */
public static void registerDescriptor(DataHandlerDescriptor desc) { public static void registerDescriptor(DataHandlerDescriptor desc) {
DataExchange api = DataExchange.getInstance(); DataExchange api = DataExchange.getInstance();
api.getDescriptors().add(desc); api.getDescriptors()
.add(desc);
} }
/** /**
@ -67,7 +67,8 @@ public class DataExchangeAPI extends DataExchange {
*/ */
public static void registerDescriptors(List<DataHandlerDescriptor> desc) { public static void registerDescriptors(List<DataHandlerDescriptor> desc) {
DataExchange api = DataExchange.getInstance(); DataExchange api = DataExchange.getInstance();
api.getDescriptors().addAll(desc); api.getDescriptors()
.addAll(desc);
} }
/** /**
@ -84,7 +85,8 @@ public class DataExchangeAPI extends DataExchange {
public static void send(DataHandler h) { public static void send(DataHandler h) {
if (h.getOriginatesOnServer()) { if (h.getOriginatesOnServer()) {
DataExchangeAPI.getInstance().server.sendToClient(h); DataExchangeAPI.getInstance().server.sendToClient(h);
} else { }
else {
DataExchangeAPI.getInstance().client.sendToServer(h); DataExchangeAPI.getInstance().client.sendToServer(h);
} }
} }
@ -157,12 +159,34 @@ public class DataExchangeAPI extends DataExchange {
* file with the new content. * file with the new content.
* <p> * <p>
* This callback is usefull if you need to reload the new content before the game is quit. * 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. * @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); onWriteCallbacks.add(callback);
} }
/**
* Returns the sync-folder for a given Mod.
* <p>
* BCLib will ensure that the contents of sync-folder on the client is the same as the one on the server.
*
* @param modID ID of the Mod
* @return The path to the sync-folder
*/
public static File getSyncFolder(String modID) {
File fl = SYNC_FOLDER.resolve(modID.replace(".", "-")
.replace(":", "-")
.replace("\\", "-")
.replace("/", "-"))
.toFile();
if (!fl.exists()){
fl.mkdirs();
}
return fl;
}
static { static {
addOnWriteCallback(Config::reloadSyncedConfig); addOnWriteCallback(Config::reloadSyncedConfig);
} }

View file

@ -104,6 +104,7 @@ class AutoFileSyncEntry extends AutoSyncID {
return data; return data;
} }
public static AutoFileSyncEntry findMatching(FileHash hash) { public static AutoFileSyncEntry findMatching(FileHash hash) {
return findMatching(hash.modID, hash.uniqueID); return findMatching(hash.modID, hash.uniqueID);
} }
@ -115,7 +116,7 @@ class AutoFileSyncEntry extends AutoSyncID {
public static AutoFileSyncEntry findMatching(String modID, String uniqueID) { public static AutoFileSyncEntry findMatching(String modID, String uniqueID) {
return DataExchange return DataExchange
.getInstance() .getInstance()
.autoSyncFiles .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()

View file

@ -4,14 +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 ru.bclib.api.dataexchange.ConnectorClientside; import ru.bclib.api.dataexchange.ConnectorClientside;
import ru.bclib.api.dataexchange.ConnectorServerside; import ru.bclib.api.dataexchange.ConnectorServerside;
import ru.bclib.api.dataexchange.DataExchangeAPI; import ru.bclib.api.dataexchange.DataExchangeAPI;
import ru.bclib.api.dataexchange.DataHandler; import ru.bclib.api.dataexchange.DataHandler;
import ru.bclib.api.dataexchange.DataHandlerDescriptor; import ru.bclib.api.dataexchange.DataHandlerDescriptor;
import ru.bclib.api.dataexchange.FileHash; import ru.bclib.api.dataexchange.FileHash;
import ru.bclib.config.Configs;
import java.io.File; import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -19,6 +22,12 @@ import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
abstract public class DataExchange { abstract public class DataExchange {
public final static Path SYNC_FOLDER = FabricLoader.getInstance()
.getGameDir()
.resolve("bclib-sync")
.toAbsolutePath();
public final static String SYNC_FOLDER_ID = "BCLIB-SYNC";
@FunctionalInterface @FunctionalInterface
public interface NeedTransferPredicate { public interface NeedTransferPredicate {
public boolean test(FileHash clientHash, FileHash serverHash, FileContentWrapper content); public boolean test(FileHash clientHash, FileHash serverHash, FileContentWrapper content);
@ -54,7 +63,9 @@ 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;
protected final List<AutoFileSyncEntry> autoSyncFiles = new ArrayList<>(4); private final List<AutoFileSyncEntry> autoSyncFiles = new ArrayList<>(4);
private boolean didLoadSyncFolder = false;
abstract protected ConnectorClientside clientSupplier(DataExchange api); abstract protected ConnectorClientside clientSupplier(DataExchange api);
abstract protected ConnectorServerside serverSupplier(DataExchange api); abstract protected ConnectorServerside serverSupplier(DataExchange api);
@ -65,6 +76,10 @@ 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(){
if (client!=null) return; if (client!=null) return;
@ -177,4 +192,43 @@ abstract public class DataExchange {
static void didReceiveFile(AutoSyncID aid, File file){ static void didReceiveFile(AutoSyncID aid, File file){
onWriteCallbacks.forEach(fkt -> fkt.accept(aid, file)); onWriteCallbacks.forEach(fkt -> fkt.accept(aid, file));
} }
private List<String> syncFolderContent;
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, "offserSyncFolder", true))
{
final File syncPath = SYNC_FOLDER.toFile();
if (!syncPath.exists()) {
syncPath.mkdirs();
}
if (syncFolderContent == null) {
syncFolderContent = new ArrayList<>(8);
addFilesForSyncFolder(syncPath);
}
}
}
private void addFilesForSyncFolder(File path){
for (final File f : path.listFiles()) {
if (f.isDirectory()) {
addFilesForSyncFolder(f);
} else if (f.isFile()) {
if (!f.getName().startsWith(".")) {
Path p = f.toPath();
p = SYNC_FOLDER.relativize(p);
syncFolderContent.add(p.toString());
}
}
}
}
} }

View file

@ -77,11 +77,11 @@ public class HelloClient extends DataHandler {
buf.writeInt(0); buf.writeInt(0);
} }
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offerConfigs", true)) { if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offerFiles", true)) {
//do only include files that exist on the server //do only include files that exist on the server
final List<AutoFileSyncEntry> existingAutoSyncFiles = DataExchange final List<AutoFileSyncEntry> existingAutoSyncFiles = DataExchange
.getInstance() .getInstance()
.autoSyncFiles .getAutoSyncFiles()
.stream() .stream()
.filter(e -> e.fileName.exists()) .filter(e -> e.fileName.exists())
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -93,9 +93,22 @@ public class HelloClient extends DataHandler {
BCLib.LOGGER.info(" - Offering File " + entry); BCLib.LOGGER.info(" - Offering File " + entry);
} }
} else { } else {
BCLib.LOGGER.info("Server will not offer Configs."); BCLib.LOGGER.info("Server will not offer Files.");
buf.writeInt(0); buf.writeInt(0);
} }
//for the moment this is only hardcoded for the sync-folder offered by BCLIB, but it can be extended in future
if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offserSyncFolder", true)) {
buf.writeInt(1); //currently we do only sync a single folder
writeString(buf, DataExchange.SYNC_FOLDER_ID); //the UID of the Folder
final List<String> fileNames = DataExchange.getInstance().getSyncFolderContent();
buf.writeInt(fileNames.size());
fileNames.forEach(fl -> writeString(buf, fl));
} else {
BCLib.LOGGER.info("Server will not offer Sync Folders.");
buf.writeInt(0);
}
Configs.MAIN_CONFIG.saveChanges();
} }
String bclibVersion ="0.0.0"; String bclibVersion ="0.0.0";
@ -124,6 +137,25 @@ public class HelloClient extends DataHandler {
autoSyncedFiles.add(t); autoSyncedFiles.add(t);
//System.out.println(t.first); //System.out.println(t.first);
} }
//since this version we also send the sync folders
if (DataFixerAPI.isLargerOrEqualVersion(bclibVersion, "0.4.1")) {
final int folderCount = buf.readInt();
for (int i=0; i<folderCount; i++){
final String folderID = readString(buf);
final int entries = buf.readInt();
List<String> files = new ArrayList<>(entries);
for (int j=0; j<entries; j++){
files.add(readString(buf));
}
if (folderID.equals(DataExchange.SYNC_FOLDER_ID)) {
//TODO: implement the syncing here
} else {
BCLib.LOGGER.warning("Unknown Sync-Folder '"+folderID+"'");
}
}
}
} }

View file

@ -84,5 +84,7 @@ public class HelloServer extends DataHandler {
} else { } else {
BCLib.LOGGER.info("Auto-Sync was disabled on the server."); BCLib.LOGGER.info("Auto-Sync was disabled on the server.");
} }
DataExchange.getInstance().loadSyncFolder();
} }
} }

View file

@ -537,4 +537,22 @@ public class DataFixerAPI {
return String.format(Locale.ROOT, "%d.%d.%d", a, b, c); return String.format(Locale.ROOT, "%d.%d.%d", a, b, c);
} }
/**
* {@code true} if the version v1 is larger than v2
* @param v1 A Version string
* @param v2 Another Version string
* @return v1 &gt; v2
*/
public static boolean isLargerVersion(String v1, String v2){
return getModVersion(v1) > getModVersion(v2);
}
/**
* {@code true} if the version v1 is larger or equal v2
* @param v1 A Version string
* @param v2 Another Version string
* @return v1 &ge; v2
*/
public static boolean isLargerOrEqualVersion(String v1, String v2){
return getModVersion(v1) >= getModVersion(v2);
}
} }