Compare files on client/server

This commit is contained in:
Frank 2021-08-06 17:40:46 +02:00
parent 8397ef7cca
commit 119e94520c
9 changed files with 371 additions and 53 deletions

View file

@ -5,29 +5,117 @@ import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.network.FriendlyByteBuf;
import ru.bclib.api.dataexchange.ConnectorClientside;
import ru.bclib.api.dataexchange.ConnectorServerside;
import ru.bclib.api.dataexchange.DataExchangeAPI;
import ru.bclib.api.dataexchange.DataHandler;
import ru.bclib.api.dataexchange.DataHandlerDescriptor;
import ru.bclib.api.dataexchange.FileHash;
import ru.bclib.util.Pair;
import ru.bclib.util.Triple;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
abstract public class DataExchange {
private static class AutoFileSyncEntry {
public final Predicate<Object> needTransfer;
public final File fileName;
@FunctionalInterface
public interface NeedTransferPredicate {
public boolean test(FileHash clientHash, FileHash serverHash, byte[] content);
}
private AutoFileSyncEntry(Predicate<Object> needTransfer, File fileName) {
final static class AutoSyncTriple extends Triple<FileHash, byte[], DataExchange.AutoFileSyncEntry>{
public AutoSyncTriple(FileHash first, byte[] second, AutoFileSyncEntry third) {
super(first, second, third);
}
}
static class AutoFileSyncEntry {
public final NeedTransferPredicate needTransfer;
public final File fileName;
public final String modID;
public final String uniqueID;
public final boolean requestContent;
private FileHash hash;
AutoFileSyncEntry(String modID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) {
this(modID, fileName.getName(), fileName, requestContent, needTransfer);
}
AutoFileSyncEntry(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer) {
this.needTransfer = needTransfer;
this.fileName = fileName;
this.modID = modID;
this.uniqueID = uniqueID;
this.requestContent = requestContent;
}
public FileHash getFileHash(){
if (hash == null) {
hash = FileHash.create(modID, fileName, uniqueID);
}
return hash;
}
public byte[] getContent(){
if (!fileName.exists()) return new byte[0];
final Path path = fileName.toPath();
try {
return Files.readAllBytes(path);
} catch (IOException e) {
}
return new byte[0];
}
public void serialize(FriendlyByteBuf buf){
getFileHash().serialize(buf);
buf.writeBoolean(requestContent);
if (requestContent) {
byte[] content = getContent();
buf.writeInt(content.length);
buf.writeByteArray(content);
}
}
public static AutoSyncTriple deserializeAndMatch(FriendlyByteBuf buf){
Pair<FileHash, byte[]> e = deserialize(buf);
AutoFileSyncEntry match = findMatching(e.first);
return new AutoSyncTriple(e.first, e.second, match);
}
public static Pair<FileHash, byte[]> deserialize(FriendlyByteBuf buf){
FileHash hash = FileHash.deserialize(buf);
boolean withContent = buf.readBoolean();
byte[] data = null;
if (withContent) {
int size = buf.readInt();
data = buf.readByteArray(size);
}
return new Pair(hash, data);
}
public static AutoFileSyncEntry findMatching(FileHash hash){
return DataExchange
.getInstance()
.autoSyncFiles
.stream()
.filter(asf -> asf.modID.equals(hash.modID) && asf.uniqueID.equals(hash.uniqueID))
.findFirst()
.orElse(null);
}
}
@ -85,8 +173,8 @@ abstract public class DataExchange {
* This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called
*/
@Environment(EnvType.CLIENT)
public void prepareClientside(){
DataExchangeAPI api = DataExchangeAPI.getInstance();
public static void prepareClientside(){
DataExchange api = DataExchange.getInstance();
api.initClientside();
}
@ -97,7 +185,7 @@ abstract public class DataExchange {
* This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called
*/
public static void prepareServerside(){
DataExchange api = DataExchangeAPI.getInstance();
DataExchange api = DataExchange.getInstance();
api.initServerSide();
}
@ -124,10 +212,36 @@ abstract public class DataExchange {
/**
* Registers a File for automatic client syncing.
*
* @param modID The ID of the calling Mod
* @param needTransfer If the predicate returns true, the file needs to get copied to the server.
* @param fileName The name of the File
* @param requestContent When {@code true} the content of the file is requested for comparison. This will copy the
* entire file from the client to the server.
* <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 ru.bclib.api.dataexchange.FileHash}
* for comparison is sufficient.
*/
protected void addAutoSyncFileData(Predicate<Object> needTransfer, File fileName){
autoSyncFiles.add(new AutoFileSyncEntry(needTransfer, fileName));
protected void addAutoSyncFileData(String modID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer){
autoSyncFiles.add(new AutoFileSyncEntry(modID, fileName, requestContent, needTransfer));
}
/**
* Registers a File for automatic client syncing.
*
* @param modID The ID of the calling Mod
* @param uniqueID A unique Identifier for the File. (see {@link ru.bclib.api.dataexchange.FileHash#uniqueID} for
* Details
* @param needTransfer If the predicate returns true, the file needs to get copied to the server.
* @param fileName The name of the File
* @param requestContent When {@code true} the content of the file is requested for comparison. This will copy the
* entire file from the client to the server.
* <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 ru.bclib.api.dataexchange.FileHash}
* for comparison is sufficient.
*/
protected void addAutoSyncFileData(String modID, String uniqueID, File fileName, boolean requestContent, NeedTransferPredicate needTransfer){
autoSyncFiles.add(new AutoFileSyncEntry(modID, uniqueID, fileName, requestContent, needTransfer));
}
}

View file

@ -7,7 +7,6 @@ import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.worldselection.EditWorldScreen;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
@ -15,10 +14,12 @@ import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.DataExchangeAPI;
import ru.bclib.api.dataexchange.DataHandler;
import ru.bclib.api.dataexchange.DataHandlerDescriptor;
import ru.bclib.api.dataexchange.FileHash;
import ru.bclib.api.datafixer.DataFixerAPI;
import ru.bclib.gui.screens.ConfirmFixScreen;
import ru.bclib.gui.screens.WarnBCLibVersionMismatch;
import ru.bclib.util.Triple;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -26,6 +27,11 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.function.Consumer;
/**
* Sent from the Server to the Client.
* <p>
* For Details refer to {@link HelloServer}
*/
public class HelloClient extends DataHandler {
public static DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "hello_client"), HelloClient::new, false, false);
@ -45,20 +51,56 @@ public class HelloClient extends DataHandler {
static String getBCLibVersion(){
return getModVersion(BCLib.MOD_ID);
}
@Override
protected void serializeData(FriendlyByteBuf buf) {
final List<String> mods = DataExchangeAPI.registeredMods();
//write BCLibVersion (=protocol version)
buf.writeInt(DataFixerAPI.getModVersion(getBCLibVersion()));
//write Plugin Versions
buf.writeInt(mods.size());
for (String modID : mods) {
writeString(buf, modID);
buf.writeInt(DataFixerAPI.getModVersion(getModVersion(modID)));
}
//send config Data
final List<DataExchange.AutoFileSyncEntry> autoSyncFiles = DataExchange.getInstance().autoSyncFiles;
buf.writeInt(autoSyncFiles.size());
for (DataExchange.AutoFileSyncEntry entry : autoSyncFiles) {
System.out.println("Serializing " + entry.getFileHash());
entry.serialize(buf);
}
}
String bclibVersion ="0.0.0";
Map<String, String> modVersion = new HashMap<>();
List<DataExchange.AutoSyncTriple> autoSyncedFiles = null;
@Override
protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) {
//read BCLibVersion (=protocol version)
bclibVersion = DataFixerAPI.getModVersion(buf.readInt());
//read Plugin Versions
modVersion = new HashMap<>();
int count = buf.readInt();
for (int i=0; i< count; i++){
for (int i=0; i< count; i++) {
String id = readString(buf);
String version = DataFixerAPI.getModVersion(buf.readInt());
modVersion.put(id, version);
}
//read config Data
count = buf.readInt();
autoSyncedFiles = new ArrayList<>(count);
for (int i=0; i< count; i++) {
System.out.println("Deserializing ");
DataExchange.AutoSyncTriple t = DataExchange.AutoFileSyncEntry.deserializeAndMatch(buf);
autoSyncedFiles.add(t);
System.out.println(t.first);
}
}
@Override
@ -75,17 +117,13 @@ public class HelloClient extends DataHandler {
String ver = getModVersion(e.getKey());
BCLib.LOGGER.info(" - " + e.getKey() + " (client="+ver+", server="+ver+")");
}
}
@Override
protected void serializeData(FriendlyByteBuf buf) {
final List<String> mods = DataExchangeAPI.registeredMods();
buf.writeInt(DataFixerAPI.getModVersion(getBCLibVersion()));
buf.writeInt(mods.size());
for (String modID : mods) {
writeString(buf, modID);
buf.writeInt(DataFixerAPI.getModVersion(getModVersion(modID)));
for (DataExchange.AutoSyncTriple e : autoSyncedFiles) {
if (e.third == null) {
BCLib.LOGGER.info(" - File " + e.first.modID + "." + e.first.uniqueID + ": Does not exist on client.");
} else if (e.third.needTransfer.test(e.third.getFileHash(), e.first, e.second)) {
BCLib.LOGGER.info(" - File " + e.first.modID + "." + e.first.uniqueID + ": Needs Transfer");
}
}
}

View file

@ -10,6 +10,34 @@ import ru.bclib.api.dataexchange.DataHandler;
import ru.bclib.api.dataexchange.DataHandlerDescriptor;
import ru.bclib.api.datafixer.DataFixerAPI;
/**
* This message is sent once a player enters the world. It initiates a sequence of Messages that will sync files between both
* client and server.
* <p>
* <tab<le>
* <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 and the Version of all Plugins on the Server</td>
* </tr>
* </table>
*/
public class HelloServer extends DataHandler {
public static DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "hello_server"), HelloServer::new, false, true);
@ -17,6 +45,11 @@ public class HelloServer extends DataHandler {
public HelloServer() {
super(DESCRIPTOR.IDENTIFIER, false);
}
@Override
protected void serializeData(FriendlyByteBuf buf) {
buf.writeInt(DataFixerAPI.getModVersion(HelloClient.getBCLibVersion()));
}
@Override
protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) {
@ -29,9 +62,4 @@ public class HelloServer extends DataHandler {
BCLib.LOGGER.info("Received Hello from Client. (server="+localBclibVersion+", client="+bclibVersion+")");
reply(new HelloClient(), server);
}
@Override
protected void serializeData(FriendlyByteBuf buf) {
buf.writeInt(DataFixerAPI.getModVersion(HelloClient.getBCLibVersion()));
}
}