Merge branch 'feature/networking' of github.com-quiqueck:quiqueck/BCLib into feature/networking
This commit is contained in:
commit
ae344c48ac
21 changed files with 744 additions and 185 deletions
|
@ -0,0 +1,247 @@
|
|||
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 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;
|
||||
|
||||
abstract public class DataExchange {
|
||||
@FunctionalInterface
|
||||
public interface NeedTransferPredicate {
|
||||
public boolean test(FileHash clientHash, FileHash serverHash, byte[] content);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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<DataHandlerDescriptor> descriptors;
|
||||
protected final List<AutoFileSyncEntry> autoSyncFiles = new ArrayList<>(4);
|
||||
|
||||
private final Function<DataExchange, ConnectorClientside> clientSupplier;
|
||||
private final Function<DataExchange, ConnectorServerside> serverSupplier;
|
||||
|
||||
protected DataExchange(Function<DataExchange, ConnectorClientside> client, Function<DataExchange, ConnectorServerside> server){
|
||||
descriptors = new HashSet<>();
|
||||
this.clientSupplier = client;
|
||||
this.serverSupplier = server;
|
||||
}
|
||||
|
||||
public Set<DataHandlerDescriptor> getDescriptors() { return descriptors; }
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
protected void initClientside(){
|
||||
if (client!=null) return;
|
||||
client = clientSupplier.apply(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.apply(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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <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, 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));
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,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><--</td>
|
||||
* <td>{@link HelloServer}</td>
|
||||
* <td>Sends the current BLib-Version installed on the Client</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@link HelloClient}</td>
|
||||
* <td>--></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);
|
||||
|
||||
|
@ -19,6 +47,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) {
|
||||
|
@ -31,9 +64,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()));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue