diff --git a/src/main/java/de/shiewk/resourcepackprivacy/ResourcePackPrivacy.java b/src/main/java/de/shiewk/resourcepackprivacy/ResourcePackPrivacy.java index 1025fd0..c77fc9e 100644 --- a/src/main/java/de/shiewk/resourcepackprivacy/ResourcePackPrivacy.java +++ b/src/main/java/de/shiewk/resourcepackprivacy/ResourcePackPrivacy.java @@ -4,11 +4,20 @@ import net.fabricmc.api.ModInitializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; + public class ResourcePackPrivacy implements ModInitializer { public static final String MOD_ID = "resourcepackprivacy"; public static final Logger LOGGER = LoggerFactory.getLogger(ResourcePackPrivacy.class); + public static void logThrowable(IOException e) { + LOGGER.error(e.toString()); + for (StackTraceElement element : e.getStackTrace()) { + LOGGER.error(element.toString()); + } + } + @Override public void onInitialize() { } diff --git a/src/main/java/de/shiewk/resourcepackprivacy/client/ResourcePackPrivacyClient.java b/src/main/java/de/shiewk/resourcepackprivacy/client/ResourcePackPrivacyClient.java index 5861802..104c608 100644 --- a/src/main/java/de/shiewk/resourcepackprivacy/client/ResourcePackPrivacyClient.java +++ b/src/main/java/de/shiewk/resourcepackprivacy/client/ResourcePackPrivacyClient.java @@ -1,16 +1,112 @@ package de.shiewk.resourcepackprivacy.client; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import de.shiewk.resourcepackprivacy.ResourcePackPrivacy; import de.shiewk.resourcepackprivacy.event.ChatAnnouncer; import de.shiewk.resourcepackprivacy.event.ScreenListener; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.minecraft.client.MinecraftClient; + +import java.io.*; +import java.net.URL; public class ResourcePackPrivacyClient implements ClientModInitializer { + private static final ObjectArrayList whitelistedURLs = new ObjectArrayList<>(); + private static final ObjectArrayList whitelistedHosts = new ObjectArrayList<>(); + private static File whitelistFile; + private static final Gson gson = new Gson(); + + public static boolean allowedURL(URL uRL) { + if (whitelistedURLs.contains(uRL.toString())){ + ResourcePackPrivacy.LOGGER.info("URL {} is whitelisted", uRL); + return true; + } else if (whitelistedHosts.contains(uRL.getHost())){ + ResourcePackPrivacy.LOGGER.info("Host {} is whitelisted", uRL.getHost()); + return true; + } + return false; + } + + public static void addWhitelistURL(URL url){ + final String urls = url.toString(); + ResourcePackPrivacy.LOGGER.info("Whitelist url {}", urls); + if (!whitelistedURLs.contains(urls)){ + whitelistedURLs.add(urls); + } + } + + public static void addWhitelistHost(URL url){ + final String h = url.getHost(); + ResourcePackPrivacy.LOGGER.info("Whitelist host {}", h); + if (!whitelistedHosts.contains(h)){ + whitelistedHosts.add(h); + } + } + + public static void loadConfig(){ + ResourcePackPrivacy.LOGGER.info("Loading config"); + try (FileReader fr = new FileReader(whitelistFile)){ + final JsonObject cfg = gson.fromJson(fr, JsonObject.class); + final JsonObject whitelist = cfg.get("whitelist").getAsJsonObject(); + final JsonArray whitelistHosts = whitelist.getAsJsonArray("hosts"); + whitelistedHosts.clear(); + for (JsonElement whitelistHost : whitelistHosts) { + whitelistedHosts.add(whitelistHost.getAsString()); + } + final JsonArray whitelistURLs = whitelist.getAsJsonArray("urls"); + whitelistedURLs.clear(); + for (JsonElement whitelistURL : whitelistURLs) { + whitelistedURLs.add(whitelistURL.getAsString()); + } + } catch (FileNotFoundException e) { + ResourcePackPrivacy.LOGGER.warn("Config file not found"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static void saveConfig() { + ResourcePackPrivacy.LOGGER.info("Saving config"); + try (FileWriter fw = new FileWriter(whitelistFile)) { + JsonObject cfg = new JsonObject(); + JsonObject whitelist = new JsonObject(); + + JsonArray hosts = new JsonArray(); + for (String whitelistedHost : whitelistedHosts) { + hosts.add(whitelistedHost); + } + whitelist.add("hosts", hosts); + + JsonArray urls = new JsonArray(); + for (String whitelistedURL : whitelistedURLs) { + urls.add(whitelistedURL); + } + whitelist.add("urls", urls); + + cfg.add("whitelist", whitelist); + + try (JsonWriter jsonWriter = new JsonWriter(fw)) { + Streams.write(cfg, jsonWriter); + } + } catch (IOException e) { + ResourcePackPrivacy.logThrowable(e); + } + } + @Override public void onInitializeClient() { + whitelistFile = new File(MinecraftClient.getInstance().runDirectory.getPath() + "/resourcepackprivacy.json"); ScreenEvents.AFTER_INIT.register(new ScreenListener()); ClientTickEvents.END_CLIENT_TICK.register(new ChatAnnouncer()); + loadConfig(); } } diff --git a/src/main/java/de/shiewk/resourcepackprivacy/event/ScreenListener.java b/src/main/java/de/shiewk/resourcepackprivacy/event/ScreenListener.java index 7edb2bc..d01d1c0 100644 --- a/src/main/java/de/shiewk/resourcepackprivacy/event/ScreenListener.java +++ b/src/main/java/de/shiewk/resourcepackprivacy/event/ScreenListener.java @@ -1,24 +1,24 @@ package de.shiewk.resourcepackprivacy.event; -import de.shiewk.resourcepackprivacy.ResourcePackPrivacy; +import de.shiewk.resourcepackprivacy.client.ResourcePackPrivacyClient; +import de.shiewk.resourcepackprivacy.mixin.AccessorConfirmScreen; import de.shiewk.resourcepackprivacy.mixin.AccessorConfirmServerResourcePackScreen; import de.shiewk.resourcepackprivacy.mixin.AccessorConfirmServerResourcePackScreenPack; -import de.shiewk.resourcepackprivacy.mixin.MixinClientCommonNetworkHandler; import de.shiewk.resourcepackprivacy.screen.ViewResourceURLsScreen; import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; import net.fabricmc.fabric.api.client.screen.v1.Screens; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.ConfirmScreen; import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.*; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.gui.widget.GridWidget; +import net.minecraft.client.gui.widget.SimplePositioningWidget; import net.minecraft.client.network.ClientCommonNetworkHandler; -import net.minecraft.text.MutableText; import net.minecraft.text.Text; import org.jetbrains.annotations.NotNull; import java.awt.*; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -46,15 +46,37 @@ public class ScreenListener implements ScreenEvents.AfterInit { } adder.add(createButton(Text.translatable(infos.size() == 1 ? "gui.resourcepackprivacy.viewURL" : "gui.resourcepackprivacy.viewURLs"), btn -> viewURLs(client, screen, infos))); - adder.add(createButton(Text.translatable(infos.size() == 1 ? "gui.resourcepackprivacy.alwaysURL" : "gui.resourcepackprivacy.alwaysURLs"), btn -> btn.active = false)); - adder.add(createLargeButton(Text.translatable("gui.resourcepackprivacy.alwaysHost", Text.literal(infos.getFirst().url().getHost()).withColor(Color.GREEN.getRGB())), btn -> btn.active = false), 2); + adder.add(createButton(Text.translatable(infos.size() == 1 ? "gui.resourcepackprivacy.alwaysURL" : "gui.resourcepackprivacy.alwaysURLs"), btn -> whitelistURLsAndAccept(btn, screen, infos))); + adder.add(createLargeButton(Text.translatable("gui.resourcepackprivacy.alwaysHost", Text.literal(infos.getFirst().url().getHost()).withColor(Color.GREEN.getRGB())), btn -> whitelistHostsAndAccept(btn, screen, infos)), 2); gw.refreshPositions(); - SimplePositioningWidget.setPos(gw, 0, 0, scaledWidth, scaledHeight, 0.5F, 0.875F); + SimplePositioningWidget.setPos(gw, 0, 0, scaledWidth, scaledHeight, 0.5F, 0.85F); gw.forEachChild(buttons::add); } } + private void whitelistURLsAndAccept(ButtonWidget btn, Screen screen, List infos){ + btn.active = false; + for (PackInfo info : infos) { + ResourcePackPrivacyClient.addWhitelistURL(info.url()); + } + ResourcePackPrivacyClient.saveConfig(); + accept(screen); + } + + private void accept(Screen screen){ + ((AccessorConfirmScreen) screen).getCallback().accept(true); + } + + private void whitelistHostsAndAccept(ButtonWidget btn, Screen screen, List infos){ + btn.active = false; + for (PackInfo info : infos) { + ResourcePackPrivacyClient.addWhitelistHost(info.url()); + } + ResourcePackPrivacyClient.saveConfig(); + accept(screen); + } + private void viewURLs(MinecraftClient client, Screen screen, List infos) { client.setScreen(new ViewResourceURLsScreen(screen, infos)); } diff --git a/src/main/java/de/shiewk/resourcepackprivacy/mixin/AccessorConfirmScreen.java b/src/main/java/de/shiewk/resourcepackprivacy/mixin/AccessorConfirmScreen.java new file mode 100644 index 0000000..ece341d --- /dev/null +++ b/src/main/java/de/shiewk/resourcepackprivacy/mixin/AccessorConfirmScreen.java @@ -0,0 +1,14 @@ +package de.shiewk.resourcepackprivacy.mixin; + +import it.unimi.dsi.fastutil.booleans.BooleanConsumer; +import net.minecraft.client.gui.screen.ConfirmScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ConfirmScreen.class) +public interface AccessorConfirmScreen { + + @Accessor("callback") + BooleanConsumer getCallback(); + +} diff --git a/src/main/java/de/shiewk/resourcepackprivacy/mixin/MixinClientCommonNetworkHandler.java b/src/main/java/de/shiewk/resourcepackprivacy/mixin/MixinClientCommonNetworkHandler.java index 25c1425..9710f99 100644 --- a/src/main/java/de/shiewk/resourcepackprivacy/mixin/MixinClientCommonNetworkHandler.java +++ b/src/main/java/de/shiewk/resourcepackprivacy/mixin/MixinClientCommonNetworkHandler.java @@ -2,7 +2,7 @@ package de.shiewk.resourcepackprivacy.mixin; import com.llamalad7.mixinextras.sugar.Local; import de.shiewk.resourcepackprivacy.ResourcePackPrivacy; -import net.fabricmc.fabric.mixin.networking.client.accessor.ClientCommonNetworkHandlerAccessor; +import de.shiewk.resourcepackprivacy.client.ResourcePackPrivacyClient; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.network.ClientCommonNetworkHandler; @@ -17,7 +17,6 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.awt.*; import java.net.URL; import java.util.UUID; @@ -29,12 +28,18 @@ public abstract class MixinClientCommonNetworkHandler { @Shadow @Final protected MinecraftClient client; + @Shadow @Final protected ClientConnection connection; + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/common/ResourcePackSendS2CPacket;hash()Ljava/lang/String;"), method = "onResourcePackSend", cancellable = true) public void onResourcePackSend(ResourcePackSendS2CPacket packet, CallbackInfo ci, @Local UUID uUID, @Local URL uRL){ ResourcePackPrivacy.LOGGER.info(packet.url()); String hash = packet.hash(); - boolean required = packet.required(); - this.client.setScreen(this.createConfirmServerResourcePackScreen(uUID, uRL, hash, required, packet.prompt().orElse(null))); - ci.cancel(); + if (ResourcePackPrivacyClient.allowedURL(uRL)){ + this.client.getServerResourcePackProvider().addResourcePack(uUID, uRL, hash); + } else { + boolean required = packet.required(); + this.client.setScreen(this.createConfirmServerResourcePackScreen(uUID, uRL, hash, required, packet.prompt().orElse(null))); + ci.cancel(); + } } } diff --git a/src/main/resources/resourcepackprivacy.mixins.json b/src/main/resources/resourcepackprivacy.mixins.json index 5a8247c..b1485d0 100644 --- a/src/main/resources/resourcepackprivacy.mixins.json +++ b/src/main/resources/resourcepackprivacy.mixins.json @@ -3,9 +3,9 @@ "minVersion": "0.8", "package": "de.shiewk.resourcepackprivacy.mixin", "compatibilityLevel": "JAVA_21", - "mixins": [ - ], + "mixins": [], "client": [ + "AccessorConfirmScreen", "AccessorConfirmServerResourcePackScreen", "AccessorConfirmServerResourcePackScreenPack", "MixinClientCommonNetworkHandler",