5 Commits

Author SHA1 Message Date
00dbcba628 fix(ball-common): 提前对服务器 id 和 name 进行空判断 2025-01-26 15:01:15 +08:00
2a4a3e9065 perf: 优化消息缓存表格 2024-11-09 22:30:51 +08:00
69432dbbc8 style: 修改错误日志 2024-08-09 04:29:36 +08:00
20f395fa45 build: 更新版本号 2024-08-08 04:47:05 +08:00
4d3d93887d feat: 添加管理员指令 2024-08-08 04:46:07 +08:00
17 changed files with 107 additions and 72 deletions

View File

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

View File

@@ -3,7 +3,6 @@ package cn.hamster3.mc.plugin.ball.bukkit.listener;
import cn.hamster3.mc.plugin.ball.bukkit.HamsterBallPlugin;
import cn.hamster3.mc.plugin.ball.bukkit.data.BukkitLocation;
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.common.data.DisplayMessage;
@@ -95,9 +94,6 @@ public class BallBukkitListener implements Listener {
@Subscribe
public void onDispatchConsoleCommand(DispatchConsoleCommandEvent event) {
if (event.getType() != null && event.getType() != BallServerType.GAME) {
return;
}
if (event.getServerID() != null && !BallAPI.getInstance().isLocalServer(event.getServerID())) {
return;
}
@@ -106,9 +102,6 @@ public class BallBukkitListener implements Listener {
@Subscribe
public void onDispatchPlayerCommand(DispatchPlayerCommandEvent event) {
if (event.getType() != null && event.getType() != BallServerType.GAME) {
return;
}
if (event.getUuid() != null) {
Player player = Bukkit.getPlayer(event.getUuid());
if (player == null) {

View File

@@ -12,7 +12,7 @@ public class BungeeBallCommand extends Command {
public static final BungeeBallCommand INSTANCE = new BungeeBallCommand();
public BungeeBallCommand() {
super("hamster-ball", "hamster.ball.admin", "ball");
super("hamster-bungee-ball", "hamster.ball.admin", "bungee-ball", "bball");
}
@Override

View File

@@ -2,7 +2,6 @@ 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 com.google.common.eventbus.Subscribe;
@@ -23,9 +22,6 @@ public class BallBungeeListener {
@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;
}
@@ -35,9 +31,6 @@ public class BallBungeeListener {
@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());

View File

@@ -140,7 +140,10 @@ public abstract class BallAPI {
") CHARSET utf8mb4;");
statement.execute("CREATE TABLE IF NOT EXISTS `hamster_ball_cached_message`(" +
"`uuid` CHAR(36) NOT NULL," +
"`message` TEXT NOT NULL" +
"`message` TEXT NOT NULL," +
"`time` DATETIME NOT NULL DEFAULT NOW()," +
"INDEX `idx_uuid` USING BTREE (`uuid`)," +
"INDEX `idx_time` USING BTREE (`time`)" +
") CHARSET utf8mb4;");
}
if (getBallConfig().isGameServerUpdatePlayerInfo()) {
@@ -257,11 +260,10 @@ public abstract class BallAPI {
*/
public void dispatchConsoleCommand(@Nullable BallServerType type, @Nullable String serverID, @NotNull String command) {
sendBallMessage(BALL_CHANNEL, new BallMessage(
getLocalServerId(), null, BallServerType.GAME,
getLocalServerId(), null, type,
BallActions.DispatchConsoleCommand.name(),
CoreAPI.getInstance().getGson().toJsonTree(new DispatchConsoleCommandEvent(type, serverID, command))
CoreAPI.getInstance().getGson().toJsonTree(new DispatchConsoleCommandEvent(serverID, command))
), false);
}
/**
@@ -273,9 +275,9 @@ public abstract class BallAPI {
*/
public void dispatchPlayerCommand(@Nullable BallServerType type, @Nullable UUID uuid, @NotNull String command) {
sendBallMessage(BALL_CHANNEL, new BallMessage(
getLocalServerId(), null, BallServerType.GAME,
getLocalServerId(), null, type,
BallActions.DispatchPlayerCommand.name(),
CoreAPI.getInstance().getGson().toJsonTree(new DispatchPlayerCommandEvent(type, uuid, command))
CoreAPI.getInstance().getGson().toJsonTree(new DispatchPlayerCommandEvent(uuid, command))
), false);
}
@@ -330,7 +332,7 @@ public abstract class BallAPI {
}
try (Connection connection = getDatasource().getConnection()) {
try (PreparedStatement statement = connection.prepareStatement(
"INSERT INTO `hamster_ball_cached_message` VALUES(?, ?);"
"INSERT INTO `hamster_ball_cached_message` VALUES(?, ?, DEFAULT);"
)) {
statement.setString(1, receiver.toString());
statement.setString(2, message.toJson().toString());

View File

@@ -1,13 +1,16 @@
package cn.hamster3.mc.plugin.ball.common.command;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.ball.common.command.adapt.ChildCommand;
import cn.hamster3.mc.plugin.ball.common.command.adapt.AdaptCommandSender;
import cn.hamster3.mc.plugin.ball.common.command.adapt.ChildCommand;
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class SudoAllConsoleCommand extends ChildCommand {
public static final SudoAllConsoleCommand INSTANCE = new SudoAllConsoleCommand();
@@ -22,7 +25,7 @@ public class SudoAllConsoleCommand extends ChildCommand {
@Override
public @NotNull String getUsage() {
return "sudo-all-console <服务器ID> <命令内容>";
return "sudo-all-console <服务器类型> <命令内容>";
}
@Override
@@ -37,22 +40,36 @@ public class SudoAllConsoleCommand extends ChildCommand {
@Override
public boolean onCommand(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
if (args.length < 1) {
if (args.length < 2) {
sender.sendMessage(BallCommand.INSTANCE.getUsage() + " " + getUsage());
return true;
}
StringBuilder builder = new StringBuilder(args[0]);
for (int i = 1; i < args.length; i++) {
BallServerType serverType;
try {
serverType = BallServerType.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException e) {
sender.sendMessage("§c未知的服务器类型: " + args[0]);
return true;
}
StringBuilder builder = new StringBuilder(args[1]);
for (int i = 2; i < args.length; i++) {
builder.append(" ").append(args[i]);
}
String command = builder.toString();
BallAPI.getInstance().dispatchConsoleCommand(null, null, command);
BallAPI.getInstance().dispatchConsoleCommand(serverType, null, command);
sender.sendMessage("§a已强制所有服务器控制台执行命令: §e/" + command);
return true;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
if (args.length == 1) {
return Arrays.stream(BallServerType.values())
.map(Enum::name)
.filter(o -> o.toLowerCase().startsWith(args[0].toLowerCase()))
.limit(10)
.collect(Collectors.toList());
}
return Collections.emptyList();
}
}

View File

@@ -3,11 +3,14 @@ package cn.hamster3.mc.plugin.ball.common.command;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.ball.common.command.adapt.ChildCommand;
import cn.hamster3.mc.plugin.ball.common.command.adapt.AdaptCommandSender;
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class SudoAllPlayerCommand extends ChildCommand {
public static final SudoAllPlayerCommand INSTANCE = new SudoAllPlayerCommand();
@@ -22,7 +25,7 @@ public class SudoAllPlayerCommand extends ChildCommand {
@Override
public @NotNull String getUsage() {
return "sudo-all-player <命令内容>";
return "sudo-all-player <服务器类型> <命令内容>";
}
@Override
@@ -37,22 +40,36 @@ public class SudoAllPlayerCommand extends ChildCommand {
@Override
public boolean onCommand(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
if (args.length < 1) {
if (args.length < 2) {
sender.sendMessage(BallCommand.INSTANCE.getUsage() + " " + getUsage());
return true;
}
StringBuilder builder = new StringBuilder(args[0]);
for (int i = 1; i < args.length; i++) {
BallServerType serverType;
try {
serverType = BallServerType.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException e) {
sender.sendMessage("§c未知的服务器类型: " + args[0]);
return true;
}
StringBuilder builder = new StringBuilder(args[1]);
for (int i = 2; i < args.length; i++) {
builder.append(" ").append(args[i]);
}
String command = builder.toString();
BallAPI.getInstance().dispatchPlayerCommand(null, null, command);
BallAPI.getInstance().dispatchPlayerCommand(serverType, null, command);
sender.sendMessage("§a已强制所有玩家执行命令: §e/" + command);
return true;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
if (args.length == 1) {
return Arrays.stream(BallServerType.values())
.map(Enum::name)
.filter(o -> o.toLowerCase().startsWith(args[0].toLowerCase()))
.limit(10)
.collect(Collectors.toList());
}
return Collections.emptyList();
}
}

View File

@@ -1,8 +1,8 @@
package cn.hamster3.mc.plugin.ball.common.command;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.ball.common.command.adapt.ChildCommand;
import cn.hamster3.mc.plugin.ball.common.command.adapt.AdaptCommandSender;
import cn.hamster3.mc.plugin.ball.common.command.adapt.ChildCommand;
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@@ -1,12 +1,14 @@
package cn.hamster3.mc.plugin.ball.common.command;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.ball.common.command.adapt.ChildCommand;
import cn.hamster3.mc.plugin.ball.common.command.adapt.AdaptCommandSender;
import cn.hamster3.mc.plugin.ball.common.command.adapt.ChildCommand;
import cn.hamster3.mc.plugin.ball.common.entity.BallPlayerInfo;
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@@ -25,7 +27,7 @@ public class SudoPlayerCommand extends ChildCommand {
@Override
public @NotNull String getUsage() {
return "sudo-player <玩家名|UUID> <命令内容>";
return "sudo-player <服务器类型> <玩家名|UUID> <命令内容>";
}
@Override
@@ -40,43 +42,59 @@ public class SudoPlayerCommand extends ChildCommand {
@Override
public boolean onCommand(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
if (args.length < 2) {
if (args.length < 3) {
sender.sendMessage(BallCommand.INSTANCE.getUsage() + " " + getUsage());
return true;
}
BallServerType serverType;
try {
serverType = BallServerType.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException e) {
sender.sendMessage("§c未知的服务器类型: " + args[0]);
return true;
}
BallPlayerInfo info;
try {
UUID uuid = UUID.fromString(args[0]);
UUID uuid = UUID.fromString(args[1]);
info = BallAPI.getInstance().getPlayerInfo(uuid);
} catch (Exception e) {
info = BallAPI.getInstance().getPlayerInfo(args[0]);
info = BallAPI.getInstance().getPlayerInfo(args[1]);
}
if (info == null) {
sender.sendMessage("§c未找到玩家 " + args[0]);
sender.sendMessage("§c未找到玩家 " + args[1]);
return true;
}
if (!info.isOnline()) {
sender.sendMessage("§c玩家 " + args[0] + " 不在线");
sender.sendMessage("§c玩家 " + args[1] + " 不在线");
return true;
}
StringBuilder builder = new StringBuilder(args[1]);
for (int i = 2; i < args.length; i++) {
StringBuilder builder = new StringBuilder(args[2]);
for (int i = 3; i < args.length; i++) {
builder.append(" ").append(args[i]);
}
String command = builder.toString();
BallAPI.getInstance().dispatchPlayerCommand(null, info.getUuid(), command);
BallAPI.getInstance().dispatchPlayerCommand(serverType, info.getUuid(), command);
sender.sendMessage("§a已强制玩家 " + info.getName() + " 执行命令: §e/" + command);
return true;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
if (args.length == 1) {
return BallAPI.getInstance().getAllPlayerInfo().values().stream()
.map(BallPlayerInfo::getName)
.filter(o -> o.toLowerCase().startsWith(args[0].toLowerCase()))
.limit(10)
.collect(Collectors.toList());
switch (args.length) {
case 1: {
return Arrays.stream(BallServerType.values())
.map(Enum::name)
.filter(o -> o.toLowerCase().startsWith(args[0].toLowerCase()))
.limit(10)
.collect(Collectors.toList());
}
case 2: {
return BallAPI.getInstance().getAllPlayerInfo().values().stream()
.map(BallPlayerInfo::getName)
.filter(o -> o.toLowerCase().startsWith(args[1].toLowerCase()))
.limit(10)
.collect(Collectors.toList());
}
}
return Collections.emptyList();
}

View File

@@ -46,7 +46,13 @@ public class BallServerInfo {
public BallServerInfo(@NotNull ConfigSection config, @NotNull BallServerType type) {
Map<String, String> env = System.getenv();
id = env.getOrDefault("BALL_SERVER_ID", config.getString("id"));
if (id == null || id.isEmpty()) {
throw new IllegalArgumentException("id 不能为空");
}
name = env.getOrDefault("BALL_SERVER_NAME", config.getString("name"));
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("name 不能为空");
}
this.type = type;
host = "0.0.0.0";
port = 0;

View File

@@ -1,6 +1,5 @@
package cn.hamster3.mc.plugin.ball.common.event.operate;
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
@@ -9,8 +8,6 @@ import org.jetbrains.annotations.Nullable;
@Data
@AllArgsConstructor
public class DispatchConsoleCommandEvent {
@Nullable
private final BallServerType type;
@Nullable
private final String serverID;
@NotNull

View File

@@ -1,6 +1,5 @@
package cn.hamster3.mc.plugin.ball.common.event.operate;
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
@@ -11,8 +10,6 @@ import java.util.UUID;
@Data
@AllArgsConstructor
public class DispatchPlayerCommandEvent {
@Nullable
private final BallServerType type;
@Nullable
private final UUID uuid;
@NotNull

View File

@@ -7,6 +7,8 @@ import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import com.google.common.eventbus.EventBus;
import redis.clients.jedis.JedisPubSub;
import java.util.logging.Level;
public class BallRedisListener extends JedisPubSub {
public static final BallRedisListener INSTANCE = new BallRedisListener();
@@ -32,7 +34,7 @@ public class BallRedisListener extends JedisPubSub {
}
eventBus.post(new MessageReceivedEvent(finalChannel, ballMessage));
} catch (Exception | Error e) {
e.printStackTrace();
BallAPI.getInstance().getLogger().log(Level.SEVERE, "解析来自频道 " + channel + " 的数据出错: " + message, e);
}
});
}

View File

@@ -100,8 +100,8 @@ public class HamsterBallPlugin {
return;
}
CommandMeta commandMeta = proxyServer.getCommandManager()
.metaBuilder("hamster-ball")
.aliases("ball")
.metaBuilder("hamster-velocity-ball")
.aliases("velocity-ball", "vball")
.plugin(this)
.build();
proxyServer.getCommandManager().register(commandMeta, VelocityBallCommand.INSTANCE);

View File

@@ -1,7 +1,6 @@
package cn.hamster3.mc.plugin.ball.velocity.listener;
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.ball.velocity.HamsterBallPlugin;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
@@ -23,9 +22,6 @@ public class BallVelocityListener {
@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;
}
@@ -35,9 +31,6 @@ public class BallVelocityListener {
@Subscribe
public void onDispatchPlayerCommand(DispatchPlayerCommandEvent event) {
if (event.getType() != null && event.getType() != BallServerType.GAME) {
return;
}
ProxyServer server = HamsterBallPlugin.getInstance().getProxyServer();
if (event.getUuid() != null) {
Player player = server.getPlayer(event.getUuid()).orElse(null);

View File

@@ -5,7 +5,7 @@ plugins {
}
group = "cn.hamster3.mc.plugin"
version = "1.6.5-SNAPSHOT"
version = "1.7.2"
description = "基于 Redis 的 Minecraft 服务端通用消息中间件"
subprojects {

View File

@@ -1,6 +1,6 @@
#Sun Aug 20 16:53:32 CST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists