feat: 在 Redis 锁定服务器 ID

This commit is contained in:
2024-03-17 10:15:47 +08:00
parent 48d4e31dca
commit e48274a08c
6 changed files with 89 additions and 62 deletions

View File

@@ -27,12 +27,10 @@
为了适配 docker 环境,本插件除了从 `config.yml` 中配置服务器信息以外,还支持从环境变量中读取 为了适配 docker 环境,本插件除了从 `config.yml` 中配置服务器信息以外,还支持从环境变量中读取
| 环境变量 | 描述 | 对应 config 值 | | 环境变量 | 描述 | 对应 config 值 |
|:----------------------------|:-------------------|:-----------------| |:-----------------|:-------------------|:-----------------|
| BALL_LOCAL_SERVER_IP | 本服务器 IP | server-info.host | | BALL_SERVER_ID | 本服务器唯一识别码,最长 32 字符 | server-info.id |
| BALL_LOCAL_SERVER_PORT | 本服务器端口 | server-info.port | | BALL_SERVER_NAME | 本服务端名称,用于展示给玩家看 | server-info.name |
| BALL_LOCAL_SERVER_INFO_ID | 本服务器唯一识别码,最长 32 字符 | server-info.id |
| BALL_LOCAL_SERVER_INFO_NAME | 本服务端名称,用于展示给玩家看 | server-info.name |
# 开发 # 开发
@@ -51,9 +49,9 @@ repositories {
dependencies { dependencies {
// 对于 Bukkit 插件 // 对于 Bukkit 插件
compileOnly("cn.hamster3.mc.plugin:ball-bukkit:1.5.7") compileOnly("cn.hamster3.mc.plugin:ball-bukkit:1.6.0")
// 对于 BungeeCord 插件 // 对于 BungeeCord 插件
compileOnly("cn.hamster3.mc.plugin:ball-bungee:1.5.7") compileOnly("cn.hamster3.mc.plugin:ball-bungee:1.6.0")
} }
``` ```
@@ -79,13 +77,13 @@ dependencies {
<dependency> <dependency>
<groupId>cn.hamster3.mc.plugin</groupId> <groupId>cn.hamster3.mc.plugin</groupId>
<artifactId>ball-bukkit</artifactId> <artifactId>ball-bukkit</artifactId>
<version>1.5.7</version> <version>1.6.0</version>
</dependency> </dependency>
<!--对于 BungeeCord 插件--> <!--对于 BungeeCord 插件-->
<dependency> <dependency>
<groupId>cn.hamster3.mc.plugin</groupId> <groupId>cn.hamster3.mc.plugin</groupId>
<artifactId>ball-bungee</artifactId> <artifactId>ball-bungee</artifactId>
<version>1.5.7</version> <version>1.6.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -5,6 +5,7 @@ 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.entity.BallServerType;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection; 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.config.YamlConfig;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
@@ -31,6 +32,8 @@ public class BallBukkitAPI extends BallAPI {
@Override @Override
public void enable() throws SQLException, InterruptedException { 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(); super.enable();
} }

View File

@@ -5,10 +5,13 @@ 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.entity.BallServerType;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection; 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.config.YamlConfig;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ListenerInfo;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -31,6 +34,14 @@ public class BallBungeeCordAPI extends BallAPI {
@Override @Override
public void enable() throws SQLException, InterruptedException { 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(); super.enable();
} }

View File

@@ -13,6 +13,7 @@ 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.BallCommonListener;
import cn.hamster3.mc.plugin.ball.common.listener.BallDebugListener; 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.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.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection; 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.data.DisplayMessage;
@@ -29,6 +30,8 @@ import javax.sql.DataSource;
import java.sql.*; import java.sql.*;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger; import java.util.logging.Logger;
@Getter @Getter
@@ -66,8 +69,8 @@ public abstract class BallAPI {
@NotNull @NotNull
private final Jedis redisSub; private final Jedis redisSub;
@NotNull @Nullable
private final Jedis redisPub; private ScheduledFuture<?> lockUpdater;
public BallAPI(@NotNull ConfigSection config, BallServerType type) { public BallAPI(@NotNull ConfigSection config, BallServerType type) {
ConfigSection serverInfoConfig = config.getSection("server-info"); ConfigSection serverInfoConfig = config.getSection("server-info");
@@ -89,7 +92,6 @@ public abstract class BallAPI {
allServerInfo = new ConcurrentHashMap<>(); allServerInfo = new ConcurrentHashMap<>();
allPlayerInfo = new ConcurrentHashMap<>(); allPlayerInfo = new ConcurrentHashMap<>();
redisSub = CoreAPI.getInstance().getJedisPool().getResource(); redisSub = CoreAPI.getInstance().getJedisPool().getResource();
redisPub = CoreAPI.getInstance().getJedisPool().getResource();
getLogger().info("频道前缀: " + ballConfig.getChannelPrefix()); getLogger().info("频道前缀: " + ballConfig.getChannelPrefix());
getLogger().info("启用子服更新玩家状态: " + ballConfig.isGameServerUpdatePlayerInfo()); getLogger().info("启用子服更新玩家状态: " + ballConfig.isGameServerUpdatePlayerInfo());
if (ballConfig.isGameServerUpdatePlayerInfo()) { if (ballConfig.isGameServerUpdatePlayerInfo()) {
@@ -102,6 +104,29 @@ public abstract class BallAPI {
} }
protected void enable() throws SQLException, InterruptedException { 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);
}
}
try (Connection connection = getDatasource().getConnection()) { try (Connection connection = getDatasource().getConnection()) {
try (Statement statement = connection.createStatement()) { try (Statement statement = connection.createStatement()) {
statement.execute("CREATE TABLE IF NOT EXISTS `hamster_ball_player_info`(" + statement.execute("CREATE TABLE IF NOT EXISTS `hamster_ball_player_info`(" +
@@ -111,44 +136,11 @@ public abstract class BallAPI {
"`proxy_server` VARCHAR(32) NOT NULL," + "`proxy_server` VARCHAR(32) NOT NULL," +
"`online` BOOLEAN NOT NULL" + "`online` BOOLEAN NOT NULL" +
") CHARSET utf8mb4;"); ") 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`(" + statement.execute("CREATE TABLE IF NOT EXISTS `hamster_ball_cached_message`(" +
"`uuid` CHAR(36) NOT NULL," + "`uuid` CHAR(36) NOT NULL," +
"`message` TEXT NOT NULL" + "`message` TEXT NOT NULL" +
") CHARSET utf8mb4;"); ") CHARSET utf8mb4;");
} }
try (PreparedStatement statement = connection.prepareStatement(
"REPLACE INTO `hamster_ball_server_info` VALUES(?, ?, ?, ?, ?);"
)) {
statement.setString(1, localServerInfo.getId());
statement.setString(2, localServerInfo.getName());
statement.setString(3, localServerInfo.getType().name());
statement.setString(4, localServerInfo.getHost());
statement.setInt(5, localServerInfo.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()) { if (getBallConfig().isGameServerUpdatePlayerInfo()) {
try (Statement statement = connection.createStatement()) { try (Statement statement = connection.createStatement()) {
try (ResultSet set = statement.executeQuery(String.format( try (ResultSet set = statement.executeQuery(String.format(
@@ -193,14 +185,15 @@ public abstract class BallAPI {
sendBallMessage(BallAPI.BALL_CHANNEL, new BallMessage( sendBallMessage(BallAPI.BALL_CHANNEL, new BallMessage(
BallActions.ServerOffline.name(), new ServerOfflineEvent(getLocalServerInfo()) BallActions.ServerOffline.name(), new ServerOfflineEvent(getLocalServerInfo())
), false, true); ), false, true);
if (lockUpdater != null) {
try (Connection connection = getDatasource().getConnection()) { lockUpdater.cancel(true);
try (PreparedStatement statement = connection.prepareStatement( lockUpdater = null;
"DELETE FROM `hamster_ball_server_info` WHERE `id`=?;" try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
)) { String key = "HamsterBall:ServerInfo:" + localServerInfo.getId();
statement.setString(1, getLocalServerId()); jedis.del(key);
statement.executeUpdate();
} }
}
try (Connection connection = getDatasource().getConnection()) {
try (PreparedStatement statement = connection.prepareStatement( try (PreparedStatement statement = connection.prepareStatement(
"UPDATE `hamster_ball_player_info` SET `online`=false WHERE `game_server`=? OR `proxy_server`=?" "UPDATE `hamster_ball_player_info` SET `online`=false WHERE `game_server`=? OR `proxy_server`=?"
)) { )) {
@@ -210,7 +203,6 @@ public abstract class BallAPI {
} }
} }
redisSub.close(); redisSub.close();
redisPub.close();
} }
/** /**
@@ -493,12 +485,17 @@ public abstract class BallAPI {
channel = ballConfig.getChannelPrefix() + channel; channel = ballConfig.getChannelPrefix() + channel;
} }
if (block) { if (block) {
redisPub.publish(channel, CoreAPI.getInstance().getGson().toJson(message)); try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
jedis.publish(channel, CoreAPI.getInstance().getGson().toJson(message));
}
eventBus.post(new MessageSentEvent(channel, message)); eventBus.post(new MessageSentEvent(channel, message));
} else { } else {
@NotNull String finalChannel = channel; @NotNull String finalChannel = channel;
CoreAPI.getInstance().getExecutorService().submit(() -> { CoreAPI.getInstance().getExecutorService().submit(() -> {
redisPub.publish(finalChannel, CoreAPI.getInstance().getGson().toJson(message)); try (Jedis jedis = CoreAPI.getInstance().getJedisPool().getResource()) {
jedis.publish(finalChannel, CoreAPI.getInstance().getGson().toJson(message));
}
eventBus.post(new MessageSentEvent(finalChannel, message));
}); });
} }
} }

View File

@@ -45,13 +45,11 @@ public class BallServerInfo {
public BallServerInfo(@NotNull ConfigSection config, BallServerType type) { public BallServerInfo(@NotNull ConfigSection config, BallServerType type) {
Map<String, String> env = System.getenv(); Map<String, String> env = System.getenv();
id = env.getOrDefault("BALL_LOCAL_SERVER_INFO_ID", config.getString("id")); id = env.getOrDefault("BALL_SERVER_ID", config.getString("id"));
name = env.getOrDefault("BALL_LOCAL_SERVER_INFO_NAME", config.getString("name")); name = env.getOrDefault("BALL_SERVER_NAME", config.getString("name"));
this.type = type; this.type = type;
host = env.getOrDefault("BALL_LOCAL_SERVER_IP", config.getString("host")); host = "0.0.0.0";
port = Integer.parseInt( port = 0;
env.getOrDefault("BALL_LOCAL_SERVER_PORT", String.valueOf(config.getInt("port")))
);
} }
@Override @Override

View File

@@ -0,0 +1,20 @@
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 cn.hamster3.mc.plugin.core.lib.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);
}
}
}