From 9205ad70f3e2d317cb0b3723556f7b2a055b782a Mon Sep 17 00:00:00 2001 From: LatvianModder Date: Tue, 21 Jan 2025 23:24:22 +0200 Subject: [PATCH] Added `// required: client` option for scripts, rewrote keybind events, fixed some PR issues --- .../kubejs/bindings/GLFWInputWrapper.java | 36 +++++ .../kubejs/bindings/event/ClientEvents.java | 5 - .../kubejs/bindings/event/KeyBindEvents.java | 16 ++ .../block/state/BlockStatePredicate.java | 7 +- .../client/BuiltinKubeJSClientPlugin.java | 12 +- .../client/KeybindRegistryKubeEvent.java | 52 +++++-- .../mods/kubejs/client/KubeJSKeybinds.java | 142 +++++++++++------- .../client/KubeJSModClientEventHandler.java | 12 +- .../mods/kubejs/command/KubeJSCommands.java | 6 +- .../mods/kubejs/core/MinecraftClientKJS.java | 23 ++- .../mods/kubejs/script/ScriptFile.java | 9 ++ .../mods/kubejs/web/KJSHTTPServer.java | 26 ++++ .../mods/kubejs/web/LocalWebServer.java | 36 +++-- .../kubejs/web/LocalWebServerRegistry.java | 12 +- .../web/LocalWebServerRegistryHolder.java | 25 --- 15 files changed, 274 insertions(+), 145 deletions(-) create mode 100644 src/main/java/dev/latvian/mods/kubejs/bindings/GLFWInputWrapper.java create mode 100644 src/main/java/dev/latvian/mods/kubejs/bindings/event/KeyBindEvents.java create mode 100644 src/main/java/dev/latvian/mods/kubejs/web/KJSHTTPServer.java delete mode 100644 src/main/java/dev/latvian/mods/kubejs/web/LocalWebServerRegistryHolder.java diff --git a/src/main/java/dev/latvian/mods/kubejs/bindings/GLFWInputWrapper.java b/src/main/java/dev/latvian/mods/kubejs/bindings/GLFWInputWrapper.java new file mode 100644 index 000000000..9428945c0 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/bindings/GLFWInputWrapper.java @@ -0,0 +1,36 @@ +package dev.latvian.mods.kubejs.bindings; + +import dev.latvian.mods.kubejs.util.Lazy; +import org.lwjgl.glfw.GLFW; + +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +public interface GLFWInputWrapper { + Lazy> MAP = Lazy.of(() -> { + var map = new HashMap(); + + try { + for (var field : GLFW.class.getDeclaredFields()) { + int mod = field.getModifiers(); + + if (field.getType() == int.class && Modifier.isPublic(mod) && Modifier.isStatic(mod) && Modifier.isFinal(mod)) { + var n = field.getName(); + + if (n.startsWith("GLFW_KEY_") || n.startsWith("GLFW_MOUSE_") || n.startsWith("GLFW_GAMEPAD_") || n.startsWith("GLFW_CURSOR_")) { + map.put(n.substring(5), field.getInt(null)); + } + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + return Map.copyOf(map); + }); + + static int get(String name) { + return MAP.get().getOrDefault(name, -1); + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/bindings/event/ClientEvents.java b/src/main/java/dev/latvian/mods/kubejs/bindings/event/ClientEvents.java index 91442c0d5..a60d0d78b 100644 --- a/src/main/java/dev/latvian/mods/kubejs/bindings/event/ClientEvents.java +++ b/src/main/java/dev/latvian/mods/kubejs/bindings/event/ClientEvents.java @@ -5,7 +5,6 @@ import dev.latvian.mods.kubejs.client.ClientPlayerKubeEvent; import dev.latvian.mods.kubejs.client.DebugInfoKubeEvent; import dev.latvian.mods.kubejs.client.EntityRendererRegistryKubeEvent; -import dev.latvian.mods.kubejs.client.KeybindRegistryKubeEvent; import dev.latvian.mods.kubejs.client.LangKubeEvent; import dev.latvian.mods.kubejs.client.MenuScreenRegistryKubeEvent; import dev.latvian.mods.kubejs.client.ParticleProviderRegistryKubeEvent; @@ -32,8 +31,4 @@ public interface ClientEvents { TargetedEventHandler ATLAS_SPRITE_REGISTRY = GROUP.client("atlasSpriteRegistry", () -> AtlasSpriteRegistryKubeEvent.class).requiredTarget(EventTargetType.ID); TargetedEventHandler LANG = GROUP.client("lang", () -> LangKubeEvent.class).requiredTarget(EventTargetType.STRING); EventHandler PARTICLE_PROVIDER_REGISTRY = GROUP.client("particleProviderRegistry", () -> ParticleProviderRegistryKubeEvent.class); - EventHandler KEYBIND_REGISTRY = GROUP.startup("keybindRegistry", () -> KeybindRegistryKubeEvent.class); - TargetedEventHandler KEY_DOWN = GROUP.client("keyDown", () -> ClientPlayerKubeEvent.class).requiredTarget(EventTargetType.STRING); - TargetedEventHandler KEY_UP = GROUP.client("keyUp", () -> ClientPlayerKubeEvent.class).requiredTarget(EventTargetType.STRING); - TargetedEventHandler KEY_PRESSED = GROUP.client("keyPressed", () -> ClientPlayerKubeEvent.class).requiredTarget(EventTargetType.STRING); } diff --git a/src/main/java/dev/latvian/mods/kubejs/bindings/event/KeyBindEvents.java b/src/main/java/dev/latvian/mods/kubejs/bindings/event/KeyBindEvents.java new file mode 100644 index 000000000..0536308ba --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/bindings/event/KeyBindEvents.java @@ -0,0 +1,16 @@ +package dev.latvian.mods.kubejs.bindings.event; + +import dev.latvian.mods.kubejs.client.KeybindRegistryKubeEvent; +import dev.latvian.mods.kubejs.client.KubeJSKeybinds; +import dev.latvian.mods.kubejs.event.EventGroup; +import dev.latvian.mods.kubejs.event.EventHandler; +import dev.latvian.mods.kubejs.event.TargetedEventHandler; + +public interface KeyBindEvents { + EventGroup GROUP = EventGroup.of("KeyBindEvents"); + + EventHandler REGISTRY = GROUP.startup("registry", () -> KeybindRegistryKubeEvent.class); + TargetedEventHandler PRESSED = GROUP.client("pressed", () -> KubeJSKeybinds.KeyEvent.class).requiredTarget(KubeJSKeybinds.TARGET); + TargetedEventHandler RELEASED = GROUP.client("released", () -> KubeJSKeybinds.TickingKeyEvent.class).requiredTarget(KubeJSKeybinds.TARGET); + TargetedEventHandler TICK = GROUP.client("tick", () -> KubeJSKeybinds.TickingKeyEvent.class).requiredTarget(KubeJSKeybinds.TARGET); +} diff --git a/src/main/java/dev/latvian/mods/kubejs/block/state/BlockStatePredicate.java b/src/main/java/dev/latvian/mods/kubejs/block/state/BlockStatePredicate.java index 4bd2a5b99..0201f2000 100644 --- a/src/main/java/dev/latvian/mods/kubejs/block/state/BlockStatePredicate.java +++ b/src/main/java/dev/latvian/mods/kubejs/block/state/BlockStatePredicate.java @@ -87,7 +87,7 @@ static BlockStatePredicate wrap(Context cx, Object o) { if (list.isEmpty()) { return Simple.NONE; - } else { + } else if (list.size() > 1) { var predicates = new ArrayList(); for (var o1 : list) { @@ -102,7 +102,8 @@ static BlockStatePredicate wrap(Context cx, Object o) { return predicates.isEmpty() ? Simple.NONE : predicates.size() == 1 ? predicates.getFirst() : new OrMatch(predicates); } - var map = cx.optionalMapOf(o); + var first = list.getFirst(); + var map = cx.optionalMapOf(first); if (map != null) { if (map.isEmpty()) { @@ -122,7 +123,7 @@ static BlockStatePredicate wrap(Context cx, Object o) { return new AndMatch(predicates); } - return ofSingle(cx, o); + return ofSingle(cx, first); } static RuleTest wrapRuleTest(Context cx, Object o) { diff --git a/src/main/java/dev/latvian/mods/kubejs/client/BuiltinKubeJSClientPlugin.java b/src/main/java/dev/latvian/mods/kubejs/client/BuiltinKubeJSClientPlugin.java index b819a4f63..f65efd4bc 100644 --- a/src/main/java/dev/latvian/mods/kubejs/client/BuiltinKubeJSClientPlugin.java +++ b/src/main/java/dev/latvian/mods/kubejs/client/BuiltinKubeJSClientPlugin.java @@ -1,7 +1,9 @@ package dev.latvian.mods.kubejs.client; import dev.latvian.mods.kubejs.KubeJS; +import dev.latvian.mods.kubejs.bindings.GLFWInputWrapper; import dev.latvian.mods.kubejs.bindings.event.ClientEvents; +import dev.latvian.mods.kubejs.bindings.event.KeyBindEvents; import dev.latvian.mods.kubejs.event.EventGroupRegistry; import dev.latvian.mods.kubejs.plugin.KubeJSPlugin; import dev.latvian.mods.kubejs.script.BindingRegistry; @@ -18,6 +20,7 @@ public class BuiltinKubeJSClientPlugin implements KubeJSPlugin { @Override public void registerEvents(EventGroupRegistry registry) { registry.register(ClientEvents.GROUP); + registry.register(KeyBindEvents.GROUP); } @Override @@ -32,6 +35,8 @@ public void registerBindings(BindingRegistry bindings) { bindings.add("setInterval", new ScheduledEvents.TimeoutJSFunction(se, false, true)); bindings.add("clearInterval", new ScheduledEvents.TimeoutJSFunction(se, true, true)); } + + bindings.add("GLFWInput", GLFWInputWrapper.MAP.get()); } @Override @@ -56,13 +61,14 @@ public void generateLang(LangKubeEvent event) { } } } + + KubeJSKeybinds.generateLang(event); } @Override public void afterScriptsLoaded(ScriptManager manager) { - if (manager.scriptType != ScriptType.CLIENT) { - return; + if (manager.scriptType == ScriptType.CLIENT) { + KubeJSKeybinds.triggerReload(); } - KubeJSKeybinds.triggerReload(); } } diff --git a/src/main/java/dev/latvian/mods/kubejs/client/KeybindRegistryKubeEvent.java b/src/main/java/dev/latvian/mods/kubejs/client/KeybindRegistryKubeEvent.java index 0314b4926..f948b2844 100644 --- a/src/main/java/dev/latvian/mods/kubejs/client/KeybindRegistryKubeEvent.java +++ b/src/main/java/dev/latvian/mods/kubejs/client/KeybindRegistryKubeEvent.java @@ -1,50 +1,60 @@ package dev.latvian.mods.kubejs.client; import com.mojang.blaze3d.platform.InputConstants; -import dev.latvian.mods.kubejs.util.ClassWrapper; +import dev.latvian.mods.kubejs.bindings.GLFWInputWrapper; import dev.latvian.mods.rhino.util.HideFromJS; import net.minecraft.client.KeyMapping; import net.neoforged.neoforge.client.settings.KeyConflictContext; import net.neoforged.neoforge.client.settings.KeyModifier; -import org.lwjgl.glfw.GLFW; import java.util.ArrayList; import java.util.List; public class KeybindRegistryKubeEvent implements ClientKubeEvent { private final List builders = new ArrayList<>(); - public final ClassWrapper GLFW = new ClassWrapper<>(GLFW.class); - public Builder register(String keybindId) { - Builder builder = new Builder(keybindId); + public Builder register(String id) { + var builder = new Builder(id); builders.add(builder); return builder; } + public Builder register(String id, String defaultKey) { + return register(id).defaultKey(defaultKey); + } + @HideFromJS - public List build() { + public List build() { return builders.stream().map(Builder::create).toList(); } public static class Builder { private final String id; private KeyConflictContext keyConflictContext = KeyConflictContext.UNIVERSAL; - private KeyModifier keyModifier = KeyModifier.NONE; + private KeyModifier modifier = KeyModifier.NONE; private InputConstants.Type inputType = InputConstants.Type.KEYSYM; - private int keyId = -1; + private int defaultKey = -1; private String category = "key.categories.kubejs"; - public Builder(String id) { + private Builder(String id) { this.id = id; } - public Builder keyConflictContext(KeyConflictContext keyConflictContext) { + public Builder conflictContext(KeyConflictContext keyConflictContext) { this.keyConflictContext = keyConflictContext; return this; } - public Builder keyModifier(KeyModifier keyModifier) { - this.keyModifier = keyModifier; + public Builder gui() { + return conflictContext(KeyConflictContext.GUI); + } + + public Builder inGame() { + return conflictContext(KeyConflictContext.IN_GAME); + } + + public Builder modifier(KeyModifier modifier) { + this.modifier = modifier; return this; } @@ -53,8 +63,16 @@ public Builder inputType(InputConstants.Type inputType) { return this; } - public Builder keyId(int keyId) { - this.keyId = keyId; + public Builder scanCodeInputType() { + return inputType(InputConstants.Type.SCANCODE); + } + + public Builder mouseInputType() { + return inputType(InputConstants.Type.MOUSE); + } + + public Builder defaultKey(String keyName) { + this.defaultKey = GLFWInputWrapper.get(keyName); return this; } @@ -64,8 +82,10 @@ public Builder category(String category) { } @HideFromJS - public KubeJSKeybinds.KubeKeybind create() { - return new KubeJSKeybinds.KubeKeybind(id, new KeyMapping("key.kubejs.%s".formatted(id), keyConflictContext, keyModifier, inputType, keyId, category)); + public KubeJSKeybinds.KubeKey create() { + var key = KubeJSKeybinds.getOrCreate(id); + key.mapping = new KeyMapping("key.kubejs.%s".formatted(id), keyConflictContext, modifier, inputType, defaultKey, category); + return key; } } } diff --git a/src/main/java/dev/latvian/mods/kubejs/client/KubeJSKeybinds.java b/src/main/java/dev/latvian/mods/kubejs/client/KubeJSKeybinds.java index a6778aa97..6e2b41312 100644 --- a/src/main/java/dev/latvian/mods/kubejs/client/KubeJSKeybinds.java +++ b/src/main/java/dev/latvian/mods/kubejs/client/KubeJSKeybinds.java @@ -1,87 +1,117 @@ package dev.latvian.mods.kubejs.client; -import dev.latvian.mods.kubejs.bindings.event.ClientEvents; +import dev.latvian.mods.kubejs.KubeJS; +import dev.latvian.mods.kubejs.bindings.StringUtilsWrapper; +import dev.latvian.mods.kubejs.bindings.event.KeyBindEvents; +import dev.latvian.mods.kubejs.event.EventTargetType; import dev.latvian.mods.kubejs.script.ScriptType; +import dev.latvian.mods.rhino.type.TypeInfo; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; -import java.util.Set; public class KubeJSKeybinds { - private static final Map registeredKeybinds = new HashMap<>(); - private static final Set listeningKeybinds = new HashSet<>(); + private static final Map REGISTERED = new LinkedHashMap<>(); + public static final EventTargetType TARGET = EventTargetType.create(KubeKey.class).identity().transformer(KubeJSKeybinds::get0).describeType(TypeInfo.STRING); + + public static class KeyEvent extends ClientPlayerKubeEvent { + protected final KubeKey key; + + public KeyEvent(LocalPlayer player, KubeKey key) { + super(player); + this.key = key; + } + } + + public static class TickingKeyEvent extends KeyEvent { + public TickingKeyEvent(LocalPlayer player, KubeKey key) { + super(player, key); + } + + public int getTicks() { + return key.ticksPressed; + } + } + + public static class KubeKey { + public final String id; + public transient KeyMapping mapping; + public transient boolean down = false; + private boolean shouldTick = false; + public transient int ticksPressed = 0; + + public KubeKey(String id) { + this.id = id; + } + + @Override + public boolean equals(Object obj) { + return this == obj || obj instanceof KubeKey o && Objects.equals(id, o.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + } public static void triggerReload() { - listeningKeybinds.clear(); - ClientEvents.KEY_DOWN.forEachListener(ScriptType.CLIENT, container -> { - String target = container.target.toString(); - if (registeredKeybinds.containsKey(target)) { - listeningKeybinds.add(new KubeKeybindState(registeredKeybinds.get(target))); - } - }); - ClientEvents.KEY_PRESSED.forEachListener(ScriptType.CLIENT, container -> { - String target = container.target.toString(); - if (registeredKeybinds.containsKey(target)) { - listeningKeybinds.add(new KubeKeybindState(registeredKeybinds.get(target))); - } - }); + for (var key : REGISTERED.values()) { + key.shouldTick = false; + } + + KeyBindEvents.TICK.forEachListener(ScriptType.CLIENT, container -> ((KubeKey) container.target).shouldTick = true); } public static void triggerKeyEvents(Minecraft client) { - for (KubeKeybindState listeningKeybind : listeningKeybinds) { - if (client.kjs$isKeyMappingDown(listeningKeybind.keybind.keyMapping)) { - ClientPlayerKubeEvent event = new ClientPlayerKubeEvent(client.player); - if (!listeningKeybind.keyDown) { - ClientEvents.KEY_PRESSED.post(event, listeningKeybind.keybind.keybindId); + for (var key : REGISTERED.values()) { + if (key.mapping == null) { + continue; + } + + if (client.kjs$isKeyMappingDown(key.mapping)) { + if (!key.down) { + key.down = true; + KeyBindEvents.PRESSED.post(ScriptType.CLIENT, key, new KeyEvent(client.player, key)); } - listeningKeybind.keyDown = true; - ClientEvents.KEY_DOWN.post(event, listeningKeybind.keybind.keybindId); + + if (key.shouldTick) { + KeyBindEvents.TICK.post(ScriptType.CLIENT, key, new TickingKeyEvent(client.player, key)); + } + + key.ticksPressed++; } else { - if (listeningKeybind.keyDown) { - ClientEvents.KEY_UP.post(new ClientPlayerKubeEvent(client.player)); + if (key.down) { + key.down = false; + KeyBindEvents.RELEASED.post(ScriptType.CLIENT, key, new TickingKeyEvent(client.player, key)); + key.ticksPressed = 0; } - listeningKeybind.keyDown = false; } } } - public static KeyMapping getKeybind(String keybindId) { - KubeKeybind kubeKeybind = registeredKeybinds.get(keybindId); - if (kubeKeybind == null) { - return null; - } - return kubeKeybind.keyMapping; + @Nullable + private static KubeKey get0(Object o) { + return o == null ? null : getOrCreate(o.toString()); } - public static void addKeybind(KubeKeybind kubeKeybind) { - registeredKeybinds.put(kubeKeybind.keybindId, kubeKeybind); + @Nullable + public static KubeKey get(String id) { + return REGISTERED.get(id); } - public record KubeKeybind(String keybindId, KeyMapping keyMapping) { + public static KubeKey getOrCreate(String id) { + return REGISTERED.computeIfAbsent(id, KubeKey::new); } - private static class KubeKeybindState { - final KubeKeybind keybind; - boolean keyDown = false; - - private KubeKeybindState(KubeKeybind keybind) { - this.keybind = keybind; - } - - @Override - public boolean equals(Object object) { - if (this == object) return true; - if (!(object instanceof KubeKeybindState that)) return false; - return Objects.equals(keybind.keybindId, that.keybind.keybindId); - } - - @Override - public int hashCode() { - return Objects.hashCode(keybind.keybindId); + public static void generateLang(LangKubeEvent event) { + for (var key : REGISTERED.values()) { + event.add(KubeJS.MOD_ID, "key.kubejs." + key.id, StringUtilsWrapper.toTitleCase(key.id)); } } } diff --git a/src/main/java/dev/latvian/mods/kubejs/client/KubeJSModClientEventHandler.java b/src/main/java/dev/latvian/mods/kubejs/client/KubeJSModClientEventHandler.java index 5c14b2d6b..3b76c5076 100644 --- a/src/main/java/dev/latvian/mods/kubejs/client/KubeJSModClientEventHandler.java +++ b/src/main/java/dev/latvian/mods/kubejs/client/KubeJSModClientEventHandler.java @@ -5,6 +5,7 @@ import dev.latvian.mods.kubejs.KubeJS; import dev.latvian.mods.kubejs.bindings.event.ClientEvents; import dev.latvian.mods.kubejs.bindings.event.ItemEvents; +import dev.latvian.mods.kubejs.bindings.event.KeyBindEvents; import dev.latvian.mods.kubejs.block.BlockBuilder; import dev.latvian.mods.kubejs.fluid.FluidBlockBuilder; import dev.latvian.mods.kubejs.fluid.FluidBuilder; @@ -143,12 +144,13 @@ public static void registerRenderers(EntityRenderersEvent.RegisterRenderers even public static void registerKeyMappings(RegisterKeyMappingsEvent event) { event.register(KubedexHighlight.keyMapping = new KeyMapping("key.kubejs.kubedex", KeyConflictContext.UNIVERSAL, KeyModifier.NONE, InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_K, "key.categories.kubejs")); - KeybindRegistryKubeEvent kubeEvent = new KeybindRegistryKubeEvent(); - ClientEvents.KEYBIND_REGISTRY.post(kubeEvent); - for (KubeJSKeybinds.KubeKeybind kubeKeybind : kubeEvent.build()) { - event.register(kubeKeybind.keyMapping()); - KubeJSKeybinds.addKeybind(kubeKeybind); + var kubeEvent = new KeybindRegistryKubeEvent(); + KeyBindEvents.REGISTRY.post(kubeEvent); + + for (var bind : kubeEvent.build()) { + event.register(bind.mapping); } + KubeJSKeybinds.triggerReload(); } 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 ed067d87c..a7c556ec5 100644 --- a/src/main/java/dev/latvian/mods/kubejs/command/KubeJSCommands.java +++ b/src/main/java/dev/latvian/mods/kubejs/command/KubeJSCommands.java @@ -299,8 +299,10 @@ private static int help(CommandSourceStack source) { link(source, ChatFormatting.GREEN, TextIcons.info(), "Support", null, "https://kubejs.com/support?" + KubeJS.QUERY); link(source, ChatFormatting.BLUE, TextIcons.copy(), "Changelog", null, "https://kubejs.com/changelog?" + KubeJS.QUERY); - if (!LocalWebServer.explorerCode.isEmpty() && source.getServer().isSingleplayer()) { - link(source, ChatFormatting.LIGHT_PURPLE, TextIcons.entityTypeTag(), "Explore", null, "https://kubejs.com/explorer#" + LocalWebServer.explorerCode); + var server = LocalWebServer.instance(); + + if (server != null && !server.explorerCode().isEmpty() && source.getServer().isSingleplayer()) { + link(source, ChatFormatting.LIGHT_PURPLE, TextIcons.entityTypeTag(), "Explore", null, "https://kubejs.com/explorer#" + server.explorerCode()); } return Command.SINGLE_SUCCESS; diff --git a/src/main/java/dev/latvian/mods/kubejs/core/MinecraftClientKJS.java b/src/main/java/dev/latvian/mods/kubejs/core/MinecraftClientKJS.java index d347507b4..f51a55aff 100644 --- a/src/main/java/dev/latvian/mods/kubejs/core/MinecraftClientKJS.java +++ b/src/main/java/dev/latvian/mods/kubejs/core/MinecraftClientKJS.java @@ -1,6 +1,7 @@ package dev.latvian.mods.kubejs.core; import com.mojang.blaze3d.platform.InputConstants; +import dev.latvian.mods.kubejs.bindings.GLFWInputWrapper; import dev.latvian.mods.kubejs.bindings.event.ItemEvents; import dev.latvian.mods.kubejs.client.ClientProperties; import dev.latvian.mods.kubejs.client.KubeJSKeybinds; @@ -81,19 +82,25 @@ public interface MinecraftClientKJS extends MinecraftEnvironmentKJS { } default boolean kjs$isKeyDown(int key) { - return InputConstants.isKeyDown(kjs$self().getWindow().getWindow(), key); + return key != -1 && InputConstants.isKeyDown(kjs$self().getWindow().getWindow(), key); } - default boolean kjs$isKeybindDown(String key) { - KeyMapping keyMapping = KubeJSKeybinds.getKeybind(key); - if (keyMapping == null) { - return false; - } - return kjs$isKeyMappingDown(keyMapping); + default boolean kjs$isKeyDown(String keyName) { + return kjs$isKeyDown(GLFWInputWrapper.get(keyName)); + } + + default boolean kjs$isKeyBindDown(String id) { + var bind = KubeJSKeybinds.get(id); + return bind != null && bind.down; + } + + default int kjs$getKeyBindPressedTicks(String id) { + var bind = KubeJSKeybinds.get(id); + return bind == null || !bind.down ? -1 : bind.ticksPressed; } default boolean kjs$isKeyMappingDown(KeyMapping key) { - if (!key.isUnbound() && key.isConflictContextAndModifierActive()) { + if (key != null && !key.isUnbound() && key.isConflictContextAndModifierActive()) { if (key.getKey().getType() == InputConstants.Type.KEYSYM) { return kjs$isKeyDown(key.getKey().getValue()); } else if (key.getKey().getType() == InputConstants.Type.MOUSE) { diff --git a/src/main/java/dev/latvian/mods/kubejs/script/ScriptFile.java b/src/main/java/dev/latvian/mods/kubejs/script/ScriptFile.java index 1375a1b76..d94afaadb 100644 --- a/src/main/java/dev/latvian/mods/kubejs/script/ScriptFile.java +++ b/src/main/java/dev/latvian/mods/kubejs/script/ScriptFile.java @@ -2,7 +2,9 @@ import dev.latvian.mods.kubejs.CommonProperties; import dev.latvian.mods.kubejs.bindings.StringUtilsWrapper; +import net.neoforged.api.distmarker.Dist; import net.neoforged.fml.ModList; +import net.neoforged.fml.loading.FMLLoader; import java.nio.file.Files; import java.util.ArrayList; @@ -24,6 +26,7 @@ public class ScriptFile implements Comparable { private boolean ignored; private String packMode; private final Set requiredMods; + private boolean requiredClient; public String[] lines; public long lastModified; @@ -36,6 +39,7 @@ public ScriptFile(ScriptPack pack, ScriptFileInfo info) throws Exception { this.ignored = false; this.packMode = ""; this.requiredMods = new HashSet<>(0); + this.requiredClient = false; this.lines = Files.readAllLines(info.path).toArray(StringUtilsWrapper.EMPTY_STRING_ARRAY); @@ -65,6 +69,7 @@ public ScriptFile(ScriptPack pack, ScriptFileInfo info) throws Exception { this.ignored = getProperty("ignored", "false").equals("true") || getProperty("ignore", "false").equals("true"); this.packMode = getProperty("packmode", ""); this.requiredMods.addAll(getProperties("requires")); + this.requiredClient = requiredMods.remove("client"); } public void load(KubeJSContext cx) throws Throwable { @@ -90,6 +95,10 @@ public String skipLoading() { return "Ignored"; } + if (requiredClient && FMLLoader.getDist() != Dist.CLIENT) { + return "Client only"; + } + if (!packMode.isEmpty() && !packMode.equals(CommonProperties.get().packMode)) { return "Pack mode mismatch"; } diff --git a/src/main/java/dev/latvian/mods/kubejs/web/KJSHTTPServer.java b/src/main/java/dev/latvian/mods/kubejs/web/KJSHTTPServer.java new file mode 100644 index 000000000..36df93824 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/web/KJSHTTPServer.java @@ -0,0 +1,26 @@ +package dev.latvian.mods.kubejs.web; + +import dev.latvian.apps.tinyserver.HTTPServer; +import net.minecraft.util.thread.BlockableEventLoop; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.function.Supplier; + +public class KJSHTTPServer extends HTTPServer { + record RequestFactory(BlockableEventLoop eventLoop) implements Supplier { + @Override + public KJSHTTPRequest get() { + return new KJSHTTPRequest(eventLoop); + } + } + + public final transient String auth; + public final transient String encodedAuth; + + KJSHTTPServer(RequestFactory requestFactory, String auth) { + super(requestFactory); + this.auth = auth; + this.encodedAuth = auth.isEmpty() ? "" : URLEncoder.encode(auth, StandardCharsets.UTF_8); + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServer.java b/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServer.java index d72c46d92..b3ae468df 100644 --- a/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServer.java +++ b/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServer.java @@ -1,6 +1,5 @@ package dev.latvian.mods.kubejs.web; -import dev.latvian.apps.tinyserver.HTTPServer; import dev.latvian.apps.tinyserver.error.BindFailedException; import dev.latvian.mods.kubejs.KubeJS; import dev.latvian.mods.kubejs.plugin.KubeJSPlugin; @@ -18,9 +17,7 @@ import java.util.List; import java.util.stream.IntStream; -public record LocalWebServer(HTTPServer server, String url, List endpoints) { - public static String explorerCode = ""; - +public record LocalWebServer(KJSHTTPServer server, String url, List endpoints, String explorerCode) { public record Endpoint(String method, String path, boolean auth) implements Comparable { @Override public int compareTo(@NotNull LocalWebServer.Endpoint o) { @@ -31,6 +28,7 @@ public int compareTo(@NotNull LocalWebServer.Endpoint o) { private static LocalWebServer instance; @Nullable + @HideFromJS public static LocalWebServer instance() { return instance; } @@ -40,10 +38,11 @@ public static void start(BlockableEventLoop eventLoop, boolean localClient) { if (instance == null) { try { var properties = WebServerProperties.get(); - var holder = new LocalWebServerRegistryHolder(eventLoop, properties.auth); + var server = new KJSHTTPServer(new KJSHTTPServer.RequestFactory(eventLoop), properties.auth); + var endpoints0 = new HashSet(); - var registry = new LocalWebServerRegistry(holder, endpoints0, false); - var registryWithAuth = /*localClient || */holder.auth.isEmpty() ? registry : new LocalWebServerRegistry(holder, endpoints0, true); + var registry = new LocalWebServerRegistry(server, endpoints0, false); + var registryWithAuth = /*localClient || */server.auth.isEmpty() ? registry : new LocalWebServerRegistry(server, endpoints0, true); KubeJSPlugins.forEachPlugin(registry, KubeJSPlugin::registerLocalWebServer); KubeJSPlugins.forEachPlugin(registryWithAuth, KubeJSPlugin::registerLocalWebServerWithAuth); @@ -55,19 +54,24 @@ public static void start(BlockableEventLoop eventLoop, boolean localClient) { publicAddress = publicAddress.substring(7); } - holder.server.setDaemon(true); - holder.server.setServerName(KubeJS.DISPLAY_NAME); - holder.server.setAddress(publicAddress.isEmpty() ? "127.0.0.1" : "0.0.0.0"); - holder.server.setPort(IntStream.range(properties.port, properties.port + 10)); - holder.server.setMaxKeepAliveConnections(3); - holder.server.setKeepAliveTimeout(Duration.ofMinutes(5L)); + server.setDaemon(true); + server.setServerName(KubeJS.DISPLAY_NAME); + server.setAddress(publicAddress.isEmpty() ? "127.0.0.1" : "0.0.0.0"); + server.setPort(IntStream.range(properties.port, properties.port + 10)); + server.setMaxKeepAliveConnections(3); + server.setKeepAliveTimeout(Duration.ofMinutes(5L)); - int port = holder.server.start(); + int port = server.start(); var url = "http://localhost:" + port; var endpoints = new ArrayList<>(endpoints0); endpoints.sort(null); - instance = new LocalWebServer(holder.server, publicAddress.isEmpty() ? url : ("https://" + publicAddress), List.copyOf(endpoints)); - explorerCode = (publicAddress.isEmpty() ? ("p=" + port) : ("a=" + URLEncoder.encode(publicAddress, StandardCharsets.UTF_8))) + (holder.auth.isEmpty() ? "" : ("&c=" + holder.encodedAuth)); + + instance = new LocalWebServer( + server, + publicAddress.isEmpty() ? url : ("https://" + publicAddress), + List.copyOf(endpoints), + (publicAddress.isEmpty() ? ("p=" + port) : ("a=" + URLEncoder.encode(publicAddress, StandardCharsets.UTF_8))) + (server.auth.isEmpty() ? "" : ("&c=" + server.encodedAuth)) + ); KubeJS.LOGGER.info("Started the local web server at " + url); } catch (BindFailedException ex) { diff --git a/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServerRegistry.java b/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServerRegistry.java index cad624551..eb766aaee 100644 --- a/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServerRegistry.java +++ b/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServerRegistry.java @@ -30,31 +30,31 @@ public HTTPResponse handle(KJSHTTPRequest req) throws Exception { } } - private final LocalWebServerRegistryHolder holder; + private final KJSHTTPServer server; private final Set endpoints; private final boolean requireAuth; - LocalWebServerRegistry(LocalWebServerRegistryHolder holder, Set endpoints, boolean requireAuth) { - this.holder = holder; + LocalWebServerRegistry(KJSHTTPServer server, Set endpoints, boolean requireAuth) { + this.server = server; this.endpoints = endpoints; this.requireAuth = requireAuth; } private HTTPHandler wrap(HTTPHandler handler) { - return requireAuth ? new AuthHandler(handler, "Bearer " + holder.auth) : handler; + return requireAuth ? new AuthHandler(handler, "Bearer " + server.auth) : handler; } @Override public void http(HTTPMethod method, String path, HTTPHandler handler) { endpoints.add(new LocalWebServer.Endpoint(method.name(), path, requireAuth)); - holder.server.http(method, path, wrap(handler)); + server.http(method, path, wrap(handler)); } @Override public > WSHandler ws(String path, WSSessionFactory factory) { endpoints.add(new LocalWebServer.Endpoint("WS", path, requireAuth)); var handler = new WSEndpointHandler<>(factory, new ConcurrentHashMap<>()); - holder.server.http(HTTPMethod.GET, path, wrap(handler)); + server.http(HTTPMethod.GET, path, wrap(handler)); return handler; } } diff --git a/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServerRegistryHolder.java b/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServerRegistryHolder.java deleted file mode 100644 index c03835ef1..000000000 --- a/src/main/java/dev/latvian/mods/kubejs/web/LocalWebServerRegistryHolder.java +++ /dev/null @@ -1,25 +0,0 @@ -package dev.latvian.mods.kubejs.web; - -import dev.latvian.apps.tinyserver.HTTPServer; -import net.minecraft.util.thread.BlockableEventLoop; - -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; - -class LocalWebServerRegistryHolder { - private final BlockableEventLoop eventLoop; - final String auth; - final String encodedAuth; - final HTTPServer server; - - public LocalWebServerRegistryHolder(BlockableEventLoop eventLoop, String auth) { - this.eventLoop = eventLoop; - this.auth = auth; - this.encodedAuth = auth.isEmpty() ? "" : URLEncoder.encode(auth, StandardCharsets.UTF_8); - this.server = new HTTPServer<>(this::createRequest); - } - - private KJSHTTPRequest createRequest() { - return new KJSHTTPRequest(eventLoop); - } -}