More options for ModSyncing and Screen to display missmatching mods

This commit is contained in:
Frank 2021-08-25 15:10:51 +02:00
parent b959e17c18
commit 4692831007
9 changed files with 206 additions and 111 deletions

View file

@ -4,9 +4,8 @@ import net.fabricmc.loader.api.FabricLoader;
import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.DataExchangeAPI;
import ru.bclib.api.dataexchange.SyncFileHash;
import ru.bclib.config.ConfigUI;
import ru.bclib.config.Configs;
import ru.bclib.config.NamedPathConfig;
import ru.bclib.config.ServerConfig;
import ru.bclib.util.PathUtil;
import java.io.File;
@ -29,77 +28,6 @@ public class AutoSync {
public boolean test(SyncFileHash clientHash, SyncFileHash serverHash, FileContentWrapper content);
}
public static class ClientConfig extends NamedPathConfig{
public static final ConfigToken<Boolean> ENABLED = ConfigToken.Boolean(true, "enabled", SYNC_CATEGORY);
@ConfigUI(leftPadding =8)
public static final DependendConfigToken<Boolean> ACCEPT_CONFIGS = DependendConfigToken.Boolean(true,"acceptConfigs", SYNC_CATEGORY, (config)->config.get(ENABLED));
@ConfigUI(leftPadding =8)
public static final DependendConfigToken<Boolean> ACCEPT_FILES = DependendConfigToken.Boolean(true,"acceptFiles", SYNC_CATEGORY, (config)->config.get(ENABLED));
@ConfigUI(leftPadding =8)
public static final DependendConfigToken<Boolean> ACCEPT_MODS = DependendConfigToken.Boolean(false,"acceptMods", SYNC_CATEGORY, (config)->config.get(ENABLED));
@ConfigUI(topPadding = 12)
public static final ConfigToken<Boolean> DEBUG_HASHES = ConfigToken.Boolean(false, "debugHashes", SYNC_CATEGORY);
public ClientConfig(){
super(BCLib.MOD_ID, "client", false);
}
public boolean shouldPrintDebugHashes() {
return get(DEBUG_HASHES);
}
public boolean isAllowingAutoSync() {
return get(ENABLED);
}
public boolean isAcceptingMods() {
return get(ACCEPT_MODS) /*&& isAllowingAutoSync()*/;
}
public boolean isAcceptingConfigs() {
return get(ACCEPT_CONFIGS) /*&& isAllowingAutoSync()*/;
}
public boolean isAcceptingFiles() {
return get(ACCEPT_FILES) /*&& isAllowingAutoSync()*/;
}
}
public static class ServerConfig extends NamedPathConfig {
public static final ConfigToken<Boolean> ENABLED = ConfigToken.Boolean(true, "enabled", SYNC_CATEGORY);
public static final DependendConfigToken<Boolean> OFFER_CONFIGS = DependendConfigToken.Boolean(true,"offerConfigs", SYNC_CATEGORY, (config)->config.get(ENABLED));
public static final DependendConfigToken<Boolean> OFFER_FILES = DependendConfigToken.Boolean(true,"offerFiles", SYNC_CATEGORY, (config)->config.get(ENABLED));
public static final DependendConfigToken<Boolean> OFFER_MODS = DependendConfigToken.Boolean(true,"offerMods", SYNC_CATEGORY, (config)->config.get(ENABLED));
public static final ConfigToken<List<String>> ADDITIONAL_MODS = ConfigToken.StringArray(new ArrayList<>(0),"additionalMods", SYNC_CATEGORY);
public ServerConfig(){
super(BCLib.MOD_ID, "server", false);
}
public boolean isAllowingAutoSync() {
return get(ENABLED);
}
public boolean isOfferingConfigs() {
return get(OFFER_CONFIGS) /*&& isAllowingAutoSync()*/;
}
public boolean isOfferingFiles() {
return get(OFFER_FILES) /*&& isAllowingAutoSync()*/;
}
public boolean isOfferingMods() {
return get(OFFER_MODS) /*&& isAllowingAutoSync()*/;
}
public String[] additionalModsForSync() {
return new String[0];
}
}
final static class AutoSyncTriple {
public final SyncFileHash serverHash;
public final byte[] serverContent;

View file

@ -3,8 +3,8 @@ package ru.bclib.api.dataexchange.handler.autosync;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.fabricmc.loader.api.metadata.ModEnvironment;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
@ -15,6 +15,7 @@ import ru.bclib.api.dataexchange.DataHandlerDescriptor;
import ru.bclib.api.dataexchange.handler.autosync.AutoSyncID.WithContentOverride;
import ru.bclib.api.dataexchange.handler.autosync.SyncFolderDescriptor.SubFile;
import ru.bclib.config.Configs;
import ru.bclib.gui.screens.ModListScreen;
import ru.bclib.gui.screens.ProgressScreen;
import ru.bclib.gui.screens.SyncFilesScreen;
import ru.bclib.gui.screens.WarnBCLibVersionMismatch;
@ -28,10 +29,11 @@ import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.Set;
import java.util.stream.Collectors;
/**
@ -40,8 +42,10 @@ import java.util.stream.Collectors;
* For Details refer to {@link HelloServer}
*/
public class HelloClient extends DataHandler.FromServer {
public interface IServerModMap extends Map<String, Pair<String, Integer>> {}
public static class ServerModMap extends HashMap<String, Pair<String, Integer>> implements IServerModMap {}
public record OfferedModInfo(String version, int size, boolean canDownload) {
}
public interface IServerModMap extends Map<String, OfferedModInfo> {}
public static class ServerModMap extends HashMap<String, OfferedModInfo> implements IServerModMap {}
public static DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "hello_client"), HelloClient::new, false, false);
@ -68,32 +72,49 @@ public class HelloClient extends DataHandler.FromServer {
protected void serializeDataOnServer(FriendlyByteBuf buf) {
final String vbclib = getBCLibVersion();
BCLib.LOGGER.info("Sending Hello to Client. (server=" + vbclib + ")");
final List<String> mods = DataExchangeAPI.registeredMods();
//write BCLibVersion (=protocol version)
buf.writeInt(ModUtil.convertModVersion(vbclib));
if (Configs.SERVER_CONFIG.isOfferingMods()) {
if (Configs.SERVER_CONFIG.isOfferingMods() || Configs.SERVER_CONFIG.isOfferingInfosForMods()) {
List<String> mods = DataExchangeAPI.registeredMods();
final List<String> inmods = mods;
if (Configs.SERVER_CONFIG.isOfferingAllMods() || Configs.SERVER_CONFIG.isOfferingInfosForMods()){
mods = new ArrayList<>(inmods.size());
mods.addAll(inmods);
mods.addAll(ModUtil
.getMods()
.entrySet()
.stream()
.filter(entry -> entry.getValue().metadata.getEnvironment()!= ModEnvironment.SERVER && !inmods.contains(entry.getKey()))
.map(entry -> entry.getKey())
.collect(Collectors.toList())
);
}
//write Plugin Versions
buf.writeInt(mods.size());
for (String modID : mods) {
final String ver = ModUtil.getModVersion(modID);
final ModInfo mi = ModUtil.getModInfo(modID);
int size = 0;
final ModInfo mi = ModUtil.getModInfo(modID);
if (mi != null) {
try {
size = (int) Files.size(mi.jarPath);
}
catch (IOException e) {
} catch (IOException e) {
BCLib.LOGGER.error("Unable to get File Size: " + e.getMessage());
}
}
writeString(buf, modID);
buf.writeInt(ModUtil.convertModVersion(ver));
buf.writeInt(size);
final boolean canDownload = size>0 && Configs.SERVER_CONFIG.isOfferingMods() && (Configs.SERVER_CONFIG.isOfferingAllMods() || inmods.contains(modID));
buf.writeBoolean(canDownload);
BCLib.LOGGER.info(" - Listing Mod " + modID + " v" + ver + " (" + PathUtil.humanReadableFileSize(size) + ")");
BCLib.LOGGER.info(" - Listing Mod " + modID + " v" + ver + " (size: " + PathUtil.humanReadableFileSize(size) + ", download="+canDownload+")");
}
}
else {
@ -132,13 +153,18 @@ public class HelloClient extends DataHandler.FromServer {
BCLib.LOGGER.info("Server will not offer Sync Folders.");
buf.writeInt(0);
}
buf.writeBoolean(Configs.SERVER_CONFIG.isOfferingInfosForMods());
}
String bclibVersion = "0.0.0";
IServerModMap modVersion = new ServerModMap();
List<AutoSync.AutoSyncTriple> autoSyncedFiles = null;
List<SyncFolderDescriptor> autoSynFolders = null;
boolean serverPublishedModInfo = false;
@Environment(EnvType.CLIENT)
@Override
@ -155,14 +181,17 @@ public class HelloClient extends DataHandler.FromServer {
final String id = readString(buf);
final String version = ModUtil.convertModVersion(buf.readInt());
final int size;
final boolean canDownload;
//since v0.4.1 we also send the size of the mod-File
if (protocolVersion_0_4_1) {
size = buf.readInt();
canDownload = buf.readBoolean();
}
else {
size = 0;
canDownload = true;
}
modVersion.put(id, new Pair<>(version, size));
modVersion.put(id, new OfferedModInfo(version, size, canDownload));
}
//read config Data
@ -184,6 +213,8 @@ public class HelloClient extends DataHandler.FromServer {
SyncFolderDescriptor desc = SyncFolderDescriptor.deserialize(buf);
autoSynFolders.add(desc);
}
serverPublishedModInfo = buf.readBoolean();
}
}
@ -292,18 +323,25 @@ public class HelloClient extends DataHandler.FromServer {
}
}
@Environment(EnvType.CLIENT)
private void processModFileSync(final List<AutoSyncID> filesToRequest) {
for (Entry<String, Pair<String, Integer>> e : modVersion.entrySet()) {
final String localVersion = ModUtil.getModVersion(e.getKey());
final Pair<String, Integer> serverInfo = e.getValue();
final boolean requestMod = !serverInfo.first.equals(localVersion) && serverInfo.second > 0;
BCLib.LOGGER.info(" - " + e.getKey() + " (client=" + localVersion + ", server=" + serverInfo.first + ", size=" + PathUtil.humanReadableFileSize(serverInfo.second) + (requestMod ? ", requesting" : "") + ")");
@Environment(EnvType.CLIENT)
private void processModFileSync(final List<AutoSyncID> filesToRequest, final Set<String> mismatchingMods) {
for (Entry<String, OfferedModInfo> e : modVersion.entrySet()) {
final String localVersion = ModUtil.getModVersion(e.getKey());
final OfferedModInfo serverInfo = e.getValue();
final boolean requestMod = !serverInfo.version.equals(localVersion) && serverInfo.size > 0 && serverInfo.canDownload;
BCLib.LOGGER.info(" - " + e.getKey() + " (client=" + localVersion + ", server=" + serverInfo.version + ", size=" + PathUtil.humanReadableFileSize(serverInfo.size) + (requestMod ? ", requesting" : "") + (serverInfo.canDownload ? "" :", not offered")+ ")");
if (requestMod) {
filesToRequest.add(new AutoSyncID.ForModFileRequest(e.getKey(), serverInfo.first));
filesToRequest.add(new AutoSyncID.ForModFileRequest(e.getKey(), serverInfo.version));
}
if (!serverInfo.version.equals(localVersion)) {
mismatchingMods.add(e.getKey());
}
}
mismatchingMods.addAll(ModListScreen.localMissing(modVersion));
mismatchingMods.addAll(ModListScreen.serverMissing(modVersion));
}
@Override
@ -328,9 +366,10 @@ public class HelloClient extends DataHandler.FromServer {
final List<AutoSyncID> filesToRequest = new ArrayList<>(2);
final List<AutoSyncID.ForDirectFileRequest> filesToRemove = new ArrayList<>(2);
final Set<String> mismatchingMods = new HashSet<>(2);
processModFileSync(filesToRequest);
processModFileSync(filesToRequest, mismatchingMods);
processSingleFileSync(filesToRequest);
processAutoSyncFolder(filesToRequest, filesToRemove);
@ -341,6 +380,9 @@ public class HelloClient extends DataHandler.FromServer {
if ((filesToRequest.size() > 0 || filesToRemove.size() > 0) && ( Configs.CLIENT_CONFIG.isAcceptingMods() || Configs.CLIENT_CONFIG.isAcceptingConfigs() || Configs.CLIENT_CONFIG.isAcceptingFiles())) {
showSyncFilesScreen(client, filesToRequest, filesToRemove);
return;
} else if (serverPublishedModInfo && mismatchingMods.size()>0) {
client.setScreen(new ModListScreen(client.screen, new TranslatableComponent("title.bclib.modmissmatch"), new TranslatableComponent("message.bclib.modmissmatch"), ModUtil.getMods(), modVersion));
return;
}
}

View file

@ -0,0 +1,41 @@
package ru.bclib.config;
import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync;
public class ClientConfig extends NamedPathConfig {
public static final ConfigToken<Boolean> ENABLED = ConfigToken.Boolean(true, "enabled", AutoSync.SYNC_CATEGORY);
@ConfigUI(leftPadding = 8)
public static final DependendConfigToken<Boolean> ACCEPT_CONFIGS = DependendConfigToken.Boolean(true, "acceptConfigs", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
@ConfigUI(leftPadding = 8)
public static final DependendConfigToken<Boolean> ACCEPT_FILES = DependendConfigToken.Boolean(true, "acceptFiles", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
@ConfigUI(leftPadding = 8)
public static final DependendConfigToken<Boolean> ACCEPT_MODS = DependendConfigToken.Boolean(false, "acceptMods", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
@ConfigUI(topPadding = 12)
public static final ConfigToken<Boolean> DEBUG_HASHES = ConfigToken.Boolean(false, "debugHashes", AutoSync.SYNC_CATEGORY);
public ClientConfig() {
super(BCLib.MOD_ID, "client", false);
}
public boolean shouldPrintDebugHashes() {
return get(DEBUG_HASHES);
}
public boolean isAllowingAutoSync() {
return get(ENABLED);
}
public boolean isAcceptingMods() {
return get(ACCEPT_MODS) /*&& isAllowingAutoSync()*/;
}
public boolean isAcceptingConfigs() {
return get(ACCEPT_CONFIGS) /*&& isAllowingAutoSync()*/;
}
public boolean isAcceptingFiles() {
return get(ACCEPT_FILES) /*&& isAllowingAutoSync()*/;
}
}

View file

@ -3,8 +3,6 @@ package ru.bclib.config;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.ClientConfig;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync.ServerConfig;
public class Configs {
// Client and Server-Config must be the first entries. They are not part of the Auto-Sync process

View file

@ -0,0 +1,52 @@
package ru.bclib.config;
import ru.bclib.BCLib;
import ru.bclib.api.dataexchange.handler.autosync.AutoSync;
import java.util.ArrayList;
import java.util.List;
public class ServerConfig extends NamedPathConfig {
public static final ConfigToken<Boolean> ENABLED = ConfigToken.Boolean(true, "enabled", AutoSync.SYNC_CATEGORY);
public static final DependendConfigToken<Boolean> OFFER_CONFIGS = DependendConfigToken.Boolean(true, "offerConfigs", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
public static final DependendConfigToken<Boolean> OFFER_FILES = DependendConfigToken.Boolean(true, "offerFiles", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
public static final DependendConfigToken<Boolean> OFFER_MODS = DependendConfigToken.Boolean(true, "offerMods", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
public static final DependendConfigToken<Boolean> OFFER_ALL_MODS = DependendConfigToken.Boolean(false, "offerAllMods", AutoSync.SYNC_CATEGORY, (config) -> config.get(OFFER_MODS));
public static final DependendConfigToken<Boolean> SEND_ALL_MOD_INFO = DependendConfigToken.Boolean(true, "sendAllModInfo", AutoSync.SYNC_CATEGORY, (config) -> config.get(ENABLED));
public static final ConfigToken<List<String>> ADDITIONAL_MODS = ConfigToken.StringArray(new ArrayList<>(0), "additionalMods", AutoSync.SYNC_CATEGORY);
public ServerConfig() {
super(BCLib.MOD_ID, "server", false);
}
public boolean isAllowingAutoSync() {
return get(ENABLED);
}
public boolean isOfferingConfigs() {
return get(OFFER_CONFIGS) /*&& isAllowingAutoSync()*/;
}
public boolean isOfferingFiles() {
return get(OFFER_FILES) /*&& isAllowingAutoSync()*/;
}
public boolean isOfferingMods() {
return get(OFFER_MODS) /*&& isAllowingAutoSync()*/;
}
public boolean isOfferingAllMods() {
return get(OFFER_ALL_MODS) /*&& isAllowingAutoSync()*/;
}
public boolean isOfferingInfosForMods() {
return get(SEND_ALL_MOD_INFO) /*&& isAllowingAutoSync()*/;
}
public String[] additionalModsForSync() {
return new String[0];
}
}

View file

@ -71,6 +71,11 @@ public abstract class GridScreen extends Screen {
return this.font;
}
@Override
public boolean isPauseScreen() {
return true;
}
@Override
public <T extends GuiEventListener & Widget & NarratableEntry> T addRenderableWidget(T guiEventListener) {
return super.addRenderableWidget(guiEventListener);

View file

@ -22,6 +22,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
@Environment(EnvType.CLIENT)
public class ModListScreen extends BCLibScreen {
@ -47,12 +48,29 @@ public class ModListScreen extends BCLibScreen {
this.description = description;
}
public static List<String> localMissing(HelloClient.IServerModMap serverInfo){
return serverInfo.keySet()
.stream()
.filter(modid -> !ModUtil.getMods().keySet().stream().filter(mod -> mod.equals(modid)).findFirst().isPresent()).collect(Collectors.toList());
}
public static List<String> serverMissing(HelloClient.IServerModMap serverInfo){
return ModUtil.getMods().entrySet()
.stream()
.filter(entry -> entry.getValue().metadata.getEnvironment() != ModEnvironment.CLIENT)
.map(entry -> entry.getKey())
.filter(modid -> !serverInfo.keySet().stream().filter(mod -> mod.equals(modid)).findFirst().isPresent()).collect(Collectors.toList());
}
public static void addModDesc(GridColumn grid, java.util.List<ModUtil.ModInfo> mods, HelloClient.IServerModMap serverInfo, GridScreen parent) {
final int STATE_OK = 0;
final int STATE_MISSING = 1;
final int STATE_SERVER_MISSING = 2;
final int STATE_VERSION = 3;
final int STATE_SERVER_MISSING_CLIENT_MOD = 4;
final int STATE_VERSION_NOT_OFFERED = 5;
final int STATE_MISSING_NOT_OFFERED = 6;
List<Triple<String, Integer, String>> items = new LinkedList<>();
if (serverInfo!=null) {
@ -60,13 +78,16 @@ public class ModListScreen extends BCLibScreen {
.stream()
.filter(modid -> !mods.stream().filter(mod -> mod.metadata.getId().equals(modid)).findFirst().isPresent())
.forEach(modid -> {
int size = serverInfo.get(modid).second;
String stateString = serverInfo.get(modid).first;
if (size>0) {
stateString = "Version: " + stateString + ", Size: " + PathUtil.humanReadableFileSize(size);
HelloClient.OfferedModInfo nfo = serverInfo.get(modid);
String stateString = nfo.version();
if (nfo.size()>0) {
stateString = "Version: " + stateString + ", Size: " + PathUtil.humanReadableFileSize(nfo.size());
}
if (nfo.canDownload()) {
stateString += ", offered by server";
}
items.add(new Triple<>(modid, STATE_MISSING, stateString));
items.add(new Triple<>(modid, nfo.canDownload()?STATE_MISSING:STATE_MISSING_NOT_OFFERED, stateString));
});
}
@ -78,12 +99,12 @@ public class ModListScreen extends BCLibScreen {
final String modID = mod.metadata.getId();
Pair<String, Integer> data = serverInfo.get(modID);
HelloClient.OfferedModInfo data = serverInfo.get(modID);
if (data!=null) {
final String modVer = data.first;
final int size = data.second;
final String modVer = data.version();
final int size = data.size();
if (!modVer.equals(mod.getVersion())) {
state = STATE_VERSION;
state = data.canDownload()?STATE_VERSION:STATE_VERSION_NOT_OFFERED;
serverVersion = modVer;
serverSize = size;
}
@ -119,10 +140,16 @@ public class ModListScreen extends BCLibScreen {
int color = GridLayout.COLOR_RED;
final String typeText;
if (state==STATE_VERSION) {
if (state==STATE_VERSION || state==STATE_VERSION_NOT_OFFERED) {
typeText = "[VERSION]";
} else if (state==STATE_MISSING) {
if (state == STATE_VERSION_NOT_OFFERED) {
color = GridLayout.COLOR_YELLOW;
}
} else if (state==STATE_MISSING || state==STATE_MISSING_NOT_OFFERED) {
typeText = "[MISSING]";
if (state == STATE_MISSING_NOT_OFFERED) {
color = GridLayout.COLOR_YELLOW;
}
} else if (state==STATE_SERVER_MISSING || state == STATE_SERVER_MISSING_CLIENT_MOD) {
typeText = "[NOT ON SERVER]";
if (state == STATE_SERVER_MISSING_CLIENT_MOD) {

View file

@ -49,9 +49,9 @@ public class SyncFilesScreen extends BCLibScreen {
row = grid.addRow();
mods = row.addCheckbox(new TranslatableComponent("message.bclib.syncfiles.mods"), hasMods, BUTTON_HEIGHT, this.font);
mods.setEnabled(hasMods);
if (hasMods) {
row.addSpacer();
row.addButton(new TranslatableComponent("title.bclib.more"), 20, font, (button)->{
row.addButton(new TranslatableComponent("title.bclib.syncfiles.modInfo"), 20, font, (button)->{
ModListScreen scr = new ModListScreen(
this,
new TranslatableComponent("title.bclib.syncfiles.modlist"),
@ -61,7 +61,7 @@ public class SyncFilesScreen extends BCLibScreen {
);
Minecraft.getInstance().setScreen(scr);
});
}
grid.addSpacerRow();

View file

@ -29,7 +29,9 @@
"title.config.bclib.client.auto_sync.syncModFolder": "Sync entire Mod-Folder from Server",
"title.config.bclib.client.auto_sync.debugHashes": "Print Auto-Sync Debug-Hashes to Log",
"title.bclib.more": "More",
"title.bclib.syncfiles.modInfo": "Mod Info",
"title.bclib.syncfiles.modlist": "Mod Information",
"message.bclib.syncfiles.modlist": "The following shows the state of your installed installed Mods.\n\nAll Mods that do not exist locally, or have a different version will on the Server will be synchronized."
"message.bclib.syncfiles.modlist": "The following shows the state of your installed installed Mods.\n\nAll Mods that do not exist locally, or have a different version will on the Server will be synchronized.",
"title.bclib.modmissmatch": "Mod Version Conflict",
"message.bclib.modmissmatch": "Some Mods on this client do not match the version of Mods on the Server.\n\nMismatching Mods can result in odd game behavior or crashes. Please make sue that you use the same mods as the server."
}