From 7582653d54acd4626509c280352ae7b1827566a6 Mon Sep 17 00:00:00 2001 From: MiniDay <372403923@qq.com> Date: Wed, 6 Aug 2025 01:42:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20Velocity=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 32 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../service/bukkit/api/ServiceMessageAPI.java | 4 +- .../service/bungee/api/ServiceMessageAPI.java | 8 +- .../bungee/event/ServiceConnectEvent.java | 2 - .../service/velocity/BallBridgePlugin.java | 50 +++ .../service/velocity/api/ServiceInfoAPI.java | 141 ++++++++ .../velocity/api/ServiceMessageAPI.java | 333 ++++++++++++++++++ .../service/velocity/event/MessageEvent.java | 20 ++ .../velocity/event/MessageReceivedEvent.java | 14 + .../velocity/event/MessageSentEvent.java | 44 +++ .../velocity/event/ServiceConnectEvent.java | 41 +++ .../velocity/listener/BridgeListener.java | 45 +++ .../listener/ServiceMainListener.java | 163 +++++++++ src/main/resources/bungee.yml | 1 - src/main/resources/plugin.yml | 1 - src/main/resources/update.yml | 4 + .../service/velocity/BuildConstants.java | 8 + 18 files changed, 896 insertions(+), 17 deletions(-) create mode 100644 src/main/java/cn/hamster3/service/velocity/BallBridgePlugin.java create mode 100644 src/main/java/cn/hamster3/service/velocity/api/ServiceInfoAPI.java create mode 100644 src/main/java/cn/hamster3/service/velocity/api/ServiceMessageAPI.java create mode 100644 src/main/java/cn/hamster3/service/velocity/event/MessageEvent.java create mode 100644 src/main/java/cn/hamster3/service/velocity/event/MessageReceivedEvent.java create mode 100644 src/main/java/cn/hamster3/service/velocity/event/MessageSentEvent.java create mode 100644 src/main/java/cn/hamster3/service/velocity/event/ServiceConnectEvent.java create mode 100644 src/main/java/cn/hamster3/service/velocity/listener/BridgeListener.java create mode 100644 src/main/java/cn/hamster3/service/velocity/listener/ServiceMainListener.java create mode 100644 src/main/resources/update.yml create mode 100644 src/main/templates/cn/hamster3/service/velocity/BuildConstants.java diff --git a/build.gradle.kts b/build.gradle.kts index abea98a..7c2e5ad 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,13 +5,11 @@ plugins { } group = "cn.hamster3.mc.plugin" -version = "1.4.5" +version = "1.5.0" description = "将 HamsterBall 转换成 HamsterService 兼容的 API" repositories { - maven { - url = uri("https://maven.airgame.net/maven-public/") - } + maven("https://maven.airgame.net/maven-public/") } dependencies { @@ -20,13 +18,31 @@ dependencies { compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT") compileOnly("net.md-5:bungeecord-api:1.20-R0.1-SNAPSHOT") + compileOnly("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT") + annotationProcessor("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT") compileOnly("cn.hamster3.mc.plugin:core-bukkit:+") compileOnly("cn.hamster3.mc.plugin:ball-bukkit:+") - compileOnly("me.clip:placeholderapi:2.11.2") + compileOnly("me.clip:placeholderapi:2.11.6") } +sourceSets.create("templates") { + java { + srcDir("src/main/templates") + } +} + +val templateSource = file("src/main/templates") +val templateDest = layout.buildDirectory.dir("generated/sources/templates") +val generateTemplates = tasks.register("generateTemplates") { + from(templateSource) + into(templateDest) + expand(project.properties) +} + +sourceSets.main.get().java.srcDir(generateTemplates.map { it.outputs }) + tasks { withType().configureEach { options.encoding = "UTF-8" @@ -36,8 +52,6 @@ tasks { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 withSourcesJar() } jar { @@ -45,9 +59,9 @@ tasks { destinationDirectory = rootProject.layout.buildDirectory } processResources { - filesMatching(listOf("plugin.yml", "bungee.yml")) { + filesMatching(listOf("plugin.yml", "bungee.yml", "update.yml")) { expand(project.properties) } duplicatesStrategy = DuplicatesStrategy.EXCLUDE } -} +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7b6c47b..c1217da 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Mar 11 19:25:59 CST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/cn/hamster3/service/bukkit/api/ServiceMessageAPI.java b/src/main/java/cn/hamster3/service/bukkit/api/ServiceMessageAPI.java index ef7b700..1fa31b6 100644 --- a/src/main/java/cn/hamster3/service/bukkit/api/ServiceMessageAPI.java +++ b/src/main/java/cn/hamster3/service/bukkit/api/ServiceMessageAPI.java @@ -250,7 +250,9 @@ public abstract class ServiceMessageAPI { } public static void kickPlayer(UUID uuid, String reason) { - kickPlayer(uuid, new JsonPrimitive(reason)); + JsonObject object = new JsonObject(); + object.addProperty("text", reason); + kickPlayer(uuid, object); } public static void kickPlayer(UUID uuid, JsonElement reason) { diff --git a/src/main/java/cn/hamster3/service/bungee/api/ServiceMessageAPI.java b/src/main/java/cn/hamster3/service/bungee/api/ServiceMessageAPI.java index 714a58b..2ba6c1f 100644 --- a/src/main/java/cn/hamster3/service/bungee/api/ServiceMessageAPI.java +++ b/src/main/java/cn/hamster3/service/bungee/api/ServiceMessageAPI.java @@ -124,7 +124,9 @@ public abstract class ServiceMessageAPI { * @param message 消息 */ public static void sendPlayerMessage(UUID uuid, String message) { - sendPlayerMessage(uuid, new JsonPrimitive(message)); + JsonObject object = new JsonObject(); + object.addProperty("text", message); + sendPlayerMessage(uuid, object); } /** @@ -223,7 +225,9 @@ public abstract class ServiceMessageAPI { } public static void kickPlayer(UUID uuid, String reason) { - kickPlayer(uuid, new JsonPrimitive(reason)); + JsonObject object = new JsonObject(); + object.addProperty("text", reason); + kickPlayer(uuid, object); } public static void kickPlayer(UUID uuid, JsonElement reason) { diff --git a/src/main/java/cn/hamster3/service/bungee/event/ServiceConnectEvent.java b/src/main/java/cn/hamster3/service/bungee/event/ServiceConnectEvent.java index b397520..936cca4 100644 --- a/src/main/java/cn/hamster3/service/bungee/event/ServiceConnectEvent.java +++ b/src/main/java/cn/hamster3/service/bungee/event/ServiceConnectEvent.java @@ -1,6 +1,5 @@ package cn.hamster3.service.bungee.event; - import net.md_5.bungee.api.plugin.Event; /** @@ -22,7 +21,6 @@ public class ServiceConnectEvent extends Event { this.cause = cause; } - /** * 是否成功连接到服务中心 * diff --git a/src/main/java/cn/hamster3/service/velocity/BallBridgePlugin.java b/src/main/java/cn/hamster3/service/velocity/BallBridgePlugin.java new file mode 100644 index 0000000..a1ef967 --- /dev/null +++ b/src/main/java/cn/hamster3/service/velocity/BallBridgePlugin.java @@ -0,0 +1,50 @@ +package cn.hamster3.service.velocity; + +import cn.hamster3.mc.plugin.ball.common.api.BallAPI; +import cn.hamster3.service.common.util.ServiceLogUtils; +import cn.hamster3.service.velocity.listener.BridgeListener; +import cn.hamster3.service.velocity.listener.ServiceMainListener; +import com.google.inject.Inject; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.plugin.Dependency; +import com.velocitypowered.api.plugin.Plugin; +import com.velocitypowered.api.proxy.ProxyServer; + +import java.util.logging.Logger; + +@Plugin( + id = "hamster-service", + name = "HamsterService", + version = BuildConstants.VERSION, + description = BuildConstants.DESCRIPTION, + authors = {"MiniDay"}, + dependencies = @Dependency(id = "hamster-ball") +) +public class BallBridgePlugin { + private static BallBridgePlugin instance; + private final ProxyServer proxyServer; + + @Inject + public BallBridgePlugin(ProxyServer proxyServer) { + this.proxyServer = proxyServer; + instance = this; + ServiceLogUtils.setLogger(Logger.getLogger("HamsterService")); + } + + public static BallBridgePlugin getInstance() { + return instance; + } + + public ProxyServer getProxyServer() { + return proxyServer; + } + + + @Subscribe(priority = 0) + public void onProxyInitialization(ProxyInitializeEvent event) { + BallAPI.getInstance().subscribeRaw("HamsterService"); + BallAPI.getInstance().getEventBus().register(BridgeListener.INSTANCE); + proxyServer.getEventManager().register(this, ServiceMainListener.INSTANCE); + } +} diff --git a/src/main/java/cn/hamster3/service/velocity/api/ServiceInfoAPI.java b/src/main/java/cn/hamster3/service/velocity/api/ServiceInfoAPI.java new file mode 100644 index 0000000..f09fa8b --- /dev/null +++ b/src/main/java/cn/hamster3/service/velocity/api/ServiceInfoAPI.java @@ -0,0 +1,141 @@ +package cn.hamster3.service.velocity.api; + +import cn.hamster3.mc.plugin.ball.common.api.BallAPI; +import cn.hamster3.mc.plugin.ball.common.entity.BallPlayerInfo; +import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo; +import cn.hamster3.mc.plugin.ball.common.entity.BallServerType; +import cn.hamster3.service.common.data.ServicePlayerInfo; +import cn.hamster3.service.common.entity.ServiceSenderInfo; +import cn.hamster3.service.common.entity.ServiceSenderType; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.UUID; +import java.util.stream.Collectors; + +@SuppressWarnings("unused") +public class ServiceInfoAPI { + /** + * 获取玩家信息,当玩家不在线时返回 null + * + * @param uuid 玩家的UUID + * @return 玩家信息 + */ + public static ServicePlayerInfo getPlayerInfo(@NotNull UUID uuid) { + return transfer(BallAPI.getInstance().getPlayerInfo(uuid)); + } + + /** + * 获取玩家信息,当玩家不在线时返回 null + * + * @param playerName 玩家ID + * @return 玩家信息 + */ + public static ServicePlayerInfo getPlayerInfo(@NotNull String playerName) { + return transfer(BallAPI.getInstance().getPlayerInfo(playerName)); + } + + /** + * 获取全部在线玩家的信息 + * + * @return 玩家们的信息 + */ + public static HashSet getOnlinePlayers() { + return BallAPI.getInstance().getAllPlayerInfo().values() + .stream() + .filter(BallPlayerInfo::isOnline) + .map(ServiceInfoAPI::transfer) + .collect(Collectors.toCollection(HashSet::new)); + } + + /** + * 获取全部在线玩家的信息 + * + * @return 玩家们的信息 + */ + public static HashSet getAllPlayerInfo() { + return BallAPI.getInstance().getAllPlayerInfo().values() + .stream() + .map(ServiceInfoAPI::transfer) + .collect(Collectors.toCollection(HashSet::new)); + } + + /** + * 获取服务端信息 + * + * @param senderName 服务端id + * @return 服务端信息 + */ + public static ServiceSenderInfo getSenderInfo(String senderName) { + return transfer(BallAPI.getInstance().getServerInfo(senderName)); + } + + /** + * 获取所有连接至服务中心的信息 + * + * @return 服务器信息 + */ + public static HashSet getAllSenderInfo() { + return BallAPI.getInstance().getAllServerInfo().values() + .stream() + .map(ServiceInfoAPI::transfer) + .collect(Collectors.toCollection(HashSet::new)); + + } + + /** + * 获取当前服务器的名称 + * + * @return 服务器id + */ + public static String getLocalServerName() { + return BallAPI.getInstance().getLocalServerId(); + } + + /** + * 获取当前服务器的别名 + * + * @return 服务器别名 + */ + public static String getLocalServerNickName() { + return BallAPI.getInstance().getLocalServerInfo().getName(); + } + + /** + * 获取当前服务器的SenderInfo对象 + * + * @return 当前服务器的发送者信息 + */ + public static ServiceSenderInfo getLocalSenderInfo() { + return new ServiceSenderInfo( + ServiceSenderType.PROXY, + BallAPI.getInstance().getLocalServerInfo().getId(), + BallAPI.getInstance().getLocalServerInfo().getName() + ); + } + + public static ServiceSenderInfo transfer(BallServerInfo info) { + if (info == null) { + return null; + } + return new ServiceSenderInfo( + info.getType() == BallServerType.PROXY ? ServiceSenderType.PROXY : ServiceSenderType.BUKKIT, + info.getId(), + info.getName() + ); + } + + public static ServicePlayerInfo transfer(BallPlayerInfo info) { + if (info == null) { + return null; + } + return new ServicePlayerInfo( + info.getUuid(), + info.getName(), + info.getGameServer(), + info.getProxyServer(), + info.isOnline() + ); + } + +} diff --git a/src/main/java/cn/hamster3/service/velocity/api/ServiceMessageAPI.java b/src/main/java/cn/hamster3/service/velocity/api/ServiceMessageAPI.java new file mode 100644 index 0000000..e875756 --- /dev/null +++ b/src/main/java/cn/hamster3/service/velocity/api/ServiceMessageAPI.java @@ -0,0 +1,333 @@ +package cn.hamster3.service.velocity.api; + +import cn.hamster3.mc.plugin.ball.common.api.BallAPI; +import cn.hamster3.mc.plugin.ball.common.data.BallMessage; +import cn.hamster3.service.common.data.ServiceLocation; +import cn.hamster3.service.common.data.ServicePlayerInfo; +import cn.hamster3.service.common.entity.ServiceMessageInfo; +import cn.hamster3.service.velocity.BallBridgePlugin; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.velocitypowered.api.proxy.Player; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; + +import java.util.UUID; + +@SuppressWarnings("unused") +public abstract class ServiceMessageAPI { + + /** + * 订阅某个标签的消息 + * + * @param tag 标签 + */ + public static void subscribeTag(String tag) { + sendServiceMessage("HamsterService", "subscribeTag", tag); + } + + /** + * 取消订阅某个标签的消息 + * + * @param tag 标签 + */ + public static void unsubscribeTag(String tag) { + sendServiceMessage("HamsterService", "unsubscribeTag", tag); + } + + /** + * 发送一条服务消息 + * + * @param tag 消息标签 + * @param action 执行动作 + */ + public static void sendServiceMessage(String tag, String action) { + BallAPI.getInstance().sendRawBallMessage(tag, action); + } + + /** + * 发送一条有附加参数的服务消息 + * + * @param tag 消息标签 + * @param action 执行动作 + * @param content 附加参数 + */ + public static void sendServiceMessage(String tag, String action, String content) { + BallAPI.getInstance().sendRawBallMessage(tag, action, content); + } + + /** + * 发送一条有附加参数的服务消息,使用 String.format() 替换附加参数 + * + * @param tag 消息标签 + * @param action 执行动作 + * @param content 附加参数 + * @param args 替换参数 + * @see String#format(String, Object...) + */ + public static void sendServiceMessage(String tag, String action, String content, Object... args) { + BallAPI.getInstance().sendRawBallMessage(tag, action, new JsonPrimitive(String.format(content, args))); + } + + /** + * 发送一条有附加参数的消息 + * + * @param tag 消息标签 + * @param action 执行动作 + * @param content 附加参数 + */ + public static void sendServiceMessage(String tag, String action, JsonElement content) { + BallAPI.getInstance().sendBallMessage(tag, new BallMessage(action, content), false, false); + } + + /** + * 自定义服务消息信息并发送 + * + * @param info 消息内容 + * @param block 是否阻塞(即必须等待消息发送完成,该方法才会返回) + */ + public static void sendServiceMessage(ServiceMessageInfo info, boolean block) { + BallMessage message = new BallMessage( + info.getSenderInfo().getName(), + info.getToServer(), + null, + info.getAction(), + info.getContent() + ); + BallAPI.getInstance().sendBallMessage(info.getTag(), message, false, block); + } + + /** + * 强制玩家执行一个 bukkit 命令 + * + * @param uuid 玩家的uuid + * @param command 命令内容 + */ + public static void dispatchBukkitCommand(UUID uuid, String command) { + sendServiceMessage("HamsterService", "dispatchBukkitCommand", command); + } + + /** + * 强制玩家执行一个代理端(指BungeeCord等)命令 + * + * @param uuid 玩家的uuid + * @param command 命令内容 + */ + public static void dispatchProxyCommand(UUID uuid, String command) { + sendServiceMessage("HamsterService", "dispatchProxyCommand", command); + } + + /** + * 给玩家发送一条消息 + * + * @param uuid 玩家 + * @param message 消息 + */ + public static void sendPlayerMessage(UUID uuid, String message) { + JsonObject object = new JsonObject(); + object.addProperty("text", message); + sendPlayerMessage(uuid, object); + } + + /** + * 给玩家发送一条消息 + * + * @param uuid 玩家 + * @param message 消息 + */ + public static void sendPlayerMessage(UUID uuid, JsonElement message) { + Player player = BallBridgePlugin.getInstance().getProxyServer().getPlayer(uuid).orElse(null); + if (player != null) { + player.sendMessage(GsonComponentSerializer.gson().deserializeFromTree(message)); + return; + } + JsonObject object = new JsonObject(); + object.addProperty("uuid", uuid.toString()); + object.add("message", message); + sendServiceMessage("HamsterService", "sendPlayerMessage", object); + } + + /** + * 给服务器的在线玩家广播一条消息 + * + * @param message 消息 + * @since 2.1.0 + */ + public static void broadcastMessage(String message) { + JsonObject object = new JsonObject(); + object.addProperty("text", message); + broadcastMessage(object); + } + + /** + * 给服务器的在线玩家广播一条消息 + * + * @param message 消息 + * @since 2.1.0 + */ + public static void broadcastMessage(JsonElement message) { + sendServiceMessage("HamsterService", "broadcastMessage", message); + } + + /** + * 把玩家传送到另一个玩家身边 + *

+ * 支持跨服传送 + * + * @param sendPlayer 被传送的玩家 + * @param toPlayer 传送的目标玩家 + * @since 2.1.0 + */ + public static void sendPlayerToPlayer(UUID sendPlayer, UUID toPlayer) { + ServicePlayerInfo sendPlayerInfo = ServiceInfoAPI.getPlayerInfo(sendPlayer); + // 如果被传送玩家不在线 + if (sendPlayerInfo == null || !sendPlayerInfo.isOnline()) { + return; + } + + ServicePlayerInfo toPlayerInfo = ServiceInfoAPI.getPlayerInfo(toPlayer); + // 如果目标玩家不在线 + if (toPlayerInfo == null || !toPlayerInfo.isOnline()) { + return; + } + + JsonObject object = new JsonObject(); + object.addProperty("sendPlayer", sendPlayer.toString()); + object.addProperty("toPlayer", toPlayer.toString()); + sendServiceMessage("HamsterService", "sendPlayerToPlayer", object); + } + + /** + * 把玩家传送到一个位置 + *

+ * 支持跨服传送 + * + * @param uuid 玩家的uuid + * @param location 坐标 + * @since 2.1.0 + */ + public static void sendPlayerToLocation(UUID uuid, ServiceLocation location) { + ServicePlayerInfo playerInfo = ServiceInfoAPI.getPlayerInfo(uuid); + + // 如果玩家不在线 + if (playerInfo == null || !playerInfo.isOnline()) { + return; + } + // 如果目标服务器不在线 + if (ServiceInfoAPI.getSenderInfo(location.getServerName()) == null) { + return; + } + + JsonObject object = new JsonObject(); + object.addProperty("uuid", uuid.toString()); + object.add("location", location.saveToJson()); + sendServiceMessage("HamsterService", "sendPlayerToLocation", object); + } + + public static void kickPlayer(UUID uuid, String reason) { + JsonObject object = new JsonObject(); + object.addProperty("text", reason); + kickPlayer(uuid, object); + } + + public static void kickPlayer(UUID uuid, JsonElement reason) { + Player player = BallBridgePlugin.getInstance().getProxyServer().getPlayer(uuid).orElse(null); + if (player != null) { + player.disconnect(GsonComponentSerializer.gson().deserializeFromTree(reason)); + return; + } + JsonObject object = new JsonObject(); + object.addProperty("uuid", uuid.toString()); + object.add("reason", reason); + sendServiceMessage("HamsterService", "kickPlayer", object); + } + + /** + * 开启/关闭 安全模式 + *

+ * 在安全模式开启时玩家将无法连接至服务器 + *

+ * 且根据 config 的配置不同,有可能会踢出全部在线玩家 + * + * @param enable 是否启用 + */ + public static void setSafeMode(boolean enable) { + sendServiceMessage("HamsterService", "setSafeMode", new JsonPrimitive(enable)); + } + + /** + * 发送一条服务消息 + * + * @param tag 消息标签 + * @param action 执行动作 + * @deprecated 你应该使用 sendServiceMessage + *

+ * 因为这个方法名有歧义 + */ + @Deprecated + public static void sendMessage(String tag, String action) { + sendServiceMessage(tag, action); + } + + /** + * 发送一条有附加参数的服务消息 + * + * @param tag 消息标签 + * @param action 执行动作 + * @param content 附加参数 + * @deprecated 你应该使用 sendServiceMessage + *

+ * 因为这个方法名有歧义 + */ + @Deprecated + public static void sendMessage(String tag, String action, String content) { + sendServiceMessage(tag, action, content); + } + + /** + * 发送一条有附加参数的服务消息,使用 String.format() 替换附加参数 + * + * @param tag 消息标签 + * @param action 执行动作 + * @param content 附加参数 + * @param args 替换参数 + * @see String#format(String, Object...) + * @deprecated 你应该使用 sendServiceMessage + *

+ * 因为这个方法名有歧义 + */ + @Deprecated + public static void sendMessage(String tag, String action, String content, Object... args) { + sendServiceMessage(tag, action, content, args); + } + + /** + * 发送一条有附加参数的服务消息 + * + * @param tag 消息标签 + * @param action 执行动作 + * @param content 附加参数 + * @deprecated 你应该使用 sendServiceMessage + *

+ * 因为这个方法名有歧义 + */ + @Deprecated + public static void sendMessage(String tag, String action, JsonElement content) { + sendServiceMessage(tag, action, content); + } + + /** + * 自定义服务消息信息并发送 + * + * @param info 消息内容 + * @param block 是否阻塞(即必须等待消息发送完成,该方法才会返回) + * @deprecated 你应该使用 sendServiceMessage + *

+ * 因为这个方法名有歧义 + */ + @Deprecated + public static void sendMessage(ServiceMessageInfo info, boolean block) { + sendServiceMessage(info, block); + } + +} diff --git a/src/main/java/cn/hamster3/service/velocity/event/MessageEvent.java b/src/main/java/cn/hamster3/service/velocity/event/MessageEvent.java new file mode 100644 index 0000000..5db5986 --- /dev/null +++ b/src/main/java/cn/hamster3/service/velocity/event/MessageEvent.java @@ -0,0 +1,20 @@ +package cn.hamster3.service.velocity.event; + +import cn.hamster3.service.common.entity.ServiceMessageInfo; + +/** + * 服务消息事件的基类 + */ +@SuppressWarnings("unused") +public class MessageEvent { + private final ServiceMessageInfo messageInfo; + + public MessageEvent(ServiceMessageInfo messageInfo) { + this.messageInfo = messageInfo; + } + + public ServiceMessageInfo getMessageInfo() { + return messageInfo; + } + +} diff --git a/src/main/java/cn/hamster3/service/velocity/event/MessageReceivedEvent.java b/src/main/java/cn/hamster3/service/velocity/event/MessageReceivedEvent.java new file mode 100644 index 0000000..f225260 --- /dev/null +++ b/src/main/java/cn/hamster3/service/velocity/event/MessageReceivedEvent.java @@ -0,0 +1,14 @@ +package cn.hamster3.service.velocity.event; + +import cn.hamster3.service.common.entity.ServiceMessageInfo; + +/** + * 从服务中心收到消息时产生的事件 + */ +@SuppressWarnings("unused") +public class MessageReceivedEvent extends MessageEvent { + + public MessageReceivedEvent(ServiceMessageInfo info) { + super(info); + } +} diff --git a/src/main/java/cn/hamster3/service/velocity/event/MessageSentEvent.java b/src/main/java/cn/hamster3/service/velocity/event/MessageSentEvent.java new file mode 100644 index 0000000..378dd78 --- /dev/null +++ b/src/main/java/cn/hamster3/service/velocity/event/MessageSentEvent.java @@ -0,0 +1,44 @@ +package cn.hamster3.service.velocity.event; + +import cn.hamster3.service.common.entity.ServiceMessageInfo; + +/** + * 消息发送出去之后产生的事件 + */ +@SuppressWarnings("unused") +public class MessageSentEvent extends MessageEvent { + private final boolean success; + private final Throwable cause; + + public MessageSentEvent(ServiceMessageInfo info) { + super(info); + success = true; + cause = null; + } + + public MessageSentEvent(ServiceMessageInfo messageInfo, Throwable cause) { + super(messageInfo); + success = false; + this.cause = cause; + } + + + /** + * 消息是否成功发送出去了 + * + * @return true代表成功发送 + */ + public boolean isSuccess() { + return success; + } + + /** + * 若消息发送失败,则失败原因为何 + * 若发送成功,则返回null + * + * @return 失败原因 + */ + public Throwable getCause() { + return cause; + } +} diff --git a/src/main/java/cn/hamster3/service/velocity/event/ServiceConnectEvent.java b/src/main/java/cn/hamster3/service/velocity/event/ServiceConnectEvent.java new file mode 100644 index 0000000..addc24b --- /dev/null +++ b/src/main/java/cn/hamster3/service/velocity/event/ServiceConnectEvent.java @@ -0,0 +1,41 @@ +package cn.hamster3.service.velocity.event; + +/** + * 服务连接事件 + */ + +@SuppressWarnings("unused") +public class ServiceConnectEvent { + private final boolean success; + private final Throwable cause; + + public ServiceConnectEvent() { + success = true; + cause = null; + } + + public ServiceConnectEvent(Throwable cause) { + success = false; + this.cause = cause; + } + + /** + * 是否成功连接到服务中心 + * + * @return true代表成功 + */ + public boolean isSuccess() { + return success; + } + + /** + * 如果连接失败了,则返回失败原因 + *

+ * 如果连接成功了,则返回null + * + * @return 失败原因 + */ + public Throwable getCause() { + return cause; + } +} diff --git a/src/main/java/cn/hamster3/service/velocity/listener/BridgeListener.java b/src/main/java/cn/hamster3/service/velocity/listener/BridgeListener.java new file mode 100644 index 0000000..69e6af0 --- /dev/null +++ b/src/main/java/cn/hamster3/service/velocity/listener/BridgeListener.java @@ -0,0 +1,45 @@ +package cn.hamster3.service.velocity.listener; + +import cn.hamster3.service.common.entity.ServiceMessageInfo; +import cn.hamster3.service.velocity.BallBridgePlugin; +import cn.hamster3.service.velocity.api.ServiceInfoAPI; +import cn.hamster3.service.velocity.event.MessageReceivedEvent; +import cn.hamster3.service.velocity.event.MessageSentEvent; +import com.google.common.eventbus.Subscribe; + +public class BridgeListener { + public static final BridgeListener INSTANCE = new BridgeListener(); + + private BridgeListener() { + } + + @Subscribe + public void onMessageSent(cn.hamster3.mc.plugin.ball.common.event.message.MessageSentEvent event) { + BallBridgePlugin.getInstance().getProxyServer().getEventManager().fireAndForget( + new MessageSentEvent( + new ServiceMessageInfo( + ServiceInfoAPI.getSenderInfo(event.getSenderID()), + event.getReceiverID(), + event.getChannel(), + event.getAction(), + event.getContent() + ) + ) + ); + } + + @Subscribe + public void onMessageReceived(cn.hamster3.mc.plugin.ball.common.event.message.MessageReceivedEvent event) { + BallBridgePlugin.getInstance().getProxyServer().getEventManager().fireAndForget( + new MessageReceivedEvent( + new ServiceMessageInfo( + ServiceInfoAPI.getSenderInfo(event.getSenderID()), + event.getReceiverID(), + event.getChannel(), + event.getAction(), + event.getContent() + ) + ) + ); + } +} diff --git a/src/main/java/cn/hamster3/service/velocity/listener/ServiceMainListener.java b/src/main/java/cn/hamster3/service/velocity/listener/ServiceMainListener.java new file mode 100644 index 0000000..a2a5ca7 --- /dev/null +++ b/src/main/java/cn/hamster3/service/velocity/listener/ServiceMainListener.java @@ -0,0 +1,163 @@ +package cn.hamster3.service.velocity.listener; + +import cn.hamster3.service.common.data.ServiceLocation; +import cn.hamster3.service.common.data.ServicePlayerInfo; +import cn.hamster3.service.common.entity.ServiceMessageInfo; +import cn.hamster3.service.common.entity.ServiceSenderInfo; +import cn.hamster3.service.common.util.ServiceLogUtils; +import cn.hamster3.service.velocity.BallBridgePlugin; +import cn.hamster3.service.velocity.api.ServiceInfoAPI; +import cn.hamster3.service.velocity.api.ServiceMessageAPI; +import cn.hamster3.service.velocity.event.MessageReceivedEvent; +import cn.hamster3.service.velocity.event.ServiceConnectEvent; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.DisconnectEvent; +import com.velocitypowered.api.event.connection.PostLoginEvent; +import com.velocitypowered.api.event.player.ServerConnectedEvent; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; + +import java.util.UUID; + +public class ServiceMainListener { + public static final ServiceMainListener INSTANCE = new ServiceMainListener(); + + private ServiceMainListener() { + } + + @Subscribe + public void onServiceConnect(ServiceConnectEvent event) { + if (!event.isSuccess()) { + return; + } + ServiceLogUtils.info("连接至服务中心成功..."); + JsonArray playerInfoArray = new JsonArray(); + for (Player player : BallBridgePlugin.getInstance().getProxyServer().getAllPlayers()) { + ServicePlayerInfo playerInfo = new ServicePlayerInfo( + player.getUniqueId(), + player.getUsername(), + player.getCurrentServer().map(o -> o.getServerInfo().getName()).orElse(""), + ServiceInfoAPI.getLocalServerName() + ); + playerInfoArray.add(playerInfo.saveToJson()); + } + ServiceMessageAPI.sendServiceMessage("HamsterService", "updatePlayerInfoArray", playerInfoArray); + } + + @Subscribe + public void onMessageReceived(MessageReceivedEvent event) { + ServiceMessageInfo info = event.getMessageInfo(); + if (!"HamsterService".equals(info.getTag())) { + return; + } + ProxyServer proxyServer = BallBridgePlugin.getInstance().getProxyServer(); + switch (info.getAction()) { + case "sendPlayerMessage": { + JsonObject object = info.getContentAsJsonObject(); + UUID uuid = UUID.fromString(object.get("uuid").getAsString()); + Player player = proxyServer.getPlayer(uuid).orElse(null); + if (player == null) { + return; + } + JsonElement message = object.get("message"); + player.sendMessage(GsonComponentSerializer.gson().deserializeFromTree(message)); + break; + } + case "broadcastMessage": { + Component component = GsonComponentSerializer.gson().deserializeFromTree(info.getContentAsJsonObject()); + proxyServer.getConsoleCommandSource().sendMessage(component); + break; + } + case "proxyConsoleCommand": { + proxyServer.getCommandManager().executeAsync(proxyServer.getConsoleCommandSource(), info.getContentAsString()); + break; + } + case "dispatchProxyCommand": { + JsonObject object = info.getContentAsJsonObject(); + UUID uuid = UUID.fromString(object.get("uuid").getAsString()); + Player player = proxyServer.getPlayer(uuid).orElse(null); + if (player == null) { + return; + } + String command = object.get("command").getAsString(); + proxyServer.getCommandManager().executeAsync(player, command); + break; + } + case "sendPlayerToLocation": { + JsonObject object = info.getContent().getAsJsonObject(); + UUID uuid = UUID.fromString(object.get("uuid").getAsString()); + Player player = proxyServer.getPlayer(uuid).orElse(null); + if (player == null) { + return; + } + ServiceLocation location = new ServiceLocation(object.getAsJsonObject("location")); + String serverName = player.getCurrentServer().map(o -> o.getServerInfo().getName()).orElse(""); + if (location.getServerName().equals(serverName)) { + return; + } + ServiceSenderInfo senderInfo = ServiceInfoAPI.getSenderInfo(location.getServerName()); + if (senderInfo == null) { + return; + } + RegisteredServer server = proxyServer.getServer(location.getServerName()).orElse(null); + player.createConnectionRequest(server).connectWithIndication(); + break; + } + case "kickPlayer": { + JsonObject object = info.getContentAsJsonObject(); + UUID uuid = UUID.fromString(object.get("uuid").getAsString()); + Player player = proxyServer.getPlayer(uuid).orElse(null); + if (player != null) { + player.disconnect(GsonComponentSerializer.gson().deserializeFromTree(object.get("reason"))); + return; + } + break; + } + } + } + + @Subscribe + public void onServerConnected(ServerConnectedEvent event) { + Player player = event.getPlayer(); + ServicePlayerInfo playerInfo = new ServicePlayerInfo( + player.getUniqueId(), + player.getUsername(), + event.getServer().getServerInfo().getName(), + ServiceInfoAPI.getLocalServerName() + ); + ServiceMessageAPI.sendServiceMessage("HamsterService", "updatePlayerInfo", playerInfo.saveToJson()); + } + + @Subscribe(priority = 100) + public void onPostLogin(PostLoginEvent event) { + Player player = event.getPlayer(); + ServicePlayerInfo playerInfo = new ServicePlayerInfo( + player.getUniqueId(), + player.getUsername(), + null, + ServiceInfoAPI.getLocalServerName(), + true + ); + ServiceMessageAPI.sendServiceMessage("HamsterService", "playerPostLogin", playerInfo.saveToJson()); + } + + @Subscribe + public void onPlayerDisconnect(DisconnectEvent event) { + Player player = event.getPlayer(); + ServicePlayerInfo playerInfo = new ServicePlayerInfo( + player.getUniqueId(), + player.getUsername(), + null, + ServiceInfoAPI.getLocalServerName(), + false + ); + ServiceMessageAPI.sendServiceMessage("HamsterService", "playerDisconnect", playerInfo.saveToJson()); + } + +} diff --git a/src/main/resources/bungee.yml b/src/main/resources/bungee.yml index 6bae94d..8159cff 100644 --- a/src/main/resources/bungee.yml +++ b/src/main/resources/bungee.yml @@ -11,7 +11,6 @@ UPDATE_CHECKER: CHECK_TYPE: GITEA_RELEASES GIT_BASE_URL: https://git.airgame.net GIT_REPO: MiniDay/hamster-ball-bridge - DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-ball-bridge/ depends: - HamsterBall diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index d81d96f..5054188 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -12,7 +12,6 @@ UPDATE_CHECKER: CHECK_TYPE: GITEA_RELEASES GIT_BASE_URL: https://git.airgame.net GIT_REPO: MiniDay/hamster-ball-bridge - DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-ball-bridge/ depend: - HamsterCore diff --git a/src/main/resources/update.yml b/src/main/resources/update.yml new file mode 100644 index 0000000..d875044 --- /dev/null +++ b/src/main/resources/update.yml @@ -0,0 +1,4 @@ +VERSION: ${version} +CHECK_TYPE: GITEA_RELEASES +GIT_BASE_URL: https://git.airgame.net +GIT_REPO: MiniDay/hamster-ball-bridge \ No newline at end of file diff --git a/src/main/templates/cn/hamster3/service/velocity/BuildConstants.java b/src/main/templates/cn/hamster3/service/velocity/BuildConstants.java new file mode 100644 index 0000000..ad27476 --- /dev/null +++ b/src/main/templates/cn/hamster3/service/velocity/BuildConstants.java @@ -0,0 +1,8 @@ +package cn.hamster3.service.velocity; + +// The constants are replaced before compilation +@SuppressWarnings("unused") +public class BuildConstants { + public static final String VERSION = "${version}"; + public static final String DESCRIPTION = "${description}"; +}