currently refactoring a lot

This commit is contained in:
kento2 2026-07-04 01:03:46 +02:00
parent 701a5ff620
commit 7fe7409a15
49 changed files with 856 additions and 767 deletions

View file

@ -4,6 +4,7 @@ plugins {
id("de.kentoj.scrow.scrow-repository")
id("de.kentoj.scrow.base-dependencies")
id("de.kentoj.scrow.database")
id("de.kentoj.scrow.reactor")
}
group = "de.kentoj.scrow"
@ -23,8 +24,6 @@ dependencies {
// https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine
implementation("com.github.ben-manes.caffeine:caffeine:3.2.4")
implementation("de.kentoj.scrow:kencommandapi-velocity")
}
val generateBuildInfo by tasks.registering {

View file

@ -33,7 +33,7 @@ public class CoreVelocityPlugin {
cmds.register(new OnlineCommand(server).getRootNode());
}
var registerWatchdog = new ServerRegisterWatchdog(ScrowAPI.getNats(), ScrowAPI.getServerManager(), server);
var registerWatchdog = new ServerRegisterWatchdog(ScrowAPI.getNats(), ScrowAPI.getInstanceManager(), server);
var sendPlayerWatchdog = new SendPlayerWatchdog(ScrowAPI.getNats(), server);
registerWatchdog.listen();
sendPlayerWatchdog.listen();

View file

@ -1,10 +1,10 @@
package de.kentoj.scrow.corevelocity;
import com.velocitypowered.api.proxy.ProxyServer;
import de.kentoj.kencommandapi.VelocityKenCommandApi;
import de.kentoj.kencommandapi.CommandAPI;
import de.kentoj.scrow.velocity.ScrowAPI;
import de.kentoj.scrowlib.connection.DatabaseConnectionFactoryImpl;
import de.kentoj.scrowlib.servermanager.ServerManagerImpl;
import de.kentoj.scrowlib.utils.EnvUtils;
import io.nats.client.Nats;
import io.nats.client.Options;
import io.r2dbc.spi.ConnectionFactories;
@ -19,7 +19,7 @@ public class ScrowAPISurface {
public static void initScrowAPI(ProxyServer server, boolean enableDatabase) {
try {
String natsHost = System.getenv("HOST_NATS");
String natsHost = EnvUtils.envOrThrow("HOST_NATS");
var options = Options.builder()
.server(natsHost)
.pedantic()
@ -30,13 +30,13 @@ public class ScrowAPISurface {
}
if (enableDatabase) {
ConnectionFactory factory = ConnectionFactories.get(System.getenv("HOST_POSTGRES"));
ConnectionFactory factory = ConnectionFactories.get(EnvUtils.envOrThrow("HOST_POSTGRES"));
ScrowAPI.setDbConFactory(new DatabaseConnectionFactoryImpl(factory));
}
ScrowAPI.setServerManager(new ServerManagerImpl(ScrowAPI.getNats()));
ScrowAPI.setInstanceManager(new ServerManagerImpl(ScrowAPI.getNats()));
ScrowAPI.setCommandApi(new VelocityKenCommandApi(server));
ScrowAPI.setCommandApi(new CommandAPI(server));
}
public static void destroyScrowAPI() {

View file

@ -2,34 +2,38 @@ package de.kentoj.scrow.corevelocity.command;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.ProxyServer;
import de.kentoj.kencommandapi.VelocityCommandContext;
import de.kentoj.kencommandapi.api.nodes.CommandNode;
import de.kentoj.kencommandapi.api.processing.CommandExecutor;
import de.kentoj.scrow.velocity.ScrowAPI;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.api.Results;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.Component;
import java.util.concurrent.CompletableFuture;
@RequiredArgsConstructor
public class OnlineCommand implements CommandExecutor<CommandSource, VelocityCommandContext> {
public class OnlineCommand implements CommandExecutor<CommandSource> {
@Getter
private final CommandNode<CommandSource, VelocityCommandContext> rootNode;
private final CommandNode<CommandSource> rootNode;
private final ProxyServer server;
public OnlineCommand(ProxyServer server) {
this.server = server;
rootNode = ScrowAPI.getCommandApi().createNode("online");
rootNode = CommandNode.node("online");
rootNode.setExecutor(this);
}
@Override
public void execute(VelocityCommandContext ctx) {
public CompletableFuture<Result<Void,String>> execute(CommandContext<CommandSource> ctx) {
int cnt = server.getPlayerCount();
var msg = cnt != 1
? "There are currently " + cnt + " players online"
: "There is currently 1 player online";
ctx.getPlayerSender().sendMessage(Component.text(msg));
ctx.getSender().sendMessage(Component.text(msg));
return Result.success().toFuture();
}
}

View file

@ -3,46 +3,50 @@ package de.kentoj.scrow.corevelocity.privmsg;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import de.kentoj.kencommandapi.VelocityCommandContext;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.nodes.CommandNode;
import de.kentoj.kencommandapi.api.processing.CommandException;
import de.kentoj.kencommandapi.api.processing.CommandExecutor;
import de.kentoj.kencommandapi.api.types.StringArgumentType;
import de.kentoj.kencommandapi.api.argument.types.StringArgumentType;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.api.Results;
import de.kentoj.kencommandapi.type.PlayerArgumentType;
import de.kentoj.scrow.velocity.ScrowAPI;
import lombok.Getter;
public class MsgCommand implements CommandExecutor<CommandSource, VelocityCommandContext> {
import java.util.concurrent.CompletableFuture;
public class MsgCommand implements CommandExecutor<CommandSource> {
@Getter
private final CommandNode<CommandSource, VelocityCommandContext> rootNode;
private final CommandArgument<CommandSource, VelocityCommandContext, Player> targetArg;
private final CommandArgument<CommandSource, VelocityCommandContext, String> msgArg;
private final CommandNode<CommandSource> rootNode;
private final CommandArgument<CommandSource, Player> targetArg;
private final CommandArgument<CommandSource, String> msgArg;
private final LastTargetCache cache;
public MsgCommand(ProxyServer server, LastTargetCache cache) {
this.cache = cache;
var cmds = ScrowAPI.getCommandApi();
targetArg = cmds.createArgument("target", new PlayerArgumentType(server));
msgArg = cmds.createArgument("msg", new StringArgumentType<>(true));
rootNode = cmds.createNode("msg", "w", "privmsg");
targetArg = CommandArgument.arg("target", new PlayerArgumentType(server));
msgArg = CommandArgument.arg("msg", new StringArgumentType<>(true));
rootNode = CommandNode.node("msg", "w", "privmsg");
rootNode.setExecutor(this);
rootNode.addArgument(targetArg);
rootNode.addArgument(msgArg);
}
@Override
public void execute(VelocityCommandContext ctx) {
public CompletableFuture<Result<Void,String>> execute(CommandContext<CommandSource> ctx) {
var target = ctx.getArg(targetArg);
var msg = ctx.getArg(msgArg);
var sender = (Player)ctx.getSender();
var isMessagingSelf = target.getUniqueId().equals(ctx.getPlayerSender().getUniqueId());
if (isMessagingSelf) throw new CommandException("You can't message yourself.");
var isMessagingSelf = target.getUniqueId().equals(sender.getUniqueId());
if (isMessagingSelf) return Result.error("You can't message yourself").toFuture();
cache.setLastTarget(ctx.getPlayerSender().getUniqueId(), target.getUniqueId());
PrivmsgHelper.handle(ctx.getPlayerSender(), target, msg);
cache.setLastTarget(sender.getUniqueId(), target.getUniqueId());
PrivmsgHelper.handle(sender, target, msg);
return Result.success().toFuture();
}
}

View file

@ -1,21 +1,24 @@
package de.kentoj.scrow.corevelocity.privmsg;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import de.kentoj.kencommandapi.VelocityCommandContext;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.nodes.CommandNode;
import de.kentoj.kencommandapi.api.processing.CommandException;
import de.kentoj.kencommandapi.api.processing.CommandExecutor;
import de.kentoj.kencommandapi.api.types.StringArgumentType;
import de.kentoj.scrow.velocity.ScrowAPI;
import de.kentoj.kencommandapi.api.argument.types.StringArgumentType;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.api.Results;
import lombok.Getter;
public class ReplyCommand implements CommandExecutor<CommandSource, VelocityCommandContext> {
import java.util.concurrent.CompletableFuture;
public class ReplyCommand implements CommandExecutor<CommandSource> {
@Getter
private final CommandNode<CommandSource, VelocityCommandContext> rootNode;
private final CommandArgument<CommandSource, VelocityCommandContext, String> msgArg;
private final CommandNode<CommandSource> rootNode;
private final CommandArgument<CommandSource, String> msgArg;
private final LastTargetCache cache;
private final ProxyServer server;
@ -24,21 +27,22 @@ public class ReplyCommand implements CommandExecutor<CommandSource, VelocityComm
this.cache = cache;
this.server = server;
var cmds = ScrowAPI.getCommandApi();
msgArg = cmds.createArgument("msg", new StringArgumentType<>(true));
rootNode = cmds.createNode("reply", "r");
msgArg = CommandArgument.arg("msg", new StringArgumentType<>(true));
rootNode = CommandNode.node("reply", "r");
rootNode.setExecutor(this);
rootNode.addArgument(msgArg);
}
@Override
public void execute(VelocityCommandContext ctx) {
var targetId = cache.getLastTarget(ctx.getPlayerSender().getUniqueId());
public CompletableFuture<Result<Void,String>> execute(CommandContext<CommandSource> ctx) {
var sender = (Player)ctx.getSender();
var targetId = cache.getLastTarget(sender.getUniqueId());
var target = server.getPlayer(targetId).orElse(null);
if (target == null) throw new CommandException("The player you last messaged is no longer online");
if (target == null) return Result.error("The player you last messaged is no longer online").toFuture();
var msg = ctx.getArg(msgArg);
cache.setLastTarget(ctx.getPlayerSender().getUniqueId(), target.getUniqueId());
PrivmsgHelper.handle(ctx.getPlayerSender(), target, msg);
cache.setLastTarget(sender.getUniqueId(), target.getUniqueId());
PrivmsgHelper.handle(sender, target, msg);
return Result.success().toFuture();
}
}

View file

@ -1,43 +1,47 @@
package de.kentoj.scrow.corevelocity.server;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import com.velocitypowered.api.proxy.ProxyServer;
import de.kentoj.scrowlib.messaging.NatsRepository;
import de.kentoj.scrowlib.messaging.respose.SafeResult;
import de.kentoj.scrowlib.messaging.MessageBroker;
import io.nats.client.Connection;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Mono;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
@Slf4j
public class SendPlayerWatchdog {
private final NatsRepository natsRepo;
private final MessageBroker natsRepo;
private final ProxyServer server;
public SendPlayerWatchdog(Connection nats, ProxyServer server) {
this.natsRepo = new NatsRepository(nats);
this.natsRepo = new MessageBroker(nats);
this.server = server;
}
public void listen() {
natsRepo.subscribe("proxy.send-player")
.flatMap(ctx ->
SafeResult.mono(handleSendPlayer(ctx.getDocument()))
)
.subscribe();
natsRepo.handle("players.send", doc -> sendPlayer(
doc.get("playerId", UUID.class),
doc.getString("handle")
));
}
private Mono<@NotNull Document> handleSendPlayer(Document doc) {
return Mono.fromCallable(() -> {
var player = server.getPlayer(UUID.fromString(doc.getString("playerId"))).orElseThrow();
var targetServer = server.getServer(doc.getString("handle")).orElseThrow();
return player.createConnectionRequest(targetServer).connect();
})
.flatMap(Mono::fromFuture)
.flatMap(res -> res.isSuccessful()
? Mono.just(new Document())
: Mono.error(new RuntimeException(res.getStatus().name().toLowerCase().replace('_', ' ')))
);
private CompletionStage<Result<Document, String>> sendPlayer(UUID playerId, String handle) {
return CompletableFuture.supplyAsync(() -> {
var player = server.getPlayer(playerId).orElseThrow();
var targetServer = server.getServer(handle).orElseThrow();
try {
var res = player.createConnectionRequest(targetServer).connect().get();
return res.isSuccessful() ? Results.success(new Document()) : Results.failure(res.getStatus().name());
} catch (InterruptedException | ExecutionException e) {
log.error("failed to send player to {}", handle, e);
return Results.failure(e.getMessage());
}
});
}
}

View file

@ -2,10 +2,9 @@ package de.kentoj.scrow.corevelocity.server;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import de.kentoj.scrowlib.messaging.NatsRepository;
import de.kentoj.scrowlib.messaging.SubscriptionContext;
import de.kentoj.scrowlib.messaging.MessageBroker;
import de.kentoj.scrowlib.messaging.respose.SafeResult;
import de.kentoj.scrowlib.servermanager.ServerManager;
import de.kentoj.scrowlib.servermanager.InstanceManager;
import io.nats.client.Connection;
import lombok.extern.java.Log;
import org.bson.Document;
@ -17,13 +16,13 @@ import java.net.InetSocketAddress;
@Log
public class ServerRegisterWatchdog {
private final NatsRepository natsRepo;
private final ServerManager serverManager;
private final MessageBroker natsRepo;
private final InstanceManager instanceManager;
private final ProxyServer server;
public ServerRegisterWatchdog(Connection nats, ServerManager serverManager, ProxyServer server) {
this.natsRepo = new NatsRepository(nats);
this.serverManager = serverManager;
public ServerRegisterWatchdog(Connection nats, InstanceManager instanceManager, ProxyServer server) {
this.natsRepo = new MessageBroker(nats);
this.instanceManager = instanceManager;
this.server = server;
}
@ -42,11 +41,11 @@ public class ServerRegisterWatchdog {
private @NotNull Mono<@NotNull Void> handleStateChange(SubscriptionContext ctx, String state, String handle) {
return switch (state) {
case "UP" -> serverManager.getInfo(handle)
case "UP" -> instanceManager.getInfo(handle)
.doOnNext(instance ->
server.registerServer(new ServerInfo(handle, new InetSocketAddress(instance.getPort()))))
.then();
case "DOWN" -> serverManager.getInfo(handle)
case "DOWN" -> instanceManager.getInfo(handle)
.doOnNext(instance ->
server.unregisterServer(new ServerInfo(handle, new InetSocketAddress(instance.getPort()))))
.then();