fix: 修复了新版玩家信息加载和跨服同步的问题

This commit is contained in:
2024-02-26 00:21:14 +08:00
parent 5af164ed1a
commit 419229ff15
14 changed files with 326 additions and 317 deletions

View File

@@ -51,9 +51,9 @@ repositories {
dependencies { dependencies {
// 对于 Bukkit 插件 // 对于 Bukkit 插件
compileOnly("cn.hamster3.mc.plugin:ball-bukkit:1.5.2") compileOnly("cn.hamster3.mc.plugin:ball-bukkit:1.5.3")
// 对于 BungeeCord 插件 // 对于 BungeeCord 插件
compileOnly("cn.hamster3.mc.plugin:ball-bungee:1.5.2") compileOnly("cn.hamster3.mc.plugin:ball-bungee:1.5.3")
} }
``` ```
@@ -79,13 +79,13 @@ dependencies {
<dependency> <dependency>
<groupId>cn.hamster3.mc.plugin</groupId> <groupId>cn.hamster3.mc.plugin</groupId>
<artifactId>ball-bukkit</artifactId> <artifactId>ball-bukkit</artifactId>
<version>1.5.2</version> <version>1.5.3</version>
</dependency> </dependency>
<!--对于 BungeeCord 插件--> <!--对于 BungeeCord 插件-->
<dependency> <dependency>
<groupId>cn.hamster3.mc.plugin</groupId> <groupId>cn.hamster3.mc.plugin</groupId>
<artifactId>ball-bungee</artifactId> <artifactId>ball-bungee</artifactId>
<version>1.5.2</version> <version>1.5.3</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -62,7 +62,7 @@ public class HamsterBallPlugin extends JavaPlugin {
BallAPI.getInstance().getEventBus().register(BallBukkitListener.INSTANCE); BallAPI.getInstance().getEventBus().register(BallBukkitListener.INSTANCE);
logger.info("已注册监听器 BallBukkitListener"); logger.info("已注册监听器 BallBukkitListener");
if (BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) { if (BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) {
BallAPI.getInstance().subscribe(BallAPI.PLAYER_INFO_UPDATE_CHANNEL); BallAPI.getInstance().subscribe(BallAPI.PLAYER_INFO_CHANNEL);
Bukkit.getPluginManager().registerEvents(UpdatePlayerInfoListener.INSTANCE, this); Bukkit.getPluginManager().registerEvents(UpdatePlayerInfoListener.INSTANCE, this);
BallAPI.getInstance().getEventBus().register(UpdatePlayerInfoListener.INSTANCE); BallAPI.getInstance().getEventBus().register(UpdatePlayerInfoListener.INSTANCE);
logger.info("已注册监听器 UpdatePlayerInfoListener"); logger.info("已注册监听器 UpdatePlayerInfoListener");
@@ -76,15 +76,17 @@ public class HamsterBallPlugin extends JavaPlugin {
playerInfo.setOnline(false); playerInfo.setOnline(false);
BallBukkitUtils.uploadPlayerInfo(playerInfo); BallBukkitUtils.uploadPlayerInfo(playerInfo);
}); });
} else {
BallAPI.getInstance().subscribeIgnorePrefix(BallAPI.PLAYER_INFO_CHANNEL);
} }
sync(() -> { sync(() -> {
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
PlaceholderHook.INSTANCE.register(); PlaceholderHook.INSTANCE.register();
logger.info("已挂载 PlaceholderAPI 变量"); logger.info("已挂载 PlaceholderAPI 变量");
} }
BallAPI.getInstance().sendBallMessage( BallAPI.getInstance().sendRawBallMessage(
BallAPI.BALL_CHANNEL, BallActions.ServerOnline.name(), BallAPI.BALL_CHANNEL, BallActions.ServerOnline.name(),
new ServerOnlineEvent(BallAPI.getInstance().getLocalServerInfo()), false new ServerOnlineEvent(BallAPI.getInstance().getLocalServerInfo())
); );
}); });
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;

View File

@@ -26,7 +26,7 @@ public class UpdatePlayerInfoListener implements Listener {
@Subscribe @Subscribe
public void onBallPlayerLogin(BallPlayerLoginEvent event) { public void onBallPlayerLogin(BallPlayerLoginEvent event) {
BallPlayerInfo info = event.getPlayerInfo(); BallPlayerInfo info = event.getPlayerInfo();
PLAYER_PROXY_SERVER.put(info.getUuid(), BallAPI.getInstance().getBallConfig().getChannelPrefix() + ":" + info.getProxyServer()); PLAYER_PROXY_SERVER.put(info.getUuid(), info.getProxyServer());
} }
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)

View File

