diff --git a/src/main/java/ru/bclib/BCLib.java b/src/main/java/ru/bclib/BCLib.java index 92201974..285c32f5 100644 --- a/src/main/java/ru/bclib/BCLib.java +++ b/src/main/java/ru/bclib/BCLib.java @@ -40,6 +40,7 @@ public class BCLib implements ModInitializer { CraftingRecipes.init(); WorldDataAPI.registerModCache(MOD_ID); DataExchangeAPI.registerMod(MOD_ID); + DataExchangeAPI.registerModDependency("wunderreich"); DataFixerAPI.registerPatch(() -> new BCLibPatch()); DataExchangeAPI.registerDescriptors(List.of( HelloClient.DESCRIPTOR, diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoFileSyncEntry.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoFileSyncEntry.java index 0b1ee2ca..f84debdc 100644 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoFileSyncEntry.java +++ b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoFileSyncEntry.java @@ -6,6 +6,7 @@ import ru.bclib.api.dataexchange.DataHandler; import ru.bclib.api.dataexchange.SyncFileHash; import ru.bclib.api.dataexchange.handler.autosync.AutoSync.NeedTransferPredicate; import ru.bclib.api.dataexchange.handler.autosync.SyncFolderDescriptor.SubFile; +import ru.bclib.api.datafixer.DataFixerAPI; import ru.bclib.util.Pair; import ru.bclib.util.PathUtil; import ru.bclib.util.PathUtil.ModInfo; @@ -54,34 +55,41 @@ class AutoFileSyncEntry extends AutoSyncID { } static class ForModFileRequest extends AutoFileSyncEntry { - public static Path getLocalPathForID(String modID){ - ModInfo mi = PathUtil.getModInfo(modID); + public static File getLocalPathForID(String modID, boolean matchLocalVersion){ + ModInfo mi = PathUtil.getModInfo(modID, matchLocalVersion); if (mi!=null){ - return mi.jarPath; + return mi.jarPath.toFile(); } return null; } - ForModFileRequest(String modID) { - super(modID, AutoSyncID.ForModFileRequest.UNIQUE_ID, getLocalPathForID(modID).toFile(), false, (a, b, c) -> false); - if (this.fileName == null){ + public final String version; + ForModFileRequest(String modID, boolean matchLocalVersion, String version) { + super(modID, AutoSyncID.ForModFileRequest.UNIQUE_ID, getLocalPathForID(modID, matchLocalVersion), false, (a, b, c) -> false); + if (this.fileName == null && matchLocalVersion){ BCLib.LOGGER.error("Unknown mod '"+modID+"'."); } + if (version==null) + this.version = PathUtil.getModVersion(modID); + else + this.version = version; } @Override public int serializeContent(FriendlyByteBuf buf) { final int res = super.serializeContent(buf); + buf.writeInt(DataFixerAPI.getModVersion(version)); return res; } static AutoFileSyncEntry.ForModFileRequest finishDeserializeContent(String modID, FriendlyByteBuf buf) { - return new AutoFileSyncEntry.ForModFileRequest(modID); + final String version = DataFixerAPI.getModVersion(buf.readInt()); + return new AutoFileSyncEntry.ForModFileRequest(modID, false, version); } @Override public String toString() { - return modID; + return "Mod " + modID + " (v" + version + ")"; } } @@ -137,7 +145,7 @@ class AutoFileSyncEntry extends AutoSyncID { entry = AutoFileSyncEntry.ForDirectFileRequest.finishDeserializeContent(uniqueID, buf); } else if (AutoSyncID.ForModFileRequest.UNIQUE_ID.equals(uniqueID)) { - entry = AutoFileSyncEntry.ForModFileRequest.finishDeserializeContent(uniqueID, buf); + entry = AutoFileSyncEntry.ForModFileRequest.finishDeserializeContent(modID, buf); } else { entry = AutoFileSyncEntry.findMatching(modID, uniqueID); @@ -205,6 +213,9 @@ class AutoFileSyncEntry extends AutoSyncID { } } return null; + } else if (aid instanceof AutoSyncID.ForModFileRequest) { + AutoSyncID.ForModFileRequest mreq = (AutoSyncID.ForModFileRequest) aid; + return new AutoFileSyncEntry.ForModFileRequest(mreq.modID, true, null); } return findMatching(aid.modID, aid.uniqueID); } diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSyncID.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSyncID.java index 74fd1979..48200ec1 100644 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSyncID.java +++ b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/AutoSyncID.java @@ -18,6 +18,11 @@ public class AutoSyncID { this.contentWrapper = contentWrapper; this.localFile = localFile; } + + @Override + public String toString() { + return super.toString() + " (Content override)"; + } } static class ForDirectFileRequest extends AutoSyncID { @@ -39,6 +44,11 @@ public class AutoSyncID { final File fl = new File(DataHandler.readString(buf)); return new ForDirectFileRequest(uniqueID, fl); } + + @Override + public String toString() { + return super.uniqueID + " (" + this.relFile + ")"; + } } static class ForModFileRequest extends AutoSyncID { @@ -59,6 +69,11 @@ public class AutoSyncID { final String version = DataFixerAPI.getModVersion(buf.readInt()); return new ForModFileRequest(modID, version); } + + @Override + public String toString() { + return super.modID + " (v" + this.version + ")"; + } } /** diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/HelloClient.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/HelloClient.java index 4d09b40f..9caf412f 100644 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/HelloClient.java +++ b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/HelloClient.java @@ -18,9 +18,13 @@ import ru.bclib.api.dataexchange.handler.autosync.SyncFolderDescriptor.SubFile; import ru.bclib.api.datafixer.DataFixerAPI; import ru.bclib.gui.screens.SyncFilesScreen; import ru.bclib.gui.screens.WarnBCLibVersionMismatch; +import ru.bclib.util.Pair; import ru.bclib.util.PathUtil; +import ru.bclib.util.PathUtil.ModInfo; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -69,10 +73,23 @@ public class HelloClient extends DataHandler.FromServer { //write Plugin Versions buf.writeInt(mods.size()); for (String modID : mods) { - writeString(buf, modID); final String ver = PathUtil.getModVersion(modID); + final ModInfo mi = PathUtil.getModInfo(modID); + int size = 0; + if (mi!=null) { + try { + size = (int)Files.size(mi.jarPath); + } + catch (IOException e) { + BCLib.LOGGER.error("Unable to get File Size: " + e.getMessage()); + } + } + + writeString(buf, modID); buf.writeInt(DataFixerAPI.getModVersion(ver)); - BCLib.LOGGER.info(" - Listing Mod " + modID + " v" + ver); + buf.writeInt(size); + + BCLib.LOGGER.info(" - Listing Mod " + modID + " v" + ver + " ("+PathUtil.humanReadableFileSize(size)+")"); } } else { @@ -114,7 +131,7 @@ public class HelloClient extends DataHandler.FromServer { } String bclibVersion = "0.0.0"; - Map modVersion = new HashMap<>(); + Map> modVersion = new HashMap<>(); List autoSyncedFiles = null; List autoSynFolders = null; @@ -123,14 +140,23 @@ public class HelloClient extends DataHandler.FromServer { protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender) { //read BCLibVersion (=protocol version) bclibVersion = DataFixerAPI.getModVersion(buf.readInt()); + final boolean protocolVersion_0_4_1 = DataFixerAPI.isLargerOrEqualVersion(bclibVersion, "0.4.1"); + //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); + final String id = readString(buf); + final String version = DataFixerAPI.getModVersion(buf.readInt()); + final int size; + //since v0.4.1 we also send the size of the mod-File + if (protocolVersion_0_4_1) { + size = buf.readInt(); + } else { + size = 0; + } + modVersion.put(id, new Pair<>(version, size)); } //read config Data @@ -146,7 +172,7 @@ public class HelloClient extends DataHandler.FromServer { autoSynFolders = new ArrayList<>(1); //since v0.4.1 we also send the sync folders - if (DataFixerAPI.isLargerOrEqualVersion(bclibVersion, "0.4.1")) { + if (protocolVersion_0_4_1) { final int folderCount = buf.readInt(); for (int i = 0; i < folderCount; i++) { SyncFolderDescriptor desc = SyncFolderDescriptor.deserialize(buf); @@ -262,14 +288,14 @@ public class HelloClient extends DataHandler.FromServer { @Environment(EnvType.CLIENT) private void processModFileSync(final List filesToRequest) { - for (Entry e : modVersion.entrySet()) { + for (Entry> e : modVersion.entrySet()) { final String localVersion = PathUtil.getModVersion(e.getKey()); - final String serverVersion = e.getValue(); - final boolean requestMod = !serverVersion.equals(localVersion); + final Pair serverInfo = e.getValue(); + final boolean requestMod = !serverInfo.first.equals(localVersion) && serverInfo.second>0; - BCLib.LOGGER.info(" - " + e.getKey() + " (client=" + localVersion + ", server=" + serverVersion + (requestMod?", requesting":"") +")"); + BCLib.LOGGER.info(" - " + e.getKey() + " (client=" + localVersion + ", server=" + serverInfo.first + ", size=" + PathUtil.humanReadableFileSize(serverInfo.second) + (requestMod?", requesting":"") +")"); if (requestMod){ - filesToRequest.add(new AutoSyncID.ForModFileRequest(e.getKey(), serverVersion)); + filesToRequest.add(new AutoSyncID.ForModFileRequest(e.getKey(), serverInfo.first)); } } } diff --git a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/SendFiles.java b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/SendFiles.java index a802ad1f..8b5d08bf 100644 --- a/src/main/java/ru/bclib/api/dataexchange/handler/autosync/SendFiles.java +++ b/src/main/java/ru/bclib/api/dataexchange/handler/autosync/SendFiles.java @@ -14,6 +14,7 @@ import ru.bclib.api.dataexchange.handler.autosync.AutoSync.ClientConfig; import ru.bclib.api.dataexchange.handler.autosync.AutoSync.Config; import ru.bclib.gui.screens.ConfirmRestartScreen; import ru.bclib.util.Pair; +import ru.bclib.util.PathUtil; import ru.bclib.util.Triple; import java.io.File; @@ -75,7 +76,7 @@ public class SendFiles extends DataHandler.FromServer { BCLib.LOGGER.info("Sending " + existingFiles.size() + " Files to Client:"); for (AutoFileSyncEntry entry : existingFiles) { int length = entry.serializeContent(buf); - BCLib.LOGGER.info(" - " + entry + " (" + length + " Bytes)"); + BCLib.LOGGER.info(" - " + entry + " (" + PathUtil.humanReadableFileSize(length) + ")"); } } @@ -101,7 +102,7 @@ public class SendFiles extends DataHandler.FromServer { Triple p = AutoFileSyncEntry.deserializeContent(buf); if (p.first != null) { receivedFiles.add(p); - BCLib.LOGGER.info(" - " + p.first + " (" + p.second.length + " Bytes)"); + BCLib.LOGGER.info(" - " + p.first + " (" + PathUtil.humanReadableFileSize(p.second.length) + ")"); } else { BCLib.LOGGER.error(" - Failed to receive File " + p.third + ", possibly sent from a Mod that is not installed on the client."); @@ -128,10 +129,36 @@ public class SendFiles extends DataHandler.FromServer { } } + @Environment(EnvType.CLIENT) public static void writeSyncedFile(AutoSyncID e, byte[] data, File fileName) { - Path path = fileName.toPath(); - BCLib.LOGGER.info(" - Writing " + path + " (" + data.length + " Bytes)"); + if (!PathUtil.MOD_BAK_FOLDER.toFile().exists()){ + PathUtil.MOD_BAK_FOLDER.toFile().mkdirs(); + } + + Path path = fileName!=null?fileName.toPath():null; + Path removeAfter = null; + if (e instanceof AutoFileSyncEntry.ForModFileRequest){ + AutoFileSyncEntry.ForModFileRequest mase = (AutoFileSyncEntry.ForModFileRequest)e; + removeAfter = path; + int count = 0; + String name = "bclib_synced_" + mase.modID + "_" + mase.version.replace(".", "_") + ".jar"; + do { + if (path != null) { + //move to the same directory as the existing Mod + path = path.getParent() + .resolve(name); + } + else { + //move to the default mode location + path = PathUtil.MOD_FOLDER.resolve(name); + } + count++; + name = "bclib_synced_" + mase.modID + "_" + mase.version.replace(".", "_") + "__" + String.format("%03d", count) + ".jar"; + } while (path.toFile().exists()); + } + + BCLib.LOGGER.info(" - Writing " + path + " (" + PathUtil.humanReadableFileSize(data.length) + ")"); try { final File parentFile = path.getParent() .toFile(); @@ -139,7 +166,23 @@ public class SendFiles extends DataHandler.FromServer { parentFile.mkdirs(); } Files.write(path, data); + if (removeAfter != null){ + final String bakFileName = removeAfter.toFile().getName(); + String collisionFreeName = bakFileName; + Path targetPath; + int count = 0; + do { + targetPath = PathUtil.MOD_BAK_FOLDER.resolve(collisionFreeName); + count++; + collisionFreeName = String.format("%03d", count) + "_" + bakFileName; + } while (targetPath.toFile().exists()); + + BCLib.LOGGER.info(" - Moving " + removeAfter + " to " +targetPath); + removeAfter.toFile().renameTo(targetPath.toFile()); + } AutoSync.didReceiveFile(e, fileName); + + } catch (IOException ioException) { BCLib.LOGGER.error(" --> Writing " + fileName + " failed: " + ioException); diff --git a/src/main/java/ru/bclib/util/PathUtil.java b/src/main/java/ru/bclib/util/PathUtil.java index eb6f7aac..495886d8 100644 --- a/src/main/java/ru/bclib/util/PathUtil.java +++ b/src/main/java/ru/bclib/util/PathUtil.java @@ -30,6 +30,9 @@ public class PathUtil { .resolve("mods") .normalize(); + public final static Path MOD_BAK_FOLDER = MOD_FOLDER.resolve("_bclib_deactivated") + .normalize(); + /** * Tests if a path is a child-path. *

@@ -95,7 +98,13 @@ public class PathUtil { @Override public String toString() { - return "ModInfo{" + "id=" + metadata.getId()+ "version=" + metadata.getVersion() + "jarPath=" + jarPath + '}'; + return "ModInfo{" + "id=" + metadata.getId() + ", version=" + metadata.getVersion() + ", jarPath=" + jarPath + '}'; + } + + public String getVersion() { + if (metadata == null) return "0.0.0"; + return metadata.getVersion() + .toString(); } } @@ -115,10 +124,11 @@ public class PathUtil { * calling {@link #invalidateCachedMods()} *

* An error message is printed if a mod fails to load, but the parsing will continue. + * * @return A map of all found mods. (key=ModID, value={@link ModInfo}) */ public static Map getMods() { - if (mods!=null) return mods; + if (mods != null) return mods; mods = new HashMap<>(); org.apache.logging.log4j.Logger logger = LogManager.getFormatterLogger("BCLib|ModLoader"); @@ -150,18 +160,24 @@ public class PathUtil { *

* The call will also return null if the mode-Version in the jar-File is not the same * as the version of the loaded Mod. + * * @param modID The mod ID to query * @return A {@link ModInfo}-Object for the querried Mod. */ - public static ModInfo getModInfo(String modID){ + public static ModInfo getModInfo(String modID) { + return getModInfo(modID, true); + } + + public static ModInfo getModInfo(String modID, boolean matchVersion) { getMods(); final ModInfo mi = mods.get(modID); - if (!getModVersion(modID).equals(mi.metadata.getVersion())) return null; + if (mi == null || !getModVersion(modID).equals(mi.getVersion())) return null; return mi; } /** * Local Mod Version for the queried Mod + * * @param modID The mod ID to query * @return The version of the locally installed Mod */ @@ -176,4 +192,28 @@ public class PathUtil { } return "0.0.0"; } + + /** + * Creates a human readable File-Size + * + * @param size Filesize in bytes + * @return A Human readable String + */ + public static String humanReadableFileSize(long size) { + final int threshold = 2; + final int factor = 1024; + if (size < 0) return "? Byte"; + if (size < factor * threshold) { + return size + " Byte"; + } + char[] units = {'K', 'M', 'G', 'T', 'P'}; + int unitIndex = 0; + double fSize = size; + do { + unitIndex++; + fSize /= 1024; + } while (fSize > factor * threshold && unitIndex < units.length); + + return String.format("%.1f %ciB", fSize, units[unitIndex - 1]); + } }