From 7163a1ac6ce39a2e45559802622ccb495b46e729 Mon Sep 17 00:00:00 2001 From: LatvianModder Date: Thu, 19 Dec 2024 04:08:17 +0200 Subject: [PATCH] Added /api/assets.zip endpoint, added event.respond(text) to basic command event --- gradle.properties | 2 +- .../mods/kubejs/BuiltinKubeJSPlugin.java | 4 +- .../mods/kubejs/command/KubeJSCommands.java | 3 +- .../kubejs/core/MinecraftEnvironmentKJS.java | 4 +- .../latvian/mods/kubejs/net/KubeJSNet.java | 2 +- .../recipe/component/TimeComponent.java | 5 +- .../kubejs/script/data/GeneratedData.java | 7 +- .../kubejs/server/BasicCommandKubeEvent.java | 22 +++++- .../java/dev/latvian/mods/kubejs/util/ID.java | 3 +- .../mods/kubejs/util/TickDuration.java | 30 +++++--- .../dev/latvian/mods/kubejs/util/TimeJS.java | 53 +++++-------- .../mods/kubejs/web/local/KubeJSWeb.java | 74 +++++++++++++++++++ 12 files changed, 147 insertions(+), 62 deletions(-) diff --git a/gradle.properties b/gradle.properties index 51af6e6b9..b25efeb68 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ mod_version=2101.7.2 neoforge_version=21.1.83 parchment_mc_version=1.21 parchment_mapping_version=2024.11.10 -rhino_version=2101.2.6-build.56 +rhino_version=2101.2.6-build.58 tiny_server_version=1.0.0-build.25 gif_lib_version=1.7 diff --git a/src/main/java/dev/latvian/mods/kubejs/BuiltinKubeJSPlugin.java b/src/main/java/dev/latvian/mods/kubejs/BuiltinKubeJSPlugin.java index 93affce6a..c3abfb825 100644 --- a/src/main/java/dev/latvian/mods/kubejs/BuiltinKubeJSPlugin.java +++ b/src/main/java/dev/latvian/mods/kubejs/BuiltinKubeJSPlugin.java @@ -536,8 +536,8 @@ public void registerTypeWrappers(TypeWrapperRegistry registry) { registry.register(RegistryPredicate.class, RegistryPredicate::of); // Java / Minecraft // - registry.register(String.class, String::valueOf); - registry.register(CharSequence.class, String::valueOf); + // registry.register(String.class, String::valueOf); + // registry.register(CharSequence.class, String::valueOf); registry.register(UUID.class, UUIDWrapper::fromString); registry.register(Pattern.class, RegExpKJS::wrap); registry.register(JsonObject.class, JsonUtils::objectOf); diff --git a/src/main/java/dev/latvian/mods/kubejs/command/KubeJSCommands.java b/src/main/java/dev/latvian/mods/kubejs/command/KubeJSCommands.java index 788b315b2..d18f6ae20 100644 --- a/src/main/java/dev/latvian/mods/kubejs/command/KubeJSCommands.java +++ b/src/main/java/dev/latvian/mods/kubejs/command/KubeJSCommands.java @@ -35,7 +35,6 @@ import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.commands.arguments.ResourceKeyArgument; import net.minecraft.commands.arguments.ResourceLocationArgument; -import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.ClickEvent; @@ -293,7 +292,7 @@ private static int help(CommandSourceStack source) { private static int customCommand(TargetedEventHandler event, CommandSourceStack source, String id, String input) { if (event.hasListeners(id)) { - var result = event.post(new BasicCommandKubeEvent(source.getLevel(), source.getEntity(), BlockPos.containing(source.getPosition()), id, input.trim()), id); + var result = event.post(new BasicCommandKubeEvent(source, id, input.trim()), id); if (result.value() instanceof Throwable ex) { source.sendFailure(Component.literal(ex.toString())); diff --git a/src/main/java/dev/latvian/mods/kubejs/core/MinecraftEnvironmentKJS.java b/src/main/java/dev/latvian/mods/kubejs/core/MinecraftEnvironmentKJS.java index 4841d9a68..850755608 100644 --- a/src/main/java/dev/latvian/mods/kubejs/core/MinecraftEnvironmentKJS.java +++ b/src/main/java/dev/latvian/mods/kubejs/core/MinecraftEnvironmentKJS.java @@ -15,7 +15,7 @@ public interface MinecraftEnvironmentKJS extends MessageSenderKJS { } default ScheduledEvents.ScheduledEvent kjs$scheduleInTicks(long ticks, ScheduledEvents.Callback callback) { - return kjs$getScheduledEvents().schedule(new TickDuration(ticks), false, callback); + return kjs$getScheduledEvents().schedule(TickDuration.of(ticks), false, callback); } default ScheduledEvents.ScheduledEvent kjs$scheduleRepeating(TemporalAmount timer, ScheduledEvents.Callback callback) { @@ -23,6 +23,6 @@ public interface MinecraftEnvironmentKJS extends MessageSenderKJS { } default ScheduledEvents.ScheduledEvent kjs$scheduleRepeatingInTicks(long ticks, ScheduledEvents.Callback callback) { - return kjs$getScheduledEvents().schedule(new TickDuration(ticks), true, callback); + return kjs$getScheduledEvents().schedule(TickDuration.of(ticks), true, callback); } } diff --git a/src/main/java/dev/latvian/mods/kubejs/net/KubeJSNet.java b/src/main/java/dev/latvian/mods/kubejs/net/KubeJSNet.java index 7744675aa..b2cdabffa 100644 --- a/src/main/java/dev/latvian/mods/kubejs/net/KubeJSNet.java +++ b/src/main/java/dev/latvian/mods/kubejs/net/KubeJSNet.java @@ -35,7 +35,7 @@ interface Kubedex { @SubscribeEvent static void register(RegisterPayloadHandlersEvent event) { - var reg = event.registrar("1"); + var reg = event.registrar("1").optional(); reg.playToClient(WEB_SERVER_JSON_UPDATE, WebServerUpdateJSONPayload.STREAM_CODEC, WebServerUpdateJSONPayload::handle); reg.playToClient(WEB_SERVER_NBT_UPDATE, WebServerUpdateNBTPayload.STREAM_CODEC, WebServerUpdateNBTPayload::handle); diff --git a/src/main/java/dev/latvian/mods/kubejs/recipe/component/TimeComponent.java b/src/main/java/dev/latvian/mods/kubejs/recipe/component/TimeComponent.java index 8bd5833b9..cc3182b0e 100644 --- a/src/main/java/dev/latvian/mods/kubejs/recipe/component/TimeComponent.java +++ b/src/main/java/dev/latvian/mods/kubejs/recipe/component/TimeComponent.java @@ -4,7 +4,6 @@ import com.mojang.serialization.Codec; import dev.latvian.mods.kubejs.recipe.KubeRecipe; import dev.latvian.mods.kubejs.util.TickDuration; -import dev.latvian.mods.kubejs.util.TimeJS; import dev.latvian.mods.rhino.Context; import dev.latvian.mods.rhino.type.TypeInfo; @@ -32,9 +31,9 @@ public boolean hasPriority(Context cx, KubeRecipe recipe, Object from) { @Override public TickDuration wrap(Context cx, KubeRecipe recipe, Object from) { if (from instanceof Number n) { - return new TickDuration((long) (n.doubleValue() * scale)); + return TickDuration.of((long) (n.doubleValue() * scale)); } else { - return new TickDuration(TimeJS.wrapDuration(from).toMillis() / 50L); + return TickDuration.wrap(from); } } diff --git a/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedData.java b/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedData.java index d36fdf077..b7fcac23e 100644 --- a/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedData.java +++ b/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedData.java @@ -1,5 +1,6 @@ package dev.latvian.mods.kubejs.script.data; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import dev.latvian.mods.kubejs.KubeJS; @@ -22,7 +23,11 @@ public record GeneratedData(ResourceLocation id, Supplier data) implemen var json = new JsonObject(); var pack = new JsonObject(); pack.addProperty("description", "KubeJS Pack"); - pack.addProperty("pack_format", 15); + pack.addProperty("pack_format", 8); + var arr = new JsonArray(); + arr.add(8); + arr.add(99); + pack.add("supported_formats", arr); json.add("pack", pack); return json.toString().getBytes(StandardCharsets.UTF_8); })); diff --git a/src/main/java/dev/latvian/mods/kubejs/server/BasicCommandKubeEvent.java b/src/main/java/dev/latvian/mods/kubejs/server/BasicCommandKubeEvent.java index ab877bef8..ae2f4d1aa 100644 --- a/src/main/java/dev/latvian/mods/kubejs/server/BasicCommandKubeEvent.java +++ b/src/main/java/dev/latvian/mods/kubejs/server/BasicCommandKubeEvent.java @@ -2,13 +2,18 @@ import dev.latvian.mods.kubejs.entity.KubeEntityEvent; import dev.latvian.mods.kubejs.level.LevelBlock; +import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import org.jetbrains.annotations.Nullable; +import java.util.function.Supplier; + public class BasicCommandKubeEvent implements KubeEntityEvent { + private final CommandSourceStack source; private final Level level; private final Entity entity; private final ServerPlayer serverPlayer; @@ -16,11 +21,12 @@ public class BasicCommandKubeEvent implements KubeEntityEvent { public final String id; public final String input; - public BasicCommandKubeEvent(Level level, @Nullable Entity entity, BlockPos pos, String id, String input) { - this.level = level; - this.entity = entity; + public BasicCommandKubeEvent(CommandSourceStack source, String id, String input) { + this.source = source; + this.level = source.getLevel(); + this.entity = source.getEntity(); this.serverPlayer = entity instanceof ServerPlayer p ? p : null; - this.pos = pos; + this.pos = BlockPos.containing(source.getPosition()); this.id = id; this.input = input; } @@ -49,4 +55,12 @@ public ServerPlayer getPlayer() { public LevelBlock getBlock() { return this.getLevel().kjs$getBlock(pos); } + + public void respondLazily(Supplier text, boolean informAdmins) { + source.sendSuccess(text, informAdmins); + } + + public void respond(Component text) { + respondLazily(() -> text, false); + } } \ No newline at end of file diff --git a/src/main/java/dev/latvian/mods/kubejs/util/ID.java b/src/main/java/dev/latvian/mods/kubejs/util/ID.java index ca6c0c161..bdc2f8337 100644 --- a/src/main/java/dev/latvian/mods/kubejs/util/ID.java +++ b/src/main/java/dev/latvian/mods/kubejs/util/ID.java @@ -3,6 +3,7 @@ import com.google.gson.JsonPrimitive; import dev.latvian.mods.kubejs.KubeJS; import dev.latvian.mods.kubejs.core.RegistryObjectKJS; +import dev.latvian.mods.kubejs.error.KubeRuntimeException; import net.minecraft.ResourceLocationException; import net.minecraft.core.Holder; import net.minecraft.resources.ResourceKey; @@ -91,7 +92,7 @@ static ResourceLocation of(@Nullable Object o, boolean preferKJS) { try { return ResourceLocation.parse(s); } catch (ResourceLocationException ex) { - throw new IllegalArgumentException("Could not create ID from '%s'!".formatted(s)); + throw new KubeRuntimeException("Could not create ID from '%s'!".formatted(s)); } } diff --git a/src/main/java/dev/latvian/mods/kubejs/util/TickDuration.java b/src/main/java/dev/latvian/mods/kubejs/util/TickDuration.java index 0954ba2a2..4f4b8b505 100644 --- a/src/main/java/dev/latvian/mods/kubejs/util/TickDuration.java +++ b/src/main/java/dev/latvian/mods/kubejs/util/TickDuration.java @@ -1,5 +1,6 @@ package dev.latvian.mods.kubejs.util; +import com.google.gson.JsonPrimitive; import com.mojang.serialization.Codec; import dev.latvian.mods.rhino.type.TypeInfo; @@ -11,19 +12,25 @@ public record TickDuration(long ticks) implements TemporalAmount { public static final TickDuration ZERO = new TickDuration(0L); private static final List UNITS = List.of(TickTemporalUnit.INSTANCE); - public static final Codec CODEC = Codec.LONG.xmap(TickDuration::new, TickDuration::ticks); - public static final Codec SECONDS_CODEC = Codec.DOUBLE.xmap(l -> new TickDuration((long) (l * 20D)), t -> t.ticks() / 20D); - public static final Codec MINUTES_CODEC = Codec.DOUBLE.xmap(l -> new TickDuration((long) (l * 1200L)), t -> t.ticks() / 1200D); - public static final Codec HOURS_CODEC = Codec.DOUBLE.xmap(l -> new TickDuration((long) (l * 72000L)), t -> t.ticks() / 72000D); + public static final Codec CODEC = Codec.LONG.xmap(TickDuration::of, TickDuration::ticks); + public static final Codec SECONDS_CODEC = Codec.DOUBLE.xmap(l -> TickDuration.of((long) (l * 20D)), t -> t.ticks() / 20D); + public static final Codec MINUTES_CODEC = Codec.DOUBLE.xmap(l -> TickDuration.of((long) (l * 1200D)), t -> t.ticks() / 1200D); + public static final Codec HOURS_CODEC = Codec.DOUBLE.xmap(l -> TickDuration.of((long) (l * 72000D)), t -> t.ticks() / 72000D); public static final TypeInfo TYPE_INFO = TypeInfo.of(TickDuration.class); // TypeInfo.NUMBER.or(TypeInfo.STRING) + public static TickDuration of(long ticks) { + return ticks == 0L ? ZERO : new TickDuration(ticks); + } + public static TickDuration wrap(Object from) { - if (from instanceof Number n) { - return new TickDuration(n.longValue()); - } else { - return new TickDuration(TimeJS.wrapDuration(from).toMillis() / 50L); - } + return switch (from) { + case null -> ZERO; + case TickDuration d -> d; + case Number n -> of(n.longValue()); + case JsonPrimitive json -> of(json.getAsLong()); + default -> of(TimeJS.wrapDuration(from).toMillis() / 50L); + }; } @Override @@ -57,4 +64,9 @@ public Temporal subtractFrom(Temporal temporal) { return temporal; } + + @Override + public String toString() { + return ticks + " ticks"; + } } diff --git a/src/main/java/dev/latvian/mods/kubejs/util/TimeJS.java b/src/main/java/dev/latvian/mods/kubejs/util/TimeJS.java index e70f0fb35..1ad85d350 100644 --- a/src/main/java/dev/latvian/mods/kubejs/util/TimeJS.java +++ b/src/main/java/dev/latvian/mods/kubejs/util/TimeJS.java @@ -1,6 +1,5 @@ package dev.latvian.mods.kubejs.util; -import com.google.gson.JsonPrimitive; import com.mojang.serialization.Codec; import dev.latvian.mods.kubejs.KubeJSCodecs; import io.netty.buffer.ByteBuf; @@ -25,63 +24,45 @@ static TemporalAmount wrapTemporalAmount(Object o) { } else if (o instanceof CharSequence) { var matcher = TEMPORAL_AMOUNT_PATTERN.matcher(o.toString()); - var millis = 0L; - var nanos = 0L; - var ticks = -1L; + var millis = 0D; + var nanos = 0D; + var ticks = Double.NaN; while (matcher.find()) { var amount = Double.parseDouble(matcher.group(1)); switch (matcher.group(2)) { case "t" -> { - if (ticks == -1L) { - ticks = 0L; + if (Double.isNaN(ticks)) { + ticks = 0D; } ticks += amount; } - case "ns" -> nanos += (long) amount; - case "ms" -> millis += (long) amount; - case "s" -> millis = (long) (amount * 1000D); - case "m" -> millis = (long) (amount * 60000D); - case "h" -> millis = (long) (amount * 60000D) * 60L; - case "d" -> millis = (long) (amount * 24D * 86400L) * 1000L; - case "w" -> millis = (long) (amount * 24D * 86400L) * 7000L; - case "M" -> millis = (long) (amount * 31556952D / 12D) * 1000L; - case "y" -> millis = (long) (amount * 31556952D) * 1000L; + case "ns" -> nanos += amount; + case "ms" -> millis += amount; + case "s" -> millis = amount * 1000D; + case "m" -> millis = amount * 60000D; + case "h" -> millis = amount * 60000D * 60L; + case "d" -> millis = amount * 24D * 86400L * 1000L; + case "w" -> millis = amount * 24D * 86400L * 7000L; + case "M" -> millis = amount * 31556952D / 12D * 1000L; + case "y" -> millis = amount * 31556952D * 1000L; default -> throw new IllegalArgumentException("Invalid temporal unit: " + matcher.group(2)); } } - if (ticks != -1L) { - return new TickDuration(ticks + millis / 50L); + if (!Double.isNaN(ticks)) { + return TickDuration.of((long) (ticks + millis / 50D)); } - return Duration.ofMillis(millis).plusNanos(nanos); + return Duration.ofMillis((long) millis).plusNanos((long) nanos); } else { throw new IllegalArgumentException("Invalid temporal amount: " + o); } } - static long tickDurationOf(Object o) { - if (o instanceof Number n) { - return n.longValue(); - } else if (o instanceof JsonPrimitive json) { - return json.getAsLong(); - } - - var t = wrapTemporalAmount(o); - - if (t instanceof TickDuration(long ticks)) { - return ticks; - } else if (t instanceof Duration d) { - return d.toMillis() / 50L; - } else { - return 0L; - } - } - static Duration wrapDuration(Object o) { var t = wrapTemporalAmount(o); diff --git a/src/main/java/dev/latvian/mods/kubejs/web/local/KubeJSWeb.java b/src/main/java/dev/latvian/mods/kubejs/web/local/KubeJSWeb.java index 0131e0e5f..efd2282eb 100644 --- a/src/main/java/dev/latvian/mods/kubejs/web/local/KubeJSWeb.java +++ b/src/main/java/dev/latvian/mods/kubejs/web/local/KubeJSWeb.java @@ -3,6 +3,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import dev.latvian.apps.tinyserver.ServerRegistry; +import dev.latvian.apps.tinyserver.content.MimeType; import dev.latvian.apps.tinyserver.http.response.HTTPPayload; import dev.latvian.apps.tinyserver.http.response.HTTPResponse; import dev.latvian.apps.tinyserver.http.response.HTTPStatus; @@ -14,6 +15,7 @@ import dev.latvian.mods.kubejs.KubeJSPaths; import dev.latvian.mods.kubejs.plugin.KubeJSPlugins; import dev.latvian.mods.kubejs.script.ScriptType; +import dev.latvian.mods.kubejs.script.data.GeneratedData; import dev.latvian.mods.kubejs.util.RegExpKJS; import dev.latvian.mods.kubejs.web.JsonContent; import dev.latvian.mods.kubejs.web.KJSHTTPRequest; @@ -36,12 +38,19 @@ import javax.imageio.ImageIO; import java.awt.RenderingHints; import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Duration; import java.util.ArrayList; +import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.function.Supplier; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; public class KubeJSWeb { public static WSHandler UPDATES = WSHandler.empty(); @@ -116,6 +125,7 @@ public static void register(LocalWebServerRegistry registry) { registry.get("/api/mods", KubeJSWeb::getMods); registry.get("/api/mods/{id}/icon", KubeJSWeb::getModIcon); + registry.get("/api/assets.zip", KubeJSWeb::getAssetsZip); registry.get("/api/browse", KubeJSWeb::getBrowse); registry.get("/api/browse/{directory}", KubeJSWeb::getBrowseDir); registry.get("/api/browse/{directory}/", KubeJSWeb::getBrowseFile); @@ -225,6 +235,70 @@ private static HTTPResponse getModIcon(KJSHTTPRequest req) throws Exception { return HTTPResponse.ok().png(img); } + private static HTTPResponse getAssetsZip(KJSHTTPRequest req) throws IOException { + if (Files.notExists(KubeJSPaths.ASSETS)) { + throw new NotFoundError("kubejs/assets directory is not found!"); + } + + var allFiles = new HashMap(); + var allZipFiles = new HashMap(); + + for (var rpath : Files.list(KubeJSPaths.ASSETS).sorted((a, b) -> a.getFileName().toString().compareToIgnoreCase(b.getFileName().toString())).toList()) { + var fn = rpath.getFileName().toString(); + + if (fn.endsWith(".zip")) { + try (var fs = FileSystems.newFileSystem(rpath)) { + var root = fs.getPath("."); + + for (var cpath : Files.walk(root).toList()) { + if (Files.isRegularFile(cpath)) { + var zpath = root.relativize(cpath).toString().replace('\\', '/'); + allZipFiles.put(zpath, Files.readAllBytes(cpath)); + } + } + } + } else if (Files.isDirectory(rpath)) { + for (var path : Files.walk(rpath).toList()) { + var zpath = KubeJSPaths.DIRECTORY.relativize(path).toString().replace('\\', '/'); + + if (Files.isRegularFile(path)) { + allFiles.put(zpath, Files.readAllBytes(path)); + } + } + } + } + + for (var entry : allZipFiles.entrySet()) { + allFiles.putIfAbsent(entry.getKey(), entry.getValue()); + } + + allFiles.remove("LICENSE"); + allFiles.remove("pack.mcmeta"); + allFiles.remove("pack.png"); + + var list = allFiles.entrySet().stream().sorted((a, b) -> a.getKey().compareToIgnoreCase(b.getKey())).toList(); + + var zipBytes = new ByteArrayOutputStream(); + + try (var out = new ZipOutputStream(zipBytes)) { + for (var path : list) { + out.putNextEntry(new ZipEntry(path.getKey())); + out.write(path.getValue()); + out.closeEntry(); + } + + out.putNextEntry(new ZipEntry("pack.mcmeta")); + out.write(GeneratedData.PACK_META.data().get()); + out.closeEntry(); + + out.putNextEntry(new ZipEntry("pack.png")); + out.write(GeneratedData.PACK_ICON.data().get()); + out.closeEntry(); + } + + return HTTPResponse.ok().content(zipBytes.toByteArray(), MimeType.ZIP).publicCache(Duration.ofSeconds(15L)); + } + private static HTTPResponse getBrowse(KJSHTTPRequest req) { return HTTPResponse.ok().content(JsonContent.array(json -> BROWSE.keySet().forEach(json::add))); }