@@ -16,6 +16,7 @@ import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
@SuppressWarnings("CallToPrintStackTrace")
public final class BallBukkitUtils { public final class BallBukkitUtils {
private BallBukkitUtils() { private BallBukkitUtils() {
} }
@@ -65,7 +66,7 @@ public final class BallBukkitUtils {
e.printStackTrace(); e.printStackTrace();
} }
BallAPI.getInstance().sendBallMessage( BallAPI.getInstance().sendBallMessage(
BallAPI.PLAYER_INFO_UPDATE_CHANNEL, BallAPI.PLAYER_INFO_CHANNEL,
BallActions.BallPlayerInfoUpdate.name(), BallActions.BallPlayerInfoUpdate.name(),
new BallPlayerInfoUpdateEvent(playerInfo) new BallPlayerInfoUpdateEvent(playerInfo)
); );

View File

@@ -2,25 +2,24 @@ package cn.hamster3.mc.plugin.ball.bungee;
import cn.hamster3.mc.plugin.ball.bungee.api.BallBungeeCordAPI; import cn.hamster3.mc.plugin.ball.bungee.api.BallBungeeCordAPI;
import cn.hamster3.mc.plugin.ball.bungee.listener.BallBungeeListener; import cn.hamster3.mc.plugin.ball.bungee.listener.BallBungeeListener;
import cn.hamster3.mc.plugin.ball.bungee.listener.BallBungeeListenerV2; import cn.hamster3.mc.plugin.ball.bungee.listener.BallBungeeMainListener;
import cn.hamster3.mc.plugin.ball.bungee.listener.UpdatePlayerInfoListener; import cn.hamster3.mc.plugin.ball.bungee.listener.UpdatePlayerInfoListener;
import cn.hamster3.mc.plugin.ball.bungee.util.BallBungeeCordUtils; import cn.hamster3.mc.plugin.ball.bungee.util.BallBungeeCordUtils;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI; 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.BallPlayerInfo;
import cn.hamster3.mc.plugin.ball.common.event.BallActions; import cn.hamster3.mc.plugin.ball.common.event.BallActions;
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent; import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent;
import lombok.Getter;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import java.util.logging.Logger; import java.util.logging.Logger;
@SuppressWarnings("CallToPrintStackTrace")
public class HamsterBallPlugin extends Plugin { public class HamsterBallPlugin extends Plugin {
@Getter
private static HamsterBallPlugin instance; private static HamsterBallPlugin instance;
public static HamsterBallPlugin getInstance() {
return instance;
}
@Override @Override
public void onLoad() { public void onLoad() {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
@@ -48,16 +47,21 @@ public class HamsterBallPlugin extends Plugin {
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
ProxyServer.getInstance().getPluginManager().registerListener(this, BallBungeeListener.INSTANCE); BallAPI.getInstance().getEventBus().register(BallBungeeListener.INSTANCE);
logger.info("已注册监听器 BallBungeeListener"); logger.info("已注册监听器 BallBungeeListener");
BallAPI.getInstance().getEventBus().register(BallBungeeListenerV2.INSTANCE); ProxyServer.getInstance().getPluginManager().registerListener(this, BallBungeeMainListener.INSTANCE);
logger.info("已注册监听器 BallBungeeListenerV2"); logger.info("已注册监听器 BallBungeeMainListener");
ProxyServer.getInstance().getPluginManager().registerListener(this, UpdatePlayerInfoListener.INSTANCE); ProxyServer.getInstance().getPluginManager().registerListener(this, UpdatePlayerInfoListener.INSTANCE);
logger.info("已注册监听器 UpdatePlayerInfoListener"); logger.info("已注册监听器 UpdatePlayerInfoListener");
BallAPI.getInstance().sendBallMessage(
if (BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) {
BallAPI.getInstance().subscribePatterns("*" + BallAPI.PLAYER_INFO_CHANNEL);
} else {
BallAPI.getInstance().subscribeIgnorePrefix(BallAPI.PLAYER_INFO_CHANNEL);
}
BallAPI.getInstance().sendRawBallMessage(
BallAPI.BALL_CHANNEL, BallActions.ServerOnline.name(), BallAPI.BALL_CHANNEL, BallActions.ServerOnline.name(),
new ServerOnlineEvent(BallAPI.getInstance().getLocalServerInfo()), new ServerOnlineEvent(BallAPI.getInstance().getLocalServerInfo())
false
); );
// 移除失效的在线玩家 // 移除失效的在线玩家
BallAPI.getInstance().getAllPlayerInfo().values() BallAPI.getInstance().getAllPlayerInfo().values()
@@ -68,7 +72,6 @@ public class HamsterBallPlugin extends Plugin {
playerInfo.setOnline(false); playerInfo.setOnline(false);
BallBungeeCordUtils.uploadPlayerInfo(playerInfo); BallBungeeCordUtils.uploadPlayerInfo(playerInfo);
}); });
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
logger.info("仓鼠球启动完成,总计耗时 " + time + " ms"); logger.info("仓鼠球启动完成,总计耗时 " + time + " ms");
} }

View File

@@ -1,62 +1,107 @@
package cn.hamster3.mc.plugin.ball.bungee.listener; package cn.hamster3.mc.plugin.ball.bungee.listener;
import cn.hamster3.mc.plugin.ball.bungee.util.BallBungeeCordUtils; import cn.hamster3.mc.plugin.ball.bungee.HamsterBallPlugin;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI; 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.BallServerType;
import cn.hamster3.mc.plugin.ball.common.event.BallActions; import cn.hamster3.mc.plugin.ball.common.event.operate.*;
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerLoginEvent; import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerPostLoginEvent; import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.audience.Audience;
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerPreLoginEvent; import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.connection.PendingConnection; import com.google.common.eventbus.Subscribe;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
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;
public final class BallBungeeListener implements Listener { import java.util.UUID;
public class BallBungeeListener {
public static final BallBungeeListener INSTANCE = new BallBungeeListener(); public static final BallBungeeListener INSTANCE = new BallBungeeListener();
private BallBungeeListener() { private BallBungeeListener() {
} }
@EventHandler(priority = EventPriority.HIGH) @Subscribe
public void onPreLogin(PreLoginEvent event) { public void onDispatchConsoleCommand(DispatchConsoleCommandEvent event) {
BallAPI.getInstance().sendBallMessage( if (event.getType() != null && event.getType() != BallServerType.PROXY) {
BallAPI.BALL_CHANNEL,
BallActions.BallPlayerPreLogin.name(),
new BallPlayerPreLoginEvent(event.getConnection().getName()),
false
);
}
@EventHandler(priority = EventPriority.HIGH)
public void onLogin(LoginEvent event) {
if (event.isCancelled()) {
return; return;
} }
PendingConnection connection = event.getConnection(); if (event.getServerID() != null && !BallAPI.getInstance().isLocalServer(event.getServerID())) {
BallAPI.getInstance().sendBallMessage( return;
BallAPI.BALL_CHANNEL, }
BallActions.BallPlayerLogin.name(), ProxyServer server = ProxyServer.getInstance();
new BallPlayerLoginEvent(new BallPlayerInfo( server.getPluginManager().dispatchCommand(server.getConsole(), event.getCommand());
connection.getUniqueId(), connection.getName(), "connecting",
BallAPI.getInstance().getLocalServerId(), true
)), false
);
} }
@EventHandler(priority = EventPriority.HIGH) @Subscribe
public void onPostLogin(PostLoginEvent event) { public void onDispatchPlayerCommandEvent(DispatchPlayerCommandEvent event) {
ProxiedPlayer player = event.getPlayer(); if (event.getType() != null && event.getType() != BallServerType.GAME) {
BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true); return;
BallAPI.getInstance().sendBallMessage( }
BallAPI.BALL_CHANNEL, ProxyServer server = ProxyServer.getInstance();
BallActions.BallPlayerPostLogin.name(), if (event.getUuid() != null) {
new BallPlayerPostLoginEvent(playerInfo), ProxiedPlayer player = server.getPlayer(event.getUuid());
false if (player == null) {
); return;
}
server.getPluginManager().dispatchCommand(player, event.getCommand());
return;
}
for (ProxiedPlayer player : server.getPlayers()) {
server.getPluginManager().dispatchCommand(player, event.getCommand());
}
}
@Subscribe
public void onKickPlayerEvent(KickPlayerEvent event) {
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(event.getUuid());
BaseComponent[] components = BungeeComponentSerializer.get().serialize(event.getReason());
player.disconnect(components);
}
@Subscribe
public void onSendMessageToPlayerEvent(SendMessageToPlayerEvent event) {
for (UUID receiver : event.getReceivers()) {
Audience audience = CoreAPI.getInstance().getAudienceProvider().player(receiver);
event.getMessage().show(audience);
}
}
@Subscribe
public void onSendPlayerToLocationEvent(SendPlayerToLocationEvent event) {
String serverID = event.getLocation().getServerID();
ServerInfo serverInfo = ProxyServer.getInstance().getServerInfo(serverID);
if (serverInfo == null) {
HamsterBallPlugin.getInstance().getLogger().warning("试图传送玩家时失败: 服务器 " + serverID + " 不在线");
return;
}
for (UUID uuid : event.getSendPlayerUUID()) {
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid);
if (player == null) {
continue;
}
if (player.getServer().getInfo().getName().equals(serverID)) {
continue;
}
player.connect(serverInfo);
}
}
@Subscribe
public void onSendPlayerToPlayerEvent(SendPlayerToPlayerEvent event) {
UUID toPlayerUUID = event.getToPlayerUUID();
ProxiedPlayer toPlayer = ProxyServer.getInstance().getPlayer(toPlayerUUID);
if (toPlayer == null) {
HamsterBallPlugin.getInstance().getLogger().warning("试图传送玩家时失败: 目标玩家 " + toPlayerUUID + " 不在线");
return;
}
ServerInfo toServer = toPlayer.getServer().getInfo();
for (UUID uuid : event.getSendPlayerUUID()) {
ProxiedPlayer sendPlayer = ProxyServer.getInstance().getPlayer(uuid);
if (sendPlayer.getServer().getInfo().getName().equals(toServer.getName())) {
continue;
}
sendPlayer.connect(toServer);
}
} }
} }

