From ab6b7d3f068bd6f4e2fdd4f7bcad9e422a51fe7c Mon Sep 17 00:00:00 2001 From: DAM <43420467+DAMcraft@users.noreply.github.com> Date: Fri, 3 Nov 2023 21:22:28 +0100 Subject: [PATCH] Now prompts users to install Meteor in case it isn't installed --- .../de/damcraft/serverseeker/SmallHttp.java | 33 ++++ .../serverseeker/gui/InstallMeteorScreen.java | 152 ++++++++++++++++++ .../serverseeker/mixin/TitleScreenMixin.java | 22 +++ src/main/resources/fabric.mod.json | 4 +- src/main/resources/serverseeker.mixins.json | 3 +- 5 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/main/java/de/damcraft/serverseeker/gui/InstallMeteorScreen.java create mode 100644 src/main/java/de/damcraft/serverseeker/mixin/TitleScreenMixin.java diff --git a/src/main/java/de/damcraft/serverseeker/SmallHttp.java b/src/main/java/de/damcraft/serverseeker/SmallHttp.java index 83204f2..bbf00d3 100644 --- a/src/main/java/de/damcraft/serverseeker/SmallHttp.java +++ b/src/main/java/de/damcraft/serverseeker/SmallHttp.java @@ -2,6 +2,7 @@ import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -25,4 +26,36 @@ public static String post(String url, String json) { return null; } } + + public static String get(String url) { + HttpClient client = HttpClient.newHttpClient(); + try { + return client.send(HttpRequest.newBuilder() + .uri(URI.create(url)) + .GET() + .build(), + HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8) + ).body(); + } + catch (IOException | InterruptedException e) { + e.printStackTrace(); + return null; + } + } + + public static HttpResponse download(String url) { + HttpClient client = HttpClient.newHttpClient(); + try { + return client.send(HttpRequest.newBuilder() + .uri(URI.create(url)) + .GET() + .build(), + HttpResponse.BodyHandlers.ofInputStream() + ); + } + catch (IOException | InterruptedException e) { + e.printStackTrace(); + return null; + } + } } diff --git a/src/main/java/de/damcraft/serverseeker/gui/InstallMeteorScreen.java b/src/main/java/de/damcraft/serverseeker/gui/InstallMeteorScreen.java new file mode 100644 index 0000000..3c59ed1 --- /dev/null +++ b/src/main/java/de/damcraft/serverseeker/gui/InstallMeteorScreen.java @@ -0,0 +1,152 @@ +package de.damcraft.serverseeker.gui; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import de.damcraft.serverseeker.SmallHttp; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.SharedConstants; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.TextWidget; +import net.minecraft.text.Text; +import net.minecraft.util.Util; + +import java.io.InputStream; +import java.net.http.HttpResponse; +import java.nio.file.Path; +import java.util.Optional; + +public class InstallMeteorScreen extends Screen { + public InstallMeteorScreen() { + super(Text.of("Meteor Client is not installed!")); + } + + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + super.render(context, mouseX, mouseY, delta); + context.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, this.height / 4 - 60 + 20, 16777215); + } + + protected void init() { + super.init(); + this.addDrawableChild(ButtonWidget.builder(Text.of("Automatically install Meteor (§arecommended§r)"), (button) -> { + String result = SmallHttp.get("https://meteorclient.com/api/stats"); + if (result == null) { + this.clearChildren(); + // Render error + this.addDrawableChild(new TextWidget( + this.width / 2 - 250, + this.height / 4, + 500, + 20, + Text.of("Failed to get install meteor automatically! Please install it manually."), + this.textRenderer + )); + this.addDrawableChild(ButtonWidget.builder(Text.of("Open install FAQ"), (button2) -> { + Util.getOperatingSystem().open("https://meteorclient.com/faq/installation"); + }).dimensions(this.width / 2 - 150, this.height / 4 + 100, 300, 20).build()); + } + Gson gson = new Gson(); + JsonObject json = gson.fromJson(result, JsonObject.class); + String currentVersion = SharedConstants.getGameVersion().getName(); + String stableVersion = json.get("mc_version").getAsString(); + String devBuildVersion = json.get("dev_build_mc_version").getAsString(); + String url; + if (currentVersion.equals(stableVersion)) { + url = "https://meteorclient.com/api/download"; + } else if (currentVersion.equals(devBuildVersion)) { + url = "https://meteorclient.com/api/download?devBuild=latest"; + } else { + this.clearChildren(); + // Render error + this.addDrawableChild(new TextWidget( + this.width / 2 - 250, + this.height / 4, + 500, + 20, + Text.of("Failed to find Meteor for your current version."), + this.textRenderer + )); + this.addDrawableChild(ButtonWidget.builder(Text.of("Open install FAQ"), (button2) -> { + Util.getOperatingSystem().open("https://meteorclient.com/faq/installation"); + }).dimensions(this.width / 2 - 150, this.height / 4 + 100, 300, 20).build()); + return; + } + HttpResponse file = SmallHttp.download(url); + if (file == null) { + this.clearChildren(); + // Render error + this.addDrawableChild(new TextWidget( + this.width / 2 - 250, + this.height / 4, + 500, + 20, + Text.of("Failed to download Meteor! Please install it manually."), + this.textRenderer + )); + this.addDrawableChild(ButtonWidget.builder(Text.of("Open install FAQ"), (button2) -> { + Util.getOperatingSystem().open("https://meteorclient.com/faq/installation"); + }).dimensions(this.width / 2 - 150, this.height / 4 + 100, 300, 20).build()); + return; + } + Optional filenameT= file.headers().firstValue("Content-Disposition"); + String filename = "meteor-client.jar"; + if (filenameT.isPresent()) { + filename = filenameT.get().split("filename=")[1]; + } + System.out.println(filename); + // Get the mods folder + Path modsFolder = FabricLoader.getInstance().getGameDir().resolve("mods"); + if (!modsFolder.toFile().exists()) { + this.clearChildren(); + // Render error + this.addDrawableChild(new TextWidget( + this.width / 2 - 250, + this.height / 4, + 500, + 20, + Text.of("Failed to find mods folder! Please install Meteor manually."), + this.textRenderer + )); + this.addDrawableChild(ButtonWidget.builder(Text.of("Open install FAQ"), (button2) -> { + Util.getOperatingSystem().open("https://meteorclient.com/faq/installation"); + }).dimensions(this.width / 2 - 150, this.height / 4 + 100, 300, 20).build()); + } + // Save the file + try { + java.nio.file.Files.copy(file.body(), modsFolder.resolve(filename)); + } catch (Exception e) { + this.clearChildren(); + // Render error + this.addDrawableChild(new TextWidget( + this.width / 2 - 250, + this.height / 4, + 500, + 20, + Text.of("Failed to save Meteor! Please install it manually."), + this.textRenderer + )); + this.addDrawableChild(ButtonWidget.builder(Text.of("Open install FAQ"), (button2) -> { + Util.getOperatingSystem().open("https://meteorclient.com/faq/installation"); + }).dimensions(this.width / 2 - 150, this.height / 4 + 100, 300, 20).build()); + return; + } + // Success message + this.clearChildren(); + this.addDrawableChild(new TextWidget( + this.width / 2 - 250, + this.height / 4, + 500, + 20, + Text.of("Successfully installed Meteor! Please restart your game."), + this.textRenderer + )); + }).dimensions(this.width / 2 - 150, this.height / 4 + 100, 300, 20).build()); + this.addDrawableChild(ButtonWidget.builder(Text.of("Manual installation"), (button) -> { + Util.getOperatingSystem().open("https://meteorclient.com/faq/installation"); + }).dimensions(this.width / 2 - 150, this.height / 4 + 100 + 25, 148, 20).build()); + this.addDrawableChild(ButtonWidget.builder(Text.translatable("menu.quit"), (button) -> { + this.client.scheduleStop(); + }).dimensions(this.width / 2 + 2, this.height / 4 + 100 + 25, 148, 20).build()); + } +} diff --git a/src/main/java/de/damcraft/serverseeker/mixin/TitleScreenMixin.java b/src/main/java/de/damcraft/serverseeker/mixin/TitleScreenMixin.java new file mode 100644 index 0000000..ee6abe8 --- /dev/null +++ b/src/main/java/de/damcraft/serverseeker/mixin/TitleScreenMixin.java @@ -0,0 +1,22 @@ +package de.damcraft.serverseeker.mixin; + +import de.damcraft.serverseeker.gui.InstallMeteorScreen; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.TitleScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public class TitleScreenMixin { + @Inject(at = @At("HEAD"), method = "init()V", cancellable = true) + private void init(CallbackInfo info) { + // Check if meteor-client is installed + if (!FabricLoader.getInstance().isModLoaded("meteor-client")) { + info.cancel(); + MinecraftClient.getInstance().setScreen(new InstallMeteorScreen()); + } + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 84b4b45..e304986 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -35,7 +35,9 @@ }, "depends": { "java": ">=17", - "minecraft": ">=1.20.2", + "minecraft": ">=1.20.2" + }, + "recommends": { "meteor-client": "*" } } diff --git a/src/main/resources/serverseeker.mixins.json b/src/main/resources/serverseeker.mixins.json index 7f57937..bf2e381 100644 --- a/src/main/resources/serverseeker.mixins.json +++ b/src/main/resources/serverseeker.mixins.json @@ -5,7 +5,8 @@ "client": [ "MultiplayerScreenAccessor", "MultiplayerScreenMixin", - "SystemsMixin" + "SystemsMixin", + "TitleScreenMixin" ], "injectors": { "defaultRequire": 1