package ru.bclib.api.dataexchange.handler; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; 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.gui.screens.Screen; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; 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.handler.AutoSyncID.WithContentOverride; import ru.bclib.api.datafixer.DataFixerAPI; import ru.bclib.config.Configs; import ru.bclib.gui.screens.SyncFilesScreen; import ru.bclib.gui.screens.WarnBCLibVersionMismatch; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Collectors; /** * Sent from the Server to the Client. *

* 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); public HelloClient() { super(DESCRIPTOR.IDENTIFIER, true); } public static String getModVersion(String modID){ Optional optional = FabricLoader.getInstance().getModContainer(modID); if (optional.isPresent()) { ModContainer modContainer = optional.get(); return modContainer.getMetadata().getVersion().toString(); } return "0.0.0"; } static String getBCLibVersion(){ return getModVersion(BCLib.MOD_ID); } @Override protected void serializeData(FriendlyByteBuf buf) { final String vbclib = getBCLibVersion(); BCLib.LOGGER.info("Sending Hello to Client. (server="+vbclib+")"); final List mods = DataExchangeAPI.registeredMods(); //write BCLibVersion (=protocol version) buf.writeInt(DataFixerAPI.getModVersion(vbclib)); if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offerMods", true)) { //write Plugin Versions buf.writeInt(mods.size()); for (String modID : mods) { writeString(buf, modID); final String ver = getModVersion(modID); buf.writeInt(DataFixerAPI.getModVersion(ver)); BCLib.LOGGER.info(" - Listing Mod " + modID + " v" + ver); } } else { BCLib.LOGGER.info("Server will not list Mods."); buf.writeInt(0); } if (Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "offerConfigs", true)) { //do only include files that exist on the server final List existingAutoSyncFiles = DataExchange .getInstance() .autoSyncFiles .stream() .filter(e -> e.fileName.exists()) .collect(Collectors.toList()); //send config Data buf.writeInt(existingAutoSyncFiles.size()); for (AutoFileSyncEntry entry : existingAutoSyncFiles) { entry.serialize(buf); BCLib.LOGGER.info(" - Offering File " + entry); } } else { BCLib.LOGGER.info("Server will not offer Configs."); buf.writeInt(0); } } String bclibVersion ="0.0.0"; Map modVersion = new HashMap<>(); List 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++) { 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 = AutoFileSyncEntry.deserializeAndMatch(buf); autoSyncedFiles.add(t); //System.out.println(t.first); } } @Override protected void runOnGameThread(Minecraft client, MinecraftServer server, boolean isClient) { final boolean debugHashes = Configs.CLIENT_CONFIG.getBoolean(Configs.MAIN_SYNC_CATEGORY, "debugHashes", false); final String localBclibVersion = getBCLibVersion(); BCLib.LOGGER.info("Received Hello from Server. (client="+localBclibVersion+", server="+bclibVersion+")"); // if (DataFixerAPI.getModVersion(localBclibVersion) != DataFixerAPI.getModVersion(bclibVersion)){ // showBCLibError(client); // return; // } final List filesToRequest = new ArrayList<>(2); for (Entry e : modVersion.entrySet()){ String ver = getModVersion(e.getKey()); BCLib.LOGGER.info(" - " + e.getKey() + " (client="+ver+", server="+ver+")"); } if (autoSyncedFiles.size()>0) { BCLib.LOGGER.info("Files offered by Server:"); } for (DataExchange.AutoSyncTriple e : autoSyncedFiles) { String actionString = ""; FileContentWrapper contentWrapper = new FileContentWrapper(e.serverContent); if (e.localMatch == null) { actionString = "(new, prepare update)"; filesToRequest.add(new AutoSyncID(e.serverHash.modID, e.serverHash.uniqueID)); } else if (e.localMatch.needTransfer.test(e.localMatch.getFileHash(), e.serverHash, contentWrapper)) { actionString = "(prepare update)"; //we did not yet receive the new content if (contentWrapper.getRawContent() == null) { filesToRequest.add(new AutoSyncID(e.serverHash.modID, e.serverHash.uniqueID)); } else { filesToRequest.add(new AutoSyncID.WithContentOverride(e.serverHash.modID, e.serverHash.uniqueID, contentWrapper, e.localMatch.fileName)); } } BCLib.LOGGER.info(" - " + e + ": " + actionString); if (debugHashes) { BCLib.LOGGER.info(" * " + e.serverHash + " (Server)"); BCLib.LOGGER.info(" * " + e.localMatch.getFileHash() + " (Client)"); BCLib.LOGGER.info(" * local Content " + (contentWrapper.getRawContent() == null)); } } if (filesToRequest.size()>0 && SendFiles.acceptFiles()) { showDownloadConfigs(client, filesToRequest); return; } } @Environment(EnvType.CLIENT) protected void showBCLibError(Minecraft client){ BCLib.LOGGER.error("BCLib differs on client and server."); client.setScreen(new WarnBCLibVersionMismatch((download) -> { Minecraft.getInstance().setScreen((Screen)null); if (download){ requestBCLibDownload((hadErrors)->{ client.stop(); }); } })); } @Environment(EnvType.CLIENT) protected void showDownloadConfigs(Minecraft client, List files){ client.setScreen(new SyncFilesScreen((download) -> { Minecraft.getInstance().setScreen((Screen)null); if (download){ BCLib.LOGGER.info("Updating local Files:"); List localChanges = new ArrayList<>(files.toArray().length); List requestFiles = new ArrayList<>(files.toArray().length); files.forEach(aid -> { if (aid instanceof WithContentOverride) { final WithContentOverride aidc = (WithContentOverride)aid; BCLib.LOGGER.info(" - " + aid + " (updating Content)"); SendFiles.writeSyncedFile(aid, aidc.contentWrapper.getRawContent(), aidc.localFile); } else { requestFiles.add(aid); BCLib.LOGGER.info(" - " + aid + " (requesting)"); } }); requestFileDownloads(requestFiles); } })); } private void requestBCLibDownload(Consumer whenFinished){ BCLib.LOGGER.warning("Starting download of BCLib"); whenFinished.accept(true); } private void requestFileDownloads(List files){ BCLib.LOGGER.info("Starting download of Files:" + files.size()); DataExchangeAPI.send(new RequestFiles(files)); } }