View File

@@ -1,107 +0,0 @@
package cn.hamster3.mc.plugin.ball.bungee.listener;
import cn.hamster3.mc.plugin.ball.bungee.HamsterBallPlugin;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
import cn.hamster3.mc.plugin.ball.common.event.operate.*;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.audience.Audience;
import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import com.google.common.eventbus.Subscribe;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.UUID;
public class BallBungeeListenerV2 {
public static final BallBungeeListenerV2 INSTANCE = new BallBungeeListenerV2();
private BallBungeeListenerV2() {
}
@Subscribe
public void onDispatchConsoleCommand(DispatchConsoleCommandEvent event) {
if (event.getType() != null && event.getType() != BallServerType.PROXY) {
return;
}
if (event.getServerID() != null && !BallAPI.getInstance().isLocalServer(event.getServerID())) {
return;
}
ProxyServer server = ProxyServer.getInstance();
server.getPluginManager().dispatchCommand(server.getConsole(), event.getCommand());
}
@Subscribe
public void onDispatchPlayerCommandEvent(DispatchPlayerCommandEvent event) {
if (event.getType() != null && event.getType() != BallServerType.GAME) {
return;
}
ProxyServer server = ProxyServer.getInstance();
if (event.getUuid() != null) {
ProxiedPlayer player = server.getPlayer(event.getUuid());
if (player == null) {
return;
}
server.getPluginManager().dispatchCommand(player, event.getCommand());
return;
}
for (ProxiedPlayer player : server.getPlayers()) {
server.getPluginManager().dispatchCommand(player, event.getCommand());
}
}
@Subscribe
public void onKickPlayerEvent(KickPlayerEvent event) {
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(event.getUuid());
BaseComponent[] components = BungeeComponentSerializer.get().serialize(event.getReason());
player.disconnect(components);
}
@Subscribe
public void onSendMessageToPlayerEvent(SendMessageToPlayerEvent event) {
for (UUID receiver : event.getReceivers()) {
Audience audience = CoreAPI.getInstance().getAudienceProvider().player(receiver);
event.getMessage().show(audience);
}
}
@Subscribe
public void onSendPlayerToLocationEvent(SendPlayerToLocationEvent event) {
String serverID = event.getLocation().getServerID();
ServerInfo serverInfo = ProxyServer.getInstance().getServerInfo(serverID);
if (serverInfo == null) {
HamsterBallPlugin.getInstance().getLogger().warning("试图传送玩家时失败: 服务器 " + serverID + " 不在线");
return;
}
for (UUID uuid : event.getSendPlayerUUID()) {
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid);
if (player == null) {
continue;
}
if (player.getServer().getInfo().getName().equals(serverID)) {
continue;
}
player.connect(serverInfo);
}
}
@Subscribe
public void onSendPlayerToPlayerEvent(SendPlayerToPlayerEvent event) {
UUID toPlayerUUID = event.getToPlayerUUID();
ProxiedPlayer toPlayer = ProxyServer.getInstance().getPlayer(toPlayerUUID);
if (toPlayer == null) {
HamsterBallPlugin.getInstance().getLogger().warning("试图传送玩家时失败: 目标玩家 " + toPlayerUUID + " 不在线");
return;
}
ServerInfo toServer = toPlayer.getServer().getInfo();
for (UUID uuid : event.getSendPlayerUUID()) {
ProxiedPlayer sendPlayer = ProxyServer.getInstance().getPlayer(uuid);
if (sendPlayer.getServer().getInfo().getName().equals(toServer.getName())) {
continue;
}
sendPlayer.connect(toServer);
}
}
}

