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