package ru.bclib.api.dataexchange.handler; import net.fabricmc.api.EnvType; 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 org.jetbrains.annotations.NotNull; 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.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; abstract public class DataExchange { @FunctionalInterface public interface NeedTransferPredicate { public boolean test(FileHash clientHash, FileHash serverHash, byte[] content); } public static class AutoSyncID { /** * A Unique ID for the referenced File. *

* Files with the same {@link #modID} need to have a unique IDs. Normally the filename from FileHash(String, File, byte[], int, int) * is used to generated that ID, but you can directly specify one using FileHash(String, String, byte[], int, int). */ @NotNull public final String uniqueID; /** * The ID of the Mod that is registering the File */ @NotNull public final String modID; public AutoSyncID(String modID, String uniqueID) { Objects.nonNull(modID); Objects.nonNull(uniqueID); this.modID = modID; this.uniqueID = uniqueID; } @Override public String toString() { return modID+"."+uniqueID; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AutoSyncID that = (AutoSyncID) o; return uniqueID.equals(that.uniqueID) && modID.equals(that.modID); } @Override public int hashCode() { return Objects.hash(uniqueID, modID); } } final static class AutoSyncTriple extends Triple{ public AutoSyncTriple(FileHash first, byte[] second, AutoFileSyncEntry third) { super(first, second, third); } @Override public String toString() { return first.modID+"."+first.uniqueID; } } private static DataExchangeAPI instance; protected static DataExchangeAPI getInstance(){ if (instance==null){ instance = new DataExchangeAPI(); } return instance; } protected ConnectorServerside server; protected ConnectorClientside client; protected final Set descriptors; protected final List autoSyncFiles = new ArrayList<>(4); abstract protected ConnectorClientside clientSupplier(DataExchange api); abstract protected ConnectorServerside serverSupplier(DataExchange api); protected DataExchange(){ descriptors = new HashSet<>(); } public Set getDescriptors() { return descriptors; } @Environment(EnvType.CLIENT) protected void initClientside(){ if (client!=null) return; client = clientSupplier(this); ClientLoginConnectionEvents.INIT.register((a, b) ->{ System.out.println("INIT"); }); ClientLoginConnectionEvents.QUERY_START.register((a, b) ->{ System.out.println("INIT"); }); ClientPlayConnectionEvents.INIT.register(client::onPlayInit); ClientPlayConnectionEvents.JOIN.register(client::onPlayReady); ClientPlayConnectionEvents.DISCONNECT.register(client::onPlayDisconnect); } protected void initServerSide(){ if (server!=null) return; server = serverSupplier(this); ServerPlayConnectionEvents.INIT.register(server::onPlayInit); ServerPlayConnectionEvents.JOIN.register(server::onPlayReady); ServerPlayConnectionEvents.DISCONNECT.register(server::onPlayDisconnect); } /** * Initializes all datastructures that need to exist in the client component. *

* This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called */ @Environment(EnvType.CLIENT) public static void prepareClientside(){ DataExchange api = DataExchange.getInstance(); api.initClientside(); } /** * Initializes all datastructures that need to exist in the server component. *

* This is automatically called by BCLib. You can register {@link DataHandler}-Objects before this Method is called */ public static void prepareServerside(){ DataExchange api = DataExchange.getInstance(); api.initServerSide(); } /** * Automatically called before the player enters the world. *

* This is automatically called by BCLib. It will send all {@link DataHandler}-Objects that have {@link DataHandlerDescriptor#sendBeforeEnter} set to* * {@code true}, */ @Environment(EnvType.CLIENT) public static void sendOnEnter(){ getInstance().descriptors.forEach((desc)-> { if (desc.sendBeforeEnter){ DataHandler h = desc.JOIN_INSTANCE.get(); if (!h.getOriginatesOnServer()) { getInstance().client.sendToServer(h); } } }); } /** * 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. *

* 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, 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. *

* 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)); } }