View File

@@ -0,0 +1,60 @@
package cn.hamster3.mc.plugin.ball.bungee.listener;
import cn.hamster3.mc.plugin.ball.bungee.util.BallBungeeCordUtils;
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.BallPlayerLoginEvent;
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerPostLoginEvent;
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerPreLoginEvent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
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;
public final class BallBungeeMainListener implements Listener {
public static final BallBungeeMainListener INSTANCE = new BallBungeeMainListener();
private BallBungeeMainListener() {
}
@EventHandler(priority = EventPriority.HIGH)
public void onPreLogin(PreLoginEvent event) {
BallAPI.getInstance().sendRawBallMessage(
BallAPI.BALL_CHANNEL,
BallActions.BallPlayerPreLogin.name(),
new BallPlayerPreLoginEvent(event.getConnection().getName())
);
}
@EventHandler(priority = EventPriority.HIGH)
public void onLogin(LoginEvent event) {
if (event.isCancelled()) {
return;
}
PendingConnection connection = event.getConnection();
BallAPI.getInstance().sendRawBallMessage(
BallAPI.BALL_CHANNEL,
BallActions.BallPlayerLogin.name(),
new BallPlayerLoginEvent(new BallPlayerInfo(
connection.getUniqueId(), connection.getName(), "connecting",
BallAPI.getInstance().getLocalServerId(), true
))
);
}
@EventHandler(priority = EventPriority.HIGH)
public void onPostLogin(PostLoginEvent event) {
ProxiedPlayer player = event.getPlayer();
BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true);
BallAPI.getInstance().sendRawBallMessage(
BallAPI.BALL_CHANNEL,
BallActions.BallPlayerPostLogin.name(),
new BallPlayerPostLoginEvent(playerInfo)
);
}
}

