diff --git a/src/main/java/de/shiewk/resourcepackprivacy/client/ResourcePackPrivacyClient.java b/src/main/java/de/shiewk/resourcepackprivacy/client/ResourcePackPrivacyClient.java index 53015bd..6636640 100644 --- a/src/main/java/de/shiewk/resourcepackprivacy/client/ResourcePackPrivacyClient.java +++ b/src/main/java/de/shiewk/resourcepackprivacy/client/ResourcePackPrivacyClient.java @@ -1,10 +1,18 @@ package de.shiewk.resourcepackprivacy.client; +import de.shiewk.resourcepackprivacy.event.ChatAnnouncer; +import de.shiewk.resourcepackprivacy.event.ScreenListener; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; public class ResourcePackPrivacyClient implements ClientModInitializer { @Override public void onInitializeClient() { + ScreenEvents.AFTER_INIT.register(new ScreenListener()); + } } diff --git a/src/main/java/de/shiewk/resourcepackprivacy/event/ScreenListener.java b/src/main/java/de/shiewk/resourcepackprivacy/event/ScreenListener.java new file mode 100644 index 0000000..7edb2bc --- /dev/null +++ b/src/main/java/de/shiewk/resourcepackprivacy/event/ScreenListener.java @@ -0,0 +1,79 @@ +package de.shiewk.resourcepackprivacy.event; + +import de.shiewk.resourcepackprivacy.ResourcePackPrivacy; +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.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; +import java.util.UUID; + +public class ScreenListener implements ScreenEvents.AfterInit { + + private static final int buttonWidth = 150; + + public record PackInfo(UUID id, URL url, String hash){} + + @Override + public void afterInit(MinecraftClient client, Screen screen, int scaledWidth, int scaledHeight) { + if (screen instanceof ConfirmScreen && screen.getClass().getEnclosingClass() == ClientCommonNetworkHandler.class){ + GridWidget gw = new GridWidget(); + gw.getMainPositioner().margin(4, 4, 4, 0); + final GridWidget.Adder adder = gw.createAdder(2); + + final List buttons = Screens.getButtons(screen); + + final List infos = getPackInfos((AccessorConfirmServerResourcePackScreen) screen); + + while (!buttons.isEmpty()){ + adder.add(buttons.removeFirst()); + } + + 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); + + gw.refreshPositions(); + SimplePositioningWidget.setPos(gw, 0, 0, scaledWidth, scaledHeight, 0.5F, 0.875F); + gw.forEachChild(buttons::add); + } + } + + private void viewURLs(MinecraftClient client, Screen screen, List infos) { + client.setScreen(new ViewResourceURLsScreen(screen, infos)); + } + + private static @NotNull List getPackInfos(AccessorConfirmServerResourcePackScreen screen) { + final List infos = new ArrayList<>(); + final List packs = screen.getPacks(); + for (Object packObj : packs) { + AccessorConfirmServerResourcePackScreenPack pack = (AccessorConfirmServerResourcePackScreenPack) packObj; + infos.add(new PackInfo(pack.getId(), pack.getURL(), pack.getHash())); + } + return infos; + } + + private ButtonWidget createButton(Text m, ButtonWidget.PressAction action){ + return ButtonWidget.builder(m, action).width(buttonWidth).build(); + } + + private ButtonWidget createLargeButton(Text m, ButtonWidget.PressAction action){ + return ButtonWidget.builder(m, action).width(buttonWidth*2+8).build(); + } +} diff --git a/src/main/java/de/shiewk/resourcepackprivacy/mixin/AccessorConfirmServerResourcePackScreen.java b/src/main/java/de/shiewk/resourcepackprivacy/mixin/AccessorConfirmServerResourcePackScreen.java new file mode 100644 index 0000000..8b04014 --- /dev/null +++ b/src/main/java/de/shiewk/resourcepackprivacy/mixin/AccessorConfirmServerResourcePackScreen.java @@ -0,0 +1,16 @@ +package de.shiewk.resourcepackprivacy.mixin; + +import net.minecraft.client.network.ClientCommonNetworkHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(targets = "net/minecraft/client/network/ClientCommonNetworkHandler/ConfirmServerResourcePackScreen") +public interface AccessorConfirmServerResourcePackScreen { + + @Accessor(value = "packs") + List getPacks(); + // ? type because the class is private + +} diff --git a/src/main/java/de/shiewk/resourcepackprivacy/mixin/AccessorConfirmServerResourcePackScreenPack.java b/src/main/java/de/shiewk/resourcepackprivacy/mixin/AccessorConfirmServerResourcePackScreenPack.java new file mode 100644 index 0000000..6567bee --- /dev/null +++ b/src/main/java/de/shiewk/resourcepackprivacy/mixin/AccessorConfirmServerResourcePackScreenPack.java @@ -0,0 +1,22 @@ +package de.shiewk.resourcepackprivacy.mixin; + + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.net.URL; +import java.util.UUID; + +@Mixin(targets = "net/minecraft/client/network/ClientCommonNetworkHandler/ConfirmServerResourcePackScreen/Pack") +public interface AccessorConfirmServerResourcePackScreenPack { + + @Accessor(value = "id") + UUID getId(); + + @Accessor(value = "url") + URL getURL(); + + @Accessor(value = "hash") + String getHash(); + +} diff --git a/src/main/java/de/shiewk/resourcepackprivacy/mixin/MixinClientCommonNetworkHandler.java b/src/main/java/de/shiewk/resourcepackprivacy/mixin/MixinClientCommonNetworkHandler.java new file mode 100644 index 0000000..25c1425 --- /dev/null +++ b/src/main/java/de/shiewk/resourcepackprivacy/mixin/MixinClientCommonNetworkHandler.java @@ -0,0 +1,40 @@ +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 net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.network.ClientCommonNetworkHandler; +import net.minecraft.network.ClientConnection; +import net.minecraft.network.packet.s2c.common.ResourcePackSendS2CPacket; +import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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; + +@Mixin(ClientCommonNetworkHandler.class) +public abstract class MixinClientCommonNetworkHandler { + + @Shadow protected abstract Screen createConfirmServerResourcePackScreen(UUID id, URL url, String hash, boolean required, @Nullable Text prompt); + + @Shadow @Final protected MinecraftClient client; + + + @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(); + } +} diff --git a/src/main/java/de/shiewk/resourcepackprivacy/screen/ViewResourceURLsScreen.java b/src/main/java/de/shiewk/resourcepackprivacy/screen/ViewResourceURLsScreen.java new file mode 100644 index 0000000..3597714 --- /dev/null +++ b/src/main/java/de/shiewk/resourcepackprivacy/screen/ViewResourceURLsScreen.java @@ -0,0 +1,48 @@ +package de.shiewk.resourcepackprivacy.screen; + +import de.shiewk.resourcepackprivacy.event.ScreenListener; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.MultilineTextWidget; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; + +import java.awt.*; +import java.util.List; + +public class ViewResourceURLsScreen extends Screen { + + private final Screen parent; + private final List infos; + public ViewResourceURLsScreen(Screen parent, List infos) { + super(Text.translatable("gui.resourcepackprivacy.viewURL")); + this.parent = parent; + this.infos = infos; + } + + @Override + public void close() { + assert client != null; + client.setScreen(parent); + } + + @Override + protected void init() { + addDrawableChild(ButtonWidget.builder(Text.translatable("gui.done"), btn -> this.close()) + .dimensions(width / 2 - 150, height - 30, 300, 20) + .build()); + + final MultilineTextWidget text = new MultilineTextWidget(getMessage(), textRenderer); + text.setCentered(true); + text.setPosition(width / 2 - (text.getWidth() / 2), height / 2 - (text.getHeight() / 2)); + addDrawableChild(text); + } + + private Text getMessage(){ + MutableText msg = Text.empty(); + for (ScreenListener.PackInfo info : infos) { + msg = msg.append(Text.literal("\n"+info.url())); + } + return Text.translatable(infos.size() == 1 ? "gui.resourcepackprivacy.url" : "gui.resourcepackprivacy.urls", msg).withColor(Color.GREEN.getRGB()); + } +} diff --git a/src/main/resources/assets/resourcepackprivacy/lang/en_us.json b/src/main/resources/assets/resourcepackprivacy/lang/en_us.json new file mode 100644 index 0000000..50e5240 --- /dev/null +++ b/src/main/resources/assets/resourcepackprivacy/lang/en_us.json @@ -0,0 +1,12 @@ +{ + "gui.resourcepackprivacy.confirmScreen": "Confirm Server Resource Pack", + "gui.resourcepackprivacy.accept": "Accept once", + "gui.resourcepackprivacy.decline": "Decline once", + "gui.resourcepackprivacy.url": "The pack will be downloaded from: %s", + "gui.resourcepackprivacy.urls": "The packs will be downloaded from: %s", + "gui.resourcepackprivacy.alwaysURL": "Always accept this URL", + "gui.resourcepackprivacy.alwaysURLs": "Always accept these URLs", + "gui.resourcepackprivacy.alwaysHost": "Always accept packs from %s", + "gui.resourcepackprivacy.viewURL": "View download URL", + "gui.resourcepackprivacy.viewURLs": "View download URLs" +} \ No newline at end of file diff --git a/src/main/resources/resourcepackprivacy.mixins.json b/src/main/resources/resourcepackprivacy.mixins.json index 551c0dd..66f692d 100644 --- a/src/main/resources/resourcepackprivacy.mixins.json +++ b/src/main/resources/resourcepackprivacy.mixins.json @@ -6,7 +6,9 @@ "mixins": [ ], "client": [ - + "AccessorConfirmServerResourcePackScreen", + "AccessorConfirmServerResourcePackScreenPack", + "MixinClientCommonNetworkHandler" ], "injectors": { "defaultRequire": 1