diff --git a/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/HamsterBallPlugin.java b/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/HamsterBallPlugin.java index 3229492..6905bad 100644 --- a/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/HamsterBallPlugin.java +++ b/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/HamsterBallPlugin.java @@ -3,6 +3,7 @@ package cn.hamster3.mc.plugin.ball.bukkit; import cn.hamster3.mc.plugin.ball.bukkit.api.BallBukkitAPI; import cn.hamster3.mc.plugin.ball.bukkit.hook.PlaceholderHook; import cn.hamster3.mc.plugin.ball.bukkit.listener.BallBukkitListener; +import cn.hamster3.mc.plugin.ball.bukkit.listener.UpdatePlayerInfoListener; import cn.hamster3.mc.plugin.ball.common.api.BallAPI; import cn.hamster3.mc.plugin.ball.common.event.BallActions; import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent; @@ -58,7 +59,13 @@ public class HamsterBallPlugin extends JavaPlugin { } Bukkit.getPluginManager().registerEvents(BallBukkitListener.INSTANCE, this); BallAPI.getInstance().getEventBus().register(BallBukkitListener.INSTANCE); - logger.info("已注册 BallBukkitListener"); + logger.info("已注册监听器 BallBukkitListener"); + if (BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) { + BallAPI.getInstance().subscribe(BallAPI.PLAYER_INFO_UPDATE_CHANNEL); + Bukkit.getPluginManager().registerEvents(UpdatePlayerInfoListener.INSTANCE, this); + BallAPI.getInstance().getEventBus().register(UpdatePlayerInfoListener.INSTANCE); + logger.info("已注册监听器 UpdatePlayerInfoListener"); + } sync(() -> { if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { PlaceholderHook.INSTANCE.register(); @@ -66,7 +73,7 @@ public class HamsterBallPlugin extends JavaPlugin { } BallAPI.getInstance().sendBallMessage( BallAPI.BALL_CHANNEL, BallActions.ServerOnline.name(), - new ServerOnlineEvent(BallAPI.getInstance().getLocalServerInfo()) + new ServerOnlineEvent(BallAPI.getInstance().getLocalServerInfo()), false ); }); long time = System.currentTimeMillis() - start; diff --git a/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/api/BallBukkitAPI.java b/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/api/BallBukkitAPI.java index 4c24ef9..5b1704a 100644 --- a/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/api/BallBukkitAPI.java +++ b/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/api/BallBukkitAPI.java @@ -3,6 +3,7 @@ package cn.hamster3.mc.plugin.ball.bukkit.api; import cn.hamster3.mc.plugin.ball.bukkit.HamsterBallPlugin; import cn.hamster3.mc.plugin.ball.bukkit.util.BallBukkitUtils; import cn.hamster3.mc.plugin.ball.common.api.BallAPI; +import cn.hamster3.mc.plugin.ball.common.config.BallConfig; import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo; import cn.hamster3.mc.plugin.ball.common.entity.BallServerType; import cn.hamster3.mc.plugin.core.common.api.CoreAPI; @@ -10,7 +11,6 @@ import io.lettuce.core.RedisClient; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.sql.DataSource; import java.sql.SQLException; @@ -18,8 +18,8 @@ import java.util.Map; import java.util.logging.Logger; public class BallBukkitAPI extends BallAPI { - public BallBukkitAPI(@NotNull BallServerInfo localServerInfo, @Nullable DataSource datasource, @NotNull RedisClient redisClient, boolean debug) { - super(localServerInfo, datasource, redisClient, debug); + public BallBukkitAPI(@NotNull BallConfig ballConfig) { + super(ballConfig); } public static BallBukkitAPI getInstance() { @@ -56,9 +56,15 @@ public class BallBukkitAPI extends BallAPI { datasource = CoreAPI.getInstance().getDataSource(); } - RedisClient redisClient = RedisClient.create(config.getString("redis-url", "redis://localhost:6379?clientName=HamsterBall")); - - instance = new BallBukkitAPI(serverInfo, datasource, redisClient, config.getBoolean("debug", false)); + BallConfig ballConfig = new BallConfig( + config.getBoolean("debug", false), + RedisClient.create(config.getString("redis-url")), + config.getString("channel-prefix", ""), + config.getBoolean("game-server-update-player-info", false), + serverInfo, + datasource + ); + instance = new BallBukkitAPI(ballConfig); } @Override diff --git a/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/listener/UpdatePlayerInfoListener.java b/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/listener/UpdatePlayerInfoListener.java new file mode 100644 index 0000000..bfc0133 --- /dev/null +++ b/ball-bukkit/src/main/java/cn/hamster3/mc/plugin/ball/bukkit/listener/UpdatePlayerInfoListener.java @@ -0,0 +1,85 @@ +package cn.hamster3.mc.plugin.ball.bukkit.listener; + +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.event.BallActions; +import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerInfoUpdateEvent; +import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerLoginEvent; +import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerLogoutEvent; +import cn.hamster3.mc.plugin.core.common.api.CoreAPI; +import com.google.common.eventbus.Subscribe; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.jetbrains.annotations.NotNull; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class UpdatePlayerInfoListener implements Listener { + public static final UpdatePlayerInfoListener INSTANCE = new UpdatePlayerInfoListener(); + + private final Map PLAYER_PROXY_SERVER = new ConcurrentHashMap<>(); + + private UpdatePlayerInfoListener() { + } + + public static void uploadPlayerInfo(@NotNull BallPlayerInfo playerInfo) { + CoreAPI.getInstance().getExecutorService().execute(() -> { + try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) { + try (PreparedStatement statement = connection.prepareStatement( + "REPLACE INTO `hamster_ball_player_info` VALUES(?, ?, ?, ?, ?);" + )) { + statement.setString(1, playerInfo.getUuid().toString()); + statement.setString(2, playerInfo.getName()); + statement.setString(3, playerInfo.getGameServer()); + statement.setString(4, playerInfo.getProxyServer()); + statement.setBoolean(5, playerInfo.isOnline()); + statement.executeUpdate(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + BallAPI.getInstance().sendBallMessage( + BallAPI.PLAYER_INFO_UPDATE_CHANNEL, + BallActions.BallPlayerInfoUpdate.name(), + new BallPlayerInfoUpdateEvent(playerInfo), + false + ); + }); + } + + @EventHandler(ignoreCancelled = true) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + BallPlayerInfo playerInfo = new BallPlayerInfo( + player.getUniqueId(), + player.getName(), + BallAPI.getInstance().getLocalServerId(), + PLAYER_PROXY_SERVER.getOrDefault(player.getUniqueId(), "unknown"), + true + ); + uploadPlayerInfo(playerInfo); + } + + @Subscribe + public void onBallPlayerLogin(BallPlayerLoginEvent event) { + BallPlayerInfo info = event.getPlayerInfo(); + PLAYER_PROXY_SERVER.put(info.getUuid(), info.getProxyServer()); + } + + @Subscribe + public void onBallPlayerLogout(BallPlayerLogoutEvent event) { + BallPlayerInfo playerInfo = BallAPI.getInstance().getPlayerInfo(event.getPlayerInfo().getUuid()); + if (!BallAPI.getInstance().isLocalServer(playerInfo.getGameServer())) { + return; + } + playerInfo.setOnline(false); + uploadPlayerInfo(playerInfo); + } +} diff --git a/ball-bukkit/src/main/resources/config.yml b/ball-bukkit/src/main/resources/config.yml index 39d28aa..e12947c 100644 --- a/ball-bukkit/src/main/resources/config.yml +++ b/ball-bukkit/src/main/resources/config.yml @@ -13,9 +13,22 @@ debug: false # 详细信息:https://github.com/lettuce-io/lettuce-core/wiki/Redis-URI-and-connection-details redis-url: "redis://localhost:6379?clientName=HamsterBall" +# 频道名前缀 +# 使用这个配置选项可以划分子服消息通信分组 +# 只有在同一个频道名的子服才能互相通信 +channel-prefix: "" + +# 是否在子服端更新玩家信息 +# 默认情况下,BC 统一管理玩家信息,包括记录 UUID 和玩家名称 +# 如果一个子服同时拥有多个 BC 入口 +# 且每个 BC 入口为不同的玩家名称分配不同的 UUID +# 则可以启用该功能以防止 UUID 紊乱的问题 +game-server-update-player-info: false + # 本服务器信息 server-info: # 服务器唯一识别码,最长 32 字符 + # 需要与 BC 端中 config.yml 里填写的服务器 ID 一致,否则插件跨服传送功能将失效 # 推荐格式:全小写英文+横杠+数字尾号 # 例如: # survival-1, survival-2(生存1区,生存2区) diff --git a/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/HamsterBallPlugin.java b/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/HamsterBallPlugin.java index fd81bcb..8e0b111 100644 --- a/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/HamsterBallPlugin.java +++ b/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/HamsterBallPlugin.java @@ -7,6 +7,7 @@ import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent; import cn.hamster3.mc.plugin.core.bungee.api.BallBungeeCordAPI; import cn.hamster3.mc.plugin.core.bungee.listener.BallBungeeListener; import cn.hamster3.mc.plugin.core.bungee.listener.BallBungeeListenerV2; +import cn.hamster3.mc.plugin.core.bungee.listener.UpdatePlayerInfoListener; import cn.hamster3.mc.plugin.core.bungee.util.BallBungeeCordUtils; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.plugin.Plugin; @@ -48,12 +49,17 @@ public class HamsterBallPlugin extends Plugin { e.printStackTrace(); } ProxyServer.getInstance().getPluginManager().registerListener(this, BallBungeeListener.INSTANCE); - logger.info("已注册 BallBungeeCordListener"); + logger.info("已注册监听器 BallBungeeListener"); BallAPI.getInstance().getEventBus().register(BallBungeeListenerV2.INSTANCE); - logger.info("已注册 BallBungeeListenerV2"); + logger.info("已注册监听器 BallBungeeListenerV2"); + if (!BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) { + ProxyServer.getInstance().getPluginManager().registerListener(this, UpdatePlayerInfoListener.INSTANCE); + logger.info("已注册监听器 UpdatePlayerInfoListener"); + } BallAPI.getInstance().sendBallMessage( BallAPI.BALL_CHANNEL, BallActions.ServerOnline.name(), - new ServerOnlineEvent(BallAPI.getInstance().getLocalServerInfo()) + new ServerOnlineEvent(BallAPI.getInstance().getLocalServerInfo()), + false ); // 移除失效的在线玩家 BallAPI.getInstance().getAllPlayerInfo().values() diff --git a/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/api/BallBungeeCordAPI.java b/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/api/BallBungeeCordAPI.java index 1811139..3dc362e 100644 --- a/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/api/BallBungeeCordAPI.java +++ b/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/api/BallBungeeCordAPI.java @@ -1,6 +1,7 @@ package cn.hamster3.mc.plugin.core.bungee.api; import cn.hamster3.mc.plugin.ball.common.api.BallAPI; +import cn.hamster3.mc.plugin.ball.common.config.BallConfig; import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo; import cn.hamster3.mc.plugin.ball.common.entity.BallServerType; import cn.hamster3.mc.plugin.core.bungee.HamsterBallPlugin; @@ -10,7 +11,6 @@ import cn.hamster3.mc.plugin.core.common.api.CoreAPI; import io.lettuce.core.RedisClient; import net.md_5.bungee.config.Configuration; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.sql.DataSource; import java.sql.SQLException; @@ -18,8 +18,8 @@ import java.util.Map; import java.util.logging.Logger; public class BallBungeeCordAPI extends BallAPI { - public BallBungeeCordAPI(@NotNull BallServerInfo localServerInfo, @Nullable DataSource datasource, @NotNull RedisClient redisClient, boolean debug) { - super(localServerInfo, datasource, redisClient, debug); + public BallBungeeCordAPI(@NotNull BallConfig ballConfig) { + super(ballConfig); } public static BallBungeeCordAPI getInstance() { @@ -53,9 +53,15 @@ public class BallBungeeCordAPI extends BallAPI { datasource = CoreAPI.getInstance().getDataSource(); } - RedisClient redisClient = RedisClient.create(config.getString("redis-url", "redis://localhost:6379?clientName=HamsterBall")); - - instance = new BallBungeeCordAPI(serverInfo, datasource, redisClient, config.getBoolean("debug", false)); + BallConfig ballConfig = new BallConfig( + config.getBoolean("debug", false), + RedisClient.create(config.getString("redis-url")), + config.getString("channel-prefix", ""), + config.getBoolean("game-server-update-player-info", false), + serverInfo, + datasource + ); + instance = new BallBungeeCordAPI(ballConfig); } @Override diff --git a/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/listener/BallBungeeListener.java b/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/listener/BallBungeeListener.java index 6c4bed3..18d5aff 100644 --- a/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/listener/BallBungeeListener.java +++ b/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/listener/BallBungeeListener.java @@ -3,11 +3,15 @@ package cn.hamster3.mc.plugin.core.bungee.listener; 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.event.BallActions; -import cn.hamster3.mc.plugin.ball.common.event.player.*; +import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerLoginEvent; +import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerPostLoginEvent; +import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerPreLoginEvent; import cn.hamster3.mc.plugin.core.bungee.util.BallBungeeCordUtils; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.*; +import net.md_5.bungee.api.event.LoginEvent; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.api.event.PreLoginEvent; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventPriority; @@ -21,8 +25,10 @@ public final class BallBungeeListener implements Listener { @EventHandler(priority = EventPriority.HIGH) public void onPreLogin(PreLoginEvent event) { BallAPI.getInstance().sendBallMessage( - BallAPI.BALL_CHANNEL, BallActions.BallPlayerPreLogin.name(), - new BallPlayerPreLoginEvent(event.getConnection().getName()) + BallAPI.BALL_CHANNEL, + BallActions.BallPlayerPreLogin.name(), + new BallPlayerPreLoginEvent(event.getConnection().getName()), + false ); } @@ -33,11 +39,12 @@ public final class BallBungeeListener implements Listener { } PendingConnection connection = event.getConnection(); BallAPI.getInstance().sendBallMessage( - BallAPI.BALL_CHANNEL, BallActions.BallPlayerLogin.name(), + BallAPI.BALL_CHANNEL, + BallActions.BallPlayerLogin.name(), new BallPlayerLoginEvent(new BallPlayerInfo( connection.getUniqueId(), connection.getName(), "connecting", BallAPI.getInstance().getLocalServerId(), true - )) + )), false ); } @@ -45,44 +52,11 @@ public final class BallBungeeListener implements Listener { public void onPostLogin(PostLoginEvent event) { ProxiedPlayer player = event.getPlayer(); BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true); - BallAPI.getInstance().sendBallMessage( - BallAPI.BALL_CHANNEL, BallActions.BallPlayerPostLogin.name(), - new BallPlayerPostLoginEvent(playerInfo) - ); - } - - @EventHandler(priority = EventPriority.HIGH) - public void onServerConnect(ServerConnectEvent event) { - ProxiedPlayer player = event.getPlayer(); - BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true); - playerInfo.setGameServer(event.getTarget().getName()); - BallAPI.getInstance().sendBallMessage( - BallAPI.BALL_CHANNEL, BallActions.BallPlayerPreConnectServer.name(), - new BallPlayerPreConnectServerEvent(playerInfo, playerInfo.getGameServer(), event.getTarget().getName()) - ); - BallBungeeCordUtils.uploadPlayerInfo(playerInfo); - } - - @EventHandler(priority = EventPriority.HIGH) - public void onServerConnected(ServerConnectedEvent event) { - ProxiedPlayer player = event.getPlayer(); - BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true); - playerInfo.setGameServer(event.getServer().getInfo().getName()); BallAPI.getInstance().sendBallMessage( BallAPI.BALL_CHANNEL, - BallActions.BallPlayerPostConnectServer.name(), - new BallPlayerPostConnectServerEvent(playerInfo) + BallActions.BallPlayerPostLogin.name(), + new BallPlayerPostLoginEvent(playerInfo), + false ); - BallBungeeCordUtils.uploadPlayerInfo(playerInfo); - } - - @EventHandler(priority = EventPriority.HIGH) - public void onPlayerDisconnect(PlayerDisconnectEvent event) { - ProxiedPlayer player = event.getPlayer(); - BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, false); - BallAPI.getInstance().sendBallMessage(BallAPI.BALL_CHANNEL, BallActions.BallPlayerLogout.name(), - new BallPlayerLogoutEvent(playerInfo) - ); - BallBungeeCordUtils.uploadPlayerInfo(playerInfo); } } diff --git a/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/listener/UpdatePlayerInfoListener.java b/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/listener/UpdatePlayerInfoListener.java new file mode 100644 index 0000000..24d6476 --- /dev/null +++ b/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/listener/UpdatePlayerInfoListener.java @@ -0,0 +1,64 @@ +package cn.hamster3.mc.plugin.core.bungee.listener; + +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.event.BallActions; +import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerLogoutEvent; +import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerPostConnectServerEvent; +import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerPreConnectServerEvent; +import cn.hamster3.mc.plugin.core.bungee.util.BallBungeeCordUtils; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.event.ServerConnectEvent; +import net.md_5.bungee.api.event.ServerConnectedEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.event.EventPriority; + +public class UpdatePlayerInfoListener implements Listener { + public static final UpdatePlayerInfoListener INSTANCE = new UpdatePlayerInfoListener(); + + private UpdatePlayerInfoListener() { + } + + @EventHandler(priority = EventPriority.HIGH) + public void onServerConnect(ServerConnectEvent event) { + ProxiedPlayer player = event.getPlayer(); + BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true); + playerInfo.setGameServer(event.getTarget().getName()); + BallAPI.getInstance().sendBallMessage( + BallAPI.BALL_CHANNEL, + BallActions.BallPlayerPreConnectServer.name(), + new BallPlayerPreConnectServerEvent(playerInfo, playerInfo.getGameServer(), event.getTarget().getName()), + false + ); + BallBungeeCordUtils.uploadPlayerInfo(playerInfo); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onServerConnected(ServerConnectedEvent event) { + ProxiedPlayer player = event.getPlayer(); + BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true); + playerInfo.setGameServer(event.getServer().getInfo().getName()); + BallAPI.getInstance().sendBallMessage( + BallAPI.BALL_CHANNEL, + BallActions.BallPlayerPostConnectServer.name(), + new BallPlayerPostConnectServerEvent(playerInfo), + false + ); + BallBungeeCordUtils.uploadPlayerInfo(playerInfo); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerDisconnect(PlayerDisconnectEvent event) { + ProxiedPlayer player = event.getPlayer(); + BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, false); + BallAPI.getInstance().sendBallMessage( + BallAPI.BALL_CHANNEL, + BallActions.BallPlayerLogout.name(), + new BallPlayerLogoutEvent(playerInfo), + false + ); + BallBungeeCordUtils.uploadPlayerInfo(playerInfo); + } +} diff --git a/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/util/BallBungeeCordUtils.java b/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/util/BallBungeeCordUtils.java index a075e89..00af80e 100644 --- a/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/util/BallBungeeCordUtils.java +++ b/ball-bungee/src/main/java/cn/hamster3/mc/plugin/core/bungee/util/BallBungeeCordUtils.java @@ -51,9 +51,10 @@ public final class BallBungeeCordUtils { e.printStackTrace(); } BallAPI.getInstance().sendBallMessage( - BallAPI.BALL_CHANNEL, + BallAPI.PLAYER_INFO_UPDATE_CHANNEL, BallActions.BallPlayerInfoUpdate.name(), - new BallPlayerInfoUpdateEvent(playerInfo) + new BallPlayerInfoUpdateEvent(playerInfo), + false ); }); } diff --git a/ball-bungee/src/main/resources/config.yml b/ball-bungee/src/main/resources/config.yml index 946ec3f..d5c1c91 100644 --- a/ball-bungee/src/main/resources/config.yml +++ b/ball-bungee/src/main/resources/config.yml @@ -13,6 +13,18 @@ debug: false # 详细信息:https://github.com/lettuce-io/lettuce-core/wiki/Redis-URI-and-connection-details redis-url: "redis://localhost:6379?clientName=HamsterBall" +# 频道名前缀 +# 使用这个配置选项可以划分子服消息通信分组 +# 只有在同一个频道名的子服才能互相通信 +channel-prefix: "" + +# 是否在子服端更新玩家信息 +# 默认情况下,BC 统一管理玩家信息,包括记录 UUID 和玩家名称 +# 如果一个子服同时拥有多个 BC 入口 +# 且每个 BC 入口为不同的玩家名称分配不同的 UUID +# 则可以启用该功能以防止 UUID 紊乱的问题 +game-server-update-player-info: false + # 本服务器信息 server-info: # 服务器唯一识别码,最长 32 字符 diff --git a/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/api/BallAPI.java b/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/api/BallAPI.java index 64bccec..3eed97b 100644 --- a/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/api/BallAPI.java +++ b/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/api/BallAPI.java @@ -1,5 +1,6 @@ package cn.hamster3.mc.plugin.ball.common.api; +import cn.hamster3.mc.plugin.ball.common.config.BallConfig; import cn.hamster3.mc.plugin.ball.common.data.BallLocation; import cn.hamster3.mc.plugin.ball.common.data.BallMessage; import cn.hamster3.mc.plugin.ball.common.entity.BallPlayerInfo; @@ -36,41 +37,39 @@ public abstract class BallAPI { * API 使用的通信频道 */ public static final String BALL_CHANNEL = "HamsterBall"; + /** + * API 使用的玩家信息更新通信频道 + */ + public static final String PLAYER_INFO_UPDATE_CHANNEL = "HamsterBall:PlayerInfo"; /** * API 实例 */ @Getter protected static BallAPI instance; @NotNull - protected final Map allServerInfo; + private final BallConfig ballConfig; @NotNull - protected final Map allPlayerInfo; + private final EventBus eventBus; @NotNull - private final BallServerInfo localServerInfo; - @Nullable - private final DataSource datasource; + private final Map allServerInfo; @NotNull - private final RedisClient redisClient; + private final Map allPlayerInfo; @NotNull private final StatefulRedisPubSubConnection subConnection; @NotNull private final StatefulRedisPubSubConnection pubConnection; - @NotNull - private final EventBus eventBus; - public BallAPI(@NotNull BallServerInfo localServerInfo, @Nullable DataSource datasource, @NotNull RedisClient redisClient, boolean debug) { - this.localServerInfo = localServerInfo; - this.datasource = datasource; - this.redisClient = redisClient; - subConnection = redisClient.connectPubSub(BallMessage.CODEC); - pubConnection = redisClient.connectPubSub(BallMessage.CODEC); + public BallAPI(@NotNull BallConfig ballConfig) { + this.ballConfig = ballConfig; + subConnection = ballConfig.getRedisClient().connectPubSub(BallMessage.CODEC); + pubConnection = ballConfig.getRedisClient().connectPubSub(BallMessage.CODEC); allServerInfo = new ConcurrentHashMap<>(); allPlayerInfo = new ConcurrentHashMap<>(); eventBus = new AsyncEventBus("HamsterBall - EventBus", CoreAPI.getInstance().getExecutorService()); eventBus.register(BallCommonListener.INSTANCE); - if (debug) { + if (ballConfig.isDebug()) { getLogger().warning("已启用调试模式"); - subConnection.addListener(BallDebugListener.INSTANCE); + eventBus.register(BallDebugListener.INSTANCE); } } @@ -141,11 +140,13 @@ public abstract class BallAPI { } } subConnection.addListener(BallRedisListener.INSTANCE); - subscribe(BALL_CHANNEL); + subConnection.sync().subscribe(BALL_CHANNEL); } protected void disable() throws SQLException, InterruptedException { - sendBallMessage(BALL_CHANNEL, new BallMessage(BallActions.ServerOffline.name(), new ServerOfflineEvent(localServerInfo)), true); + sendBallMessage(BallAPI.BALL_CHANNEL, new BallMessage( + BallActions.ServerOffline.name(), new ServerOfflineEvent(getLocalServerInfo()) + ), false, true); try (Connection connection = getDatasource().getConnection()) { try (PreparedStatement statement = connection.prepareStatement( @@ -163,7 +164,7 @@ public abstract class BallAPI { } } getLogger().info("正在关闭 redission"); - redisClient.close(); + ballConfig.getRedisClient().close(); getLogger().info("已关闭 redission"); } @@ -205,10 +206,8 @@ public abstract class BallAPI { sendBallMessage(BALL_CHANNEL, new BallMessage( getLocalServerId(), null, BallServerType.PROXY, BallActions.BroadcastPlayerMessage.name(), - CoreAPI.getInstance().getGson().toJsonTree( - new BroadcastPlayerMessageEvent(message) - ) - )); + CoreAPI.getInstance().getGson().toJsonTree(new BroadcastPlayerMessageEvent(message)) + ), false); } /** @@ -222,10 +221,9 @@ public abstract class BallAPI { sendBallMessage(BALL_CHANNEL, new BallMessage( getLocalServerId(), null, BallServerType.GAME, BallActions.DispatchConsoleCommand.name(), - CoreAPI.getInstance().getGson().toJsonTree( - new DispatchConsoleCommandEvent(type, serverID, command) - ) - )); + CoreAPI.getInstance().getGson().toJsonTree(new DispatchConsoleCommandEvent(type, serverID, command)) + ), false); + } /** @@ -239,10 +237,8 @@ public abstract class BallAPI { sendBallMessage(BALL_CHANNEL, new BallMessage( getLocalServerId(), null, BallServerType.GAME, BallActions.DispatchPlayerCommand.name(), - CoreAPI.getInstance().getGson().toJsonTree( - new DispatchPlayerCommandEvent(type, uuid, command) - ) - )); + CoreAPI.getInstance().getGson().toJsonTree(new DispatchPlayerCommandEvent(type, uuid, command)) + ), false); } /** @@ -265,10 +261,8 @@ public abstract class BallAPI { sendBallMessage(BALL_CHANNEL, new BallMessage( getLocalServerId(), null, BallServerType.PROXY, BallActions.KickPlayer.name(), - CoreAPI.getInstance().getGson().toJsonTree( - new KickPlayerEvent(uuid, reason) - ) - )); + CoreAPI.getInstance().getGson().toJsonTree(new KickPlayerEvent(uuid, reason)) + ), false); } /** @@ -312,10 +306,8 @@ public abstract class BallAPI { sendBallMessage(BALL_CHANNEL, new BallMessage( getLocalServerId(), null, BallServerType.PROXY, BallActions.SendMessageToPlayer.name(), - CoreAPI.getInstance().getGson().toJsonTree( - new SendMessageToPlayerEvent(new HashSet<>(receivers), message) - ) - )); + CoreAPI.getInstance().getGson().toJsonTree(new SendMessageToPlayerEvent(new HashSet<>(receivers), message)) + ), false); } /** @@ -346,7 +338,7 @@ public abstract class BallAPI { */ public void sendPlayerToLocation(@NotNull Collection sendPlayerUUID, @NotNull BallLocation location, @Nullable DisplayMessage doneMessage) { sendBallMessage(BALL_CHANNEL, BallActions.SendPlayerToLocation.name(), - new SendPlayerToLocationEvent(new HashSet<>(sendPlayerUUID), location, doneMessage) + new SendPlayerToLocationEvent(new HashSet<>(sendPlayerUUID), location, doneMessage), false ); } @@ -375,25 +367,39 @@ public abstract class BallAPI { * @param doneTargetMessage 传送完成后目标玩家显示的消息,自动将 %player_name% 替换成被传送者的名称 */ public void sendPlayerToPlayer(@NotNull Collection sendPlayerUUID, @NotNull UUID toPlayer, @Nullable DisplayMessage doneMessage, @Nullable DisplayMessage doneTargetMessage) { - sendBallMessage(BALL_CHANNEL, BallActions.SendPlayerToPlayer.name(), - new SendPlayerToPlayerEvent(new HashSet<>(sendPlayerUUID), toPlayer, doneMessage, doneTargetMessage) + sendBallMessage( + BALL_CHANNEL, + BallActions.SendPlayerToPlayer.name(), + new SendPlayerToPlayerEvent(new HashSet<>(sendPlayerUUID), toPlayer, doneMessage, doneTargetMessage), + false ); } /** * 发送一条服务消息 * - * @param channel 消息标签 + * @param channel 消息频道 * @param action 执行动作 */ public void sendBallMessage(@NotNull String channel, @NotNull String action) { sendBallMessage(channel, new BallMessage(action)); } + /** + * 发送一条服务消息 + * + * @param channel 消息频道 + * @param action 执行动作 + * @param prefix 是否自动为消息频道添加分组前缀 + */ + public void sendBallMessage(@NotNull String channel, @NotNull String action, boolean prefix) { + sendBallMessage(channel, new BallMessage(action), prefix); + } + /** * 发送一条有附加参数的服务消息 * - * @param channel 消息标签 + * @param channel 消息频道 * @param action 执行动作 * @param content 附加参数 */ @@ -401,37 +407,67 @@ public abstract class BallAPI { sendBallMessage(channel, new BallMessage(action, content)); } + /** + * 发送一条有附加参数的服务消息 + * + * @param channel 消息频道 + * @param action 执行动作 + * @param content 附加参数 + * @param prefix 是否自动为消息频道添加分组前缀 + */ + public void sendBallMessage(@NotNull String channel, @NotNull String action, @NotNull Object content, boolean prefix) { + sendBallMessage(channel, new BallMessage(action, content), prefix); + } + /** * 发送自定义消息 * * @param message 消息内容 */ public void sendBallMessage(@NotNull String channel, @NotNull BallMessage message) { - sendBallMessage(channel, message, false); + sendBallMessage(channel, message, true, false); + } + + /** + * 发送自定义消息 + * + * @param message 消息内容 + * @param prefix 是否自动为消息频道添加分组前缀 + */ + public void sendBallMessage(@NotNull String channel, @NotNull BallMessage message, boolean prefix) { + sendBallMessage(channel, message, prefix, false); } /** * 自定义服务消息信息并发送 * - * @param channel 消息标签 + * @param channel 消息频道 * @param message 消息内容 + * @param prefix 是否自动为消息频道添加分组前缀 * @param block 是否阻塞(设置为 true 则必须等待消息写入网络的操作完成后,该方法才会退出) */ - public void sendBallMessage(@NotNull String channel, @NotNull BallMessage message, boolean block) { + public void sendBallMessage(@NotNull String channel, @NotNull BallMessage message, boolean prefix, boolean block) { + if (prefix) { + channel = ballConfig.getChannelPrefix() + ":" + channel; + } if (block) { pubConnection.sync().publish(channel, message); eventBus.post(new MessageSentEvent(channel, message)); } else { + @NotNull String finalChannel = channel; pubConnection.async().publish(channel, message).whenComplete((aLong, throwable) -> { if (throwable != null) { return; } - eventBus.post(new MessageSentEvent(channel, message)); + eventBus.post(new MessageSentEvent(finalChannel, message)); }); } } public void subscribe(@NotNull String... channel) { + for (int i = 0; i < channel.length; i++) { + channel[i] = ballConfig.getChannelPrefix() + ":" + channel[i]; + } subConnection.sync().subscribe(channel); } @@ -440,6 +476,9 @@ public abstract class BallAPI { } public void unsubscribe(@NotNull String... channel) { + for (int i = 0; i < channel.length; i++) { + channel[i] = ballConfig.getChannelPrefix() + ":" + channel[i]; + } subConnection.sync().unsubscribe(channel); } @@ -454,12 +493,12 @@ public abstract class BallAPI { */ @NotNull public BallServerInfo getLocalServerInfo() { - return localServerInfo; + return ballConfig.getServerInfo(); } @NotNull public String getLocalServerId() { - return localServerInfo.getId(); + return ballConfig.getServerInfo().getId(); } /** @@ -579,6 +618,11 @@ public abstract class BallAPI { @NotNull public DataSource getDatasource() { - return datasource == null ? CoreAPI.getInstance().getDataSource() : datasource; + return ballConfig.getDatasource() == null ? CoreAPI.getInstance().getDataSource() : ballConfig.getDatasource(); + } + + @NotNull + public RedisClient getRedisClient() { + return ballConfig.getRedisClient(); } } diff --git a/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/config/BallConfig.java b/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/config/BallConfig.java index c6e08bc..1940e98 100644 --- a/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/config/BallConfig.java +++ b/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/config/BallConfig.java @@ -1,17 +1,25 @@ package cn.hamster3.mc.plugin.ball.common.config; import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo; +import io.lettuce.core.RedisClient; import lombok.AllArgsConstructor; -import lombok.Data; +import lombok.Getter; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -@Data +import javax.sql.DataSource; + +@Getter @AllArgsConstructor public class BallConfig { + private boolean debug; @NotNull - private BallServerInfo localInfo; + private RedisClient redisClient; @NotNull - private String host; - private int port; - private int eventLoopThread; + private String channelPrefix; + private boolean gameServerUpdatePlayerInfo; + @NotNull + private BallServerInfo serverInfo; + @Nullable + private DataSource datasource; } diff --git a/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/listener/BallDebugListener.java b/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/listener/BallDebugListener.java index 80b6d51..45cd33a 100644 --- a/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/listener/BallDebugListener.java +++ b/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/listener/BallDebugListener.java @@ -1,38 +1,23 @@ package cn.hamster3.mc.plugin.ball.common.listener; import cn.hamster3.mc.plugin.ball.common.api.BallAPI; -import cn.hamster3.mc.plugin.ball.common.data.BallMessage; -import io.lettuce.core.pubsub.RedisPubSubListener; +import cn.hamster3.mc.plugin.ball.common.event.message.MessageReceivedEvent; +import cn.hamster3.mc.plugin.ball.common.event.message.MessageSentEvent; +import com.google.common.eventbus.Subscribe; -public class BallDebugListener implements RedisPubSubListener { +public class BallDebugListener { public static final BallDebugListener INSTANCE = new BallDebugListener(); private BallDebugListener() { } - @Override - public void message(String channel, BallMessage event) { - BallAPI.getInstance().getLogger().info("从 " + channel + " 收到了一条消息: " + event); + @Subscribe + public void onMessageReceived(MessageReceivedEvent event) { + BallAPI.getInstance().getLogger().info("从 " + event.getChannel() + " 收到了一条消息: " + event.getMessage()); } - @Override - public void message(String pattern, String channel, BallMessage event) { - BallAPI.getInstance().getLogger().info("从 " + pattern + "(" + channel + ") 收到了一条消息: " + event); - } - - @Override - public void subscribed(String channel, long count) { - } - - @Override - public void psubscribed(String pattern, long count) { - } - - @Override - public void unsubscribed(String channel, long count) { - } - - @Override - public void punsubscribed(String pattern, long count) { + @Subscribe + public void onMessageSent(MessageSentEvent event) { + BallAPI.getInstance().getLogger().info("从 " + event.getChannel() + " 发送了了一条消息: " + event.getMessage()); } } diff --git a/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/listener/BallRedisListener.java b/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/listener/BallRedisListener.java index 66e806b..4ccfae0 100644 --- a/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/listener/BallRedisListener.java +++ b/ball-common/src/main/java/cn/hamster3/mc/plugin/ball/common/listener/BallRedisListener.java @@ -129,21 +129,21 @@ public class BallRedisListener implements RedisPubSubListener