View File

@@ -26,11 +26,10 @@ public class UpdatePlayerInfoListener implements Listener {
ProxiedPlayer player = event.getPlayer(); ProxiedPlayer player = event.getPlayer();
BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true); BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true);
playerInfo.setGameServer(event.getTarget().getName()); playerInfo.setGameServer(event.getTarget().getName());
BallAPI.getInstance().sendBallMessage( BallAPI.getInstance().sendRawBallMessage(
BallAPI.BALL_CHANNEL, BallAPI.BALL_CHANNEL,
BallActions.BallPlayerPreConnectServer.name(), BallActions.BallPlayerPreConnectServer.name(),
new BallPlayerPreConnectServerEvent(playerInfo, playerInfo.getGameServer(), event.getTarget().getName()), new BallPlayerPreConnectServerEvent(playerInfo, playerInfo.getGameServer(), event.getTarget().getName())
false
); );
BallBungeeCordUtils.uploadPlayerInfo(playerInfo); BallBungeeCordUtils.uploadPlayerInfo(playerInfo);
} }
@@ -40,11 +39,10 @@ public class UpdatePlayerInfoListener implements Listener {
ProxiedPlayer player = event.getPlayer(); ProxiedPlayer player = event.getPlayer();
BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true); BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, true);
playerInfo.setGameServer(event.getServer().getInfo().getName()); playerInfo.setGameServer(event.getServer().getInfo().getName());
BallAPI.getInstance().sendBallMessage( BallAPI.getInstance().sendRawBallMessage(
BallAPI.BALL_CHANNEL, BallAPI.BALL_CHANNEL,
BallActions.BallPlayerPostConnectServer.name(), BallActions.BallPlayerPostConnectServer.name(),
new BallPlayerPostConnectServerEvent(playerInfo), new BallPlayerPostConnectServerEvent(playerInfo)
false
); );
BallBungeeCordUtils.uploadPlayerInfo(playerInfo); BallBungeeCordUtils.uploadPlayerInfo(playerInfo);
} }
@@ -53,11 +51,10 @@ public class UpdatePlayerInfoListener implements Listener {
public void onPlayerDisconnect(PlayerDisconnectEvent event) { public void onPlayerDisconnect(PlayerDisconnectEvent event) {
ProxiedPlayer player = event.getPlayer(); ProxiedPlayer player = event.getPlayer();
BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, false); BallPlayerInfo playerInfo = BallBungeeCordUtils.getPlayerInfo(player, false);
BallAPI.getInstance().sendBallMessage( BallAPI.getInstance().sendRawBallMessage(
BallAPI.BALL_CHANNEL, BallAPI.BALL_CHANNEL,
BallActions.BallPlayerLogout.name(), BallActions.BallPlayerLogout.name(),
new BallPlayerLogoutEvent(playerInfo), new BallPlayerLogoutEvent(playerInfo)
false
); );
BallBungeeCordUtils.uploadPlayerInfo(playerInfo); BallBungeeCordUtils.uploadPlayerInfo(playerInfo);
} }

View File

