Compare commits
15 Commits
5c913fa2db
...
1.8.1
Author | SHA1 | Date | |
---|---|---|---|
e937924317 | |||
de10bd7feb | |||
1e0417c814 | |||
c3b2248c60 | |||
25828267f6 | |||
95c0a23857 | |||
00dbcba628 | |||
2a4a3e9065 | |||
69432dbbc8 | |||
20f395fa45 | |||
4d3d93887d | |||
518517a4e0 | |||
3570930b1f | |||
5724e5e662 | |||
e4425c0b1b |
29
.gitea/workflows/main.yaml
Normal file
29
.gitea/workflows/main.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Publish Project
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
cache-dependency-path: gradle/wrapper/gradle-wrapper.properties
|
||||
- name: Build Project
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_MAVEN_AIRGAME_USERNAME: ${{ secrets.MAVEN_AIRGAME_USERNAME }}
|
||||
ORG_GRADLE_PROJECT_MAVEN_AIRGAME_PASSWORD: ${{ secrets.MAVEN_AIRGAME_PASSWORD }}
|
||||
run: chmod +x gradlew && ./gradlew build publish --no-daemon
|
||||
- name: Publish to Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: build/*.jar
|
10
README.md
10
README.md
@@ -11,8 +11,6 @@
|
||||
3. 命令行窗口中执行`./gradlew clean build`
|
||||
4. 构建成品在 `build` 文件夹
|
||||
|
||||
也可访问我的[Jenkins网站](https://jenkins.airgame.net/job/opensource/job/hamster-ball/)获取最新版
|
||||
|
||||
# 安装步骤
|
||||
|
||||
1. 关闭服务器
|
||||
@@ -49,9 +47,9 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// 对于 Bukkit 插件
|
||||
compileOnly("cn.hamster3.mc.plugin:ball-bukkit:1.6.4")
|
||||
compileOnly("cn.hamster3.mc.plugin:ball-bukkit:1.8.1")
|
||||
// 对于 BungeeCord 插件
|
||||
compileOnly("cn.hamster3.mc.plugin:ball-bungee:1.6.4")
|
||||
compileOnly("cn.hamster3.mc.plugin:ball-bungee:1.8.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -77,13 +75,13 @@ dependencies {
|
||||
<dependency>
|
||||
<groupId>cn.hamster3.mc.plugin</groupId>
|
||||
<artifactId>ball-bukkit</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<version>1.8.1</version>
|
||||
</dependency>
|
||||
<!--对于 BungeeCord 插件-->
|
||||
<dependency>
|
||||
<groupId>cn.hamster3.mc.plugin</groupId>
|
||||
<artifactId>ball-bungee</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<version>1.8.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -20,6 +20,6 @@ tasks {
|
||||
archiveBaseName = "HamsterBall-Bukkit"
|
||||
}
|
||||
shadowJar {
|
||||
destinationDirectory = rootProject.buildDir
|
||||
destinationDirectory = rootProject.layout.buildDirectory
|
||||
}
|
||||
}
|
||||
|
@@ -6,18 +6,26 @@ import cn.hamster3.mc.plugin.ball.bukkit.listener.BallBukkitListener;
|
||||
import cn.hamster3.mc.plugin.ball.bukkit.listener.UpdatePlayerInfoListener;
|
||||
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.command.BallCommand;
|
||||
import cn.hamster3.mc.plugin.ball.common.command.adapt.AdaptCommandSender;
|
||||
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.server.ServerOnlineEvent;
|
||||
import cn.hamster3.mc.plugin.core.bukkit.api.CoreBukkitAPI;
|
||||
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -129,4 +137,35 @@ public class HamsterBallPlugin extends JavaPlugin {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球已关闭,总计耗时 " + time + " ms");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
return BallCommand.INSTANCE.onCommand(new AdaptCommandSender() {
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull Component message) {
|
||||
CoreBukkitAPI.getInstance().getAudienceProvider().sender(sender).sendMessage(message);
|
||||
}
|
||||
}, args);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
return BallCommand.INSTANCE.onTabComplete(new AdaptCommandSender() {
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull Component message) {
|
||||
CoreBukkitAPI.getInstance().getAudienceProvider().sender(sender).sendMessage(message);
|
||||
}
|
||||
}, args);
|
||||
}
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -42,6 +42,11 @@ server-info:
|
||||
# 不填则自动获取 server.properties 文件中的设置
|
||||
# port: 25577
|
||||
|
||||
# Redis 配置
|
||||
# 如果注释该选项则默认使用 HamsterCore 中的连接配置
|
||||
# 否则 HamsterBall 将会使用与 HamsterCore 不同的 Redis 链接
|
||||
# redis-url: redis://localhost:6379/0?clientName=HamsterBall&timeout=5s
|
||||
|
||||
# 数据库连接池配置
|
||||
# 如果注释该选项则默认使用 HamsterCore 中的连接池配置
|
||||
# 否则 HamsterBall 将会使用与 HamsterCore 不同的数据库链接
|
||||
@@ -53,9 +58,6 @@ server-info:
|
||||
# # MySQL数据库链接填写格式:
|
||||
# # jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数
|
||||
# url: "jdbc:mysql://localhost:3306/Test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true"
|
||||
# # 如果你不需要做多端跨服,那么请使用 sqlite 作本地数据库 ↓
|
||||
# # driver: "org.sqlite.JDBC"
|
||||
# # url: "jdbc:sqlite:./plugins/HamsterCore/database.db"
|
||||
# # 用户名
|
||||
# username: "root"
|
||||
# # 密码
|
||||
|
@@ -12,7 +12,7 @@ UPDATE_CHECKER:
|
||||
CHECK_TYPE: GITEA_RELEASES
|
||||
GIT_BASE_URL: https://git.airgame.net
|
||||
GIT_REPO: MiniDay/hamster-ball
|
||||
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-ball/
|
||||
|
||||
|
||||
load: STARTUP
|
||||
|
||||
@@ -21,3 +21,11 @@ depend:
|
||||
|
||||
softdepend:
|
||||
- PlaceholderAPI
|
||||
|
||||
commands:
|
||||
hamster-ball:
|
||||
aliases: [ ball ]
|
||||
|
||||
permissions:
|
||||
hamster.ball.admin:
|
||||
default: op
|
||||
|
@@ -18,6 +18,6 @@ tasks {
|
||||
archiveBaseName = "HamsterBall-BungeeCord"
|
||||
}
|
||||
shadowJar {
|
||||
destinationDirectory = rootProject.buildDir
|
||||
destinationDirectory = rootProject.layout.buildDirectory
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package cn.hamster3.mc.plugin.ball.bungee;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.bungee.api.BallBungeeCordAPI;
|
||||
import cn.hamster3.mc.plugin.ball.bungee.command.BungeeBallCommand;
|
||||
import cn.hamster3.mc.plugin.ball.bungee.listener.BallBungeeListener;
|
||||
import cn.hamster3.mc.plugin.ball.bungee.listener.BallBungeeMainListener;
|
||||
import cn.hamster3.mc.plugin.ball.bungee.listener.BungeeServerListener;
|
||||
@@ -71,6 +72,8 @@ public class HamsterBallPlugin extends Plugin {
|
||||
ProxyServer.getInstance().stop("仓鼠球启动失败");
|
||||
return;
|
||||
}
|
||||
ProxyServer.getInstance().getPluginManager().registerCommand(this, BungeeBallCommand.INSTANCE);
|
||||
logger.info("已注册命令 BungeeBallCommand");
|
||||
BallAPI.getInstance().getEventBus().register(BallBungeeListener.INSTANCE);
|
||||
logger.info("已注册监听器 BallBungeeListener");
|
||||
ProxyServer.getInstance().getPluginManager().registerListener(this, BallBungeeMainListener.INSTANCE);
|
||||
|
@@ -0,0 +1,32 @@
|
||||
package cn.hamster3.mc.plugin.ball.bungee.command;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.command.BallCommand;
|
||||
import cn.hamster3.mc.plugin.ball.common.command.adapt.AdaptCommandSender;
|
||||
import cn.hamster3.mc.plugin.core.bungee.api.CoreBungeeAPI;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BungeeBallCommand extends Command {
|
||||
public static final BungeeBallCommand INSTANCE = new BungeeBallCommand();
|
||||
|
||||
public BungeeBallCommand() {
|
||||
super("hamster-bungee-ball", "hamster.ball.admin", "bungee-ball", "bball");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
BallCommand.INSTANCE.onCommand(new AdaptCommandSender() {
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull Component message) {
|
||||
CoreBungeeAPI.getInstance().getAudienceProvider().sender(sender).sendMessage(message);
|
||||
}
|
||||
}, args);
|
||||
}
|
||||
}
|
@@ -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());
|
||||
|
@@ -11,7 +11,6 @@ UPDATE_CHECKER:
|
||||
CHECK_TYPE: GITEA_RELEASES
|
||||
GIT_BASE_URL: https://git.airgame.net
|
||||
GIT_REPO: MiniDay/hamster-ball
|
||||
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-ball/
|
||||
|
||||
depend:
|
||||
- HamsterCore
|
||||
|
@@ -33,6 +33,11 @@ server-info:
|
||||
# 不填则自动设置为 25577
|
||||
port: 25577
|
||||
|
||||
# Redis 配置
|
||||
# 如果注释该选项则默认使用 HamsterCore 中的连接配置
|
||||
# 否则 HamsterBall 将会使用与 HamsterCore 不同的 Redis 链接
|
||||
# redis-url: redis://localhost:6379/0?clientName=HamsterBall&timeout=5s
|
||||
|
||||
# 数据库连接池配置
|
||||
# 如果注释该选项则默认使用 HamsterCore 中的连接池配置
|
||||
# 否则 HamsterBall 将会使用与 HamsterCore 不同的数据库链接
|
||||
|
@@ -20,11 +20,14 @@ import cn.hamster3.mc.plugin.core.common.data.DisplayMessage;
|
||||
import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
|
||||
import com.google.common.eventbus.AsyncEventBus;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import io.lettuce.core.RedisClient;
|
||||
import io.lettuce.core.api.StatefulRedisConnection;
|
||||
import io.lettuce.core.api.sync.RedisCommands;
|
||||
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.*;
|
||||
@@ -58,6 +61,8 @@ public abstract class BallAPI {
|
||||
@NotNull
|
||||
private final DataSource datasource;
|
||||
@NotNull
|
||||
private final RedisClient redisClient;
|
||||
@NotNull
|
||||
private final BallServerInfo localServerInfo;
|
||||
|
||||
@NotNull
|
||||
@@ -69,7 +74,7 @@ public abstract class BallAPI {
|
||||
private final Map<UUID, BallPlayerInfo> allPlayerInfo;
|
||||
|
||||
@NotNull
|
||||
private final Jedis redisSub;
|
||||
private final StatefulRedisPubSubConnection<String, String> redisPubSub;
|
||||
@Nullable
|
||||
private ScheduledFuture<?> lockUpdater;
|
||||
|
||||
@@ -79,6 +84,11 @@ public abstract class BallAPI {
|
||||
throw new IllegalArgumentException("配置文件中未找到 server-info 节点");
|
||||
}
|
||||
localServerInfo = new BallServerInfo(serverInfoConfig, type);
|
||||
if (config.hasKey("redis-url")) {
|
||||
redisClient = RedisClient.create(config.getString("redis-url"));
|
||||
} else {
|
||||
redisClient = CoreAPI.getInstance().getRedisClient();
|
||||
}
|
||||
ConfigSection section = config.getSection("datasource");
|
||||
if (section != null) {
|
||||
getLogger().info("启用仓鼠球自定义数据库连接池");
|
||||
@@ -92,7 +102,7 @@ public abstract class BallAPI {
|
||||
eventBus.register(BallCommonListener.INSTANCE);
|
||||
allServerInfo = new ConcurrentHashMap<>();
|
||||
allPlayerInfo = new ConcurrentHashMap<>();
|
||||
redisSub = CoreAPI.getInstance().getJedisPool().getResource();
|
||||
redisPubSub = getRedisClient().connectPubSub();
|
||||
getLogger().info("频道前缀: " + ballConfig.getChannelPrefix());
|
||||
getLogger().info("启用子服更新玩家状态: " + ballConfig.isGameServerUpdatePlayerInfo());
|
||||
if (ballConfig.isGameServerUpdatePlayerInfo()) {
|
||||
@@ -102,29 +112,31 @@ public abstract class BallAPI {
|
||||
getLogger().warning("已启用调试模式");
|
||||
eventBus.register(BallDebugListener.INSTANCE);
|
||||
}
|
||||
CoreAPI.getInstance().getExecutorService().submit(() -> redisSub.subscribe(BallRedisListener.INSTANCE, BALL_CHANNEL));
|
||||
redisPubSub.addListener(BallRedisListener.INSTANCE);
|
||||
redisPubSub.sync().subscribe(BALL_CHANNEL);
|
||||
}
|
||||
|
||||
protected void enable() throws SQLException, InterruptedException {
|
||||
try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
|
||||
try (StatefulRedisConnection<String, String> connect = getRedisClient().connect()) {
|
||||
RedisCommands<String, String> redis = connect.sync();
|
||||
String key = "HamsterBall:ServerInfo:" + localServerInfo.getId();
|
||||
if (jedis.exists(key) && ballConfig.isSingletonServerID()) {
|
||||
if (redis.exists(key) > 0 && ballConfig.isSingletonServerID()) {
|
||||
throw new IllegalStateException("已经有一个服务器占用了该 ID");
|
||||
}
|
||||
jedis.hset(key, "id", localServerInfo.getId());
|
||||
jedis.hset(key, "name", localServerInfo.getName());
|
||||
jedis.hset(key, "type", localServerInfo.getType().name());
|
||||
jedis.hset(key, "host", localServerInfo.getHost());
|
||||
jedis.hset(key, "port", String.valueOf(localServerInfo.getPort()));
|
||||
jedis.expire(key, 180);
|
||||
redis.hset(key, "id", localServerInfo.getId());
|
||||
redis.hset(key, "name", localServerInfo.getName());
|
||||
redis.hset(key, "type", localServerInfo.getType().name());
|
||||
redis.hset(key, "host", localServerInfo.getHost());
|
||||
redis.hset(key, "port", String.valueOf(localServerInfo.getPort()));
|
||||
redis.expire(key, 180);
|
||||
lockUpdater = CoreAPI.getInstance().getScheduledService().scheduleAtFixedRate(LockUpdateThread.INSTANCE, 1, 1, TimeUnit.MINUTES);
|
||||
for (String serverInfoKey : jedis.keys("HamsterBall:ServerInfo:*")) {
|
||||
for (String serverInfoKey : redis.keys("HamsterBall:ServerInfo:*")) {
|
||||
BallServerInfo info = new BallServerInfo(
|
||||
jedis.hget(serverInfoKey, "id"),
|
||||
jedis.hget(serverInfoKey, "name"),
|
||||
BallServerType.valueOf(jedis.hget(serverInfoKey, "type")),
|
||||
jedis.hget(serverInfoKey, "host"),
|
||||
Integer.parseInt(jedis.hget(serverInfoKey, "port"))
|
||||
redis.hget(serverInfoKey, "id"),
|
||||
redis.hget(serverInfoKey, "name"),
|
||||
BallServerType.valueOf(redis.hget(serverInfoKey, "type")),
|
||||
redis.hget(serverInfoKey, "host"),
|
||||
Integer.parseInt(redis.hget(serverInfoKey, "port"))
|
||||
);
|
||||
allServerInfo.put(info.getId(), info);
|
||||
}
|
||||
@@ -132,16 +144,19 @@ public abstract class BallAPI {
|
||||
try (Connection connection = getDatasource().getConnection()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.execute("CREATE TABLE IF NOT EXISTS `hamster_ball_player_info`(" +
|
||||
"`uuid` CHAR(36) PRIMARY KEY," +
|
||||
"`name` VARCHAR(16) NOT NULL," +
|
||||
"`game_server` VARCHAR(32) NOT NULL," +
|
||||
"`proxy_server` VARCHAR(32) NOT NULL," +
|
||||
"`online` BOOLEAN NOT NULL" +
|
||||
") CHARSET utf8mb4;");
|
||||
"`uuid` CHAR(36) PRIMARY KEY," +
|
||||
"`name` VARCHAR(16) NOT NULL," +
|
||||
"`game_server` VARCHAR(32) NOT NULL," +
|
||||
"`proxy_server` VARCHAR(32) NOT NULL," +
|
||||
"`online` BOOLEAN NOT NULL" +
|
||||
") CHARSET utf8mb4;");
|
||||
statement.execute("CREATE TABLE IF NOT EXISTS `hamster_ball_cached_message`(" +
|
||||
"`uuid` CHAR(36) NOT NULL," +
|
||||
"`message` TEXT NOT NULL" +
|
||||
") CHARSET utf8mb4;");
|
||||
"`uuid` CHAR(36) 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()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
@@ -189,9 +204,10 @@ public abstract class BallAPI {
|
||||
if (lockUpdater != null) {
|
||||
lockUpdater.cancel(true);
|
||||
lockUpdater = null;
|
||||
try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
|
||||
try (StatefulRedisConnection<String, String> connect = getRedisClient().connect()) {
|
||||
RedisCommands<String, String> redis = connect.sync();
|
||||
String key = "HamsterBall:ServerInfo:" + localServerInfo.getId();
|
||||
jedis.del(key);
|
||||
redis.del(key);
|
||||
}
|
||||
}
|
||||
try (Connection connection = getDatasource().getConnection()) {
|
||||
@@ -203,7 +219,7 @@ public abstract class BallAPI {
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
redisSub.close();
|
||||
redisPubSub.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,25 +273,24 @@ 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);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制玩家执行命令
|
||||
*
|
||||
* @param type 执行对象的服务端类型
|
||||
* @param uuid 执行对象的 UUID
|
||||
* @param type 执行对象的服务端类型,null代表所有类型
|
||||
* @param uuid 执行对象的 UUID,null代表所有玩家
|
||||
* @param command 命令内容
|
||||
*/
|
||||
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 +345,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());
|
||||
@@ -486,16 +501,12 @@ public abstract class BallAPI {
|
||||
channel = ballConfig.getChannelPrefix() + channel;
|
||||
}
|
||||
if (block) {
|
||||
try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
|
||||
jedis.publish(channel, CoreAPI.getInstance().getGson().toJson(message));
|
||||
}
|
||||
redisPubSub.sync().publish(channel, CoreAPI.getInstance().getGson().toJson(message));
|
||||
eventBus.post(new MessageSentEvent(channel, message));
|
||||
} else {
|
||||
@NotNull String finalChannel = channel;
|
||||
CoreAPI.getInstance().getExecutorService().submit(() -> {
|
||||
try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
|
||||
jedis.publish(finalChannel, CoreAPI.getInstance().getGson().toJson(message));
|
||||
}
|
||||
CoreAPI.getInstance().getExecutorService().execute(() -> {
|
||||
redisPubSub.sync().publish(finalChannel, CoreAPI.getInstance().getGson().toJson(message));
|
||||
eventBus.post(new MessageSentEvent(finalChannel, message));
|
||||
});
|
||||
}
|
||||
@@ -521,14 +532,7 @@ public abstract class BallAPI {
|
||||
* @param channels 频道名称
|
||||
*/
|
||||
public void subscribeRaw(@NotNull String... channels) {
|
||||
BallRedisListener.INSTANCE.subscribe(channels);
|
||||
// CoreAPI.getInstance().getExecutorService().submit(() -> {
|
||||
// try {
|
||||
// redisSub.subscribe(BallRedisListener.INSTANCE, channels);
|
||||
// } catch (Exception | Error e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// });
|
||||
redisPubSub.sync().subscribe(channels);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -537,10 +541,7 @@ public abstract class BallAPI {
|
||||
* @param patterns 频道名称正则表达式
|
||||
*/
|
||||
public void subscribePatterns(@NotNull String patterns) {
|
||||
BallRedisListener.INSTANCE.psubscribe(patterns);
|
||||
// CoreAPI.getInstance().getExecutorService().submit(
|
||||
// () -> redisSub.psubscribe(BallRedisListener.INSTANCE, patterns)
|
||||
// );
|
||||
redisPubSub.sync().psubscribe(patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -563,7 +564,7 @@ public abstract class BallAPI {
|
||||
* @param channels 频道名称
|
||||
*/
|
||||
public void unsubscribeRaw(@NotNull String... channels) {
|
||||
BallRedisListener.INSTANCE.unsubscribe(channels);
|
||||
redisPubSub.sync().unsubscribe(channels);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -572,7 +573,7 @@ public abstract class BallAPI {
|
||||
* @param patterns 频道名称正则表达式
|
||||
*/
|
||||
public void unsubscribePatterns(@NotNull String patterns) {
|
||||
BallRedisListener.INSTANCE.punsubscribe(patterns);
|
||||
redisPubSub.sync().punsubscribe(patterns);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.command;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.command.adapt.ParentCommand;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BallCommand extends ParentCommand {
|
||||
public static final BallCommand INSTANCE = new BallCommand();
|
||||
|
||||
private BallCommand() {
|
||||
addChildCommand(PlayerInfoCommand.INSTANCE);
|
||||
addChildCommand(SudoPlayerCommand.INSTANCE);
|
||||
addChildCommand(SudoAllPlayerCommand.INSTANCE);
|
||||
addChildCommand(SudoConsoleCommand.INSTANCE);
|
||||
addChildCommand(SudoAllConsoleCommand.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "hamster-ball";
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
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.BallPlayerInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PlayerInfoCommand extends ChildCommand {
|
||||
public static final PlayerInfoCommand INSTANCE = new PlayerInfoCommand();
|
||||
|
||||
private PlayerInfoCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "player-info";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getUsage() {
|
||||
return "player-info <玩家名|UUID>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getDescription() {
|
||||
return "查看玩家信息";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull AdaptCommandSender sender) {
|
||||
return sender.hasPermission("hamster.ball.admin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(BallCommand.INSTANCE.getUsage() + " " + getUsage());
|
||||
return true;
|
||||
}
|
||||
BallPlayerInfo info;
|
||||
try {
|
||||
UUID uuid = UUID.fromString(args[0]);
|
||||
info = BallAPI.getInstance().getPlayerInfo(uuid);
|
||||
} catch (Exception e) {
|
||||
info = BallAPI.getInstance().getPlayerInfo(args[0]);
|
||||
}
|
||||
if (info == null) {
|
||||
sender.sendMessage("§c未找到玩家 " + args[0] + " 的信息");
|
||||
return true;
|
||||
}
|
||||
sender.sendMessage("§a玩家名称: " + info.getName());
|
||||
sender.sendMessage("§a玩家UUID: " + info.getUuid());
|
||||
sender.sendMessage("§a玩家在线: " + info.isOnline());
|
||||
sender.sendMessage("§a接入点: " + info.getProxyServer());
|
||||
sender.sendMessage("§a所在子服: " + info.getGameServer());
|
||||
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());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
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.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();
|
||||
|
||||
private SudoAllConsoleCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "sudo-all-console";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getUsage() {
|
||||
return "sudo-all-console <服务器类型> <命令内容>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getDescription() {
|
||||
return "强制所有控制台执行指令";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull AdaptCommandSender sender) {
|
||||
return sender.hasPermission("hamster.ball.admin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
|
||||
if (args.length < 2) {
|
||||
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;
|
||||
}
|
||||
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(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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
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();
|
||||
|
||||
private SudoAllPlayerCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "sudo-all-player";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getUsage() {
|
||||
return "sudo-all-player <服务器类型> <命令内容>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getDescription() {
|
||||
return "强制所有玩家执行指令";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull AdaptCommandSender sender) {
|
||||
return sender.hasPermission("hamster.ball.admin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
|
||||
if (args.length < 2) {
|
||||
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;
|
||||
}
|
||||
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(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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
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.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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SudoConsoleCommand extends ChildCommand {
|
||||
public static final SudoConsoleCommand INSTANCE = new SudoConsoleCommand();
|
||||
|
||||
private SudoConsoleCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "sudo-console";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getUsage() {
|
||||
return "sudo-console <服务器ID> <命令内容>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getDescription() {
|
||||
return "强制控制台执行指令";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull AdaptCommandSender sender) {
|
||||
return sender.hasPermission("hamster.ball.admin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(BallCommand.INSTANCE.getUsage() + " " + getUsage());
|
||||
return true;
|
||||
}
|
||||
BallServerInfo info = BallAPI.getInstance().getServerInfo(args[0]);
|
||||
if (info == null) {
|
||||
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, info.getId(), 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().getAllServerInfo().values().stream()
|
||||
.map(BallServerInfo::getId)
|
||||
.filter(o -> o.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.limit(10)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@@ -0,0 +1,101 @@
|
||||
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.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;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SudoPlayerCommand extends ChildCommand {
|
||||
public static final SudoPlayerCommand INSTANCE = new SudoPlayerCommand();
|
||||
|
||||
private SudoPlayerCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "sudo-player";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getUsage() {
|
||||
return "sudo-player <服务器类型> <玩家名|UUID> <命令内容>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getDescription() {
|
||||
return "强制玩家执行指令";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull AdaptCommandSender sender) {
|
||||
return sender.hasPermission("hamster.ball.admin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
|
||||
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[1]);
|
||||
info = BallAPI.getInstance().getPlayerInfo(uuid);
|
||||
} catch (Exception e) {
|
||||
info = BallAPI.getInstance().getPlayerInfo(args[1]);
|
||||
}
|
||||
if (info == null) {
|
||||
sender.sendMessage("§c未找到玩家 " + args[1]);
|
||||
return true;
|
||||
}
|
||||
if (!info.isOnline()) {
|
||||
sender.sendMessage("§c玩家 " + args[1] + " 不在线");
|
||||
return true;
|
||||
}
|
||||
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(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) {
|
||||
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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.command.adapt;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface AdaptCommandSender {
|
||||
boolean hasPermission(@NotNull String permission);
|
||||
|
||||
void sendMessage(@NotNull Component message);
|
||||
|
||||
default void sendMessage(@NotNull String message) {
|
||||
sendMessage(Component.text(message));
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.command.adapt;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ChildCommand {
|
||||
@NotNull
|
||||
public abstract String getName();
|
||||
|
||||
@NotNull
|
||||
public abstract String getUsage();
|
||||
|
||||
@NotNull
|
||||
public abstract String getDescription();
|
||||
|
||||
public abstract boolean hasPermission(@NotNull AdaptCommandSender sender);
|
||||
|
||||
public abstract boolean onCommand(@NotNull AdaptCommandSender sender, @NotNull String[] args);
|
||||
|
||||
@Nullable
|
||||
public abstract List<String> onTabComplete(@NotNull AdaptCommandSender sender, @NotNull String[] args);
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.command.adapt;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class ParentCommand extends ChildCommand {
|
||||
@NotNull
|
||||
private final Map<String, ChildCommand> childCommands;
|
||||
|
||||
public ParentCommand() {
|
||||
childCommands = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public abstract String getName();
|
||||
|
||||
@Nullable
|
||||
public ParentCommand getParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<ChildCommand> getChildCommands() {
|
||||
return childCommands.values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getUsage() {
|
||||
ParentCommand parent = getParent();
|
||||
if (parent == null) {
|
||||
return "/" + getName();
|
||||
}
|
||||
return parent.getUsage() + " " + getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull AdaptCommandSender sender) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getDescription() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有子命令
|
||||
* <p>
|
||||
* 如果子命令也是 ParentCommand 类型,则继续递归获取该 ParentCommand 的子命令
|
||||
*
|
||||
* @return 所有子命令
|
||||
*/
|
||||
@NotNull
|
||||
public List<ChildCommand> getEndChildCommands() {
|
||||
ArrayList<ChildCommand> list = new ArrayList<>();
|
||||
for (ChildCommand command : getChildCommands()) {
|
||||
if (command instanceof ParentCommand) {
|
||||
list.addAll(((ParentCommand) command).getEndChildCommands());
|
||||
} else {
|
||||
list.add(command);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void addChildCommand(@NotNull ChildCommand command) {
|
||||
childCommands.put(command.getName(), command);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, String> getCommandHelp(AdaptCommandSender sender) {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
for (ChildCommand child : getChildCommands()) {
|
||||
if (!child.hasPermission(sender)) {
|
||||
continue;
|
||||
}
|
||||
if (child instanceof ParentCommand) {
|
||||
Map<String, String> childMap = ((ParentCommand) child).getCommandHelp(sender);
|
||||
map.putAll(childMap);
|
||||
continue;
|
||||
}
|
||||
map.put(getUsage() + " " + child.getUsage(), child.getDescription());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public void sendHelp(@NotNull AdaptCommandSender sender) {
|
||||
sender.sendMessage("§e==================== [ " + getName() + " 使用帮助] ====================");
|
||||
Map<String, String> map = getCommandHelp(sender);
|
||||
int maxLength = map.keySet().stream()
|
||||
.map(String::length)
|
||||
.max(Integer::compareTo)
|
||||
.orElse(-1);
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
sender.sendMessage(String.format("§a%-" + maxLength + "s - %s", entry.getKey(), entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
|
||||
if (!hasPermission(sender)) {
|
||||
sender.sendMessage(Component.translatable("commands.help.failed").color(NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
if (args.length == 0) {
|
||||
sendHelp(sender);
|
||||
return true;
|
||||
}
|
||||
for (ChildCommand childCommand : getChildCommands()) {
|
||||
if (!childCommand.getName().equalsIgnoreCase(args[0])) {
|
||||
continue;
|
||||
}
|
||||
if (!childCommand.hasPermission(sender)) {
|
||||
sender.sendMessage(Component.translatable("commands.help.failed").color(NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
return childCommand.onCommand(sender, Arrays.copyOfRange(args, 1, args.length));
|
||||
}
|
||||
sender.sendMessage(Component.translatable("commands.help.failed").color(NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull AdaptCommandSender sender, @NotNull String[] args) {
|
||||
if (args.length == 0) {
|
||||
return getChildCommands().stream()
|
||||
.filter(o -> o.hasPermission(sender))
|
||||
.map(ChildCommand::getName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
for (ChildCommand child : getChildCommands()) {
|
||||
if (args[0].equalsIgnoreCase(child.getName())) {
|
||||
return child.onTabComplete(sender, Arrays.copyOfRange(args, 1, args.length));
|
||||
}
|
||||
}
|
||||
args[0] = args[0].toLowerCase();
|
||||
return getChildCommands().stream()
|
||||
.filter(o -> o.hasPermission(sender))
|
||||
.map(ChildCommand::getName)
|
||||
.filter(o -> o.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -5,16 +5,17 @@ import cn.hamster3.mc.plugin.ball.common.data.BallMessage;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.message.MessageReceivedEvent;
|
||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import redis.clients.jedis.JedisPubSub;
|
||||
import io.lettuce.core.pubsub.RedisPubSubListener;
|
||||
|
||||
public class BallRedisListener extends JedisPubSub {
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class BallRedisListener implements RedisPubSubListener<String, String> {
|
||||
public static final BallRedisListener INSTANCE = new BallRedisListener();
|
||||
|
||||
private BallRedisListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String channel, String message) {
|
||||
public void handle(String channel, String message) {
|
||||
CoreAPI.getInstance().getExecutorService().submit(() -> {
|
||||
try {
|
||||
String finalChannel = channel;
|
||||
@@ -32,33 +33,38 @@ 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPMessage(String pattern, String channel, String message) {
|
||||
onMessage(channel, message);
|
||||
public void message(String channel, String message) {
|
||||
handle(channel, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(String channel, int subscribedChannels) {
|
||||
public void message(String pattern, String channel, String message) {
|
||||
handle(channel, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribed(String channel, long count) {
|
||||
BallAPI.getInstance().getLogger().info("已订阅 redis 频道 " + channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnsubscribe(String channel, int subscribedChannels) {
|
||||
BallAPI.getInstance().getLogger().info("已取消订阅 redis 频道 " + channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPSubscribe(String pattern, int subscribedChannels) {
|
||||
public void psubscribed(String pattern, long count) {
|
||||
BallAPI.getInstance().getLogger().info("已订阅 redis 频道(正则) " + pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPUnsubscribe(String pattern, int subscribedChannels) {
|
||||
public void unsubscribed(String channel, long count) {
|
||||
BallAPI.getInstance().getLogger().info("已取消订阅 redis 频道 " + channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void punsubscribed(String pattern, long count) {
|
||||
BallAPI.getInstance().getLogger().info("已取消订阅 redis 频道(正则) " + pattern);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.thread;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
|
||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import io.lettuce.core.api.StatefulRedisConnection;
|
||||
import io.lettuce.core.api.sync.RedisCommands;
|
||||
|
||||
public class LockUpdateThread implements Runnable {
|
||||
public static final LockUpdateThread INSTANCE = new LockUpdateThread();
|
||||
@@ -13,8 +13,9 @@ public class LockUpdateThread implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
String key = "HamsterBall:ServerInfo:" + BallAPI.getInstance().getLocalServerInfo().getId();
|
||||
try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
|
||||
jedis.expire(key, 180);
|
||||
try (StatefulRedisConnection<String, String> connect = BallAPI.getInstance().getRedisClient().connect()) {
|
||||
RedisCommands<String, String> redis = connect.sync();
|
||||
redis.expire(key, 180);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ 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.server.ServerOnlineEvent;
|
||||
import cn.hamster3.mc.plugin.ball.velocity.api.CoreVelocityAPI;
|
||||
import cn.hamster3.mc.plugin.ball.velocity.command.VelocityBallCommand;
|
||||
import cn.hamster3.mc.plugin.ball.velocity.listener.BallVelocityListener;
|
||||
import cn.hamster3.mc.plugin.ball.velocity.listener.BallVelocityMainListener;
|
||||
import cn.hamster3.mc.plugin.ball.velocity.listener.UpdatePlayerInfoListener;
|
||||
@@ -12,6 +13,7 @@ import cn.hamster3.mc.plugin.ball.velocity.listener.VelocityServerListener;
|
||||
import cn.hamster3.mc.plugin.ball.velocity.util.BallVelocityUtils;
|
||||
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.velocitypowered.api.command.CommandMeta;
|
||||
import com.velocitypowered.api.event.PostOrder;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
@@ -29,7 +31,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@Plugin(
|
||||
id = "hamster-ball",
|
||||
@@ -60,12 +61,12 @@ public class HamsterBallPlugin {
|
||||
this.slf4jLogger = slf4jLogger;
|
||||
this.proxyServer = proxyServer;
|
||||
dataFolder = dataPath.toFile();
|
||||
logger.info("仓鼠球正在初始化");
|
||||
slf4jLogger.info("仓鼠球正在初始化");
|
||||
instance = this;
|
||||
try {
|
||||
File dataFolder = getDataFolder();
|
||||
if (dataFolder.mkdir()) {
|
||||
logger.info("已生成插件存档文件夹");
|
||||
slf4jLogger.info("已生成插件存档文件夹");
|
||||
}
|
||||
File configFile = new File(dataFolder, "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
@@ -77,37 +78,42 @@ public class HamsterBallPlugin {
|
||||
}
|
||||
config = YamlConfig.load(configFile);
|
||||
CoreVelocityAPI.init(config);
|
||||
logger.info("已初始化 BallAPI");
|
||||
slf4jLogger.info("已初始化 BallAPI");
|
||||
} catch (Exception e) {
|
||||
slf4jLogger.error("BallAPI 初始化失败", e);
|
||||
proxyServer.shutdown(Component.text("由于 HamsterBall 初始化失败, 服务器将立即关闭"));
|
||||
}
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球初始化完成,总计耗时 " + time + " ms");
|
||||
slf4jLogger.info("仓鼠球初始化完成,总计耗时 " + time + " ms");
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.EARLY)
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||
long start = System.currentTimeMillis();
|
||||
java.util.logging.Logger logger = getLogger();
|
||||
logger.info("仓鼠球正在启动");
|
||||
slf4jLogger.info("仓鼠球正在启动");
|
||||
try {
|
||||
CoreVelocityAPI.getInstance().enable();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "仓鼠球启动失败", e);
|
||||
logger.info("由于仓鼠球启动失败,服务器将立即关闭");
|
||||
slf4jLogger.error("仓鼠球启动失败", e);
|
||||
slf4jLogger.info("由于仓鼠球启动失败,服务器将立即关闭");
|
||||
proxyServer.shutdown(Component.text("仓鼠球启动失败"));
|
||||
return;
|
||||
}
|
||||
CommandMeta commandMeta = proxyServer.getCommandManager()
|
||||
.metaBuilder("hamster-velocity-ball")
|
||||
.aliases("velocity-ball", "vball")
|
||||
.plugin(this)
|
||||
.build();
|
||||
proxyServer.getCommandManager().register(commandMeta, VelocityBallCommand.INSTANCE);
|
||||
BallAPI.getInstance().getEventBus().register(BallVelocityListener.INSTANCE);
|
||||
logger.info("已注册监听器 BallVelocityListener");
|
||||
slf4jLogger.info("已注册监听器 BallVelocityListener");
|
||||
proxyServer.getEventManager().register(this, BallVelocityMainListener.INSTANCE);
|
||||
logger.info("已注册监听器 BallVelocityMainListener");
|
||||
slf4jLogger.info("已注册监听器 BallVelocityMainListener");
|
||||
proxyServer.getEventManager().register(this, UpdatePlayerInfoListener.INSTANCE);
|
||||
logger.info("已注册监听器 UpdatePlayerInfoListener");
|
||||
slf4jLogger.info("已注册监听器 UpdatePlayerInfoListener");
|
||||
if (config.getBoolean("auto-register-game-server", false)) {
|
||||
BallAPI.getInstance().getEventBus().register(VelocityServerListener.INSTANCE);
|
||||
logger.info("已注册监听器 VelocityServerListener");
|
||||
slf4jLogger.info("已注册监听器 VelocityServerListener");
|
||||
VelocityServerListener.INSTANCE.onEnable();
|
||||
}
|
||||
|
||||
@@ -130,20 +136,19 @@ public class HamsterBallPlugin {
|
||||
BallVelocityUtils.uploadPlayerInfo(playerInfo);
|
||||
});
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球启动完成,总计耗时 " + time + " ms");
|
||||
slf4jLogger.info("仓鼠球启动完成,总计耗时 {} ms", time);
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LATE)
|
||||
public void onProxyShutdown(ProxyShutdownEvent event) {
|
||||
long start = System.currentTimeMillis();
|
||||
java.util.logging.Logger logger = getLogger();
|
||||
logger.info("仓鼠球正在关闭");
|
||||
slf4jLogger.info("仓鼠球正在关闭");
|
||||
try {
|
||||
CoreVelocityAPI.getInstance().disable();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "关闭仓鼠球时遇到了一个异常", e);
|
||||
slf4jLogger.error("关闭仓鼠球时遇到了一个异常", e);
|
||||
}
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球已关闭,总计耗时 " + time + " ms");
|
||||
slf4jLogger.info("仓鼠球已关闭,总计耗时 {} ms", time);
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package cn.hamster3.mc.plugin.ball.velocity.command;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.command.BallCommand;
|
||||
import cn.hamster3.mc.plugin.ball.common.command.adapt.AdaptCommandSender;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.command.SimpleCommand;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class VelocityBallCommand implements SimpleCommand {
|
||||
public static final VelocityBallCommand INSTANCE = new VelocityBallCommand();
|
||||
|
||||
private VelocityBallCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Invocation invocation) {
|
||||
BallCommand.INSTANCE.onCommand(adaptCommandSender(invocation.source()), invocation.arguments());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Invocation invocation) {
|
||||
return invocation.source().hasPermission("hamster.ball.admin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(Invocation invocation) {
|
||||
return BallCommand.INSTANCE.onTabComplete(adaptCommandSender(invocation.source()), invocation.arguments());
|
||||
}
|
||||
|
||||
private AdaptCommandSender adaptCommandSender(@NotNull CommandSource source) {
|
||||
return new AdaptCommandSender() {
|
||||
@Override
|
||||
public boolean hasPermission(@NotNull String permission) {
|
||||
return source.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull Component message) {
|
||||
source.sendMessage(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@@ -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);
|
||||
|
@@ -33,6 +33,11 @@ server-info:
|
||||
# 不填则自动设置为 25577
|
||||
port: 25577
|
||||
|
||||
# Redis 配置
|
||||
# 如果注释该选项则默认使用 HamsterCore 中的连接配置
|
||||
# 否则 HamsterBall 将会使用与 HamsterCore 不同的 Redis 链接
|
||||
# redis-url: redis://localhost:6379/0?clientName=HamsterBall&timeout=5s
|
||||
|
||||
# 数据库连接池配置
|
||||
# 如果注释该选项则默认使用 HamsterCore 中的连接池配置
|
||||
# 否则 HamsterBall 将会使用与 HamsterCore 不同的数据库链接
|
||||
|
@@ -2,4 +2,3 @@ VERSION: ${version}
|
||||
CHECK_TYPE: GITEA_RELEASES
|
||||
GIT_BASE_URL: https://git.airgame.net
|
||||
GIT_REPO: MiniDay/hamster-ball
|
||||
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-ball/
|
||||
|
@@ -1,18 +1,24 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven("https://maven.airgame.net/maven-public/")
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("java-library")
|
||||
id("maven-publish")
|
||||
id("com.github.johnrengelman.shadow") version "8+"
|
||||
id("com.gradleup.shadow") version "8.3.6"
|
||||
}
|
||||
|
||||
group = "cn.hamster3.mc.plugin"
|
||||
version = "1.6.5-SNAPSHOT"
|
||||
version = "1.8.1"
|
||||
description = "基于 Redis 的 Minecraft 服务端通用消息中间件"
|
||||
|
||||
subprojects {
|
||||
apply {
|
||||
plugin("java-library")
|
||||
plugin("maven-publish")
|
||||
plugin("com.github.johnrengelman.shadow")
|
||||
plugin("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
group = rootProject.group
|
||||
@@ -59,10 +65,9 @@ subprojects {
|
||||
repositories {
|
||||
maven {
|
||||
url = uri("https://maven.airgame.net/public")
|
||||
|
||||
credentials {
|
||||
username = rootProject.properties.getOrDefault("maven_username", "").toString()
|
||||
password = rootProject.properties.getOrDefault("maven_password", "").toString()
|
||||
username = findProperty("MAVEN_AIRGAME_USERNAME")?.toString() ?: ""
|
||||
password = findProperty("MAVEN_AIRGAME_PASSWORD")?.toString() ?: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
|
||||
|
Reference in New Issue
Block a user