From ce56c6e1057d4d159ff86839dd5465c4243287e1 Mon Sep 17 00:00:00 2001 From: kento2 Date: Wed, 19 Aug 2026 02:15:15 +0200 Subject: [PATCH] . --- .bazelrc | 1 + README.md | 5 +- buildserver/configurator/README.md | 58 +++++----- .../lab0x13/scrow/configurator/ConfigApi.java | 25 +++++ .../scrow/configurator/ConfigApiImpl.java | 75 +++++++++++++ .../lab0x13/scrow/configurator/ConfigKey.java | 15 +++ .../configurator/ConfiguratorPlugin.java | 4 +- ...figOptionAction.java => ConfigAction.java} | 17 ++- .../configurator/action/GlobalShowAction.java | 9 +- .../scrow/configurator/action/PickAction.java | 18 ++-- .../configurator/command/ConfigCommand.java | 6 +- .../command/ConfigNodeArgumentType.java | 19 ++-- .../configurator/command/KeyLiteral.java | 34 ++++-- .../configurator/command/KeyLiteralImpl.java | 56 ++++++++++ .../configurator/node/ConfigListNode.java | 54 ---------- .../scrow/configurator/node/ConfigNode.java | 33 +++++- .../configurator/node/ConfigRootNode.java | 43 -------- .../configurator/node/ConfigValueNode.java | 43 -------- .../scrow/configurator/node/SubFielded.java | 11 -- .../noderegistry/ConfigNodeWalker.java | 102 ------------------ .../configurator/noderegistry/WalkResult.java | 35 ------ .../configurator/type/ConfigComplexType.java | 38 +++---- .../configurator/type/ConfigListType.java | 51 +++++++++ .../configurator/type/ConfigMapType.java | 54 ---------- .../type/ConfigPrimitiveType.java | 12 +-- .../scrow/configurator/type/ConfigType.java | 24 ++--- .../scrow/configurator/type/Serializable.java | 19 ++++ .../type/location/LocationConfigType.java | 35 +++--- .../location/LocationPickCurrentAction.java | 5 - .../type/location/LocationTeleportAction.java | 12 +-- .../type/world/SetWorldAction.java | 39 +++++++ .../type/world/WorldConfigType.java | 41 +------ core/bukkit/BUILD | 8 ++ .../kentoj/scrow/bukkit/CoreImplPlugin.java | 8 -- .../command/instance/InstanceCache.java | 40 ------- .../command/instance/InstanceCommand.java | 24 ----- .../instance/InstanceDeployLiteral.java | 41 ------- .../instance/InstanceDestroyLiteral.java | 48 --------- .../command/instance/InstanceListLiteral.java | 38 ------- .../bukkit/command/instance/LobbyCommand.java | 29 ----- .../bukkit/command/instance/PlayCommand.java | 54 ---------- core/bukkit/plugin/main/resources/plugin.yml | 2 +- core/velocity/BUILD | 27 ++++- .../corevelocity/CoreVelocityPlugin.java | 16 +-- .../network/FriendNotifications.java | 23 ---- .../network/ServerRegisterWatchdog.java | 38 ------- .../ConsulV1ServerInstanceScanner.java | 60 +++++++++++ .../serverregistration/RegistrationTask.java | 29 +++++ .../ServerInstanceScanner.java | 11 ++ rules/BUILD | 37 +++++++ rules/dev_server.bzl | 51 +++++++++ rules/fetch_file.bzl | 22 ++++ rules/publish_plugin.bzl | 68 ++++++++++++ 53 files changed, 776 insertions(+), 891 deletions(-) create mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigApi.java create mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigApiImpl.java create mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigKey.java rename buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/{ConfigOptionAction.java => ConfigAction.java} (50%) create mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteralImpl.java delete mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigListNode.java delete mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigRootNode.java delete mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigValueNode.java delete mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/SubFielded.java delete mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/noderegistry/ConfigNodeWalker.java delete mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/noderegistry/WalkResult.java create mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigListType.java delete mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigMapType.java create mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/Serializable.java create mode 100644 buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/world/SetWorldAction.java delete mode 100644 core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceCache.java delete mode 100644 core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceCommand.java delete mode 100644 core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceDeployLiteral.java delete mode 100644 core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceDestroyLiteral.java delete mode 100644 core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceListLiteral.java delete mode 100644 core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/LobbyCommand.java delete mode 100644 core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/PlayCommand.java delete mode 100644 core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/network/FriendNotifications.java delete mode 100644 core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/network/ServerRegisterWatchdog.java create mode 100644 core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/ConsulV1ServerInstanceScanner.java create mode 100644 core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/RegistrationTask.java create mode 100644 core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/ServerInstanceScanner.java create mode 100644 rules/BUILD create mode 100644 rules/dev_server.bzl create mode 100644 rules/fetch_file.bzl create mode 100644 rules/publish_plugin.bzl diff --git a/.bazelrc b/.bazelrc index 7746d6d..0d74a9b 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,4 +1,5 @@ build --define=maven_repo=https://git.lab0x13.site/api/packages/scrow/maven +build --define=generic_repo=https://git.lab0x13.site/api/packages/scrow/generic build --remote_cache=https://cache.lab0x13.site build --remote_upload_local_results=false diff --git a/README.md b/README.md index 1a70e94..5c150af 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,6 @@ Building compile the bukkit-impl plugin: ```shell -bazel build //core-bukkit-impl:bukkit-impl_deploy.jar +bazel build //core/bukkit:plugin ``` -This produces `bazel-bin/core-bukkit-impl/bukkit-impl.jar` with all dependencies built-in except for -LuckPerms. \ No newline at end of file +This produces `bazel-bin/core/bukkit/plugin.jar` I think. \ No newline at end of file diff --git a/buildserver/configurator/README.md b/buildserver/configurator/README.md index 351821e..a9f718b 100644 --- a/buildserver/configurator/README.md +++ b/buildserver/configurator/README.md @@ -1,40 +1,34 @@ # configurator -helper for generating json configs interactively. -Plugins that hook into this plugin can register configuration nodes like "lobby.spawnpoint" with type Location. -server operators can then go ingame and do `/cfg key lobby.spawnpoint` to set a spawn location and then -`/cfg export` ig +This plugin/API allows developers to define configuration fields by name(or rather key) and data type, and have admins easily set +the fields' values in-game using commands such as `/cfg key chat.messageDelay set 50 ms`. -## TODO + +the key literal in `/cfg key` is required, because there are other modes like `/cfg check` that checks +the config for uninitialized values. + -- lists -- exporting -- proper namespaces -- split into api and impl -- more types(region pickers especially; also itemstacks) -- type variants like location without yaw/pitch +# Design -## design +Plugins register configuration nodes by key. A key consists of a unique identifier and a type. +The type handles (de)serialization and defines actions. Actions can be used on nodes. +Nodes hold values. -in json we have primitives(like "hi", 1, false"), structures(like {"foo":"bar}) and lists(like []). -everything consists of primitives eventually so we can go from +The `/config` or `/cfg` command is used by admins to configure the plugin: +A lobby plugin might register a node with the identifier `lobby.spawnLocation` and type `ConfigLocationType`, +which gives us the command `/cfg key lobby.spawnLocation`. +Let's say the ConfigLocationType defines an action to use the player's location as the value("pick-current") +and an action to teleport the user to the defined location("teleport"). +Thus, we can do `/cfg key lobby.spawnLocation pick-current` or `/cfg key lobby.spawnLocation teleport`. +Tab completion makes this pretty obvious. -```json -{ - "arenas": [ - { - "name": "skyfall", - "minPlayers": 3, - "maxPlayers": 8 - } - ] -} -``` -to -```text -arenas.0.name = Primitive("skyfall") -arenas.0.minPlayers = Primitive(3) -arenas.0.maxPlayers = Primitive(8) -``` +## Lists -We can define a complex type as "type has a string field called name" and it will just use \ No newline at end of file +Now maybe we want to make our lobby have some launch pads(i.e. some pressure plates that launch the player). +So, lists would be handy. +For lists, we simply use a Node of type List, so for our launch pads, we could make a node +with identifier "lobby.launchpads" and type `ConfigListType`. +Now to add a launch pad, we can just `/cfg key lobby.launchpads append`, which creates a new uninitialized element +in the list, followed by `/cfg key lobby.launchpads.0 pick-current` which actually sets the node's value. + +The node `lobby.launchpads.0` is not a real registered node. It is a Sub-Node of the `lobby.launchpads` node. \ No newline at end of file diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigApi.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigApi.java new file mode 100644 index 0000000..c94dda6 --- /dev/null +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigApi.java @@ -0,0 +1,25 @@ +package site.lab0x13.scrow.configurator; + +import org.jetbrains.annotations.Nullable; +import site.lab0x13.scrow.configurator.node.ConfigNode; + +import java.util.Map; + +public interface ConfigApi { + + void register(ConfigKey key, @Nullable V defaultValue); + + default void register(ConfigKey key) { + this.register(key, null); + } + + @Nullable ConfigNode getNode(ConfigKey key); + + default @Nullable V get(ConfigKey key) { + var n = getNode(key); + if (n == null) return null; + return n.value(); + } + + Map> allNodes(); +} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigApiImpl.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigApiImpl.java new file mode 100644 index 0000000..54fb5b5 --- /dev/null +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigApiImpl.java @@ -0,0 +1,75 @@ +package site.lab0x13.scrow.configurator; + +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import site.lab0x13.scrow.configurator.node.ConfigNode; + +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.Map; + +public class ConfigApiImpl implements ConfigApi { + + private final Logger log = LoggerFactory.getLogger(ConfigApiImpl.class); + + private final Map> nodes = new HashMap<>(); + + @Override + public void register(ConfigKey key, @Nullable V defaultValue) { + var oldValue = nodes.put(key.id(), new ConfigNode<>(key.type(), defaultValue)); + if (oldValue != null) + log.warn("Overwriting config key {}", key); + } + + /* + Sub-Nodes make this a bit difficult. We need to get the last real registered node in the + identifier first, and then descend into the Sub-Nodes. + */ + @Override + public @Nullable ConfigNode getNode(ConfigKey key) { + ConfigNode node; + var id = key.id(); + var fields = new ArrayDeque(); + while (true) { + node = (ConfigNode) nodes.get(id); + if (node != null) + break; + var dot = id.lastIndexOf('.'); + if (dot < 0) + return null; + fields.add(id.substring(dot + 1)); + id = id.substring(0, dot); + } + + while (!fields.isEmpty()) { + var field = fields.pop(); + if (node.value() == null) + return null; + if (node.type().getSubNodeNames(node.value()).isEmpty()) + return null; + node = (ConfigNode) node.type().getSubNode(node.value(), field); + if (node == null) + return null; + } + return (ConfigNode) node; + } + + @Override + public Map> allNodes() { + var result = new HashMap>(); + nodes.forEach((id, node) -> addNodeRecursively(result, id, (ConfigNode) node)); + return result; + } + + private void addNodeRecursively(Map> result, String nodeId, ConfigNode node) { + result.put(nodeId, node); + if (node.value() != null) + for (String fieldName : node.type().getSubNodeNames(node.value())) { + var subNode = (ConfigNode) node.type().getSubNode(node.value(), fieldName); + if (subNode == null) + continue; + addNodeRecursively(result, nodeId + "." + fieldName, subNode); + } + } +} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigKey.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigKey.java new file mode 100644 index 0000000..828e077 --- /dev/null +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfigKey.java @@ -0,0 +1,15 @@ +package site.lab0x13.scrow.configurator; + +import site.lab0x13.scrow.configurator.type.ConfigType; + +/** + * @param id unique identifier with, e.g `foo.bar` + * @param type of the value + */ +public record ConfigKey(String id, ConfigType type) { + + @Override + public String toString() { + return "(id=" + id + ",type=" + type.getClass().getCanonicalName() + ")"; + } +} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfiguratorPlugin.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfiguratorPlugin.java index 35064bd..964470a 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfiguratorPlugin.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/ConfiguratorPlugin.java @@ -3,8 +3,6 @@ package site.lab0x13.scrow.configurator; import de.kentoj.scrow.bukkit.ScrowAPI; import org.bukkit.plugin.java.JavaPlugin; import site.lab0x13.scrow.configurator.command.ConfigCommand; -import site.lab0x13.scrow.configurator.node.ConfigRootNode; -import site.lab0x13.scrow.configurator.node.ConfigValueNode; import site.lab0x13.scrow.configurator.type.location.LocationConfigType; public class ConfiguratorPlugin extends JavaPlugin { @@ -12,7 +10,7 @@ public class ConfiguratorPlugin extends JavaPlugin { @Override public void onEnable() { ConfigRootNode nodeRegistry = new ConfigRootNode(); - nodeRegistry.register("spawnLocation", new ConfigValueNode<>(LocationConfigType.INSTANCE)); + nodeRegistry.register("spawnLocation", new Config<>(LocationConfigType.INSTANCE)); ScrowAPI.playerCommands().register(new ConfigCommand(nodeRegistry).rootLiteral()); } } \ No newline at end of file diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/ConfigOptionAction.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/ConfigAction.java similarity index 50% rename from buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/ConfigOptionAction.java rename to buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/ConfigAction.java index ccf44d1..4fdb780 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/ConfigOptionAction.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/ConfigAction.java @@ -2,25 +2,20 @@ package site.lab0x13.scrow.configurator.action; import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.platform.MessageStyle; import org.bukkit.entity.Player; +import site.lab0x13.scrow.configurator.node.ConfigNode; import java.util.List; -public interface ConfigOptionAction { - - /* FIXME not too clean */ - List> GLOBAL_ACTIONS = List.of(new GlobalShowAction<>()); - +/** + * @param type of ConfigNode + */ +public interface ConfigAction> { String name(); List> arguments(); boolean requiresValue(); - void execute( - MessageStyle style, - T node, - CommandContext ctx - ); + void execute(N node, CommandContext ctx); } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/GlobalShowAction.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/GlobalShowAction.java index d355603..b4f9c04 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/GlobalShowAction.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/GlobalShowAction.java @@ -5,16 +5,15 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.platform.MessageStyle; -import de.kentoj.scrowlib.convention.ScrowMessageStyle; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import org.bukkit.entity.Player; import site.lab0x13.scrow.configurator.node.ConfigNode; +import site.lab0x13.scrow.configurator.type.ConfigType; import java.util.List; -public final class GlobalShowAction> implements ConfigOptionAction { +public final class GlobalShowAction implements ConfigAction> { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); @@ -34,11 +33,11 @@ public final class GlobalShowAction> implements ConfigOp } @Override - public void execute(MessageStyle style, S node, CommandContext ctx) { + public void execute(ConfigNode node, CommandContext ctx) { var jsonElement = node.type().serialize(node.value()); var prettyJson = GSON.toJson(jsonElement, JsonElement.class); var component = Component.text(prettyJson + " [click to copy]") .clickEvent(ClickEvent.copyToClipboard(prettyJson)); - ctx.sender().sendMessage(style.ok(component)); + ctx.sender().sendMessage(ctx.style().ok(component)); } } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/PickAction.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/PickAction.java index 8a8b086..54bf8c7 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/PickAction.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/action/PickAction.java @@ -2,7 +2,6 @@ package site.lab0x13.scrow.configurator.action; import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.platform.MessageStyle; import org.bukkit.Sound; import org.bukkit.entity.Player; import site.lab0x13.scrow.configurator.node.ConfigNode; @@ -13,9 +12,9 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -public abstract class PickAction> implements ConfigOptionAction { +public abstract class PickAction> implements ConfigAction { - protected abstract CompletableFuture pick(Player player); + protected abstract CompletableFuture pick(Player player); @Override public String name() { @@ -28,16 +27,21 @@ public abstract class PickAction> implements ConfigOp } @Override - public void execute(MessageStyle style, T node, CommandContext ctx) { + public boolean requiresValue() { + return false; + } + + @Override + public void execute(N node, CommandContext ctx) { pick(ctx.sender()) .orTimeout(10, TimeUnit.MINUTES) .whenComplete((picked, ex) -> { if (ex instanceof TimeoutException) { - ctx.sender().sendMessage(style.err("Timed out picking value.")); + ctx.sender().sendMessage(ctx.style().err("Timed out picking value.")); + return; } - - node.value(picked); ctx.sender().playSound(ctx.sender().getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.5f, 0f); + node.value(picked); }); } } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigCommand.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigCommand.java index e55edd4..d756e8f 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigCommand.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigCommand.java @@ -5,17 +5,17 @@ import de.kentoj.kencommandapi.api.literal.RootLiteral; import de.kentoj.scrowlib.convention.ScrowMessageStyle; import net.kyori.adventure.text.format.TextColor; import org.bukkit.entity.Player; -import site.lab0x13.scrow.configurator.node.ConfigRootNode; +import site.lab0x13.scrow.configurator.ConfigApi; import site.lab0x13.scrow.configurator.noderegistry.ConfigNodeWalker; public final class ConfigCommand { private final RootLiteral rootLiteral; - public ConfigCommand(ConfigRootNode nodeRegistry) { + public ConfigCommand(ConfigApi configApi) { var style = new ScrowMessageStyle("config", TextColor.color(0x28B088)); rootLiteral = Literal.builder("config", "cfg") .withPermission("command.config") - .withLiteral(new KeyLiteral(style, new ConfigNodeWalker(nodeRegistry)).literal()) + .withLiteral(new KeyLiteral(style, configApi).literal()) .asRootLiteral(style); } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigNodeArgumentType.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigNodeArgumentType.java index e018158..92da501 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigNodeArgumentType.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/ConfigNodeArgumentType.java @@ -5,14 +5,10 @@ import com.leakyabstractions.result.core.Results; import de.kentoj.kencommandapi.api.argument.ArgumentType; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider; import org.bukkit.entity.Player; -import site.lab0x13.scrow.configurator.node.ConfigListNode; import site.lab0x13.scrow.configurator.node.ConfigNode; -import site.lab0x13.scrow.configurator.node.ConfigRootNode; -import site.lab0x13.scrow.configurator.node.SubFielded; import site.lab0x13.scrow.configurator.noderegistry.ConfigNodeWalker; import java.util.Collections; -import java.util.stream.IntStream; public class ConfigNodeArgumentType implements ArgumentType> { @@ -37,17 +33,16 @@ public class ConfigNodeArgumentType implements ArgumentType { var res = nodeWalker.walk(input); switch (res.type()) { - case NO_FIELDS, SUCCESS -> Collections.emptyList(); case NO_SUCH_FIELD, INDEX_OUT_OF_RANGE, INDEX_EXPECTED -> { - if (res.node() instanceof ConfigListNode list) - return IntStream.range(0, list.size()).mapToObj(Integer::toString).toList(); - if (res.node() instanceof SubFielded subFielded) - return subFielded.fields().keySet().stream() - .map(it -> res.parentKey() + "." + it) - .toList(); + assert res.node() != null; + return res.node().getFieldNames().stream() + .map(it -> res.parentKey() + "." + it) + .toList(); + } + default -> { + return Collections.emptyList(); } } - return Collections.emptyList(); }; } } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteral.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteral.java index d0b18be..0ce8a73 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteral.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteral.java @@ -4,9 +4,9 @@ import de.kentoj.kencommandapi.api.literal.Literal; import de.kentoj.kencommandapi.api.literal.LiteralBuilder; import de.kentoj.scrowlib.convention.ScrowMessageStyle; import org.bukkit.entity.Player; -import site.lab0x13.scrow.configurator.action.ConfigOptionAction; +import site.lab0x13.scrow.configurator.ConfigApi; +import site.lab0x13.scrow.configurator.action.ConfigAction; import site.lab0x13.scrow.configurator.node.ConfigNode; -import site.lab0x13.scrow.configurator.noderegistry.ConfigNodeWalker; import java.util.ArrayList; import java.util.List; @@ -16,23 +16,41 @@ class KeyLiteral { private final ScrowMessageStyle style; private final Literal literal; - KeyLiteral(ScrowMessageStyle style, ConfigNodeWalker nodeWalker) { + KeyLiteral(ScrowMessageStyle style, ConfigApi api) { + /* + All registered nodes should be added as literals. SubNoded nodes should be added + recursively. + When foo is a list node(implements SubNoded): + /cfg key foo <- list node itself is registered, has actions like append, delete, etc. + /cfg key foo.0 <- operate on first element, has actions of foo.0's type + */ this.style = style; var builder = Literal.builder("key"); - nodeWalker.getAllNodes().forEach((key, node) -> + api.allNodes().forEach((key, node) -> addNodeLiteral(builder, key, node)); - literal = builder.build(); + this.literal = builder.build(); } private void addNodeLiteral(LiteralBuilder builder, String key, ConfigNode node) { var nodeLiteralBuilder = Literal.builder(key); addNodeActionLiteral(nodeLiteralBuilder, (ConfigNode) node, new ArrayList<>(node.type().actions())); - addNodeActionLiteral(nodeLiteralBuilder, (ConfigNode) node, ConfigOptionAction.GLOBAL_ACTIONS); + /* + FUCK ME WE NEED TO ADD LITERALS AT RUNTIME. + I just need /cfg key + except nodes are registered at runtime so we need to register literals at runtime. + or we make it an argument, but then we can't make action's work nicely(only hackishly at best); + or a dy + */ + for (String fieldName : node.type().getSubNodeNames(node.value())) { + var subNode = node.type().getSubNode(node.value(), fieldName); + if (subNode == null) continue; + addNodeActionLiteral(); + } builder.withLiteral(nodeLiteralBuilder.build()); } - private void addNodeActionLiteral(LiteralBuilder builder, ConfigNode node, List> actions) { + private void addNodeActionLiteral(LiteralBuilder builder, ConfigNode node, List> actions) { for (var _action : actions) { var action = (ConfigOptionAction>) _action; var actionLiteralBuilder = Literal.builder(action.name()); @@ -43,7 +61,7 @@ class KeyLiteral { ctx.sender().sendMessage(style.err("No value set.")); return; } - action.execute(style, node, ctx); + action.execute(node, ctx); }); builder.withLiteral(actionLiteralBuilder.build()); } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteralImpl.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteralImpl.java new file mode 100644 index 0000000..6b35a8c --- /dev/null +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/command/KeyLiteralImpl.java @@ -0,0 +1,56 @@ +package site.lab0x13.scrow.configurator.command; + +import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; +import de.kentoj.kencommandapi.api.invocation.CommandExecutor; +import de.kentoj.kencommandapi.api.literal.Literal; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import site.lab0x13.scrow.configurator.node.ConfigNode; + +import java.util.List; + +public class KeyLiteralImpl implements Literal { + private final ConfigNode node; + + public KeyLiteralImpl(String key, ConfigNode node) { + this.key = key; + this.node = node; + } + + @Override + public @Nullable Literal getLiteral(@NotNull String name) { + node. + return null; + } + + @Override + public @NotNull List> literals() { + gh + } + + @Override + public @NotNull String[] names() { + return new String[]{key}; + } + + @Override + public @Nullable String description() { + return null; + } + + @Override + public List> arguments() { + return List.of(); + } + + @Override + public @Nullable CommandExecutor executor() { + return null; + } + + @Override + public @Nullable String permission() { + return ""; + } +} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigListNode.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigListNode.java deleted file mode 100644 index 85ff529..0000000 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigListNode.java +++ /dev/null @@ -1,54 +0,0 @@ -package site.lab0x13.scrow.configurator.node; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import site.lab0x13.scrow.configurator.type.ConfigType; - -import java.util.ArrayList; -import java.util.List; - -public final class ConfigListNode - extends ArrayList> - implements ConfigNode>>, List> { - - private final ConfigType> elementType; - - public ConfigListNode(ConfigType> elementType) { - this.elementType = elementType; - } - - public ConfigType> elementType() { - return this.elementType; - } - - @Override - public ConfigType>> type() { - return new ConfigType<>() { - @Override - public JsonElement serialize(List> value) { - var list = new JsonArray(); - forEach(n -> list.add(n.type().serialize(n.value()))); - return list; - } - - @Override - public List> deserialize(JsonElement json) { - var res = new ArrayList>(); - var list = json.getAsJsonArray(); - list.forEach(e -> res.add(elementType.deserialize(e))); - return res; - } - }; - } - - @Override - public List> value() { - return this; - } - - @Override - public void value(List> value) { - this.clear(); - this.addAll(value); - } -} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigNode.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigNode.java index 6c4f127..94a838a 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigNode.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigNode.java @@ -1,11 +1,36 @@ package site.lab0x13.scrow.configurator.node; +import org.jetbrains.annotations.Nullable; import site.lab0x13.scrow.configurator.type.ConfigType; -public sealed interface ConfigNode permits ConfigValueNode, ConfigListNode, ConfigRootNode { - ConfigType type(); +/** + * @param type of the value + */ +public final class ConfigNode { - T value(); + private final ConfigType type; + private final @Nullable V defaultValue; + private @Nullable V value; - void value(T value); + public ConfigNode(ConfigType type, @Nullable V defaultValue) { + this.type = type; + this.defaultValue = defaultValue; + reset(); + } + + public void reset() { + this.value(defaultValue); + } + + public V value() { + return value; + } + + public void value(V value) { + this.value = value; + } + + public ConfigType type() { + return type; + } } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigRootNode.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigRootNode.java deleted file mode 100644 index 44eb312..0000000 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigRootNode.java +++ /dev/null @@ -1,43 +0,0 @@ -package site.lab0x13.scrow.configurator.node; - -import org.jetbrains.annotations.Nullable; -import site.lab0x13.scrow.configurator.type.ConfigMapType; -import site.lab0x13.scrow.configurator.type.ConfigType; - -import java.util.HashMap; -import java.util.Map; - -public final class ConfigRootNode implements ConfigNode>>, SubFielded { - - private final ConfigMapType> type = new ConfigMapType<>(new HashMap<>()); - private Map> value = new HashMap<>(); - - public void register(String name, ConfigNode node) { - value.put(name, node); - } - - @Override - public ConfigType>> type() { - return type; - } - - @Override - public Map> value() { - return value; - } - - @Override - public void value(Map> value) { - this.value = value; - } - - @Override - public Map> fields() { - return value; - } - - @Override - public @Nullable ConfigNode getField(String name) { - return value.get(name); - } -} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigValueNode.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigValueNode.java deleted file mode 100644 index 8f828e2..0000000 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/ConfigValueNode.java +++ /dev/null @@ -1,43 +0,0 @@ -package site.lab0x13.scrow.configurator.node; - -import site.lab0x13.scrow.configurator.type.ConfigType; - -public final class ConfigValueNode implements ConfigNode { - private final ConfigType type; - private final T defaultValue; - private T value; - - public ConfigValueNode(ConfigType type, T defaultValue) { - this.type = type; - this.defaultValue = defaultValue; - this.value = defaultValue; - } - - public ConfigValueNode(ConfigType type) { - this(type, null); - } - - public void reset() { - this.value = defaultValue; - } - - public T defaultValue() { - return defaultValue; - } - - public T value() { - return value; - } - - public void value(T value) { - this.value = value; - } - - public boolean required() { - return defaultValue == null; - } - - public ConfigType type() { - return type; - } -} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/SubFielded.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/SubFielded.java deleted file mode 100644 index bff58dc..0000000 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/node/SubFielded.java +++ /dev/null @@ -1,11 +0,0 @@ -package site.lab0x13.scrow.configurator.node; - -import org.jetbrains.annotations.Nullable; - -import java.util.Map; - -public interface SubFielded { - Map> fields(); - - @Nullable ConfigNode getField(String name); -} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/noderegistry/ConfigNodeWalker.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/noderegistry/ConfigNodeWalker.java deleted file mode 100644 index 4bab103..0000000 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/noderegistry/ConfigNodeWalker.java +++ /dev/null @@ -1,102 +0,0 @@ -package site.lab0x13.scrow.configurator.noderegistry; - -import org.jetbrains.annotations.Nullable; -import site.lab0x13.scrow.configurator.node.ConfigListNode; -import site.lab0x13.scrow.configurator.node.ConfigNode; -import site.lab0x13.scrow.configurator.node.ConfigRootNode; -import site.lab0x13.scrow.configurator.node.SubFielded; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -public final class ConfigNodeWalker { - - private final ConfigRootNode rootNode; - - public ConfigNodeWalker(ConfigRootNode rootNode) { - this.rootNode = rootNode; - } - - public Map> getAllNodes() { - var result = new HashMap>(); - processNode("", result, rootNode); - return result; - } - - private void processNode(String key, Map> result, ConfigNode node) { - result.put(key, node); - if (node instanceof ConfigListNode list) { - for (int i = 0; i < list.size(); i++) { - var element = list.get(i); - processNode(key + "." + i, result, element); - } - } else { - /* FIXME not clean at all */ - SubFielded sf = null; - if (node instanceof SubFielded n) - sf = n; - else if (node.type() instanceof SubFielded n) - sf = n; - if (sf != null) - sf.fields().forEach((name, v) -> - processNode(key + "." + name, result, v)); - } - } - - public WalkResult walk(String key) { - return walk(Arrays.stream(key.split("\\.")).iterator()); - } - - public WalkResult walk(Iterator iter) { - var cursor = new Cursor(iter); - ConfigNode cur = rootNode; - String token; - - while ((token = cursor.nextToken()) != null) { - if (token.isEmpty()) continue; - if (cur instanceof ConfigListNode list) { - int index; - try { - index = Integer.parseInt(token); - } catch (NumberFormatException _) { - return new WalkResult(WalkResult.Type.INDEX_EXPECTED, cursor.key(), cur); - } - if (index < 0 || index >= list.size()) - return new WalkResult(WalkResult.Type.INDEX_OUT_OF_RANGE, cursor.key(), cur); - cur = list.get(index); - } else if (cur.type() instanceof SubFielded subFielded) { - cur = subFielded.getField(token); - if (cur == null) - return new WalkResult(WalkResult.Type.NO_SUCH_FIELD, cursor.key(), cur); - } else { - return new WalkResult(WalkResult.Type.NO_FIELDS, cursor.key(), cur); - } - } - - return new WalkResult(WalkResult.Type.SUCCESS, cursor.key(), cur); - } - - private static class Cursor { - private String key = ""; - - private final Iterator iter; - - private Cursor(Iterator iter) { - this.iter = iter; - } - - public @Nullable String nextToken() { - if (!iter.hasNext()) return null; - var token = iter.next(); - key += "." + token; - key = key.replaceFirst("\\.", ""); - return token; - } - - public String key() { - return key; - } - } -} \ No newline at end of file diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/noderegistry/WalkResult.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/noderegistry/WalkResult.java deleted file mode 100644 index ee5541d..0000000 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/noderegistry/WalkResult.java +++ /dev/null @@ -1,35 +0,0 @@ -package site.lab0x13.scrow.configurator.noderegistry; - -import org.jetbrains.annotations.Nullable; -import site.lab0x13.scrow.configurator.node.ConfigNode; - -import java.util.function.Function; - -public record WalkResult( - Type type, - String key, - @Nullable ConfigNode node -) { - public String parentKey() { - var dotInd = key.lastIndexOf('.'); - return dotInd < 0 ? "" : key.substring(0, dotInd - 1); - } - - public @Nullable String error() { - return type().message.apply(this); - } - - public enum Type { - INDEX_EXPECTED(r -> "Numeric index expected at " + r.key() + "."), - INDEX_OUT_OF_RANGE(_ -> "Index out of range."), - NO_FIELDS(r -> r.key() + " has no fields."), - NO_SUCH_FIELD(r -> "Invalid key " + r.key()), - SUCCESS(_ -> null); - - private final Function message; - - Type(Function message) { - this.message = message; - } - } -} \ No newline at end of file diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigComplexType.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigComplexType.java index ef02ef6..f01abf6 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigComplexType.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigComplexType.java @@ -2,27 +2,24 @@ package site.lab0x13.scrow.configurator.type; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import org.jetbrains.annotations.Nullable; -import site.lab0x13.scrow.configurator.node.SubFielded; -import site.lab0x13.scrow.configurator.node.ConfigNode; import java.util.HashMap; import java.util.Map; +import java.util.function.Function; -public abstract class ConfigComplexType implements ConfigType, SubFielded { +public abstract class ConfigComplexType implements ConfigType { - private final Map> entries = new HashMap<>(); + private final Map> fields = new HashMap<>(); protected abstract T deserialize(JsonObject obj); - protected void addEntry(String name, ConfigNode node) { - entries.put(name, node); + protected void addField(String name, ConfigType type, Function getter) { + fields.put(name, new Field<>(type, getter)); } - @SuppressWarnings("unchecked") - protected S readNode(JsonObject object, String name) { - var node = (ConfigNode) entries.get(name); - return (S) node.type().deserialize(object.get(name)); + protected S readField(JsonObject object, String name) { + var field = fields.get(name); + return (S) field.type().deserialize(object.get(name)); } @Override @@ -33,20 +30,19 @@ public abstract class ConfigComplexType implements ConfigType, SubFielded @Override public JsonElement serialize(T value) { var obj = new JsonObject(); - entries.forEach((name, node) -> writeNode(obj, name, node)); + fields.forEach((name, field) -> writeNode(obj, name, field, value)); return obj; } - private static void writeNode(JsonObject object, String name, ConfigNode node) { - var value = node.type().serialize(node.value()); - object.add(name, value); + private void writeNode(JsonObject object, String name, Field field, T value) { + var fieldValue = field.getter.apply(value); + var serializedFieldValue = field.type().serialize(fieldValue); + object.add(name, serializedFieldValue); } - public Map> fields() { - return new HashMap<>(entries); - } - - public @Nullable ConfigNode getField(String name) { - return entries.get(name); + public record Field( + ConfigType type, + Function getter + ) { } } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigListType.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigListType.java new file mode 100644 index 0000000..dcd7265 --- /dev/null +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigListType.java @@ -0,0 +1,51 @@ +package site.lab0x13.scrow.configurator.type; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.jetbrains.annotations.Nullable; +import site.lab0x13.scrow.configurator.node.ConfigNode; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; + +public record ConfigListType( + ConfigType elementType +) implements ConfigType>> { + + @Override + public List> deserialize(JsonElement json) { + var result = new ArrayList>(); + var obj = json.getAsJsonObject(); + obj.asMap().forEach((k, v) -> { + var subNode = new ConfigNode<>(elementType, null); + subNode.value(elementType.deserialize(v)); + result.set(Integer.parseInt(k), subNode); + }); + return result; + } + + @Override + public JsonElement serialize(List> value) { + var res = new JsonObject(); + for (int i = 0; i < value.size(); i++) { + var subNodeValue = value.get(i).value(); + if (subNodeValue == null) continue; + var element = elementType.serialize(subNodeValue); + res.add(Integer.toString(i), element); + } + return res; + } + + @Override + public List getSubNodeNames(List> value) { + return IntStream.range(0, value.size()) + .mapToObj(Integer::toString) + .toList(); + } + + @Override + public @Nullable ConfigNode getSubNode(List> value, String name) { + return value.get(Integer.parseInt(name)); + } +} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigMapType.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigMapType.java deleted file mode 100644 index ce49a09..0000000 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigMapType.java +++ /dev/null @@ -1,54 +0,0 @@ -package site.lab0x13.scrow.configurator.type; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import org.jetbrains.annotations.Nullable; -import site.lab0x13.scrow.configurator.node.ConfigNode; -import site.lab0x13.scrow.configurator.node.SubFielded; - -import java.util.HashMap; -import java.util.Map; - -public class ConfigMapType implements ConfigType>, SubFielded { - - private final Map> types; - - public ConfigMapType(Map> types) { - this.types = types; - } - - @SuppressWarnings("unchecked") - @Override - public Map deserialize(JsonElement json) { - var result = new HashMap(); - var obj = json.getAsJsonObject(); - types.forEach((k, v) -> { - var type = (ConfigType) v; - var value = (T) type.deserialize(obj.get(k)); - result.put(k, value); - }); - return result; - } - - @SuppressWarnings("unchecked") - @Override - public JsonElement serialize(Map value) { - var res = new JsonObject(); - types.forEach((k, v) -> { - var type = (ConfigType) v; - var serialized = type.serialize(value.get(k)); - res.add(k, serialized); - }); - return res; - } - - @Override - public Map> fields() { - return Map.of(); - } - - @Override - public @Nullable ConfigNode getField(String name) { - return null; - } -} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigPrimitiveType.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigPrimitiveType.java index aba8c71..9df1e9a 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigPrimitiveType.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigPrimitiveType.java @@ -6,7 +6,7 @@ import com.google.gson.JsonPrimitive; import java.math.BigInteger; import java.util.function.Function; -public class ConfigPrimitiveType implements ConfigType { +public final class ConfigPrimitiveType implements ConfigType { public static final ConfigPrimitiveType DOUBLE = new ConfigPrimitiveType<>(JsonPrimitive::new, JsonPrimitive::getAsDouble); @@ -25,21 +25,21 @@ public class ConfigPrimitiveType implements ConfigType { public static final ConfigPrimitiveType BYTE = new ConfigPrimitiveType<>(JsonPrimitive::new, JsonPrimitive::getAsByte); - private final Function serializer; - private final Function deserializer; + private final Function serializer; + private final Function deserializer; - private ConfigPrimitiveType(Function serializer, Function deserializer) { + private ConfigPrimitiveType(Function serializer, Function deserializer) { this.serializer = serializer; this.deserializer = deserializer; } @Override - public JsonElement serialize(T value) { + public JsonElement serialize(V value) { return serializer.apply(value); } @Override - public T deserialize(JsonElement json) { + public V deserialize(JsonElement json) { return deserializer.apply(json.getAsJsonPrimitive()); } } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigType.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigType.java index 7185f6f..a8e2e3d 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigType.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/ConfigType.java @@ -1,28 +1,26 @@ package site.lab0x13.scrow.configurator.type; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import site.lab0x13.scrow.configurator.action.ConfigOptionAction; +import org.jetbrains.annotations.Nullable; +import site.lab0x13.scrow.configurator.action.ConfigAction; import site.lab0x13.scrow.configurator.node.ConfigNode; import java.util.Collections; import java.util.List; -public interface ConfigType { +/** + * @param type of the stored value + */ +public interface ConfigType extends Serializable { - default List> actions() { + default List> actions() { return Collections.emptyList(); } - JsonElement serialize(T value); - - T deserialize(JsonElement json); - - default String toJson(T value) { - return serialize(value).toString(); + default List getSubNodeNames(V value) { + return Collections.emptyList(); } - default T fromJson(String json) { - return deserialize(JsonParser.parseString(json)); + default @Nullable ConfigNode getSubNode(V value, String name) { + return null; } } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/Serializable.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/Serializable.java new file mode 100644 index 0000000..561ed63 --- /dev/null +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/Serializable.java @@ -0,0 +1,19 @@ +package site.lab0x13.scrow.configurator.type; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +public interface Serializable { + + JsonElement serialize(V value); + + V deserialize(JsonElement json); + + default String toJson(V value) { + return serialize(value).toString(); + } + + default V fromJson(String json) { + return deserialize(JsonParser.parseString(json)); + } +} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationConfigType.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationConfigType.java index 0d16a35..3aa2e09 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationConfigType.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationConfigType.java @@ -2,9 +2,7 @@ package site.lab0x13.scrow.configurator.type.location; import com.google.gson.JsonObject; import org.bukkit.Location; -import site.lab0x13.scrow.configurator.action.ConfigOptionAction; -import site.lab0x13.scrow.configurator.node.ConfigNode; -import site.lab0x13.scrow.configurator.node.ConfigValueNode; +import site.lab0x13.scrow.configurator.action.ConfigAction; import site.lab0x13.scrow.configurator.type.ConfigComplexType; import site.lab0x13.scrow.configurator.type.ConfigPrimitiveType; import site.lab0x13.scrow.configurator.type.world.WorldConfigType; @@ -16,31 +14,28 @@ public class LocationConfigType extends ConfigComplexType { public static final LocationConfigType INSTANCE = new LocationConfigType(); private LocationConfigType() { - addEntry("world", new ConfigValueNode<>(WorldConfigType.INSTANCE)); - addEntry("x", new ConfigValueNode<>(ConfigPrimitiveType.DOUBLE)); - addEntry("y", new ConfigValueNode<>(ConfigPrimitiveType.DOUBLE)); - addEntry("z", new ConfigValueNode<>(ConfigPrimitiveType.DOUBLE)); - addEntry("yaw", new ConfigValueNode<>(ConfigPrimitiveType.FLOAT)); - addEntry("pitch", new ConfigValueNode<>(ConfigPrimitiveType.FLOAT)); + addField("world", WorldConfigType.INSTANCE, Location::getWorld); + addField("x", ConfigPrimitiveType.DOUBLE, Location::getX); + addField("y", ConfigPrimitiveType.DOUBLE, Location::getY); + addField("z", ConfigPrimitiveType.DOUBLE, Location::getZ); + addField("yaw", ConfigPrimitiveType.FLOAT, Location::getYaw); + addField("pitch", ConfigPrimitiveType.FLOAT, Location::getPitch); } @Override - public List> actions() { - return List.of( - new LocationTeleportAction(), - new LocationPickCurrentAction() - ); + public List> actions() { + return List.of(new LocationTeleportAction(), new LocationPickCurrentAction()); } @Override public Location deserialize(JsonObject obj) { return new Location( - readNode(obj, "world"), - readNode(obj, "x"), - readNode(obj, "y"), - readNode(obj, "z"), - readNode(obj, "yaw"), - readNode(obj, "pitch") + readField(obj, "world"), + readField(obj, "x"), + readField(obj, "y"), + readField(obj, "z"), + readField(obj, "yaw"), + readField(obj, "pitch") ); } } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationPickCurrentAction.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationPickCurrentAction.java index 74de842..5f058a8 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationPickCurrentAction.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationPickCurrentAction.java @@ -13,11 +13,6 @@ class LocationPickCurrentAction extends PickAction pick(Player player) { return CompletableFuture.completedFuture(player.getLocation()); diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationTeleportAction.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationTeleportAction.java index 8ec5ae2..1fab31c 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationTeleportAction.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/location/LocationTeleportAction.java @@ -2,17 +2,15 @@ package site.lab0x13.scrow.configurator.type.location; import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.platform.MessageStyle; -import de.kentoj.scrowlib.convention.ScrowMessageStyle; import org.bukkit.Location; import org.bukkit.entity.Player; -import site.lab0x13.scrow.configurator.action.ConfigOptionAction; -import site.lab0x13.scrow.configurator.node.ConfigValueNode; +import site.lab0x13.scrow.configurator.action.ConfigAction; +import site.lab0x13.scrow.configurator.node.ConfigNode; import java.util.Collections; import java.util.List; -class LocationTeleportAction implements ConfigOptionAction> { +class LocationTeleportAction implements ConfigAction> { @Override public String name() { @@ -30,8 +28,8 @@ class LocationTeleportAction implements ConfigOptionAction node, CommandContext ctx) { + public void execute(ConfigNode node, CommandContext ctx) { ctx.sender().teleport(node.value()); - ctx.sender().sendMessage(style.ok("You've been teleported.")); + ctx.sender().sendMessage(ctx.style().ok("You've been teleported.")); } } diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/world/SetWorldAction.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/world/SetWorldAction.java new file mode 100644 index 0000000..9f433aa --- /dev/null +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/world/SetWorldAction.java @@ -0,0 +1,39 @@ +package site.lab0x13.scrow.configurator.type.world; + +import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; +import de.kentoj.kencommandapi.api.invocation.CommandContext; +import de.kentoj.kencommandapi.type.WorldArgumentType; +import org.bukkit.World; +import org.bukkit.entity.Player; +import site.lab0x13.scrow.configurator.action.ConfigAction; +import site.lab0x13.scrow.configurator.node.ConfigNode; + +import java.util.List; + +class SetWorldAction implements ConfigAction> { + + private final CommandArgumentSpec arg = + CommandArgumentSpec.builder("world", new WorldArgumentType()).build(); + + @Override + public String name() { + return "set"; + } + + @Override + public List> arguments() { + return List.of(arg); + } + + @Override + public boolean requiresValue() { + return false; + } + + @Override + public void execute(ConfigNode node, CommandContext ctx) { + World world = ctx.getArg(arg); + node.value(world); + ctx.sender().sendMessage(ctx.style().ok("Set world to " + world.getName() + "(" + world.getUID() + ")")); + } +} diff --git a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/world/WorldConfigType.java b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/world/WorldConfigType.java index ea43d37..adb3686 100644 --- a/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/world/WorldConfigType.java +++ b/buildserver/configurator/main/java/site/lab0x13/scrow/configurator/type/world/WorldConfigType.java @@ -2,18 +2,9 @@ package site.lab0x13.scrow.configurator.type.world; import com.google.gson.JsonElement; import com.google.gson.JsonPrimitive; -import com.leakyabstractions.result.api.Result; -import com.leakyabstractions.result.core.Results; -import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; -import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.platform.MessageStyle; -import de.kentoj.kencommandapi.type.WorldArgumentType; -import de.kentoj.scrowlib.convention.ScrowMessageStyle; import org.bukkit.Bukkit; import org.bukkit.World; -import org.bukkit.entity.Player; -import site.lab0x13.scrow.configurator.action.ConfigOptionAction; -import site.lab0x13.scrow.configurator.node.ConfigValueNode; +import site.lab0x13.scrow.configurator.action.ConfigAction; import site.lab0x13.scrow.configurator.type.ConfigType; import java.util.List; @@ -37,33 +28,7 @@ public class WorldConfigType implements ConfigType { } @Override - public List> actions() { - return List.of(new SetAction()); - } - - private static class SetAction implements ConfigOptionAction> { - @Override - public String name() { - return "set"; - } - - @Override - public List> arguments() { - return List.of( - CommandArgumentSpec.builder("world", new WorldArgumentType()).build() - ); - } - - @Override - public boolean requiresValue() { - return false; - } - - @Override - public void execute(MessageStyle style, ConfigValueNode node, CommandContext ctx) { - World world = ctx.getArg("world"); - node.value(world); - ctx.sender().sendMessage(style.ok("Set world to " + world.getName() + "("+ world.getUID() + ")")); - } + public List> actions() { + return List.of(new SetWorldAction()); } } diff --git a/core/bukkit/BUILD b/core/bukkit/BUILD index e610e3b..4a826b7 100644 --- a/core/bukkit/BUILD +++ b/core/bukkit/BUILD @@ -43,4 +43,12 @@ genrule( outs = ["plugin.jar"], cmd = "cp $< $@", visibility = ["//visibility:public"], +) + +load("//rules:publish_plugin.bzl", "publish_plugin") +publish_plugin( + name = "plugin-publish", + package = "core-bukkit", + filename = "CoreBukkit.jar", + src = ":plugin", ) \ No newline at end of file diff --git a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java b/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java index c00ffd4..b8afa7f 100644 --- a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java +++ b/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java @@ -2,10 +2,6 @@ package de.kentoj.scrow.bukkit; import de.kentoj.scrow.bukkit.command.economy.CoinsCommand; import de.kentoj.scrow.bukkit.command.friends.FriendCommand; -import de.kentoj.scrow.bukkit.command.instance.InstanceCache; -import de.kentoj.scrow.bukkit.command.instance.InstanceCommand; -import de.kentoj.scrow.bukkit.command.instance.LobbyCommand; -import de.kentoj.scrow.bukkit.command.instance.PlayCommand; import de.kentoj.scrow.bukkit.internal.player.prefix.CachedPrefixProvider; import de.kentoj.scrow.bukkit.internal.player.prefix.LuckPermsPrefixSetter; import de.kentoj.scrow.bukkit.style.ChatStyleListener; @@ -27,10 +23,6 @@ public final class CoreImplPlugin extends JavaPlugin { ScrowAPI.playerCommands().register(new CoinsCommand().rootLiteral()); ScrowAPI.playerCommands().register(new FriendCommand().rootLiteral()); - var instanceCache = new InstanceCache(); - ScrowAPI.commands().register(new InstanceCommand(instanceCache).rootLiteral()); - ScrowAPI.playerCommands().register(new PlayCommand().rootLiteral()); - ScrowAPI.playerCommands().register(new LobbyCommand().rootLiteral()); var luckperms = LuckPermsProvider.get(); new LuckPermsPrefixSetter(luckperms, cachedPrefixProvider).init(this); diff --git a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceCache.java b/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceCache.java deleted file mode 100644 index 9b16a88..0000000 --- a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceCache.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.kentoj.scrow.bukkit.command.instance; - -import de.kentoj.scrow.bukkit.ScrowAPI; -import de.kentoj.scrowlib.instancemanager.ServerInstance; -import org.bson.Document; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -public class InstanceCache { - - private final ConcurrentMap cache = new ConcurrentHashMap<>(); - - public InstanceCache() { - ScrowAPI.messageBroker().subscribe("event.instance.up", doc -> { - var inst = toInstance(doc); - cache.put(inst.handle(), inst); - }); - ScrowAPI.messageBroker().subscribe("event.instance.down", doc -> - cache.remove(doc.getString("handle"))); - } - - public Collection getInstances() { - return cache.values(); - } - - public @Nullable ServerInstance getInstance(String handle) { - return cache.get(handle); - } - - private ServerInstance toInstance(Document doc) { - return new ServerInstance( - doc.getString("handle"), - doc.getString("template"), - doc.getInteger("port") - ); - } -} diff --git a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceCommand.java b/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceCommand.java deleted file mode 100644 index b47a6aa..0000000 --- a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package de.kentoj.scrow.bukkit.command.instance; - -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.literal.RootLiteral; -import de.kentoj.scrowlib.convention.ScrowMessageStyle; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.command.CommandSender; - -public final class InstanceCommand { - - private final RootLiteral rootLiteral; - - public InstanceCommand(InstanceCache instanceCache) { - rootLiteral = Literal.builder("instance", "server-manager") - .withLiteral(new InstanceListLiteral().literal()) - .withLiteral(new InstanceDeployLiteral().literal()) - .withLiteral(new InstanceDestroyLiteral(instanceCache).literal()) - .asRootLiteral(new ScrowMessageStyle("server-manager", NamedTextColor.RED)); - } - - public RootLiteral rootLiteral() { - return rootLiteral; - } -} diff --git a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceDeployLiteral.java b/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceDeployLiteral.java deleted file mode 100644 index b553024..0000000 --- a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceDeployLiteral.java +++ /dev/null @@ -1,41 +0,0 @@ -package de.kentoj.scrow.bukkit.command.instance; - -import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; -import de.kentoj.kencommandapi.api.argument.types.StringArgumentType; -import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.scrow.bukkit.ScrowAPI; -import de.kentoj.scrow.bukkit.scheduler.Tasks; -import org.bukkit.command.CommandSender; - -import java.util.concurrent.CompletableFuture; - -public final class InstanceDeployLiteral { - - private final Literal literal; - private final CommandArgumentSpec templateArg; - - InstanceDeployLiteral() { - templateArg = CommandArgumentSpec.builder("template", new StringArgumentType()) - .build(); - literal = Literal.builder("deploy") - .withArgument(templateArg) - .withExecutor(this::deployInstance) - .build(); - } - - private CompletableFuture deployInstance(CommandContext ctx) { - var template = ctx.getArg(templateArg); - ctx.sender().sendMessage(ctx.style().ok("Deploying instance... this may take a while.")); - return Tasks.supplyAsync(() -> - ScrowAPI.instanceManager().deployInstance(template)) - .thenSync(instance -> { - ctx.sender().sendMessage(ctx.style().ok("Deployed instance of " + template + " with handle " + instance.handle() + ".")); - // FIXME handle unknown server - }).toFuture(); - } - - public Literal literal() { - return literal; - } -} diff --git a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceDestroyLiteral.java b/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceDestroyLiteral.java deleted file mode 100644 index 9d397ed..0000000 --- a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceDestroyLiteral.java +++ /dev/null @@ -1,48 +0,0 @@ -package de.kentoj.scrow.bukkit.command.instance; - -import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; -import de.kentoj.kencommandapi.api.argument.types.StringArgumentType; -import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.scrow.bukkit.ScrowAPI; -import de.kentoj.scrow.bukkit.scheduler.Tasks; -import de.kentoj.scrowlib.convention.ScrowMessageStyle; -import de.kentoj.scrowlib.instancemanager.ServerInstance; -import org.bukkit.command.CommandSender; - -import java.util.concurrent.CompletableFuture; - -public final class InstanceDestroyLiteral { - - private final Literal literal; - private final CommandArgumentSpec handleArg; - - InstanceDestroyLiteral(InstanceCache cache) { - handleArg = CommandArgumentSpec.builder("handle", new StringArgumentType()) - .withSuggestionProvider((_, _) -> - cache.getInstances().stream() - .map(ServerInstance::handle) - .toList()) - .build(); - literal = Literal.builder("destroy") - .withArgument(handleArg) - .withExecutor(this::destroyInstance) - .build(); - } - - private CompletableFuture destroyInstance(CommandContext ctx) { - var handle = ctx.getArg(handleArg); - return Tasks.supplyAsync(() -> ScrowAPI.instanceManager().destroyInstance(handle)) - .thenSync(found -> { - if (!found) { - ctx.sender().sendMessage(ctx.style().err("No instance with that handle found.")); - return; - } - ctx.sender().sendMessage(ctx.style().ok("Destroyed server with handle " + handle + ".")); - }).toFuture(); - } - - public Literal literal() { - return literal; - } -} diff --git a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceListLiteral.java b/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceListLiteral.java deleted file mode 100644 index 4dfa710..0000000 --- a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/InstanceListLiteral.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.kentoj.scrow.bukkit.command.instance; - -import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.scrow.bukkit.ScrowAPI; -import de.kentoj.scrow.bukkit.scheduler.Tasks; -import net.kyori.adventure.text.Component; -import org.bukkit.command.CommandSender; - -import java.util.concurrent.CompletableFuture; - -public final class InstanceListLiteral { - - private final Literal literal; - - InstanceListLiteral() { - this.literal = Literal.builder("list") - .withExecutor(this::displayInstanceList) - .build(); - } - - private CompletableFuture displayInstanceList(CommandContext ctx) { - return Tasks.supplyAsync(() -> - ScrowAPI.instanceManager().instances()) - .thenSync(instances -> { - var msg = Component.text("Running instances:"); - for (var inst : instances) { - msg = msg.appendNewline() - .append(Component.text(" - " + inst.handle() + " on port " + inst.port())); - } - ctx.sender().sendMessage(ctx.style().ok(msg)); - }).toFuture(); - } - - public Literal literal() { - return literal; - } -} diff --git a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/LobbyCommand.java b/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/LobbyCommand.java deleted file mode 100644 index a078296..0000000 --- a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/LobbyCommand.java +++ /dev/null @@ -1,29 +0,0 @@ -package de.kentoj.scrow.bukkit.command.instance; - -import com.leakyabstractions.result.api.Result; -import com.leakyabstractions.result.core.Results; -import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.literal.RootLiteral; -import de.kentoj.scrowlib.convention.ScrowMessageStyle; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -public final class LobbyCommand { - - private final RootLiteral rootLiteral; - - public LobbyCommand() { - rootLiteral = Literal.builder("lobby", "l", "hub") - .withSyncExecutor(this::sendToLobby) - .asRootLiteral(ScrowMessageStyle.GENERIC); - } - - private void sendToLobby(CommandContext ctx) { - Bukkit.dispatchCommand(ctx.sender(), "play lobby"); - } - - public RootLiteral rootLiteral() { - return rootLiteral; - } -} diff --git a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/PlayCommand.java b/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/PlayCommand.java deleted file mode 100644 index a964a14..0000000 --- a/core/bukkit/plugin/main/java/de/kentoj/scrow/bukkit/command/instance/PlayCommand.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.kentoj.scrow.bukkit.command.instance; - -import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; -import de.kentoj.kencommandapi.api.argument.types.StringArgumentType; -import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.literal.RootLiteral; -import de.kentoj.scrow.bukkit.ScrowAPI; -import de.kentoj.scrow.bukkit.scheduler.Tasks; -import de.kentoj.scrowlib.convention.ScrowMessageStyle; -import org.bukkit.entity.Player; - -import java.util.Objects; -import java.util.concurrent.CompletableFuture; - -public final class PlayCommand { - - private final RootLiteral rootLiteral; - private final CommandArgumentSpec gamemodeArg; - - public PlayCommand() { - var style = ScrowMessageStyle.GENERIC; - gamemodeArg = CommandArgumentSpec.builder("gamemode", new StringArgumentType()).build(); - rootLiteral = Literal.builder("play", "queue") - .withArgument(gamemodeArg) - .withExecutor(this::execute) - .asRootLiteral(style); - } - - private CompletableFuture execute(CommandContext ctx) { - var gamemode = ctx.getArg(gamemodeArg); - return Tasks.supplyAsync(() -> ScrowAPI.instanceManager().instances()) - .mapSync(instances -> { - var instance = instances.stream() - .filter(inst -> inst.template().equals(gamemode)) - .findFirst() - .orElse(null); - if (instance == null) { - ctx.sender().sendMessage(ctx.style().err("No instance for " + gamemode + " found.")); - return null; - } - - return instance.handle(); - }) - .yieldIf(Objects::isNull) - .thenAsync(handle -> - ScrowAPI.getPlayer(ctx.sender()).sendToInstance(handle) - ).toFuture(); - } - - public RootLiteral rootLiteral() { - return rootLiteral; - } -} diff --git a/core/bukkit/plugin/main/resources/plugin.yml b/core/bukkit/plugin/main/resources/plugin.yml index 973ef1c..795722e 100644 --- a/core/bukkit/plugin/main/resources/plugin.yml +++ b/core/bukkit/plugin/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: "CoreBukkit" -version: "${version}" +version: "UNVERSIONED" depend: [ "LuckPerms" ] main: "de.kentoj.scrow.bukkit.CoreImplPlugin" api-version: "1.21" diff --git a/core/velocity/BUILD b/core/velocity/BUILD index 980f100..e1e1327 100644 --- a/core/velocity/BUILD +++ b/core/velocity/BUILD @@ -1,6 +1,7 @@ load("@rules_jvm_external//:defs.bzl", "artifact") load("@rules_java//java:java_library.bzl", "java_library") load("@rules_java//java:java_binary.bzl", "java_binary") +load("@rules_jvm_external//:defs.bzl", "artifact", "java_plugin_artifact") java_library( name = "api", @@ -20,7 +21,7 @@ java_library( ) java_binary( - name = "plugin", + name = "_plugin", srcs = glob(["plugin/main/java/**/*.java", "api/main/java/**/*.java"]), create_executable = False, deps = [ @@ -30,5 +31,29 @@ java_binary( artifact("io.nats:jnats"), artifact("de.kentoj.scrow:kencommandapi-velocity"), artifact("org.mongodb:bson"), + artifact("org.postgresql:postgresql"), + artifact("com.google.code.gson:gson"), + ], + plugins = [ + java_plugin_artifact( + "com.velocitypowered:velocity-api", + "com.velocitypowered.api.plugin.ap.PluginAnnotationProcessor", + ), ], ) + +genrule( + name = "plugin", + srcs = [":_plugin_deploy.jar"], + outs = ["plugin.jar"], + cmd = "cp $< $@", + visibility = ["//visibility:public"], +) + +load("//rules:publish_plugin.bzl", "publish_plugin") +publish_plugin( + name = "plugin-publish", + package = "core-velocity", + filename = "CoreVelocity.jar", + src = ":plugin", +) diff --git a/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/CoreVelocityPlugin.java b/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/CoreVelocityPlugin.java index 47ef523..a6a08ea 100644 --- a/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/CoreVelocityPlugin.java +++ b/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/CoreVelocityPlugin.java @@ -6,17 +6,21 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.proxy.ProxyServer; import de.kentoj.scrow.corevelocity.misc.OnlineCommand; -import de.kentoj.scrow.corevelocity.network.FriendNotifications; import de.kentoj.scrow.corevelocity.network.SendPlayerWatchdog; -import de.kentoj.scrow.corevelocity.network.ServerRegisterWatchdog; import de.kentoj.scrow.corevelocity.privmsg.LastTargetCache; import de.kentoj.scrow.corevelocity.privmsg.MsgCommand; import de.kentoj.scrow.corevelocity.privmsg.PrivMsgHandler; import de.kentoj.scrow.corevelocity.privmsg.ReplyCommand; +import de.kentoj.scrow.corevelocity.serverregistration.ConsulV1ServerInstanceScanner; +import de.kentoj.scrow.corevelocity.serverregistration.RegistrationTask; +import de.kentoj.scrow.corevelocity.serverregistration.ServerInstanceScanner; import de.kentoj.scrow.velocity.ScrowAPI; import de.kentoj.scrowlib.convention.ScrowMessageStyle; +import de.kentoj.scrowlib.utils.EnvUtils; import net.kyori.adventure.text.format.NamedTextColor; +import java.time.Duration; + @Plugin( id = "corevelocity", name = "CoreVelocity", @@ -39,11 +43,11 @@ public final class CoreVelocityPlugin { ScrowAPI.playerCommands().register(new MsgCommand(server, lastTargetCache, privmsgHelper, style).rootLiteral()); ScrowAPI.commands().register(new OnlineCommand(server).rootLiteral()); - var registerWatchdog = new ServerRegisterWatchdog(server); var sendPlayerWatchdog = new SendPlayerWatchdog(server); - registerWatchdog.listen(); sendPlayerWatchdog.listen(); - - server.getEventManager().register(this, new FriendNotifications()); + ServerInstanceScanner instanceScanner = new ConsulV1ServerInstanceScanner(EnvUtils.envOrThrow("HOST_CONSUL")); + server.getScheduler().buildTask(this, new RegistrationTask(server, instanceScanner)) + .repeat(Duration.ofSeconds(3)) + .schedule(); } } \ No newline at end of file diff --git a/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/network/FriendNotifications.java b/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/network/FriendNotifications.java deleted file mode 100644 index 6cfe45d..0000000 --- a/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/network/FriendNotifications.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.kentoj.scrow.corevelocity.network; - -import com.velocitypowered.api.event.Subscribe; -import com.velocitypowered.api.event.connection.DisconnectEvent; -import com.velocitypowered.api.event.connection.PostLoginEvent; -import de.kentoj.scrow.velocity.ScrowAPI; -import de.kentoj.scrowlib.messaging.MessageBroker; -import org.bson.Document; - -public class FriendNotifications { - - private final MessageBroker messageBroker = ScrowAPI.messageBroker(); - - @Subscribe - private void onConnect(PostLoginEvent ev) { - messageBroker.publish("event.player.connect", new Document("playerId", ev.getPlayer().getUniqueId())); - } - - @Subscribe - private void onDisconnect(DisconnectEvent ev) { - messageBroker.publish("event.player.disconnect", new Document("playerId", ev.getPlayer().getUniqueId())); - } -} diff --git a/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/network/ServerRegisterWatchdog.java b/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/network/ServerRegisterWatchdog.java deleted file mode 100644 index 1942986..0000000 --- a/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/network/ServerRegisterWatchdog.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.kentoj.scrow.corevelocity.network; - -import com.velocitypowered.api.proxy.ProxyServer; -import com.velocitypowered.api.proxy.server.ServerInfo; -import de.kentoj.scrow.velocity.ScrowAPI; -import de.kentoj.scrowlib.messaging.MessageBroker; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.InetSocketAddress; - -public class ServerRegisterWatchdog { - - private static final Logger log = LoggerFactory.getLogger(ServerRegisterWatchdog.class); - - private final MessageBroker messageBroker = ScrowAPI.messageBroker(); - private final ProxyServer server; - - public ServerRegisterWatchdog(ProxyServer server) { - this.server = server; - } - - public void listen() { - messageBroker.subscribe("event.instance.up", doc -> { - var handle = doc.getString("handle"); - var port = doc.getInteger("port"); - server.unregisterServer(new ServerInfo(handle, new InetSocketAddress(port))); - log.info("registered server {}", handle); - }); - - messageBroker.subscribe("event.instance.down", doc -> { - var handle = doc.getString("handle"); - var port = doc.getInteger("port"); - server.unregisterServer(new ServerInfo(handle, new InetSocketAddress(port))); - log.info("unregistered server {}", handle); - }); - } -} diff --git a/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/ConsulV1ServerInstanceScanner.java b/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/ConsulV1ServerInstanceScanner.java new file mode 100644 index 0000000..1d49539 --- /dev/null +++ b/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/ConsulV1ServerInstanceScanner.java @@ -0,0 +1,60 @@ +package de.kentoj.scrow.corevelocity.serverregistration; + +import com.google.common.hash.Hashing; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.velocitypowered.api.proxy.server.ServerInfo; + +import java.io.Closeable; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.stream.Stream; + +public class ConsulV1ServerInstanceScanner implements ServerInstanceScanner, Closeable { + + private final String consulUrl; + private final HttpClient client; + + public ConsulV1ServerInstanceScanner(String consulUrl) { + this.consulUrl = consulUrl; + this.client = HttpClient.newHttpClient(); + } + + @Override + // FIXME query cluster-wide + public Stream queryMinecraftServers() throws IOException, InterruptedException { + var url = consulUrl + "/v1/agent/services/?filter=" + URLEncoder.encode("\"minecraft\" in Tags", StandardCharsets.UTF_8); + var request = HttpRequest.newBuilder(URI.create(url)) + .GET() + .build(); + var response = client.send(request, HttpResponse.BodyHandlers.ofString()); + var jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject(); + return jsonResponse.entrySet().stream() + .map(Map.Entry::getValue) + .map(JsonElement::getAsJsonObject) + .map(service -> { + var name = toServerName(service.get("Service").getAsString(), service.get("ID").getAsString()); + var host = InetSocketAddress.createUnresolved(service.get("Address").getAsString(), + service.get("Port").getAsInt()); + return new ServerInfo(name, host); + }); + } + + private String toServerName(String serviceName, String id) { + return serviceName + "-" + Integer.toUnsignedString(Hashing.murmur3_32_fixed() + .hashString(id, StandardCharsets.UTF_8) + .asInt(), 32); + } + + @Override + public void close() throws IOException { + client.close(); + } +} diff --git a/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/RegistrationTask.java b/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/RegistrationTask.java new file mode 100644 index 0000000..efeef14 --- /dev/null +++ b/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/RegistrationTask.java @@ -0,0 +1,29 @@ +package de.kentoj.scrow.corevelocity.serverregistration; + +import com.velocitypowered.api.proxy.ProxyServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class RegistrationTask implements Runnable { + + private static final Logger log = LoggerFactory.getLogger(RegistrationTask.class); + + private final ProxyServer server; + private final ServerInstanceScanner watcher; + + public RegistrationTask(ProxyServer server, ServerInstanceScanner watcher) { + this.server = server; + this.watcher = watcher; + } + + @Override + public void run() { + try { + watcher.queryMinecraftServers().forEach(server::registerServer); + } catch (IOException | InterruptedException e) { + throw new RuntimeException("Failed querying available servers", e); + } + } +} diff --git a/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/ServerInstanceScanner.java b/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/ServerInstanceScanner.java new file mode 100644 index 0000000..4a3e1ee --- /dev/null +++ b/core/velocity/plugin/main/java/de/kentoj/scrow/corevelocity/serverregistration/ServerInstanceScanner.java @@ -0,0 +1,11 @@ +package de.kentoj.scrow.corevelocity.serverregistration; + +import com.velocitypowered.api.proxy.server.ServerInfo; + +import java.io.IOException; +import java.util.stream.Stream; + +public interface ServerInstanceScanner { + + Stream queryMinecraftServers() throws IOException, InterruptedException; +} diff --git a/rules/BUILD b/rules/BUILD new file mode 100644 index 0000000..5fa0378 --- /dev/null +++ b/rules/BUILD @@ -0,0 +1,37 @@ +load("//rules:dev_server.bzl", "dev_server") +load("//rules:fetch_file.bzl", "fetch_file") + +dev_server( + name = "dev", + server_jar = ":papermc", + plugins = [ + ":luckperms", + ":viaversion", + ":viabackwards", + ], + local_plugins = [ + "//core/bukkit:plugin", + "//buildserver/configurator:plugin", + ], +) + +fetch_file( + name = "papermc", + filename = "paper.jar", + url = "https://fill-data.papermc.io/v1/objects/5953a1f1deaa3572be420c3a5b18406c4b752412a78be4acab7c8b467633caed/paper-26.2-100.jar", +) +fetch_file( + name = "luckperms", + filename = "LuckPerms.jar", + url = "https://download.luckperms.net/1652/bukkit/loader/LuckPerms-Bukkit-5.5.65.jar", +) +fetch_file( + name = "viaversion", + filename = "ViaVersion.jar", + url = "https://hangarcdn.papermc.io/plugins/ViaVersion/ViaVersion/versions/5.11.0/PAPER/ViaVersion-5.11.0.jar" +) +fetch_file( + name = "viabackwards", + filename = "ViaBackwards.jar", + url = "https://hangarcdn.papermc.io/plugins/ViaVersion/ViaBackwards/versions/5.11.0/PAPER/ViaBackwards-5.11.0.jar" +) \ No newline at end of file diff --git a/rules/dev_server.bzl b/rules/dev_server.bzl new file mode 100644 index 0000000..342d9aa --- /dev/null +++ b/rules/dev_server.bzl @@ -0,0 +1,51 @@ +def _dev_server_impl(ctx): + script = ctx.actions.declare_file(ctx.label.name + "_runner") + content = """ +#!/bin/sh +set -eu + +cp -f '%s' server.jar +printf eula=true > eula.txt +mkdir -p plugins +rm -f plugins/*.jar +for plugin in %s +do + cp -v \"$plugin\" plugins/ +done + +mkdir -p postgres-data +podman run \ + --rm \ + --name scrow-postgres-dev \ + -p 5432:5432 \ + -v ./postgres-data/:/data \ + -e POSTGRES_USER=postgres \ + -e POSTGRES_HOST_AUTH_METHOD=trust \ + -d \ + docker.io/postgres:alpine || true +echo "Starting paper server in $PWD" +exec env HOST_POSTGRES='jdbc:postgresql://127.0.0.1:5432/postgres' java -jar server.jar --nogui %s +""" % ( + ctx.file.server_jar.short_path, + " ".join([p.short_path for p in ctx.files.plugins]), + " ".join(["--add-plugin %s" % p.short_path for p in ctx.files.local_plugins]), +) + ctx.actions.write( + output = script, + content = content, + is_executable = True, + ) + return DefaultInfo( + executable = script, + runfiles = ctx.runfiles(files = [ctx.file.server_jar] + ctx.files.plugins + ctx.files.local_plugins) + ) + +dev_server = rule( + implementation = _dev_server_impl, + executable = True, + attrs = { + "server_jar": attr.label(allow_single_file = True, mandatory = True), + "plugins": attr.label_list(), + "local_plugins": attr.label_list(), + }, +) diff --git a/rules/fetch_file.bzl b/rules/fetch_file.bzl new file mode 100644 index 0000000..d0a7b03 --- /dev/null +++ b/rules/fetch_file.bzl @@ -0,0 +1,22 @@ +# TODO implement sha256sum attribute + checksum verification + +def _fetch_file_impl(ctx): + out_file = ctx.actions.declare_file(ctx.attr.filename) + ctx.actions.run_shell( + outputs = [out_file], + command = "curl -Lso \"$1\" \"$2\"", + arguments = [out_file.path, ctx.attr.url], + execution_requirements = { + "requires-network": "1", + }, + progress_message = "Downloading plugin %s" % ctx.attr.filename, + ) + return [DefaultInfo(files = depset([out_file]))] + +fetch_file = rule( + implementation = _fetch_file_impl, + attrs = { + "url": attr.string(mandatory = True), + "filename": attr.string(mandatory = True), + }, +) diff --git a/rules/publish_plugin.bzl b/rules/publish_plugin.bzl new file mode 100644 index 0000000..1780aaf --- /dev/null +++ b/rules/publish_plugin.bzl @@ -0,0 +1,68 @@ +# see https://forgejo.org/docs/latest/user/packages/generic/ +# We need to delete the previous version to +# FIXME we're leaking credentials in the process listing(args can be seen in ps(1) output) +# solvable by using a temporary .netrc file(create with umask 0600 or smth) + +_TEMPLATE = """#!/bin/sh +set -eux +: ${GENERIC_USERNAME:=__USERNAME__} +: ${GENERIC_PASSWORD:=__PASSWORD__} +: ${GENERIC_REPO:=__REPOSITORY__} + +URL="$GENERIC_REPO"'/__PACKAGE__/UNVERSIONED/__FILENAME__' +CREDS="$GENERIC_USERNAME:$GENERIC_PASSWORD" + +delete_previous_version() { + curl \ + --fail \ + --user "$CREDS" \ + -X DELETE \ + "$URL" || true +} + +publish_new_version() { + curl \ + --fail \ + --user "$CREDS" \ + --upload-file '__SOURCE__' \ + "$URL" +} + +echo "Deleting previous version at $URL" +delete_previous_version || echo "Note: 404 is expected if this is the first published version" +echo "Pushing new version to $URL" +publish_new_version +""" + +def _publish_plugin_impl(ctx): + script = ctx.actions.declare_file(ctx.label.name + "_publisher") + src = ctx.attr.src[DefaultInfo].files.to_list()[0] + repository = ctx.var.get("generic_repo", "") + username = ctx.var.get("generic_user", "") + password = ctx.var.get("generic_password", "") + ctx.actions.write( + output = script, + is_executable = True, + content = _TEMPLATE + .replace("__USERNAME__", username) + .replace("__PASSWORD__", password) + .replace("__REPOSITORY__", repository) + .replace("__PACKAGE__", ctx.attr.package) + .replace("__SOURCE__", src.short_path) + .replace("__FILENAME__", ctx.attr.filename) + ) + + return DefaultInfo( + executable = script, + runfiles = ctx.runfiles(files = [src]), + ) + +publish_plugin = rule( + implementation = _publish_plugin_impl, + executable = True, + attrs = { + "src": attr.label(allow_single_file = True, mandatory = True), + "package": attr.string(mandatory = True), + "filename": attr.string(mandatory = True), + }, +) \ No newline at end of file