This commit is contained in:
kento2 2026-08-19 02:15:15 +02:00
parent 46978ae799
commit ce56c6e105
53 changed files with 776 additions and 891 deletions

View file

@ -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",
)

View file

@ -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();
}
}

View file

@ -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()));
}
}

View file

@ -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);
});
}
}

View file

@ -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<ServerInfo> 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();
}
}

View file

@ -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);
}
}
}

View file

@ -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<ServerInfo> queryMinecraftServers() throws IOException, InterruptedException;
}