Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
f82de6a99a | |||
7d4394c123 |
20
README.md
20
README.md
@@ -1,6 +1,6 @@
|
||||
# [HamsterBall](https://git.airgame.net/MiniDay/hamster-ball)
|
||||
|
||||
基于 Redis 的 Minecraft 服务端通用消息中间件
|
||||
仓鼠球:一个基于 Redis 的 Minecraft 服务端通用消息中间件(原HamsterService)
|
||||
|
||||
该插件依赖于 [仓鼠核心](https://git.airgame.net/MiniDay/hamster-core)
|
||||
|
||||
@@ -27,10 +27,12 @@
|
||||
|
||||
为了适配 docker 环境,本插件除了从 `config.yml` 中配置服务器信息以外,还支持从环境变量中读取
|
||||
|
||||
| 环境变量 | 描述 | 对应 config 值 |
|
||||
|:-----------------|:-------------------|:-----------------|
|
||||
| BALL_SERVER_ID | 本服务器唯一识别码,最长 32 字符 | server-info.id |
|
||||
| BALL_SERVER_NAME | 本服务端名称,用于展示给玩家看 | server-info.name |
|
||||
| 环境变量 | 描述 | 对应 config 值 |
|
||||
|:----------------------------|:-------------------|:-----------------|
|
||||
| BALL_LOCAL_SERVER_IP | 本服务器 IP | server-info.host |
|
||||
| BALL_LOCAL_SERVER_PORT | 本服务器端口 | server-info.port |
|
||||
| BALL_LOCAL_SERVER_INFO_ID | 本服务器唯一识别码,最长 32 字符 | server-info.id |
|
||||
| BALL_LOCAL_SERVER_INFO_NAME | 本服务端名称,用于展示给玩家看 | server-info.name |
|
||||
|
||||
# 开发
|
||||
|
||||
@@ -49,9 +51,9 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// 对于 Bukkit 插件
|
||||
compileOnly("cn.hamster3.mc.plugin:ball-bukkit:1.6.3")
|
||||
compileOnly("cn.hamster3.mc.plugin:ball-bukkit:1.5.7")
|
||||
// 对于 BungeeCord 插件
|
||||
compileOnly("cn.hamster3.mc.plugin:ball-bungee:1.6.3")
|
||||
compileOnly("cn.hamster3.mc.plugin:ball-bungee:1.5.7")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -77,13 +79,13 @@ dependencies {
|
||||
<dependency>
|
||||
<groupId>cn.hamster3.mc.plugin</groupId>
|
||||
<artifactId>ball-bukkit</artifactId>
|
||||
<version>1.6.3</version>
|
||||
<version>1.5.7</version>
|
||||
</dependency>
|
||||
<!--对于 BungeeCord 插件-->
|
||||
<dependency>
|
||||
<groupId>cn.hamster3.mc.plugin</groupId>
|
||||
<artifactId>ball-bungee</artifactId>
|
||||
<version>1.6.3</version>
|
||||
<version>1.5.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -3,11 +3,15 @@
|
||||
evaluationDependsOn(":ball-common")
|
||||
|
||||
dependencies {
|
||||
api(project(":ball-common")) { isTransitive = false }
|
||||
implementation(project(":ball-common")) {
|
||||
isTransitive = false
|
||||
}
|
||||
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
|
||||
compileOnly("cn.hamster3.mc.plugin:core-bukkit:+")
|
||||
|
||||
compileOnly("me.clip:placeholderapi:2.11.5") { isTransitive = false }
|
||||
compileOnly("cn.hamster3.mc.plugin:core-bukkit:1.2.2")
|
||||
compileOnly("me.clip:placeholderapi:2.11.5") {
|
||||
isTransitive = false
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
@@ -9,19 +9,14 @@ 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.server.ServerOnlineEvent;
|
||||
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public class HamsterBallPlugin extends JavaPlugin {
|
||||
@Getter
|
||||
private static HamsterBallPlugin instance;
|
||||
@@ -44,25 +39,10 @@ public class HamsterBallPlugin extends JavaPlugin {
|
||||
reloadConfig();
|
||||
logger.info("已读取配置文件");
|
||||
try {
|
||||
File dataFolder = getDataFolder();
|
||||
if (dataFolder.mkdir()) {
|
||||
logger.info("已生成插件存档文件夹");
|
||||
}
|
||||
File configFile = new File(dataFolder, "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
Files.copy(
|
||||
Objects.requireNonNull(getResource("config.yml")),
|
||||
configFile.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
);
|
||||
}
|
||||
YamlConfig config = YamlConfig.load(configFile);
|
||||
BallBukkitAPI.init(config);
|
||||
BallBukkitAPI.init();
|
||||
logger.info("已初始化 BallAPI");
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "BallAPI 初始化失败", e);
|
||||
Bukkit.shutdown();
|
||||
return;
|
||||
e.printStackTrace();
|
||||
}
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球初始化完成,总计耗时 " + time + " ms");
|
||||
@@ -76,8 +56,9 @@ public class HamsterBallPlugin extends JavaPlugin {
|
||||
try {
|
||||
BallBukkitAPI.getInstance().enable();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "仓鼠球启动失败", e);
|
||||
logger.info("由于仓鼠球启动失败,服务器将立即关闭");
|
||||
getLogger().info("仓鼠球启动失败,原因:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
getLogger().info("由于仓鼠球启动失败,服务器将立即关闭");
|
||||
Bukkit.shutdown();
|
||||
return;
|
||||
}
|
||||
@@ -100,7 +81,7 @@ public class HamsterBallPlugin extends JavaPlugin {
|
||||
BallBukkitUtils.uploadPlayerInfo(playerInfo);
|
||||
});
|
||||
} else {
|
||||
BallAPI.getInstance().subscribeRaw(BallAPI.PLAYER_INFO_CHANNEL);
|
||||
BallAPI.getInstance().subscribeIgnorePrefix(BallAPI.PLAYER_INFO_CHANNEL);
|
||||
}
|
||||
sync(() -> {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
|
||||
@@ -124,7 +105,7 @@ public class HamsterBallPlugin extends JavaPlugin {
|
||||
try {
|
||||
BallBukkitAPI.getInstance().disable();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "关闭仓鼠球时遇到了一个异常", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球已关闭,总计耗时 " + time + " ms");
|
||||
|
@@ -1,36 +1,73 @@
|
||||
package cn.hamster3.mc.plugin.ball.bukkit.api;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.bukkit.HamsterBallPlugin;
|
||||
import cn.hamster3.mc.plugin.ball.bukkit.util.BallBukkitUtils;
|
||||
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
|
||||
import cn.hamster3.mc.plugin.ball.common.config.BallConfig;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
|
||||
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
|
||||
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
|
||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class BallBukkitAPI extends BallAPI {
|
||||
public BallBukkitAPI(@NotNull ConfigSection config) {
|
||||
super(config, BallServerType.GAME);
|
||||
public BallBukkitAPI(@NotNull BallConfig ballConfig) {
|
||||
super(ballConfig);
|
||||
}
|
||||
|
||||
public static BallBukkitAPI getInstance() {
|
||||
return (BallBukkitAPI) instance;
|
||||
}
|
||||
|
||||
public static void init(@NotNull YamlConfig config) {
|
||||
public static void init() {
|
||||
if (instance != null) {
|
||||
return;
|
||||
}
|
||||
instance = new BallBukkitAPI(config);
|
||||
HamsterBallPlugin plugin = HamsterBallPlugin.getInstance();
|
||||
plugin.saveDefaultConfig();
|
||||
plugin.reloadConfig();
|
||||
|
||||
FileConfiguration config = plugin.getConfig();
|
||||
Map<String, String> env = System.getenv();
|
||||
|
||||
BallServerInfo serverInfo = new BallServerInfo(
|
||||
env.getOrDefault("BALL_LOCAL_SERVER_INFO_ID", config.getString("server-info.id")),
|
||||
env.getOrDefault("BALL_LOCAL_SERVER_INFO_NAME", config.getString("server-info.name")),
|
||||
BallServerType.GAME,
|
||||
env.getOrDefault("BALL_LOCAL_SERVER_IP", config.getString("server-info.host", Bukkit.getIp())),
|
||||
Integer.parseInt(
|
||||
env.getOrDefault("BALL_LOCAL_SERVER_PORT", String.valueOf(config.getInt("server-info.port", Bukkit.getPort())))
|
||||
)
|
||||
);
|
||||
|
||||
DataSource datasource;
|
||||
if (config.contains("datasource")) {
|
||||
plugin.getLogger().info("启用仓鼠球自定义数据库连接池");
|
||||
datasource = BallBukkitUtils.getDataSource(config.getConfigurationSection("datasource"));
|
||||
} else {
|
||||
plugin.getLogger().info("复用 HamsterCore 的数据库连接池");
|
||||
datasource = CoreAPI.getInstance().getDataSource();
|
||||
}
|
||||
|
||||
BallConfig ballConfig = new BallConfig(
|
||||
config.getBoolean("debug", false),
|
||||
config.getString("channel-prefix", "") + ":",
|
||||
config.getBoolean("game-server-update-player-info", false),
|
||||
config.getStringList("load-player-info-filter"),
|
||||
serverInfo,
|
||||
datasource
|
||||
);
|
||||
instance = new BallBukkitAPI(ballConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() throws SQLException, InterruptedException {
|
||||
instance.getLocalServerInfo().setHost(Bukkit.getIp().isEmpty() ? "127.0.0.1" : Bukkit.getIp());
|
||||
instance.getLocalServerInfo().setPort(Bukkit.getPort());
|
||||
super.enable();
|
||||
}
|
||||
|
||||
|
@@ -7,11 +7,11 @@ 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;
|
||||
import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.audience.Audience;
|
||||
import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.text.TextReplacementConfig;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.TextReplacementConfig;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -30,8 +30,8 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public class BallBukkitListener implements Listener {
|
||||
public static final BallBukkitListener INSTANCE = new BallBukkitListener();
|
||||
|
||||
@@ -81,7 +81,7 @@ public class BallBukkitListener implements Listener {
|
||||
}
|
||||
removeCachedPlayerMessage(uuid);
|
||||
} catch (SQLException e) {
|
||||
BallAPI.getInstance().getLogger().log(Level.SEVERE, "发送玩家缓存消息时出现了一个异常", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -5,17 +5,50 @@ import cn.hamster3.mc.plugin.ball.common.entity.BallPlayerInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.BallActions;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerInfoUpdateEvent;
|
||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||
import cn.hamster3.mc.plugin.core.lib.com.zaxxer.hikari.HikariConfig;
|
||||
import cn.hamster3.mc.plugin.core.lib.com.zaxxer.hikari.HikariDataSource;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public final class BallBukkitUtils {
|
||||
private BallBukkitUtils() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static DataSource getDataSource(@Nullable ConfigurationSection datasourceConfig) {
|
||||
if (datasourceConfig == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
HikariConfig hikariConfig = new HikariConfig();
|
||||
String driver = datasourceConfig.getString("driver");
|
||||
hikariConfig.setDriverClassName(driver);
|
||||
hikariConfig.setJdbcUrl(datasourceConfig.getString("url"));
|
||||
hikariConfig.setUsername(datasourceConfig.getString("username"));
|
||||
hikariConfig.setPassword(datasourceConfig.getString("password"));
|
||||
hikariConfig.setMaximumPoolSize(datasourceConfig.getInt("maximum-pool-size", 3));
|
||||
hikariConfig.setMinimumIdle(datasourceConfig.getInt("minimum-idle", 1));
|
||||
long keepAliveTime = datasourceConfig.getLong("keep-alive-time", 0);
|
||||
if (keepAliveTime > 5000) {
|
||||
hikariConfig.setKeepaliveTime(keepAliveTime);
|
||||
}
|
||||
hikariConfig.setIdleTimeout(datasourceConfig.getLong("idle-timeout", 10 * 60 * 1000));
|
||||
hikariConfig.setMaxLifetime(datasourceConfig.getLong("max-lifetime", 30 * 60 * 1000));
|
||||
hikariConfig.setValidationTimeout(datasourceConfig.getLong("validation-timeout", 5000));
|
||||
hikariConfig.setPoolName("HamsterBall-Pool");
|
||||
return new HikariDataSource(hikariConfig);
|
||||
} catch (Exception | Error e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void uploadPlayerInfo(@NotNull BallPlayerInfo playerInfo) {
|
||||
CoreAPI.getInstance().getExecutorService().execute(() -> {
|
||||
try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
|
||||
@@ -30,7 +63,7 @@ public final class BallBukkitUtils {
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
BallAPI.getInstance().getLogger().log(Level.SEVERE, "更新玩家数据时遇到了一个异常", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
BallAPI.getInstance().sendBallMessage(
|
||||
BallAPI.PLAYER_INFO_CHANNEL,
|
||||
|
@@ -11,7 +11,7 @@ channel-prefix: ""
|
||||
# 如果一个群组服同时拥有多个 BC 入口
|
||||
# 且每个 BC 入口为不同的玩家名称分配不同的 UUID
|
||||
# (例如正版、盗版双入口,或网易多入口接同一个子服)
|
||||
# 则可以启用该功能以防止同一个名称占用多个 UUID 的问题
|
||||
# 则可以启用该功能以防止 UUID 紊乱的问题
|
||||
game-server-update-player-info: false
|
||||
|
||||
# 该选项仅在 game-server-update-player-info 为 true 时有效
|
||||
@@ -41,18 +41,16 @@ server-info:
|
||||
# 如果你需要让每个服务器单独存储仓鼠球信息
|
||||
# 这个选项就会很有用
|
||||
#datasource:
|
||||
# # 数据库链接驱动地址,旧版服务端(低于1.13)请使用:com.mysql.jdbc.Driver
|
||||
# driver: "com.mysql.cj.jdbc.Driver"
|
||||
# # MySQL数据库链接填写格式:
|
||||
# # 数据库链接驱动地址
|
||||
# driver: "com.mysql.jdbc.Driver"
|
||||
# # 数据库链接填写格式:
|
||||
# # 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"
|
||||
# # 除非你知道自己在做什么,否则不建议随意更改参数
|
||||
# url: "jdbc:mysql://localhost:3306/Test1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true"
|
||||
# # 用户名
|
||||
# username: "root"
|
||||
# username: "Test"
|
||||
# # 密码
|
||||
# password: "Root123.."
|
||||
# password: "Test123.."
|
||||
# # 最小闲置链接数
|
||||
# # 推荐值:1~3
|
||||
# minimum-idle: 0
|
||||
|
@@ -4,15 +4,8 @@ version: ${version}
|
||||
api-version: 1.13
|
||||
|
||||
author: MiniDay
|
||||
description: ${description}
|
||||
website: https://git.airgame.net/MiniDay/hamster-ball
|
||||
|
||||
UPDATE_CHECKER:
|
||||
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/
|
||||
description: 仓鼠球:一个基于 Redis 的 Minecraft 服务端通用消息中间件(原HamsterService)
|
||||
|
||||
load: STARTUP
|
||||
|
||||
|
@@ -3,9 +3,12 @@
|
||||
evaluationDependsOn(":ball-common")
|
||||
|
||||
dependencies {
|
||||
api(project(":ball-common")) { isTransitive = false }
|
||||
implementation(project(":ball-common")) {
|
||||
isTransitive = false
|
||||
}
|
||||
compileOnly("net.md-5:bungeecord-api:1.20-R0.1")
|
||||
compileOnly("cn.hamster3.mc.plugin:core-bungee:+")
|
||||
|
||||
compileOnly("cn.hamster3.mc.plugin:core-bungee:1.2.2")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
@@ -3,29 +3,22 @@ package cn.hamster3.mc.plugin.ball.bungee;
|
||||
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.BallBungeeMainListener;
|
||||
import cn.hamster3.mc.plugin.ball.bungee.listener.BungeeServerListener;
|
||||
import cn.hamster3.mc.plugin.ball.bungee.listener.UpdatePlayerInfoListener;
|
||||
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.server.ServerOnlineEvent;
|
||||
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public class HamsterBallPlugin extends Plugin {
|
||||
@Getter
|
||||
private static HamsterBallPlugin instance;
|
||||
@Getter
|
||||
private YamlConfig config;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
@@ -34,25 +27,11 @@ public class HamsterBallPlugin extends Plugin {
|
||||
logger.info("仓鼠球正在初始化");
|
||||
instance = this;
|
||||
try {
|
||||
File dataFolder = getDataFolder();
|
||||
if (dataFolder.mkdir()) {
|
||||
logger.info("已生成插件存档文件夹");
|
||||
}
|
||||
File configFile = new File(dataFolder, "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
Files.copy(
|
||||
getResourceAsStream("config.yml"),
|
||||
configFile.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
);
|
||||
}
|
||||
config = YamlConfig.load(configFile);
|
||||
BallBungeeCordAPI.init(config);
|
||||
BallBungeeCordAPI.init();
|
||||
logger.info("已初始化 BallAPI");
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "BallAPI 初始化失败", e);
|
||||
ProxyServer.getInstance().stop("由于 HamsterBall 初始化失败, 服务器将立即关闭");
|
||||
return;
|
||||
e.printStackTrace();
|
||||
ProxyServer.getInstance().stop("由于 HamsterBall 未能成功连接, 服务器将立即关闭");
|
||||
}
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球初始化完成,总计耗时 " + time + " ms");
|
||||
@@ -66,10 +45,7 @@ public class HamsterBallPlugin extends Plugin {
|
||||
try {
|
||||
BallBungeeCordAPI.getInstance().enable();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "仓鼠球启动失败", e);
|
||||
logger.info("由于仓鼠球启动失败,服务器将立即关闭");
|
||||
ProxyServer.getInstance().stop("仓鼠球启动失败");
|
||||
return;
|
||||
e.printStackTrace();
|
||||
}
|
||||
BallAPI.getInstance().getEventBus().register(BallBungeeListener.INSTANCE);
|
||||
logger.info("已注册监听器 BallBungeeListener");
|
||||
@@ -77,16 +53,11 @@ public class HamsterBallPlugin extends Plugin {
|
||||
logger.info("已注册监听器 BallBungeeMainListener");
|
||||
ProxyServer.getInstance().getPluginManager().registerListener(this, UpdatePlayerInfoListener.INSTANCE);
|
||||
logger.info("已注册监听器 UpdatePlayerInfoListener");
|
||||
if (config.getBoolean("auto-register-game-server", false)) {
|
||||
BallAPI.getInstance().getEventBus().register(BungeeServerListener.INSTANCE);
|
||||
logger.info("已注册监听器 BungeeServerListener");
|
||||
BungeeServerListener.INSTANCE.onEnable();
|
||||
}
|
||||
|
||||
if (BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) {
|
||||
BallAPI.getInstance().subscribePatterns("*" + BallAPI.PLAYER_INFO_CHANNEL);
|
||||
} else {
|
||||
BallAPI.getInstance().subscribeRaw(BallAPI.PLAYER_INFO_CHANNEL);
|
||||
BallAPI.getInstance().subscribeIgnorePrefix(BallAPI.PLAYER_INFO_CHANNEL);
|
||||
}
|
||||
BallAPI.getInstance().sendRawBallMessage(
|
||||
BallAPI.BALL_CHANNEL, BallActions.ServerOnline.name(),
|
||||
@@ -113,7 +84,9 @@ public class HamsterBallPlugin extends Plugin {
|
||||
try {
|
||||
BallBungeeCordAPI.getInstance().disable();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "关闭仓鼠球时遇到了一个异常", e);
|
||||
getLogger().info("仓鼠球启动失败,原因:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
ProxyServer.getInstance().stop("由于仓鼠球启动失败,服务器将立即关闭");
|
||||
}
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球已关闭,总计耗时 " + time + " ms");
|
||||
|
@@ -1,44 +1,70 @@
|
||||
package cn.hamster3.mc.plugin.ball.bungee.api;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.bungee.HamsterBallPlugin;
|
||||
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.config.BallConfig;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
|
||||
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
|
||||
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import cn.hamster3.mc.plugin.core.bungee.util.CoreBungeeCordUtils;
|
||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class BallBungeeCordAPI extends BallAPI {
|
||||
public BallBungeeCordAPI(@NotNull ConfigSection config) {
|
||||
super(config, BallServerType.PROXY);
|
||||
public BallBungeeCordAPI(@NotNull BallConfig ballConfig) {
|
||||
super(ballConfig);
|
||||
}
|
||||
|
||||
public static BallBungeeCordAPI getInstance() {
|
||||
return (BallBungeeCordAPI) instance;
|
||||
}
|
||||
|
||||
public static void init(@NotNull YamlConfig config) {
|
||||
public static void init() {
|
||||
if (instance != null) {
|
||||
return;
|
||||
}
|
||||
instance = new BallBungeeCordAPI(config);
|
||||
HamsterBallPlugin plugin = HamsterBallPlugin.getInstance();
|
||||
Configuration config = CoreBungeeCordUtils.getPluginConfig(plugin);
|
||||
Map<String, String> env = System.getenv();
|
||||
|
||||
BallServerInfo serverInfo = new BallServerInfo(
|
||||
env.getOrDefault("BALL_LOCAL_SERVER_INFO_ID", config.getString("server-info.id")),
|
||||
env.getOrDefault("BALL_LOCAL_SERVER_INFO_NAME", config.getString("server-info.name")),
|
||||
BallServerType.PROXY,
|
||||
env.getOrDefault("BALL_LOCAL_SERVER_IP", config.getString("server-info.host", "0.0.0.0")),
|
||||
Integer.parseInt(
|
||||
env.getOrDefault("BALL_LOCAL_SERVER_PORT", config.getString("server-info.port", "25577"))
|
||||
)
|
||||
);
|
||||
|
||||
DataSource datasource;
|
||||
if (config.contains("datasource")) {
|
||||
plugin.getLogger().info("启用仓鼠球自定义数据库连接池");
|
||||
datasource = BallBungeeCordUtils.getDataSource(config.getSection("datasource"));
|
||||
} else {
|
||||
plugin.getLogger().info("复用 HamsterCore 的数据库连接池");
|
||||
datasource = CoreAPI.getInstance().getDataSource();
|
||||
}
|
||||
|
||||
BallConfig ballConfig = new BallConfig(
|
||||
config.getBoolean("debug", false),
|
||||
config.getString("channel-prefix", "") + ":",
|
||||
config.getBoolean("game-server-update-player-info", false),
|
||||
config.getStringList("load-player-info-filter"),
|
||||
serverInfo,
|
||||
datasource
|
||||
);
|
||||
instance = new BallBungeeCordAPI(ballConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() throws SQLException, InterruptedException {
|
||||
for (ListenerInfo listenerInfo : ProxyServer.getInstance().getConfig().getListeners()) {
|
||||
if (!(listenerInfo.getSocketAddress() instanceof InetSocketAddress)) {
|
||||
continue;
|
||||
}
|
||||
InetSocketAddress address = (InetSocketAddress) listenerInfo.getSocketAddress();
|
||||
instance.getLocalServerInfo().setHost(address.getHostString());
|
||||
instance.getLocalServerInfo().setPort(address.getPort());
|
||||
}
|
||||
super.enable();
|
||||
}
|
||||
|
||||
|
@@ -5,9 +5,9 @@ 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.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
@@ -55,9 +55,6 @@ public class BallBungeeListener {
|
||||
@Subscribe
|
||||
public void onKickPlayerEvent(KickPlayerEvent event) {
|
||||
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(event.getUuid());
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
BaseComponent[] components = BungeeComponentSerializer.get().serialize(event.getReason());
|
||||
player.disconnect(components);
|
||||
}
|
||||
@@ -73,8 +70,8 @@ public class BallBungeeListener {
|
||||
@Subscribe
|
||||
public void onSendPlayerToLocationEvent(SendPlayerToLocationEvent event) {
|
||||
String serverID = event.getLocation().getServerID();
|
||||
ServerInfo toServer = ProxyServer.getInstance().getServerInfo(serverID);
|
||||
if (toServer == null) {
|
||||
ServerInfo serverInfo = ProxyServer.getInstance().getServerInfo(serverID);
|
||||
if (serverInfo == null) {
|
||||
HamsterBallPlugin.getInstance().getLogger().warning("试图传送玩家时失败: 服务器 " + serverID + " 不在线");
|
||||
return;
|
||||
}
|
||||
@@ -86,12 +83,12 @@ public class BallBungeeListener {
|
||||
if (player.getServer().getInfo().getName().equals(serverID)) {
|
||||
continue;
|
||||
}
|
||||
player.connect(toServer);
|
||||
player.connect(serverInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onSendPlayerToPlayer(SendPlayerToPlayerEvent event) {
|
||||
public void onSendPlayerToPlayerEvent(SendPlayerToPlayerEvent event) {
|
||||
UUID toPlayerUUID = event.getToPlayerUUID();
|
||||
ProxiedPlayer toPlayer = ProxyServer.getInstance().getPlayer(toPlayerUUID);
|
||||
if (toPlayer == null) {
|
||||
@@ -101,9 +98,6 @@ public class BallBungeeListener {
|
||||
ServerInfo toServer = toPlayer.getServer().getInfo();
|
||||
for (UUID uuid : event.getSendPlayerUUID()) {
|
||||
ProxiedPlayer sendPlayer = ProxyServer.getInstance().getPlayer(uuid);
|
||||
if (sendPlayer == null) {
|
||||
continue;
|
||||
}
|
||||
if (sendPlayer.getServer().getInfo().getName().equals(toServer.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
@@ -1,59 +0,0 @@
|
||||
package cn.hamster3.mc.plugin.ball.bungee.listener;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOfflineEvent;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Map;
|
||||
|
||||
public class BungeeServerListener {
|
||||
public static final BungeeServerListener INSTANCE = new BungeeServerListener();
|
||||
|
||||
private BungeeServerListener() {
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
for (BallServerInfo info : BallAPI.getInstance().getAllServerInfo().values()) {
|
||||
if (info.getType() != BallServerType.GAME) {
|
||||
continue;
|
||||
}
|
||||
ProxyServer.getInstance().getServers().put(info.getId(), getServerInfo(info));
|
||||
BallAPI.getInstance().getLogger().info("已添加服务器入口: " + info.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onServerOnline(ServerOnlineEvent event) {
|
||||
if (event.getType() != BallServerType.GAME) {
|
||||
return;
|
||||
}
|
||||
ProxyServer.getInstance().getServers().put(event.getId(), getServerInfo(event));
|
||||
BallAPI.getInstance().getLogger().info("已添加服务器入口: " + event.getId());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onServerOffline(ServerOfflineEvent event) {
|
||||
if (event.getType() != BallServerType.GAME) {
|
||||
return;
|
||||
}
|
||||
Map<String, ServerInfo> map = ProxyServer.getInstance().getServers();
|
||||
if (map.remove(event.getId()) != null) {
|
||||
BallAPI.getInstance().getLogger().info("已移除服务器入口: " + event.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private ServerInfo getServerInfo(BallServerInfo serverInfo) {
|
||||
return ProxyServer.getInstance().constructServerInfo(
|
||||
serverInfo.getId(),
|
||||
new InetSocketAddress(serverInfo.getHost(), serverInfo.getPort()),
|
||||
serverInfo.getName(),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
@@ -5,15 +5,20 @@ import cn.hamster3.mc.plugin.ball.common.entity.BallPlayerInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.BallActions;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerInfoUpdateEvent;
|
||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||
import cn.hamster3.mc.plugin.core.lib.com.zaxxer.hikari.HikariConfig;
|
||||
import cn.hamster3.mc.plugin.core.lib.com.zaxxer.hikari.HikariDataSource;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
public final class BallBungeeCordUtils {
|
||||
private BallBungeeCordUtils() {
|
||||
}
|
||||
@@ -44,7 +49,7 @@ public final class BallBungeeCordUtils {
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
BallAPI.getInstance().getLogger().log(Level.SEVERE, "更新玩家数据时遇到了一个异常", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) {
|
||||
BallAPI.getInstance().sendRawBallMessage(
|
||||
@@ -55,4 +60,32 @@ public final class BallBungeeCordUtils {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static DataSource getDataSource(@Nullable Configuration datasourceConfig) {
|
||||
if (datasourceConfig == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
HikariConfig hikariConfig = new HikariConfig();
|
||||
String driver = datasourceConfig.getString("driver");
|
||||
hikariConfig.setDriverClassName(driver);
|
||||
hikariConfig.setJdbcUrl(datasourceConfig.getString("url"));
|
||||
hikariConfig.setUsername(datasourceConfig.getString("username"));
|
||||
hikariConfig.setPassword(datasourceConfig.getString("password"));
|
||||
hikariConfig.setMaximumPoolSize(datasourceConfig.getInt("maximum-pool-size", 3));
|
||||
hikariConfig.setMinimumIdle(datasourceConfig.getInt("minimum-idle", 1));
|
||||
long keepAliveTime = datasourceConfig.getLong("keep-alive-time", 0);
|
||||
if (keepAliveTime > 5000) {
|
||||
hikariConfig.setKeepaliveTime(keepAliveTime);
|
||||
}
|
||||
hikariConfig.setIdleTimeout(datasourceConfig.getLong("idle-timeout", 10 * 60 * 1000));
|
||||
hikariConfig.setMaxLifetime(datasourceConfig.getLong("max-lifetime", 30 * 60 * 1000));
|
||||
hikariConfig.setValidationTimeout(datasourceConfig.getLong("validation-timeout", 5000));
|
||||
hikariConfig.setPoolName("HamsterBall-Pool");
|
||||
return new HikariDataSource(hikariConfig);
|
||||
} catch (Exception | Error e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,15 +3,7 @@ main: cn.hamster3.mc.plugin.ball.bungee.HamsterBallPlugin
|
||||
version: ${version}
|
||||
|
||||
author: MiniDay
|
||||
description: ${description}
|
||||
website: https://git.airgame.net/MiniDay/hamster-ball
|
||||
|
||||
UPDATE_CHECKER:
|
||||
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/
|
||||
description: 仓鼠球:一个基于 Redis 的 Minecraft 服务端通用消息中间件(原HamsterService)
|
||||
|
||||
depend:
|
||||
- HamsterCore
|
||||
|
@@ -8,14 +8,12 @@ channel-prefix: ""
|
||||
|
||||
# 是否在子服端更新玩家信息
|
||||
# 默认情况下,BC 统一管理玩家信息,包括记录 UUID 和玩家名称
|
||||
# 如果一个群组服同时拥有多个 BC 入口,且每个 BC 入口为不同的玩家名称分配不同的 UUID
|
||||
# 如果一个群组服同时拥有多个 BC 入口
|
||||
# 且每个 BC 入口为不同的玩家名称分配不同的 UUID
|
||||
# (例如正版、盗版双入口,或网易多入口接同一个子服)
|
||||
# 则可以启用该功能以防止同一个名称占用多个 UUID 的问题
|
||||
# 则可以启用该功能以防止 UUID 紊乱的问题
|
||||
game-server-update-player-info: false
|
||||
|
||||
# 启用后,子服启动时会自动注册该子服的入口配置,关闭时也会自动移除该子服的入口配置
|
||||
auto-register-game-server: false
|
||||
|
||||
# 本服务器信息
|
||||
server-info:
|
||||
# 服务器唯一识别码,最长 32 字符
|
||||
@@ -36,20 +34,21 @@ server-info:
|
||||
# 这个选项就会很有用
|
||||
#datasource:
|
||||
# # 数据库链接驱动地址
|
||||
# driver: "com.mysql.cj.jdbc.Driver"
|
||||
# # MySQL数据库链接填写格式:
|
||||
# driver: "com.mysql.jdbc.Driver"
|
||||
# # 数据库链接填写格式:
|
||||
# # jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数
|
||||
# url: "jdbc:mysql://localhost:3306/Test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true"
|
||||
# # 除非你知道自己在做什么,否则不建议随意更改参数
|
||||
# url: "jdbc:mysql://localhost:3306/Test1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true"
|
||||
# # 用户名
|
||||
# username: "root"
|
||||
# username: "Test"
|
||||
# # 密码
|
||||
# password: "Root123.."
|
||||
# password: "Test123.."
|
||||
# # 最小闲置链接数
|
||||
# # 推荐值:1~3
|
||||
# minimum-idle: 0
|
||||
# # 最大链接数
|
||||
# # 推荐值:不低于5
|
||||
# maximum-pool-size: 5
|
||||
# # 推荐值:不低于3
|
||||
# maximum-pool-size: 3
|
||||
# # 保持连接池可用的间隔
|
||||
# # 除非你的服务器数据库连接经常断开,否则不建议启用该选项
|
||||
# # 单位:毫秒
|
||||
|
@@ -1,7 +1,7 @@
|
||||
@file:Suppress("VulnerableLibrariesLocal")
|
||||
@file:Suppress("VulnerableLibrariesLocal", "GradlePackageVersionRange", "GradlePackageUpdate")
|
||||
|
||||
dependencies {
|
||||
compileOnly("cn.hamster3.mc.plugin:core-common:+")
|
||||
compileOnly("cn.hamster3.mc.plugin:core-common:1.2.0")
|
||||
|
||||
compileOnly("com.google.code.gson:gson:2.8.0")
|
||||
compileOnly("com.google.guava:guava:31.0-jre")
|
||||
|
@@ -13,30 +13,24 @@ import cn.hamster3.mc.plugin.ball.common.event.server.ServerOfflineEvent;
|
||||
import cn.hamster3.mc.plugin.ball.common.listener.BallCommonListener;
|
||||
import cn.hamster3.mc.plugin.ball.common.listener.BallDebugListener;
|
||||
import cn.hamster3.mc.plugin.ball.common.listener.BallRedisListener;
|
||||
import cn.hamster3.mc.plugin.ball.common.thread.LockUpdateThread;
|
||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
|
||||
import cn.hamster3.mc.plugin.core.common.data.DisplayMessage;
|
||||
import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
|
||||
import cn.hamster3.mc.plugin.core.lib.io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
|
||||
import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.text.Component;
|
||||
import com.google.common.eventbus.AsyncEventBus;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
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.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Getter
|
||||
@SuppressWarnings({"unused", "CallToPrintStackTrace"})
|
||||
public abstract class BallAPI {
|
||||
/**
|
||||
* API 使用的通信频道
|
||||
@@ -46,53 +40,32 @@ public abstract class BallAPI {
|
||||
* API 使用的玩家信息更新通信频道
|
||||
*/
|
||||
public static final String PLAYER_INFO_CHANNEL = "HamsterBall:PlayerInfo";
|
||||
|
||||
/**
|
||||
* API 实例
|
||||
*/
|
||||
@Getter
|
||||
protected static BallAPI instance;
|
||||
|
||||
@NotNull
|
||||
private final BallConfig ballConfig;
|
||||
@NotNull
|
||||
private final DataSource datasource;
|
||||
@NotNull
|
||||
private final BallServerInfo localServerInfo;
|
||||
|
||||
@NotNull
|
||||
private final EventBus eventBus;
|
||||
|
||||
@NotNull
|
||||
private final Map<String, BallServerInfo> allServerInfo;
|
||||
@NotNull
|
||||
private final Map<UUID, BallPlayerInfo> allPlayerInfo;
|
||||
|
||||
@NotNull
|
||||
private final Jedis redisSub;
|
||||
@Nullable
|
||||
private ScheduledFuture<?> lockUpdater;
|
||||
private final StatefulRedisPubSubConnection<String, BallMessage> redisPub;
|
||||
@NotNull
|
||||
private final StatefulRedisPubSubConnection<String, BallMessage> redisSub;
|
||||
|
||||
public BallAPI(@NotNull ConfigSection config, @NotNull BallServerType type) {
|
||||
ConfigSection serverInfoConfig = config.getSection("server-info");
|
||||
if (serverInfoConfig == null) {
|
||||
throw new IllegalArgumentException("配置文件中未找到 server-info 节点");
|
||||
}
|
||||
localServerInfo = new BallServerInfo(serverInfoConfig, type);
|
||||
ConfigSection section = config.getSection("datasource");
|
||||
if (section != null) {
|
||||
getLogger().info("启用仓鼠球自定义数据库连接池");
|
||||
datasource = CoreUtils.getDataSource(section);
|
||||
} else {
|
||||
getLogger().info("复用 HamsterCore 的数据库连接池");
|
||||
datasource = CoreAPI.getInstance().getDataSource();
|
||||
}
|
||||
ballConfig = new BallConfig(config);
|
||||
eventBus = new AsyncEventBus("HamsterBall", CoreAPI.getInstance().getExecutorService());
|
||||
eventBus.register(BallCommonListener.INSTANCE);
|
||||
public BallAPI(@NotNull BallConfig ballConfig) {
|
||||
this.ballConfig = ballConfig;
|
||||
redisPub = CoreAPI.getInstance().getRedisClient().connectPubSub(BallMessage.REDIS_CODEC);
|
||||
redisSub = CoreAPI.getInstance().getRedisClient().connectPubSub(BallMessage.REDIS_CODEC);
|
||||
allServerInfo = new ConcurrentHashMap<>();
|
||||
allPlayerInfo = new ConcurrentHashMap<>();
|
||||
redisSub = CoreAPI.getInstance().getJedisPool().getResource();
|
||||
eventBus = new AsyncEventBus("HamsterBall - EventBus", CoreAPI.getInstance().getExecutorService());
|
||||
eventBus.register(BallCommonListener.INSTANCE);
|
||||
getLogger().info("频道前缀: " + ballConfig.getChannelPrefix());
|
||||
getLogger().info("启用子服更新玩家状态: " + ballConfig.isGameServerUpdatePlayerInfo());
|
||||
if (ballConfig.isGameServerUpdatePlayerInfo()) {
|
||||
@@ -105,29 +78,8 @@ public abstract class BallAPI {
|
||||
}
|
||||
|
||||
protected void enable() throws SQLException, InterruptedException {
|
||||
try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
|
||||
String key = "HamsterBall:ServerInfo:" + localServerInfo.getId();
|
||||
if (jedis.exists(key)) {
|
||||
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);
|
||||
lockUpdater = CoreAPI.getInstance().getScheduledService().scheduleAtFixedRate(LockUpdateThread.INSTANCE, 1, 1, TimeUnit.MINUTES);
|
||||
for (String serverInfoKey : jedis.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"))
|
||||
);
|
||||
allServerInfo.put(info.getId(), info);
|
||||
}
|
||||
}
|
||||
BallServerInfo localInfo = getLocalServerInfo();
|
||||
|
||||
try (Connection connection = getDatasource().getConnection()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.execute("CREATE TABLE IF NOT EXISTS `hamster_ball_player_info`(" +
|
||||
@@ -137,11 +89,44 @@ public abstract class BallAPI {
|
||||
"`proxy_server` VARCHAR(32) NOT NULL," +
|
||||
"`online` BOOLEAN NOT NULL" +
|
||||
") CHARSET utf8mb4;");
|
||||
statement.execute("CREATE TABLE IF NOT EXISTS `hamster_ball_server_info`(" +
|
||||
"`id` VARCHAR(32) PRIMARY KEY NOT NULL," +
|
||||
"`name` VARCHAR(32) NOT NULL," +
|
||||
"`type` VARCHAR(16) NOT NULL," +
|
||||
"`host` VARCHAR(32) NOT NULL," +
|
||||
"`port` INT 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;");
|
||||
}
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"REPLACE INTO `hamster_ball_server_info` VALUES(?, ?, ?, ?, ?);"
|
||||
)) {
|
||||
statement.setString(1, localInfo.getId());
|
||||
statement.setString(2, localInfo.getName());
|
||||
statement.setString(3, localInfo.getType().name());
|
||||
statement.setString(4, localInfo.getHost());
|
||||
statement.setInt(5, localInfo.getPort());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT * FROM `hamster_ball_server_info`;"
|
||||
)) {
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
String serverID = set.getString("id");
|
||||
allServerInfo.put(serverID, new BallServerInfo(
|
||||
serverID,
|
||||
set.getString("name"),
|
||||
BallServerType.valueOf(set.getString("type")),
|
||||
set.getString("host"),
|
||||
set.getInt("port")
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getBallConfig().isGameServerUpdatePlayerInfo()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
try (ResultSet set = statement.executeQuery(String.format(
|
||||
@@ -179,22 +164,22 @@ public abstract class BallAPI {
|
||||
}
|
||||
getLogger().info("从数据库中加载了 " + allServerInfo.size() + " 条服务器信息");
|
||||
getLogger().info("从数据库中加载了 " + allPlayerInfo.size() + " 条玩家信息");
|
||||
subscribeRaw(BALL_CHANNEL);
|
||||
redisPub.addListener(BallRedisListener.INSTANCE);
|
||||
subscribeIgnorePrefix(BALL_CHANNEL);
|
||||
}
|
||||
|
||||
protected void disable() throws SQLException, InterruptedException {
|
||||
sendBallMessage(BallAPI.BALL_CHANNEL, new BallMessage(
|
||||
BallActions.ServerOffline.name(), new ServerOfflineEvent(getLocalServerInfo())
|
||||
), false, true);
|
||||
if (lockUpdater != null) {
|
||||
lockUpdater.cancel(true);
|
||||
lockUpdater = null;
|
||||
try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
|
||||
String key = "HamsterBall:ServerInfo:" + localServerInfo.getId();
|
||||
jedis.del(key);
|
||||
}
|
||||
}
|
||||
|
||||
try (Connection connection = getDatasource().getConnection()) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `hamster_ball_server_info` WHERE `id`=?;"
|
||||
)) {
|
||||
statement.setString(1, getLocalServerId());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"UPDATE `hamster_ball_player_info` SET `online`=false WHERE `game_server`=? OR `proxy_server`=?"
|
||||
)) {
|
||||
@@ -203,7 +188,6 @@ public abstract class BallAPI {
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
redisSub.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,7 +321,7 @@ public abstract class BallAPI {
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
BallAPI.getInstance().getLogger().log(Level.SEVERE, "更新玩家缓存消息时遇到了一个异常", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -486,15 +470,13 @@ 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));
|
||||
}
|
||||
redisSub.sync().publish(channel, 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));
|
||||
redisSub.async().publish(channel, message).whenComplete((aLong, throwable) -> {
|
||||
if (throwable != null) {
|
||||
return;
|
||||
}
|
||||
eventBus.post(new MessageSentEvent(finalChannel, message));
|
||||
});
|
||||
@@ -506,28 +488,22 @@ public abstract class BallAPI {
|
||||
* <p>
|
||||
* 会自动加上 config 中设置的频道前缀
|
||||
*
|
||||
* @param channels 频道名称
|
||||
* @param channel 频道名称
|
||||
*/
|
||||
public void subscribe(@NotNull String... channels) {
|
||||
for (int i = 0; i < channels.length; i++) {
|
||||
channels[i] = ballConfig.getChannelPrefix() + channels[i];
|
||||
public void subscribe(@NotNull String... channel) {
|
||||
for (int i = 0; i < channel.length; i++) {
|
||||
channel[i] = ballConfig.getChannelPrefix() + channel[i];
|
||||
}
|
||||
subscribeRaw(channels);
|
||||
redisPub.sync().subscribe(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略频道前缀配置,订阅 redis 消息频道
|
||||
*
|
||||
* @param channels 频道名称
|
||||
* @param channel 频道名称
|
||||
*/
|
||||
public void subscribeRaw(@NotNull String... channels) {
|
||||
CoreAPI.getInstance().getExecutorService().submit(() -> {
|
||||
try {
|
||||
redisSub.subscribe(BallRedisListener.INSTANCE, channels);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
public void subscribeIgnorePrefix(@NotNull String... channel) {
|
||||
redisPub.sync().subscribe(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -536,15 +512,11 @@ public abstract class BallAPI {
|
||||
* @param patterns 频道名称正则表达式
|
||||
*/
|
||||
public void subscribePatterns(@NotNull String patterns) {
|
||||
CoreAPI.getInstance().getExecutorService().submit(
|
||||
() -> redisSub.psubscribe(BallRedisListener.INSTANCE, patterns)
|
||||
);
|
||||
redisPub.sync().psubscribe(patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅 redis 消息频道
|
||||
* <p>
|
||||
* 会自动加上 config 中设置的频道前缀
|
||||
* 取消订阅 redis 频道
|
||||
*
|
||||
* @param channel 频道名称
|
||||
*/
|
||||
@@ -552,16 +524,16 @@ public abstract class BallAPI {
|
||||
for (int i = 0; i < channel.length; i++) {
|
||||
channel[i] = ballConfig.getChannelPrefix() + channel[i];
|
||||
}
|
||||
BallRedisListener.INSTANCE.unsubscribe(channel);
|
||||
redisPub.sync().unsubscribe(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略频道前缀配置,取消订阅 redis 消息频道
|
||||
* 忽略仓鼠球频道前缀配置,取消订阅 redis 频道
|
||||
*
|
||||
* @param channel 频道名称
|
||||
*/
|
||||
public void unsubscribeIgnorePrefix(@NotNull String... channel) {
|
||||
BallRedisListener.INSTANCE.unsubscribe(channel);
|
||||
redisPub.sync().unsubscribe(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -570,12 +542,22 @@ public abstract class BallAPI {
|
||||
* @param patterns 频道名称正则表达式
|
||||
*/
|
||||
public void unsubscribePatterns(@NotNull String patterns) {
|
||||
BallRedisListener.INSTANCE.punsubscribe(patterns);
|
||||
redisPub.sync().punsubscribe(patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地服务器ID
|
||||
*
|
||||
* @return 服务器ID
|
||||
*/
|
||||
@NotNull
|
||||
public BallServerInfo getLocalServerInfo() {
|
||||
return ballConfig.getServerInfo();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getLocalServerId() {
|
||||
return localServerInfo.getId();
|
||||
return ballConfig.getServerInfo().getId();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -692,4 +674,9 @@ public abstract class BallAPI {
|
||||
|
||||
@NotNull
|
||||
public abstract Logger getLogger();
|
||||
|
||||
@NotNull
|
||||
public DataSource getDatasource() {
|
||||
return ballConfig.getDatasource() == null ? CoreAPI.getInstance().getDataSource() : ballConfig.getDatasource();
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.config;
|
||||
|
||||
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@@ -15,12 +17,8 @@ public class BallConfig {
|
||||
private String channelPrefix;
|
||||
private boolean gameServerUpdatePlayerInfo;
|
||||
private List<String> loadPlayerInfoFilter;
|
||||
|
||||
public BallConfig(@NotNull ConfigSection config) {
|
||||
debug = config.getBoolean("debug", false);
|
||||
channelPrefix = config.getString("channel-prefix", "");
|
||||
channelPrefix = channelPrefix.isEmpty() ? channelPrefix : channelPrefix + ":";
|
||||
gameServerUpdatePlayerInfo = config.getBoolean("game-server-update-player-info", false);
|
||||
loadPlayerInfoFilter = config.getStringList("load-player-info-filter");
|
||||
}
|
||||
@NotNull
|
||||
private BallServerInfo serverInfo;
|
||||
@Nullable
|
||||
private DataSource datasource;
|
||||
}
|
||||
|
@@ -3,10 +3,11 @@ package cn.hamster3.mc.plugin.ball.common.data;
|
||||
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
|
||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||
import cn.hamster3.mc.plugin.core.lib.io.lettuce.core.codec.RedisCodec;
|
||||
import cn.hamster3.mc.plugin.core.lib.io.lettuce.core.codec.StringCodec;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
@@ -15,6 +16,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -23,9 +25,33 @@ import java.util.UUID;
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuppressWarnings("unused")
|
||||
public class BallMessage {
|
||||
/**
|
||||
* lettuce 编解码器
|
||||
*/
|
||||
public static final RedisCodec<String, BallMessage> REDIS_CODEC = new RedisCodec<String, BallMessage>() {
|
||||
@Override
|
||||
public String decodeKey(ByteBuffer bytes) {
|
||||
return StringCodec.UTF8.decodeKey(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BallMessage decodeValue(ByteBuffer bytes) {
|
||||
String string = StringCodec.UTF8.decodeValue(bytes);
|
||||
return CoreAPI.getInstance().getGson().fromJson(string, BallMessage.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer encodeKey(String key) {
|
||||
return StringCodec.UTF8.encodeKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer encodeValue(BallMessage value) {
|
||||
return StringCodec.UTF8.encodeValue(CoreAPI.getInstance().getGson().toJson(value));
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 消息发送者
|
||||
*/
|
||||
@@ -74,6 +100,34 @@ public class BallMessage {
|
||||
this.content = CoreAPI.getInstance().getGson().toJsonTree(content);
|
||||
}
|
||||
|
||||
public BallMessage(@NotNull String senderID, @Nullable String receiverID, @Nullable BallServerType receiverType, @NotNull String action, @Nullable JsonElement content) {
|
||||
this.senderID = senderID;
|
||||
this.receiverID = receiverID;
|
||||
this.receiverType = receiverType;
|
||||
this.action = action;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化至 Json
|
||||
*
|
||||
* @return json对象
|
||||
*/
|
||||
@NotNull
|
||||
public JsonObject toJson() {
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty("senderID", senderID);
|
||||
if (receiverID != null) {
|
||||
object.addProperty("toServer", receiverID);
|
||||
}
|
||||
if (receiverType != null) {
|
||||
object.addProperty("toServer", receiverType.name());
|
||||
}
|
||||
object.addProperty("action", action);
|
||||
object.add("content", content);
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以 Java 对象获取消息内容
|
||||
*
|
||||
@@ -145,6 +199,6 @@ public class BallMessage {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return CoreAPI.getInstance().getGson().toJson(this);
|
||||
return toJson().toString();
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,9 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.entity;
|
||||
|
||||
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -43,15 +40,6 @@ public class BallServerInfo {
|
||||
*/
|
||||
private int port;
|
||||
|
||||
public BallServerInfo(@NotNull ConfigSection config, @NotNull BallServerType type) {
|
||||
Map<String, String> env = System.getenv();
|
||||
id = env.getOrDefault("BALL_SERVER_ID", config.getString("id"));
|
||||
name = env.getOrDefault("BALL_SERVER_NAME", config.getString("name"));
|
||||
this.type = type;
|
||||
host = "0.0.0.0";
|
||||
port = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
@@ -13,6 +13,7 @@ public enum BallActions {
|
||||
BallPlayerLogin,
|
||||
BallPlayerPostLogin,
|
||||
BallPlayerPreConnectServer,
|
||||
BallPlayerConnectServer,
|
||||
BallPlayerPostConnectServer,
|
||||
BallPlayerLogout,
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.event.message;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.data.BallMessage;
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -22,4 +23,11 @@ public class MessageEvent extends BallMessage {
|
||||
setAction(message.getAction());
|
||||
setContent(message.getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull JsonObject toJson() {
|
||||
JsonObject object = super.toJson();
|
||||
object.addProperty("channel", channel);
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.event.operate;
|
||||
|
||||
import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.text.Component;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@@ -0,0 +1,27 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.event.player;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallPlayerInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* 玩家进入子服
|
||||
* <p>
|
||||
* 仅在使用 velocity 代理端时才会触发这个事件
|
||||
*
|
||||
* @see BallPlayerPreConnectServerEvent 玩家准备进入子服
|
||||
* @see BallPlayerConnectServerEvent 玩家进入子服
|
||||
* @see BallPlayerPostConnectServerEvent 玩家已经进入子服
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class BallPlayerConnectServerEvent {
|
||||
@NotNull
|
||||
private final BallPlayerInfo playerInfo;
|
||||
@Nullable
|
||||
private final String from;
|
||||
@NotNull
|
||||
private final String to;
|
||||
}
|
@@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
* 玩家已经进入子服
|
||||
*
|
||||
* @see BallPlayerPreConnectServerEvent 玩家准备进入子服
|
||||
* @see BallPlayerConnectServerEvent 玩家进入子服
|
||||
* @see BallPlayerPostConnectServerEvent 玩家已经进入子服
|
||||
*/
|
||||
@Data
|
||||
|
@@ -10,6 +10,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
* 玩家准备进入子服
|
||||
*
|
||||
* @see BallPlayerPreConnectServerEvent 玩家准备进入子服
|
||||
* @see BallPlayerConnectServerEvent 玩家进入子服
|
||||
* @see BallPlayerPostConnectServerEvent 玩家已经进入子服
|
||||
*/
|
||||
@Data
|
||||
|
@@ -1,18 +0,0 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.event.server;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 服务器上线
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BallServerEvent extends BallServerInfo {
|
||||
public BallServerEvent(BallServerInfo info) {
|
||||
super(info.getId(), info.getName(), info.getType(), info.getHost(), info.getPort());
|
||||
}
|
||||
}
|
@@ -3,16 +3,19 @@ package cn.hamster3.mc.plugin.ball.common.event.server;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* 服务器离线
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ServerOfflineEvent extends BallServerEvent {
|
||||
public ServerOfflineEvent(BallServerInfo info) {
|
||||
super(info);
|
||||
public class ServerOfflineEvent {
|
||||
@NotNull
|
||||
private final BallServerInfo serverInfo;
|
||||
|
||||
@NotNull
|
||||
public String getServerID() {
|
||||
return serverInfo.getId();
|
||||
}
|
||||
}
|
||||
|
@@ -3,16 +3,15 @@ package cn.hamster3.mc.plugin.ball.common.event.server;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* 服务器上线
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ServerOnlineEvent extends BallServerEvent {
|
||||
public ServerOnlineEvent(BallServerInfo info) {
|
||||
super(info);
|
||||
}
|
||||
public class ServerOnlineEvent {
|
||||
@NotNull
|
||||
private final BallServerInfo serverInfo;
|
||||
|
||||
}
|
||||
|
@@ -83,6 +83,11 @@ public class BallCommonListener {
|
||||
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);
|
||||
@@ -120,6 +125,12 @@ public class BallCommonListener {
|
||||
BallAPI.getInstance().getEventBus().post(event);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onBallPlayerConnectServer(BallPlayerConnectServerEvent event) {
|
||||
BallPlayerInfo info = event.getPlayerInfo();
|
||||
BallAPI.getInstance().getAllPlayerInfo().put(info.getUuid(), info);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onBallPlayerInfoUpdate(BallPlayerInfoUpdateEvent event) {
|
||||
BallPlayerInfo info = event.getPlayerInfo();
|
||||
@@ -128,11 +139,12 @@ public class BallCommonListener {
|
||||
|
||||
@Subscribe
|
||||
public void onServerOnline(ServerOnlineEvent event) {
|
||||
BallAPI.getInstance().getAllServerInfo().put(event.getId(), event);
|
||||
switch (event.getType()) {
|
||||
BallServerInfo info = event.getServerInfo();
|
||||
BallAPI.getInstance().getAllServerInfo().put(info.getId(), info);
|
||||
switch (info.getType()) {
|
||||
case GAME: {
|
||||
BallAPI.getInstance().getAllPlayerInfo().forEach((uuid, playerInfo) -> {
|
||||
if (playerInfo.getGameServer().equals(event.getId())) {
|
||||
if (playerInfo.getGameServer().equals(info.getId())) {
|
||||
playerInfo.setOnline(false);
|
||||
}
|
||||
});
|
||||
@@ -140,7 +152,7 @@ public class BallCommonListener {
|
||||
}
|
||||
case PROXY: {
|
||||
BallAPI.getInstance().getAllPlayerInfo().forEach((uuid, playerInfo) -> {
|
||||
if (playerInfo.getProxyServer().equals(event.getId())) {
|
||||
if (playerInfo.getProxyServer().equals(info.getId())) {
|
||||
playerInfo.setOnline(false);
|
||||
}
|
||||
});
|
||||
@@ -151,7 +163,7 @@ public class BallCommonListener {
|
||||
|
||||
@Subscribe
|
||||
public void onServerOffline(ServerOfflineEvent event) {
|
||||
String serverID = event.getId();
|
||||
String serverID = event.getServerID();
|
||||
BallServerInfo info = BallAPI.getInstance().getAllServerInfo().remove(serverID);
|
||||
if (info == null) {
|
||||
return;
|
||||
|
@@ -3,62 +3,57 @@ package cn.hamster3.mc.plugin.ball.common.listener;
|
||||
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
|
||||
import cn.hamster3.mc.plugin.ball.common.data.BallMessage;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.message.MessageReceivedEvent;
|
||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||
import cn.hamster3.mc.plugin.core.lib.io.lettuce.core.pubsub.RedisPubSubListener;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import redis.clients.jedis.JedisPubSub;
|
||||
|
||||
public class BallRedisListener extends JedisPubSub {
|
||||
public class BallRedisListener implements RedisPubSubListener<String, BallMessage> {
|
||||
public static final BallRedisListener INSTANCE = new BallRedisListener();
|
||||
|
||||
private BallRedisListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String channel, String message) {
|
||||
CoreAPI.getInstance().getExecutorService().submit(() -> {
|
||||
try {
|
||||
String finalChannel = channel;
|
||||
if (finalChannel.startsWith(BallAPI.getInstance().getBallConfig().getChannelPrefix())) {
|
||||
finalChannel = finalChannel.substring(BallAPI.getInstance().getBallConfig().getChannelPrefix().length());
|
||||
}
|
||||
BallMessage ballMessage = CoreAPI.getInstance().getGson().fromJson(message, BallMessage.class);
|
||||
BallAPI ballAPI = BallAPI.getInstance();
|
||||
EventBus eventBus = ballAPI.getEventBus();
|
||||
if (ballMessage.getReceiverType() != null && ballMessage.getReceiverType() != ballAPI.getLocalServerInfo().getType()) {
|
||||
return;
|
||||
}
|
||||
if (ballMessage.getReceiverID() != null && !ballAPI.isLocalServer(ballMessage.getReceiverID())) {
|
||||
return;
|
||||
}
|
||||
eventBus.post(new MessageReceivedEvent(finalChannel, ballMessage));
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
public void message(String channel, BallMessage ballMessage) {
|
||||
if (channel.startsWith(BallAPI.getInstance().getBallConfig().getChannelPrefix())) {
|
||||
channel = channel.substring(BallAPI.getInstance().getBallConfig().getChannelPrefix().length());
|
||||
}
|
||||
BallAPI ballAPI = BallAPI.getInstance();
|
||||
EventBus eventBus = ballAPI.getEventBus();
|
||||
if (ballMessage.getReceiverType() != null && ballMessage.getReceiverType() != ballAPI.getLocalServerInfo().getType()) {
|
||||
return;
|
||||
}
|
||||
if (ballMessage.getReceiverID() != null && !ballAPI.isLocalServer(ballMessage.getReceiverID())) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
eventBus.post(new MessageReceivedEvent(channel, ballMessage));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPMessage(String pattern, String channel, String message) {
|
||||
onMessage(channel, message);
|
||||
public void message(String pattern, String channel, BallMessage info) {
|
||||
message(channel, info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(String channel, int subscribedChannels) {
|
||||
public void subscribed(String channel, long count) {
|
||||
BallAPI.getInstance().getLogger().info("已订阅 redis 频道 " + channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnsubscribe(String channel, int subscribedChannels) {
|
||||
public void psubscribed(String pattern, long count) {
|
||||
BallAPI.getInstance().getLogger().info("已取消订阅 redis 频道(正则) " + pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribed(String channel, long count) {
|
||||
BallAPI.getInstance().getLogger().info("已取消订阅 redis 频道 " + channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPSubscribe(String pattern, int subscribedChannels) {
|
||||
BallAPI.getInstance().getLogger().info("已订阅 redis 频道(正则) " + pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPUnsubscribe(String pattern, int subscribedChannels) {
|
||||
public void punsubscribed(String pattern, long count) {
|
||||
BallAPI.getInstance().getLogger().info("已取消订阅 redis 频道(正则) " + pattern);
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +0,0 @@
|
||||
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;
|
||||
|
||||
public class LockUpdateThread implements Runnable {
|
||||
public static final LockUpdateThread INSTANCE = new LockUpdateThread();
|
||||
|
||||
private LockUpdateThread() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String key = "HamsterBall:ServerInfo:" + BallAPI.getInstance().getLocalServerInfo().getId();
|
||||
try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
|
||||
jedis.expire(key, 180);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
@file:Suppress("VulnerableLibrariesLocal")
|
||||
|
||||
evaluationDependsOn(":ball-common")
|
||||
|
||||
dependencies {
|
||||
api(project(":ball-common")) { isTransitive = false }
|
||||
compileOnly("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT")
|
||||
annotationProcessor("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT")
|
||||
compileOnly("cn.hamster3.mc.plugin:core-common:+")
|
||||
}
|
||||
|
||||
sourceSets.create("templates") {
|
||||
java {
|
||||
srcDir("src/main/templates")
|
||||
}
|
||||
}
|
||||
|
||||
val templateSource = file("src/main/templates")
|
||||
val templateDest = layout.buildDirectory.dir("generated/sources/templates")
|
||||
val generateTemplates = tasks.register<Copy>("generateTemplates") {
|
||||
from(templateSource)
|
||||
into(templateDest)
|
||||
expand(project.properties)
|
||||
}
|
||||
|
||||
sourceSets.main.get().java.srcDir(generateTemplates.map { it.outputs })
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<Jar> {
|
||||
archiveBaseName = "HamsterBall-Velocity"
|
||||
}
|
||||
shadowJar {
|
||||
destinationDirectory = rootProject.layout.buildDirectory
|
||||
}
|
||||
}
|
@@ -1,149 +0,0 @@
|
||||
package cn.hamster3.mc.plugin.ball.velocity;
|
||||
|
||||
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.server.ServerOnlineEvent;
|
||||
import cn.hamster3.mc.plugin.ball.velocity.api.CoreVelocityAPI;
|
||||
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;
|
||||
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.event.PostOrder;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||
import com.velocitypowered.api.plugin.Dependency;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
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",
|
||||
name = "HamsterBall",
|
||||
version = BuildConstants.VERSION,
|
||||
description = BuildConstants.DESCRIPTION,
|
||||
authors = {"MiniDay"},
|
||||
dependencies = @Dependency(id = "hamster-core")
|
||||
)
|
||||
public class HamsterBallPlugin {
|
||||
@Getter
|
||||
private static HamsterBallPlugin instance;
|
||||
@Getter
|
||||
private final java.util.logging.Logger logger;
|
||||
@Getter
|
||||
private final Logger slf4jLogger;
|
||||
@Getter
|
||||
private final ProxyServer proxyServer;
|
||||
@Getter
|
||||
private final File dataFolder;
|
||||
@Getter
|
||||
private YamlConfig config;
|
||||
|
||||
@Inject
|
||||
public HamsterBallPlugin(Logger slf4jLogger, ProxyServer proxyServer, @DataDirectory Path dataPath) {
|
||||
long start = System.currentTimeMillis();
|
||||
logger = java.util.logging.Logger.getLogger("hamster-ball");
|
||||
this.slf4jLogger = slf4jLogger;
|
||||
this.proxyServer = proxyServer;
|
||||
dataFolder = dataPath.toFile();
|
||||
logger.info("仓鼠球正在初始化");
|
||||
instance = this;
|
||||
try {
|
||||
File dataFolder = getDataFolder();
|
||||
if (dataFolder.mkdir()) {
|
||||
logger.info("已生成插件存档文件夹");
|
||||
}
|
||||
File configFile = new File(dataFolder, "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
Files.copy(
|
||||
Objects.requireNonNull(getClass().getResourceAsStream("/config.yml")),
|
||||
configFile.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
);
|
||||
}
|
||||
config = YamlConfig.load(configFile);
|
||||
CoreVelocityAPI.init(config);
|
||||
logger.info("已初始化 BallAPI");
|
||||
} catch (Exception e) {
|
||||
slf4jLogger.error("BallAPI 初始化失败", e);
|
||||
proxyServer.shutdown(Component.text("由于 HamsterBall 初始化失败, 服务器将立即关闭"));
|
||||
}
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球初始化完成,总计耗时 " + time + " ms");
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.EARLY)
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||
long start = System.currentTimeMillis();
|
||||
java.util.logging.Logger logger = getLogger();
|
||||
logger.info("仓鼠球正在启动");
|
||||
try {
|
||||
CoreVelocityAPI.getInstance().enable();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "仓鼠球启动失败", e);
|
||||
logger.info("由于仓鼠球启动失败,服务器将立即关闭");
|
||||
proxyServer.shutdown(Component.text("仓鼠球启动失败"));
|
||||
return;
|
||||
}
|
||||
BallAPI.getInstance().getEventBus().register(BallVelocityListener.INSTANCE);
|
||||
logger.info("已注册监听器 BallVelocityListener");
|
||||
proxyServer.getEventManager().register(this, BallVelocityMainListener.INSTANCE);
|
||||
logger.info("已注册监听器 BallVelocityMainListener");
|
||||
proxyServer.getEventManager().register(this, UpdatePlayerInfoListener.INSTANCE);
|
||||
logger.info("已注册监听器 UpdatePlayerInfoListener");
|
||||
if (config.getBoolean("auto-register-game-server", false)) {
|
||||
BallAPI.getInstance().getEventBus().register(VelocityServerListener.INSTANCE);
|
||||
logger.info("已注册监听器 VelocityServerListener");
|
||||
VelocityServerListener.INSTANCE.onEnable();
|
||||
}
|
||||
|
||||
if (BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) {
|
||||
BallAPI.getInstance().subscribePatterns("*" + BallAPI.PLAYER_INFO_CHANNEL);
|
||||
} else {
|
||||
BallAPI.getInstance().subscribeRaw(BallAPI.PLAYER_INFO_CHANNEL);
|
||||
}
|
||||
BallAPI.getInstance().sendRawBallMessage(
|
||||
BallAPI.BALL_CHANNEL, BallActions.ServerOnline.name(),
|
||||
new ServerOnlineEvent(BallAPI.getInstance().getLocalServerInfo())
|
||||
);
|
||||
// 移除失效的在线玩家
|
||||
BallAPI.getInstance().getAllPlayerInfo().values()
|
||||
.stream()
|
||||
.filter(BallPlayerInfo::isOnline)
|
||||
.filter(o -> BallAPI.getInstance().isLocalServer(o.getProxyServer()))
|
||||
.forEach(playerInfo -> {
|
||||
playerInfo.setOnline(false);
|
||||
BallVelocityUtils.uploadPlayerInfo(playerInfo);
|
||||
});
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球启动完成,总计耗时 " + time + " ms");
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LATE)
|
||||
public void onProxyShutdown(ProxyShutdownEvent event) {
|
||||
long start = System.currentTimeMillis();
|
||||
java.util.logging.Logger logger = getLogger();
|
||||
logger.info("仓鼠球正在关闭");
|
||||
try {
|
||||
CoreVelocityAPI.getInstance().disable();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "关闭仓鼠球时遇到了一个异常", e);
|
||||
}
|
||||
long time = System.currentTimeMillis() - start;
|
||||
logger.info("仓鼠球已关闭,总计耗时 " + time + " ms");
|
||||
}
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
package cn.hamster3.mc.plugin.ball.velocity.api;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
|
||||
import cn.hamster3.mc.plugin.ball.velocity.HamsterBallPlugin;
|
||||
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
|
||||
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
|
||||
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class CoreVelocityAPI extends BallAPI {
|
||||
public CoreVelocityAPI(@NotNull ConfigSection config) {
|
||||
super(config, BallServerType.PROXY);
|
||||
}
|
||||
|
||||
public static CoreVelocityAPI getInstance() {
|
||||
return (CoreVelocityAPI) instance;
|
||||
}
|
||||
|
||||
public static void init(@NotNull YamlConfig config) {
|
||||
if (instance != null) {
|
||||
return;
|
||||
}
|
||||
instance = new CoreVelocityAPI(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() throws SQLException, InterruptedException {
|
||||
ProxyConfig config = HamsterBallPlugin.getInstance().getProxyServer().getConfiguration();
|
||||
try {
|
||||
Field field = config.getClass().getDeclaredField("bind");
|
||||
field.setAccessible(true);
|
||||
String bind = (String) field.get(config);
|
||||
int i = bind.lastIndexOf(":");
|
||||
String substring = bind.substring(i + 1);
|
||||
instance.getLocalServerInfo().setHost(bind.substring(0, i));
|
||||
instance.getLocalServerInfo().setPort(Integer.parseInt(substring));
|
||||
} catch (Exception e) {
|
||||
HamsterBallPlugin.getInstance().getSlf4jLogger().error("获取 Velocity 监听端口时遇到了一个异常", e);
|
||||
}
|
||||
super.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() throws SQLException, InterruptedException {
|
||||
super.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Logger getLogger() {
|
||||
return HamsterBallPlugin.getInstance().getLogger();
|
||||
}
|
||||
}
|
@@ -1,121 +0,0 @@
|
||||
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;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BallVelocityListener {
|
||||
public static final BallVelocityListener INSTANCE = new BallVelocityListener();
|
||||
|
||||
private 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;
|
||||
}
|
||||
ProxyServer server = HamsterBallPlugin.getInstance().getProxyServer();
|
||||
server.getCommandManager().executeAsync(server.getConsoleCommandSource(), event.getCommand());
|
||||
}
|
||||
|
||||
@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);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
server.getCommandManager().executeAsync(player, event.getCommand());
|
||||
return;
|
||||
}
|
||||
for (Player player : server.getAllPlayers()) {
|
||||
server.getCommandManager().executeAsync(player, event.getCommand());
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onKickPlayer(KickPlayerEvent event) {
|
||||
ProxyServer server = HamsterBallPlugin.getInstance().getProxyServer();
|
||||
Player player = server.getPlayer(event.getUuid()).orElse(null);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
player.disconnect(event.getReason());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onSendMessageToPlayer(SendMessageToPlayerEvent event) {
|
||||
for (UUID receiver : event.getReceivers()) {
|
||||
Audience audience = CoreAPI.getInstance().getAudienceProvider().player(receiver);
|
||||
event.getMessage().show(audience);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onSendPlayerToLocation(SendPlayerToLocationEvent event) {
|
||||
ProxyServer proxyServer = HamsterBallPlugin.getInstance().getProxyServer();
|
||||
String serverID = event.getLocation().getServerID();
|
||||
RegisteredServer toServer = proxyServer.getServer(serverID).orElse(null);
|
||||
if (toServer == null) {
|
||||
HamsterBallPlugin.getInstance().getLogger().warning("试图传送玩家时失败: 服务器 " + serverID + " 不在线");
|
||||
return;
|
||||
}
|
||||
for (UUID uuid : event.getSendPlayerUUID()) {
|
||||
Player player = proxyServer.getPlayer(uuid).orElse(null);
|
||||
if (player == null) {
|
||||
continue;
|
||||
}
|
||||
RegisteredServer currentServer = player.getCurrentServer().map(ServerConnection::getServer).orElse(null);
|
||||
if (currentServer != null && currentServer.getServerInfo().getName().equals(serverID)) {
|
||||
continue;
|
||||
}
|
||||
player.createConnectionRequest(toServer).fireAndForget();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onSendPlayerToPlayer(SendPlayerToPlayerEvent event) {
|
||||
ProxyServer proxyServer = HamsterBallPlugin.getInstance().getProxyServer();
|
||||
UUID toPlayerUUID = event.getToPlayerUUID();
|
||||
Player toPlayer = proxyServer.getPlayer(toPlayerUUID).orElse(null);
|
||||
if (toPlayer == null) {
|
||||
HamsterBallPlugin.getInstance().getLogger().warning("试图传送玩家时失败: 目标玩家 " + toPlayerUUID + " 不在线");
|
||||
return;
|
||||
}
|
||||
RegisteredServer toServer = toPlayer.getCurrentServer().map(ServerConnection::getServer).orElse(null);
|
||||
if (toServer == null) {
|
||||
HamsterBallPlugin.getInstance().getLogger().warning("试图传送玩家时失败: 目标玩家 " + toPlayerUUID + " 不在任何服务器中");
|
||||
return;
|
||||
}
|
||||
for (UUID uuid : event.getSendPlayerUUID()) {
|
||||
Player sendPlayer = proxyServer.getPlayer(uuid).orElse(null);
|
||||
if (sendPlayer == null) {
|
||||
continue;
|
||||
}
|
||||
ServerInfo currentServer = sendPlayer.getCurrentServer().map(ServerConnection::getServerInfo).orElse(null);
|
||||
if (currentServer != null && currentServer.getName().equals(toServer.getServerInfo().getName())) {
|
||||
continue;
|
||||
}
|
||||
sendPlayer.createConnectionRequest(toServer).fireAndForget();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
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.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 cn.hamster3.mc.plugin.ball.velocity.util.BallVelocityUtils;
|
||||
import com.velocitypowered.api.event.PostOrder;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.LoginEvent;
|
||||
import com.velocitypowered.api.event.connection.PostLoginEvent;
|
||||
import com.velocitypowered.api.event.connection.PreLoginEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
|
||||
public final class BallVelocityMainListener {
|
||||
public static final BallVelocityMainListener INSTANCE = new BallVelocityMainListener();
|
||||
|
||||
private BallVelocityMainListener() {
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LATE)
|
||||
public void onPreLogin(PreLoginEvent event) {
|
||||
BallAPI.getInstance().sendRawBallMessage(
|
||||
BallAPI.BALL_CHANNEL,
|
||||
BallActions.BallPlayerPreLogin.name(),
|
||||
new BallPlayerPreLoginEvent(event.getUsername())
|
||||
);
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LATE)
|
||||
public void onLogin(LoginEvent event) {
|
||||
if (!event.getResult().isAllowed()) {
|
||||
return;
|
||||
}
|
||||
Player player = event.getPlayer();
|
||||
BallAPI.getInstance().sendRawBallMessage(
|
||||
BallAPI.BALL_CHANNEL,
|
||||
BallActions.BallPlayerLogin.name(),
|
||||
new BallPlayerLoginEvent(new BallPlayerInfo(
|
||||
player.getUniqueId(), player.getUsername(), "connecting",
|
||||
BallAPI.getInstance().getLocalServerId(), true
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LATE)
|
||||
public void onPostLogin(PostLoginEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
BallPlayerInfo playerInfo = BallVelocityUtils.getPlayerInfo(player, true);
|
||||
BallAPI.getInstance().sendRawBallMessage(
|
||||
BallAPI.BALL_CHANNEL,
|
||||
BallActions.BallPlayerPostLogin.name(),
|
||||
new BallPlayerPostLoginEvent(playerInfo)
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,67 +0,0 @@
|
||||
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.BallPlayerInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.BallActions;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerLogoutEvent;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerPostConnectServerEvent;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerPreConnectServerEvent;
|
||||
import cn.hamster3.mc.plugin.ball.velocity.util.BallVelocityUtils;
|
||||
import com.velocitypowered.api.event.PostOrder;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
|
||||
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
|
||||
public class UpdatePlayerInfoListener {
|
||||
public static final UpdatePlayerInfoListener INSTANCE = new UpdatePlayerInfoListener();
|
||||
|
||||
private UpdatePlayerInfoListener() {
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LATE)
|
||||
public void onServerConnect(ServerPreConnectEvent event) {
|
||||
if (event.getResult().isAllowed()) {
|
||||
return;
|
||||
}
|
||||
String name = event.getResult().getServer()
|
||||
.map(o -> o.getServerInfo().getName())
|
||||
.orElse(event.getOriginalServer().getServerInfo().getName());
|
||||
Player player = event.getPlayer();
|
||||
BallPlayerInfo playerInfo = BallVelocityUtils.getPlayerInfo(player, true);
|
||||
playerInfo.setGameServer(name);
|
||||
BallAPI.getInstance().sendRawBallMessage(
|
||||
BallAPI.BALL_CHANNEL,
|
||||
BallActions.BallPlayerPreConnectServer.name(),
|
||||
new BallPlayerPreConnectServerEvent(playerInfo, playerInfo.getGameServer(), name)
|
||||
);
|
||||
BallVelocityUtils.uploadPlayerInfo(playerInfo);
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@Subscribe(order = PostOrder.LATE)
|
||||
public void onServerPostConnect(ServerPostConnectEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
BallPlayerInfo playerInfo = BallVelocityUtils.getPlayerInfo(player, true);
|
||||
playerInfo.setGameServer(player.getCurrentServer().map(o -> o.getServerInfo().getName()).orElse(""));
|
||||
BallAPI.getInstance().sendRawBallMessage(
|
||||
BallAPI.BALL_CHANNEL,
|
||||
BallActions.BallPlayerPostConnectServer.name(),
|
||||
new BallPlayerPostConnectServerEvent(playerInfo)
|
||||
);
|
||||
BallVelocityUtils.uploadPlayerInfo(playerInfo);
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LATE)
|
||||
public void onDisconnect(DisconnectEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
BallPlayerInfo playerInfo = BallVelocityUtils.getPlayerInfo(player, false);
|
||||
BallAPI.getInstance().sendRawBallMessage(
|
||||
BallAPI.BALL_CHANNEL,
|
||||
BallActions.BallPlayerLogout.name(),
|
||||
new BallPlayerLogoutEvent(playerInfo)
|
||||
);
|
||||
BallVelocityUtils.uploadPlayerInfo(playerInfo);
|
||||
}
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
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.BallServerInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
|
||||
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.velocity.HamsterBallPlugin;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class VelocityServerListener {
|
||||
public static final VelocityServerListener INSTANCE = new VelocityServerListener();
|
||||
|
||||
private VelocityServerListener() {
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
for (BallServerInfo info : BallAPI.getInstance().getAllServerInfo().values()) {
|
||||
if (info.getType() != BallServerType.GAME) {
|
||||
continue;
|
||||
}
|
||||
ProxyServer server = HamsterBallPlugin.getInstance().getProxyServer();
|
||||
server.getServer(info.getId())
|
||||
.map(RegisteredServer::getServerInfo)
|
||||
.ifPresent(server::unregisterServer);
|
||||
ServerInfo serverInfo = new ServerInfo(info.getId(), new InetSocketAddress(info.getHost(), info.getPort()));
|
||||
server.registerServer(serverInfo);
|
||||
BallAPI.getInstance().getLogger().info("已添加服务器入口: " + info.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onServerOnline(ServerOnlineEvent event) {
|
||||
if (event.getType() != BallServerType.GAME) {
|
||||
return;
|
||||
}
|
||||
ProxyServer server = HamsterBallPlugin.getInstance().getProxyServer();
|
||||
server.getServer(event.getId())
|
||||
.map(RegisteredServer::getServerInfo)
|
||||
.ifPresent(server::unregisterServer);
|
||||
ServerInfo serverInfo = new ServerInfo(event.getId(), new InetSocketAddress(event.getHost(), event.getPort()));
|
||||
server.registerServer(serverInfo);
|
||||
BallAPI.getInstance().getLogger().info("已添加服务器入口: " + event.getId());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onServerOffline(ServerOfflineEvent event) {
|
||||
if (event.getType() != BallServerType.GAME) {
|
||||
return;
|
||||
}
|
||||
ProxyServer server = HamsterBallPlugin.getInstance().getProxyServer();
|
||||
ServerInfo serverInfo = server.getServer(event.getId())
|
||||
.map(RegisteredServer::getServerInfo)
|
||||
.orElse(null);
|
||||
if (serverInfo != null) {
|
||||
server.unregisterServer(serverInfo);
|
||||
BallAPI.getInstance().getLogger().info("已移除服务器入口: " + event.getId());
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
package cn.hamster3.mc.plugin.ball.velocity.util;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallPlayerInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.BallActions;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.player.BallPlayerInfoUpdateEvent;
|
||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public final class BallVelocityUtils {
|
||||
private BallVelocityUtils() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static BallPlayerInfo getPlayerInfo(@NotNull Player player, boolean online) {
|
||||
ServerConnection server = player.getCurrentServer().orElse(null);
|
||||
return new BallPlayerInfo(
|
||||
player.getUniqueId(),
|
||||
player.getUsername(),
|
||||
server == null ? "connecting" : server.getServerInfo().getName(),
|
||||
BallAPI.getInstance().getLocalServerId(),
|
||||
online
|
||||
);
|
||||
}
|
||||
|
||||
public static void uploadPlayerInfo(@NotNull BallPlayerInfo playerInfo) {
|
||||
CoreAPI.getInstance().getExecutorService().execute(() -> {
|
||||
try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"REPLACE INTO `hamster_ball_player_info` VALUES(?, ?, ?, ?, ?);"
|
||||
)) {
|
||||
statement.setString(1, playerInfo.getUuid().toString());
|
||||
statement.setString(2, playerInfo.getName());
|
||||
statement.setString(3, playerInfo.getGameServer());
|
||||
statement.setString(4, playerInfo.getProxyServer());
|
||||
statement.setBoolean(5, playerInfo.isOnline());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
BallAPI.getInstance().getLogger().log(Level.SEVERE, "更新玩家数据时遇到了一个异常", e);
|
||||
}
|
||||
if (!BallAPI.getInstance().getBallConfig().isGameServerUpdatePlayerInfo()) {
|
||||
BallAPI.getInstance().sendRawBallMessage(
|
||||
BallAPI.PLAYER_INFO_CHANNEL,
|
||||
BallActions.BallPlayerInfoUpdate.name(),
|
||||
new BallPlayerInfoUpdateEvent(playerInfo)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,67 +0,0 @@
|
||||
# 是否允许在控制台输出调试信息
|
||||
debug: false
|
||||
|
||||
# 频道名前缀
|
||||
# 使用这个配置选项可以划分子服消息通信分组
|
||||
# 只有在同一个频道名的子服才能互相通信
|
||||
channel-prefix: ""
|
||||
|
||||
# 是否在子服端更新玩家信息
|
||||
# 默认情况下,BC 统一管理玩家信息,包括记录 UUID 和玩家名称
|
||||
# 如果一个群组服同时拥有多个 BC 入口,且每个 BC 入口为不同的玩家名称分配不同的 UUID
|
||||
# (例如正版、盗版双入口,或网易多入口接同一个子服)
|
||||
# 则可以启用该功能以防止同一个名称占用多个 UUID 的问题
|
||||
game-server-update-player-info: false
|
||||
|
||||
# 启用后,子服启动时会自动注册该子服的入口配置,关闭时也会自动移除该子服的入口配置
|
||||
auto-register-game-server: false
|
||||
|
||||
# 本服务器信息
|
||||
server-info:
|
||||
# 服务器唯一识别码,最长 32 字符
|
||||
id: "Velocity"
|
||||
# 服务端名称,常用于展示给玩家看
|
||||
name: "代理端"
|
||||
# 当前子服的地址
|
||||
# 不填则自动设置为 0.0.0.0
|
||||
host: 0.0.0.0
|
||||
# 当前子服端口
|
||||
# 不填则自动设置为 25577
|
||||
port: 25577
|
||||
|
||||
# 数据库连接池配置
|
||||
# 如果注释该选项则默认使用 HamsterCore 中的连接池配置
|
||||
# 否则 HamsterBall 将会使用与 HamsterCore 不同的数据库链接
|
||||
# 如果你需要让每个服务器单独存储仓鼠球信息
|
||||
# 这个选项就会很有用
|
||||
#datasource:
|
||||
# # 数据库链接驱动地址
|
||||
# driver: "com.mysql.cj.jdbc.Driver"
|
||||
# # MySQL数据库链接填写格式:
|
||||
# # jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数
|
||||
# url: "jdbc:mysql://localhost:3306/Test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true"
|
||||
# # 用户名
|
||||
# username: "root"
|
||||
# # 密码
|
||||
# password: "Root123.."
|
||||
# # 最小闲置链接数
|
||||
# # 推荐值:1~3
|
||||
# minimum-idle: 0
|
||||
# # 最大链接数
|
||||
# # 推荐值:不低于5
|
||||
# maximum-pool-size: 5
|
||||
# # 保持连接池可用的间隔
|
||||
# # 除非你的服务器数据库连接经常断开,否则不建议启用该选项
|
||||
# # 单位:毫秒
|
||||
# # 默认值为0(禁用)
|
||||
# keep-alive-time: 0
|
||||
# # 连接闲置回收时间
|
||||
# # 单位:毫秒
|
||||
# # 推荐值:600000(10分钟)
|
||||
# idle-timeout: 600000
|
||||
# # 链接最长存活时间
|
||||
# # 单位:毫秒
|
||||
# max-lifetime: 1800000
|
||||
# # 验证连接存活的超时时间
|
||||
# # 单位:毫秒
|
||||
# validation-timeout: 5000
|
@@ -1,5 +0,0 @@
|
||||
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,8 +0,0 @@
|
||||
package cn.hamster3.mc.plugin.ball.velocity;
|
||||
|
||||
// The constants are replaced before compilation
|
||||
@SuppressWarnings("unused")
|
||||
public class BuildConstants {
|
||||
public static final String VERSION = "${version}";
|
||||
public static final String DESCRIPTION = "${description}";
|
||||
}
|
@@ -1,12 +1,11 @@
|
||||
plugins {
|
||||
id("java-library")
|
||||
id("java")
|
||||
id("maven-publish")
|
||||
id("com.github.johnrengelman.shadow") version "8+"
|
||||
}
|
||||
|
||||
group = "cn.hamster3.mc.plugin"
|
||||
version = "1.6.3"
|
||||
description = "基于 Redis 的 Minecraft 服务端通用消息中间件"
|
||||
version = "1.5.7"
|
||||
|
||||
subprojects {
|
||||
apply {
|
||||
@@ -17,7 +16,6 @@ subprojects {
|
||||
|
||||
group = rootProject.group
|
||||
version = rootProject.version
|
||||
description = rootProject.description
|
||||
|
||||
repositories {
|
||||
maven("https://maven.airgame.net/maven-public/")
|
||||
@@ -45,6 +43,12 @@ subprojects {
|
||||
from(rootProject.file("LICENSE"))
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
jar {
|
||||
archiveClassifier = "dev"
|
||||
}
|
||||
shadowJar {
|
||||
archiveClassifier = "FIX"
|
||||
}
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
}
|
||||
@@ -58,7 +62,7 @@ subprojects {
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url = uri("https://maven.airgame.net/public")
|
||||
url = uri("https://maven.airgame.net/maven-airgame")
|
||||
|
||||
credentials {
|
||||
username = rootProject.properties.getOrDefault("maven_username", "").toString()
|
||||
|
@@ -9,4 +9,3 @@ rootProject.name = "hamster-ball"
|
||||
include("ball-common")
|
||||
include("ball-bukkit")
|
||||
include("ball-bungee")
|
||||
include("ball-velocity")
|
||||
|
Reference in New Issue
Block a user