@@ -18,6 +18,7 @@ import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
@SuppressWarnings("CallToPrintStackTrace")
public final class BallBungeeCordUtils { public final class BallBungeeCordUtils {
private BallBungeeCordUtils() { private BallBungeeCordUtils() {
} }
@@ -51,8 +52,8 @@ public final class BallBungeeCordUtils {
e.printStackTrace(); e.printStackTrace();
} }
if (!BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) { if (!BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) {
BallAPI.getInstance().sendBallMessage( BallAPI.getInstance().sendRawBallMessage(
BallAPI.PLAYER_INFO_UPDATE_CHANNEL, BallAPI.PLAYER_INFO_CHANNEL,
BallActions.BallPlayerInfoUpdate.name(), BallActions.BallPlayerInfoUpdate.name(),
new BallPlayerInfoUpdateEvent(playerInfo) new BallPlayerInfoUpdateEvent(playerInfo)
); );

View File

@@ -30,7 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger; import java.util.logging.Logger;
@Getter @Getter
@SuppressWarnings("unused") @SuppressWarnings({"unused", "CallToPrintStackTrace"})
public abstract class BallAPI { public abstract class BallAPI {
/** /**
* API 使用的通信频道 * API 使用的通信频道
@@ -39,7 +39,7 @@ public abstract class BallAPI {
/** /**
* API 使用的玩家信息更新通信频道 * API 使用的玩家信息更新通信频道
*/ */
public static final String PLAYER_INFO_UPDATE_CHANNEL = "HamsterBall:PlayerInfo"; public static final String PLAYER_INFO_CHANNEL = "HamsterBall:PlayerInfo";
/** /**
* API 实例 * API 实例
*/ */
@@ -354,8 +354,8 @@ public abstract class BallAPI {
* @param doneMessage 传送完成后显示的消息 * @param doneMessage 传送完成后显示的消息
*/ */
public void sendPlayerToLocation(@NotNull Collection<UUID> sendPlayerUUID, @NotNull BallLocation location, @Nullable DisplayMessage doneMessage) { public void sendPlayerToLocation(@NotNull Collection<UUID> sendPlayerUUID, @NotNull BallLocation location, @Nullable DisplayMessage doneMessage) {
sendBallMessage(BALL_CHANNEL, BallActions.SendPlayerToLocation.name(), sendRawBallMessage(BALL_CHANNEL, BallActions.SendPlayerToLocation.name(),
new SendPlayerToLocationEvent(new HashSet<>(sendPlayerUUID), location, doneMessage), false new SendPlayerToLocationEvent(new HashSet<>(sendPlayerUUID), location, doneMessage)
); );
} }
@@ -384,11 +384,10 @@ public abstract class BallAPI {
* @param doneTargetMessage 传送完成后目标玩家显示的消息,自动将 %player_name% 替换成被传送者的名称 * @param doneTargetMessage 传送完成后目标玩家显示的消息,自动将 %player_name% 替换成被传送者的名称
*/ */
public void sendPlayerToPlayer(@NotNull Collection<UUID> sendPlayerUUID, @NotNull UUID toPlayer, @Nullable DisplayMessage doneMessage, @Nullable DisplayMessage doneTargetMessage) { public void sendPlayerToPlayer(@NotNull Collection<UUID> sendPlayerUUID, @NotNull UUID toPlayer, @Nullable DisplayMessage doneMessage, @Nullable DisplayMessage doneTargetMessage) {
sendBallMessage( sendRawBallMessage(
BALL_CHANNEL, BALL_CHANNEL,
BallActions.SendPlayerToPlayer.name(), BallActions.SendPlayerToPlayer.name(),
new SendPlayerToPlayerEvent(new HashSet<>(sendPlayerUUID), toPlayer, doneMessage, doneTargetMessage), new SendPlayerToPlayerEvent(new HashSet<>(sendPlayerUUID), toPlayer, doneMessage, doneTargetMessage)
false
); );
} }
@@ -403,14 +402,13 @@ public abstract class BallAPI {
} }
/** /**
* 发送一条服务消息 * 发送一条服务消息,不自动添加频道前缀
* *
* @param channel 消息频道 * @param channel 消息频道
* @param action 执行动作 * @param action 执行动作
* @param prefix 是否自动为消息频道添加分组前缀
*/ */
public void sendBallMessage(@NotNull String channel, @NotNull String action, boolean prefix) { public void sendRawBallMessage(@NotNull String channel, @NotNull String action) {
sendBallMessage(channel, new BallMessage(action), prefix); sendBallMessage(channel, new BallMessage(action), false);
} }
/** /**
@@ -425,15 +423,14 @@ public abstract class BallAPI {
} }
/** /**
* 发送一条有附加参数的服务消息 * 发送一条有附加参数的服务消息,不自动添加频道前缀
* *
* @param channel 消息频道 * @param channel 消息频道
* @param action 执行动作 * @param action 执行动作
* @param content 附加参数 * @param content 附加参数
* @param prefix 是否自动为消息频道添加分组前缀
*/ */
public void sendBallMessage(@NotNull String channel, @NotNull String action, @NotNull Object content, boolean prefix) { public void sendRawBallMessage(@NotNull String channel, @NotNull String action, @NotNull Object content) {
sendBallMessage(channel, new BallMessage(action, content), prefix); sendBallMessage(channel, new BallMessage(action, content), false);
} }
/** /**
@@ -449,10 +446,10 @@ public abstract class BallAPI {
* 发送自定义消息 * 发送自定义消息
* *
* @param message 消息内容 * @param message 消息内容
* @param prefix 是否自动为消息频道添加分组前缀 * @param addPrefix 是否自动为消息频道添加分组前缀
*/ */
public void sendBallMessage(@NotNull String channel, @NotNull BallMessage message, boolean prefix) { public void sendBallMessage(@NotNull String channel, @NotNull BallMessage message, boolean addPrefix) {
sendBallMessage(channel, message, prefix, false); sendBallMessage(channel, message, addPrefix, false);
} }
/** /**
@@ -460,11 +457,11 @@ public abstract class BallAPI {
* *
* @param channel 消息频道 * @param channel 消息频道
* @param message 消息内容 * @param message 消息内容
* @param prefix 是否自动为消息频道添加分组前缀 * @param addPrefix 是否自动为消息频道添加分组前缀
* @param block 是否阻塞(设置为 true 则必须等待消息写入网络的操作完成后,该方法才会退出) * @param block 是否阻塞(设置为 true 则必须等待消息写入网络的操作完成后,该方法才会退出)
*/ */
public void sendBallMessage(@NotNull String channel, @NotNull BallMessage message, boolean prefix, boolean block) { public void sendBallMessage(@NotNull String channel, @NotNull BallMessage message, boolean addPrefix, boolean block) {
if (prefix) { if (addPrefix) {
channel = ballConfig.getChannelPrefix() + channel; channel = ballConfig.getChannelPrefix() + channel;
} }
if (block) { if (block) {

View File

@@ -3,12 +3,14 @@ package cn.hamster3.mc.plugin.ball.common.listener;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI; 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.BallPlayerInfo;
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo; import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerConnectServerEvent; 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.message.MessageReceivedEvent;
import cn.hamster3.mc.plugin.ball.common.event.operate.*;
import cn.hamster3.mc.plugin.ball.common.event.player.*;
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOfflineEvent; import cn.hamster3.mc.plugin.ball.common.event.server.ServerOfflineEvent;
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent; import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import org.jetbrains.annotations.NotNull;
public class BallCommonListener { public class BallCommonListener {
public static final BallCommonListener INSTANCE = new BallCommonListener(); public static final BallCommonListener INSTANCE = new BallCommonListener();
@@ -17,19 +19,126 @@ public class BallCommonListener {
} }
@Subscribe @Subscribe
public void onBallPlayerConnectServer(@NotNull BallPlayerConnectServerEvent event) { public void onMessageReceived(MessageReceivedEvent message) {
String channel = message.getChannel();
if (!BallAPI.BALL_CHANNEL.equals(channel)) {
return;
}
switch (BallActions.valueOf(message.getAction())) {
// operate type
case BroadcastPlayerMessage: {
BroadcastPlayerMessageEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), BroadcastPlayerMessageEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case DispatchConsoleCommand: {
DispatchConsoleCommandEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), DispatchConsoleCommandEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case DispatchPlayerCommand: {
DispatchPlayerCommandEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), DispatchPlayerCommandEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case KickPlayer: {
KickPlayerEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), KickPlayerEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case SendMessageToPlayer: {
SendMessageToPlayerEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), SendMessageToPlayerEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case SendPlayerToLocation: {
SendPlayerToLocationEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), SendPlayerToLocationEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case SendPlayerToPlayer: {
SendPlayerToPlayerEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), SendPlayerToPlayerEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
// player type
case BallPlayerPreLogin: {
BallPlayerPreLoginEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), BallPlayerPreLoginEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case BallPlayerLogin: {
BallPlayerLoginEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), BallPlayerLoginEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case BallPlayerPostLogin: {
BallPlayerPostLoginEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), BallPlayerPostLoginEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case BallPlayerPreConnectServer: {
BallPlayerPreConnectServerEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), BallPlayerPreConnectServerEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case BallPlayerConnectServer: {
BallPlayerConnectServerEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), BallPlayerConnectServerEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case BallPlayerPostConnectServer: {
BallPlayerPostConnectServerEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), BallPlayerPostConnectServerEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case BallPlayerLogout: {
BallPlayerLogoutEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), BallPlayerLogoutEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
// server type
case ServerOffline: {
ServerOfflineEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), ServerOfflineEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
case ServerOnline: {
ServerOnlineEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), ServerOnlineEvent.class);
BallAPI.getInstance().getEventBus().post(event);
break;
}
}
}
@Subscribe
public void handlePlayerInfo(MessageReceivedEvent message) {
if (!message.getChannel().contains(BallAPI.PLAYER_INFO_CHANNEL)) {
return;
}
if (BallActions.valueOf(message.getAction()) != BallActions.BallPlayerInfoUpdate) {
return;
}
BallPlayerInfoUpdateEvent event = CoreAPI.getInstance().getGson().fromJson(message.getContent(), BallPlayerInfoUpdateEvent.class);
BallAPI.getInstance().getEventBus().post(event);
}
@Subscribe
public void onBallPlayerConnectServer(BallPlayerConnectServerEvent event) {
BallPlayerInfo info = event.getPlayerInfo(); BallPlayerInfo info = event.getPlayerInfo();
BallAPI.getInstance().getAllPlayerInfo().put(info.getUuid(), info); BallAPI.getInstance().getAllPlayerInfo().put(info.getUuid(), info);
} }
@Subscribe @Subscribe
public void onBallPlayerInfoUpdate(@NotNull BallPlayerInfoUpdateEvent event) { public void onBallPlayerInfoUpdate(BallPlayerInfoUpdateEvent event) {
BallPlayerInfo info = event.getPlayerInfo(); BallPlayerInfo info = event.getPlayerInfo();
BallAPI.getInstance().getAllPlayerInfo().put(info.getUuid(), info); BallAPI.getInstance().getAllPlayerInfo().put(info.getUuid(), info);
} }
@Subscribe @Subscribe
public void onServerOnline(@NotNull ServerOnlineEvent event) { public void onServerOnline(ServerOnlineEvent event) {
BallServerInfo info = event.getServerInfo(); BallServerInfo info = event.getServerInfo();
BallAPI.getInstance().getAllServerInfo().put(info.getId(), info); BallAPI.getInstance().getAllServerInfo().put(info.getId(), info);
switch (info.getType()) { switch (info.getType()) {
@@ -53,7 +162,7 @@ public class BallCommonListener {
} }
@Subscribe @Subscribe
public void onServerOffline(@NotNull ServerOfflineEvent event) { public void onServerOffline(ServerOfflineEvent event) {
String serverID = event.getServerID(); String serverID = event.getServerID();
BallServerInfo info = BallAPI.getInstance().getAllServerInfo().remove(serverID); BallServerInfo info = BallAPI.getInstance().getAllServerInfo().remove(serverID);
if (info == null) { if (info == null) {

View File

@@ -2,13 +2,7 @@ package cn.hamster3.mc.plugin.ball.common.listener;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI; import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.ball.common.data.BallMessage; import cn.hamster3.mc.plugin.ball.common.data.BallMessage;
import cn.hamster3.mc.plugin.ball.common.event.BallActions;
import cn.hamster3.mc.plugin.ball.common.event.message.MessageReceivedEvent; import cn.hamster3.mc.plugin.ball.common.event.message.MessageReceivedEvent;
import cn.hamster3.mc.plugin.ball.common.event.operate.*;
import cn.hamster3.mc.plugin.ball.common.event.player.*;
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOfflineEvent;
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.lib.io.lettuce.core.pubsub.RedisPubSubListener; import cn.hamster3.mc.plugin.core.lib.io.lettuce.core.pubsub.RedisPubSubListener;
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.EventBus;
@@ -32,99 +26,6 @@ public class BallRedisListener implements RedisPubSubListener<String, BallMessag
return; return;
} }
eventBus.post(new MessageReceivedEvent(channel, ballMessage)); eventBus.post(new MessageReceivedEvent(channel, ballMessage));
if (!BallAPI.BALL_CHANNEL.equals(channel)) {
return;
}
switch (BallActions.valueOf(ballMessage.getAction())) {
case BroadcastPlayerMessage: {
BroadcastPlayerMessageEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), BroadcastPlayerMessageEvent.class);
eventBus.post(event);
break;
}
case DispatchConsoleCommand: {
DispatchConsoleCommandEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), DispatchConsoleCommandEvent.class);
eventBus.post(event);
break;
}
case DispatchPlayerCommand: {
DispatchPlayerCommandEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), DispatchPlayerCommandEvent.class);
eventBus.post(event);
break;
}
case KickPlayer: {
KickPlayerEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), KickPlayerEvent.class);
eventBus.post(event);
break;
}
case SendMessageToPlayer: {
SendMessageToPlayerEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), SendMessageToPlayerEvent.class);
eventBus.post(event);
break;
}
case SendPlayerToLocation: {
SendPlayerToLocationEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), SendPlayerToLocationEvent.class);
eventBus.post(event);
break;
}
case SendPlayerToPlayer: {
SendPlayerToPlayerEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), SendPlayerToPlayerEvent.class);
eventBus.post(event);
break;
}
case BallPlayerPreLogin: {
BallPlayerPreLoginEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), BallPlayerPreLoginEvent.class);
eventBus.post(event);
break;
}
case BallPlayerLogin: {
BallPlayerLoginEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), BallPlayerLoginEvent.class);
eventBus.post(event);
break;
}
case BallPlayerPostLogin: {
BallPlayerPostLoginEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), BallPlayerPostLoginEvent.class);
eventBus.post(event);
break;
}
case BallPlayerPreConnectServer: {
BallPlayerPreConnectServerEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), BallPlayerPreConnectServerEvent.class);
eventBus.post(event);
break;
}
case BallPlayerConnectServer: {
BallPlayerConnectServerEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), BallPlayerConnectServerEvent.class);
eventBus.post(event);
break;
}
case BallPlayerPostConnectServer: {
BallPlayerPostConnectServerEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), BallPlayerPostConnectServerEvent.class);
eventBus.post(event);
break;
}
case BallPlayerLogout: {
BallPlayerLogoutEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), BallPlayerLogoutEvent.class);
eventBus.post(event);
break;
}
case BallPlayerInfoUpdate: {
BallPlayerInfoUpdateEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), BallPlayerInfoUpdateEvent.class);
eventBus.post(event);
break;
}
case ServerOffline: {
ServerOfflineEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), ServerOfflineEvent.class);
eventBus.post(event);
break;
}
case ServerOnline: {
ServerOnlineEvent event = CoreAPI.getInstance().getGson().fromJson(ballMessage.getContent(), ServerOnlineEvent.class);
eventBus.post(event);
break;
}
}
} }
@Override @Override

View File

@@ -5,7 +5,7 @@ plugins {
} }
group = "cn.hamster3.mc.plugin" group = "cn.hamster3.mc.plugin"
version = "1.5.2" version = "1.5.3"
subprojects { subprojects {
apply { apply {