diff --git a/build.gradle b/build.gradle index 971650f..fefac94 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,8 @@ group = project.maven_group repositories{ maven { url "https://maven.shedaniel.me/" } maven { url "https://maven.nucleoid.xyz/" } + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } + mavenCentral() } dependencies { @@ -27,6 +29,7 @@ dependencies { modImplementation include("eu.pb4:map-canvas-api:${project.mapcanvas_version}") modImplementation include("eu.pb4:sgui:${project.sgui_version}") + modImplementation include("me.lucko:fabric-permissions-api:0.2-SNAPSHOT") } loom { diff --git a/gradle.properties b/gradle.properties index 13afe8f..564ee10 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,16 +3,16 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://fabricmc.net/versions.html - minecraft_version=1.21-rc1 - yarn_mappings=1.21-rc1+build.1 + minecraft_version=1.21 + yarn_mappings=1.21+build.1 loader_version=0.15.11 # Mod Properties - mod_version = 0.7.0+1.21 + mod_version = 0.7.1+1.21 maven_group = space.essem archives_base_name = image2map # Dependencies fabric_version=0.100.1+1.21 - mapcanvas_version=0.4.0+1.21 + mapcanvas_version=0.4.1+1.21 sgui_version=1.6.0+1.21 \ No newline at end of file diff --git a/src/main/java/space/essem/image2map/Image2Map.java b/src/main/java/space/essem/image2map/Image2Map.java index eea227c..ed201df 100644 --- a/src/main/java/space/essem/image2map/Image2Map.java +++ b/src/main/java/space/essem/image2map/Image2Map.java @@ -10,6 +10,7 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.logging.LogUtils; import eu.pb4.sgui.api.GuiHelpers; +import me.lucko.fabric.api.permissions.v0.Permissions; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; @@ -25,6 +26,8 @@ import net.minecraft.item.Items; import net.minecraft.nbt.*; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.Style; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.util.Hand; @@ -44,11 +47,18 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; +import java.net.URI; import java.net.URL; import java.net.URLConnection; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.time.temporal.TemporalUnit; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; @@ -63,8 +73,9 @@ public class Image2Map implements ModInitializer { public void onInitialize() { CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { dispatcher.register(literal("image2map") - .requires(source -> source.hasPermissionLevel(CONFIG.minPermLevel)) + .requires(Permissions.require("image2map.use", CONFIG.minPermLevel)) .then(literal("create") + .requires(Permissions.require("image2map.create", 0)) .then(argument("width", IntegerArgumentType.integer(1)) .then(argument("height", IntegerArgumentType.integer(1)) .then(argument("mode", StringArgumentType.word()).suggests(new DitherModeSuggestionProvider()) @@ -80,6 +91,7 @@ public void onInitialize() { ) ) .then(literal("preview") + .requires(Permissions.require("image2map.preview", 0)) .then(argument("path", StringArgumentType.greedyString()) .executes(this::openPreview) ) @@ -96,9 +108,24 @@ private int openPreview(CommandContext context) throws Comm source.sendFeedback(() -> Text.literal("Getting image..."), false); - getImage(input).orTimeout(60, TimeUnit.SECONDS).handleAsync((image, ex) -> { - if (image == null || ex != null) { - source.sendFeedback(() -> Text.literal("That doesn't seem to be a valid image!"), false); + getImage(input).orTimeout(20, TimeUnit.SECONDS).handleAsync((image, ex) -> { + if (ex instanceof TimeoutException) { + source.sendFeedback(() -> Text.literal("Downloading or reading of the image took too long!"), false); + return null; + } else if (ex != null) { + if (ex instanceof RuntimeException ru && ru.getCause() != null) { + ex = ru.getCause(); + } + + Throwable finalEx = ex; + source.sendFeedback(() -> Text.literal("The image isn't valid (hover for more info)!") + .setStyle(Style.EMPTY.withColor(Formatting.RED).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(finalEx.getMessage())))), false); + return null; + } + + if (image == null) { + source.sendFeedback(() -> Text.literal("That doesn't seem to be a valid image (unknown reason)!"), false); + return null; } if (GuiHelpers.getCurrentGui(source.getPlayer()) instanceof PreviewGui previewGui) { @@ -141,11 +168,13 @@ private CompletableFuture getImage(String input) { return CompletableFuture.supplyAsync(() -> { try { if (isValid(input)) { - URL url = new URL(input); - URLConnection connection = url.openConnection(); - connection.setRequestProperty("User-Agent", "Image2Map mod"); - connection.connect(); - return ImageIO.read(connection.getInputStream()); + try(var client = HttpClient.newHttpClient()) { + var req = HttpRequest.newBuilder().GET().uri(URI.create(input)).timeout(Duration.ofSeconds(30)) + .setHeader("User-Agent", "Image2Map mod").build(); + + var stream = client.send(req, HttpResponse.BodyHandlers.ofInputStream()); + return ImageIO.read(stream.body()); + } } else if (CONFIG.allowLocalFiles) { File file = new File(input); return ImageIO.read(file); @@ -153,7 +182,7 @@ private CompletableFuture getImage(String input) { return null; } } catch (Throwable e) { - return null; + throw new RuntimeException(e); } }); } @@ -174,9 +203,24 @@ private int createMap(CommandContext context) throws Comman source.sendFeedback(() -> Text.literal("Getting image..."), false); - getImage(input).orTimeout(60, TimeUnit.SECONDS).handleAsync((image, ex) -> { - if (image == null || ex != null) { - source.sendFeedback(() -> Text.literal("That doesn't seem to be a valid image!"), false); + getImage(input).orTimeout(20, TimeUnit.SECONDS).handleAsync((image, ex) -> { + if (ex instanceof TimeoutException) { + source.sendFeedback(() -> Text.literal("Downloading or reading of the image took too long!"), false); + return null; + } else if (ex != null) { + if (ex instanceof RuntimeException ru && ru.getCause() != null) { + ex = ru.getCause(); + } + + Throwable finalEx = ex; + source.sendFeedback(() -> Text.literal("The image isn't valid (hover for more info)!") + .setStyle(Style.EMPTY.withColor(Formatting.RED).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(finalEx.getMessage())))), false); + return null; + } + + if (image == null) { + source.sendFeedback(() -> Text.literal("That doesn't seem to be a valid image (unknown reason)!"), false); + return null; } int width;